package htmlhouse
import (
"bytes"
"database/sql"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"os"
"regexp"
"strconv"
"strings"
"time"
"github.com/gorilla/mux"
"github.com/writeas/impart"
"github.com/writeas/nerds/store"
"github.com/writeas/web-core/bots"
)
func createHouse(app *app, w http.ResponseWriter, r *http.Request) error {
html := r.FormValue("html")
if strings.TrimSpace(html) == "" {
return impart.HTTPError{http.StatusBadRequest, "Supply something to publish."}
}
public := r.FormValue("public") == "true"
houseID := store.GenerateFriendlyRandomString(8)
_, err := app.db.Exec("INSERT INTO houses (id, html) VALUES (?, ?)", houseID, html)
if err != nil {
return err
}
if err = app.session.writeToken(w, houseID); err != nil {
return err
}
resUser := newSessionInfo(houseID)
if public && passesPublicFilter(app, html) {
go addPublicAccess(app, houseID, html)
}
return impart.WriteSuccess(w, resUser, http.StatusCreated)
}
func validTitle(title string) bool {
return title != "" && strings.TrimSpace(title) != "HTMLhouse"
}
func removePublicAccess(app *app, houseID string) error {
var approved sql.NullInt64
err := app.db.QueryRow("SELECT approved FROM publichouses WHERE house_id = ?", houseID).Scan(&approved)
switch {
case err == sql.ErrNoRows:
return nil
case err != nil:
fmt.Printf("Couldn't fetch for public removal: %v\n", err)
return nil
}
if approved.Valid && approved.Int64 == 0 {
// Page has been banned, so do nothing
} else {
_, err = app.db.Exec("DELETE FROM publichouses WHERE house_id = ?", houseID)
if err != nil {
return err
}
}
return nil
}
func addPublicAccess(app *app, houseID, html string) error {
// Parse title of page
title := titleReg.FindStringSubmatch(html)[1]
if !validTitle(title) {
//
was invalid, so look for an
header := headerReg.FindStringSubmatch(html)[1]
if validTitle(header) {
// was valid, so use that instead of
title = header
}
}
title = strings.TrimSpace(title)
// Get thumbnail
data := url.Values{}
data.Set("url", fmt.Sprintf("%s/%s.html", app.cfg.HostName, houseID))
u, err := url.ParseRequestURI(app.cfg.PreviewsHost)
if err != nil {
fmt.Fprintf(os.Stderr, "Error parsing request URI: %v\n", err)
return err
}
u.Path = "/"
urlStr := fmt.Sprintf("%v", u)
client := &http.Client{}
r, err := http.NewRequest("POST", urlStr, bytes.NewBufferString(data.Encode()))
if err != nil {
fmt.Printf("Error creating request: %v", err)
}
r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
r.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))
var thumbURL string
resp, err := client.Do(r)
if err != nil {
fmt.Printf("Error requesting thumbnail: %v", err)
return impart.HTTPError{http.StatusInternalServerError, "Couldn't generate thumbnail"}
} else {
defer resp.Body.Close()
body, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode == http.StatusOK {
thumbURL = string(body)
}
}
// Add to public houses table
approved := sql.NullInt64{Valid: false}
if app.cfg.AutoApprove {
approved.Int64 = 1
approved.Valid = true
}
_, err = app.db.Exec("INSERT INTO publichouses (house_id, title, thumb_url, added, updated, approved) VALUES (?, ?, ?, NOW(), NOW(), ?) ON DUPLICATE KEY UPDATE title = ?, updated = NOW()", houseID, title, thumbURL, approved, title)
if err != nil {
return err
}
// Tweet about it
tweet(app, houseID, title)
return nil
}
func renovateHouse(app *app, w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)
houseID := vars["house"]
html := r.FormValue("html")
if strings.TrimSpace(html) == "" {
return impart.HTTPError{http.StatusBadRequest, "Supply something to publish."}
}
public := r.FormValue("public") == "true"
authHouseID, err := app.session.readToken(r)
if err != nil {
return err
}
if authHouseID != houseID {
return impart.HTTPError{http.StatusUnauthorized, "Bad token for this ⌂ house ⌂."}
}
_, err = app.db.Exec("UPDATE houses SET html = ? WHERE id = ?", html, houseID)
if err != nil {
return err
}
if err = app.session.writeToken(w, houseID); err != nil {
return err
}
resUser := newSessionInfo(houseID)
if public {
go addPublicAccess(app, houseID, html)
} else {
go removePublicAccess(app, houseID)
}
return impart.WriteSuccess(w, resUser, http.StatusOK)
}
func getHouseStats(app *app, houseID string) (*time.Time, int64, error) {
var created time.Time
var views int64
err := app.db.QueryRow("SELECT created, view_count FROM houses WHERE id = ?", houseID).Scan(&created, &views)
switch {
case err == sql.ErrNoRows:
return nil, 0, impart.HTTPError{http.StatusNotFound, "Return to sender. Address unknown."}
case err != nil:
fmt.Printf("Couldn't fetch: %v\n", err)
return nil, 0, err
}
return &created, views, nil
}
func getHouseHTML(app *app, houseID string) (string, error) {
var html string
err := app.db.QueryRow("SELECT html FROM houses WHERE id = ?", houseID).Scan(&html)
switch {
case err == sql.ErrNoRows:
return "", impart.HTTPError{http.StatusNotFound, "Return to sender. Address unknown."}
case err != nil:
fmt.Printf("Couldn't fetch: %v\n", err)
return "", err
}
return html, nil
}
func getPublicHousesData(app *app, w http.ResponseWriter, r *http.Request) error {
houses, err := getPublicHouses(app, true)
if err != nil {
return err
}
for i := range *houses {
(*houses)[i].process(app)
}
return impart.WriteSuccess(w, houses, http.StatusOK)
}
// regular expressions for extracting data
var (
htmlReg = regexp.MustCompile("")
titleReg = regexp.MustCompile("(.+)")
headerReg = regexp.MustCompile("(.+)
")
)
func getHouse(app *app, w http.ResponseWriter, r *http.Request) error {
vars := mux.Vars(r)
houseID := vars["house"]
// Fetch HTML
html, err := getHouseHTML(app, houseID)
if err != nil {
if err, ok := err.(impart.HTTPError); ok {
if err.Status == http.StatusNotFound {
page, err := ioutil.ReadFile(app.cfg.StaticDir + "/404.html")
if err != nil {
page = []byte("HTMLlot.")
}
w.WriteHeader(http.StatusNotFound)
fmt.Fprintf(w, "%s", page)
return err
}
}
return err
}
// Add nofollow meta tag
if strings.Index(html, "") == -1 {
html = htmlReg.ReplaceAllString(html, "")
}
html = strings.Replace(html, "", "", 1)
// Add links back to HTMLhouse
homeLink := "<⌂/>"
watermark := fmt.Sprintf("", homeLink, houseID, houseID)
if strings.Index(html, "") == -1 {
html = strings.Replace(html, "", "", 1)
}
html = strings.Replace(html, "", fmt.Sprintf("%sHTMLlot.