package handlers import ( "bytes" "database/sql" "encoding/xml" "log" "net/http" "strconv" "strings" "time" "golang.org/x/net/html" "paterissa.net/mblog/internal/models" ) type Item struct { XMLName xml.Name `xml:"item"` Title string `xml:"title"` Link string `xml:"link"` Guid string `xml:"guid"` Description string `xml:"description"` PubDate string `xml:"pubDate"` Content string `xml:"content:encoded"` } type Channel struct { XMLName xml.Name `xml:"channel"` Title string `xml:"title"` Link string `xml:"link"` Description string `xml:"description"` Items []Item `xml:"item"` } type rssExportContext struct { err *log.Logger db *sql.DB serv string } func (ctx *rssExportContext) feed(w http.ResponseWriter, r *http.Request) { feed := &Channel{ Title: "Paterissa", Link: "https://" + ctx.serv, Description: "Blog feed", } rows, err := ctx.db.Query("SELECT p.id, u.name, p.time, p.brief, p.content FROM posts p INNER JOIN logins u ON p.user_id = u.id ORDER BY p.id DESC LIMIT 20;") if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) ctx.err.Printf("Could not load posts from DB: %v\n", err) return } defer rows.Close() for rows.Next() { var p models.Post if err = rows.Scan(&p.Id, &p.Name, &p.Time, &p.Brief, &p.Content); err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) ctx.err.Printf("Could not load posts from DB: %v\n", err) return } p.FormattedTime = p.Time.Format(time.RFC1123Z) title := "" z := html.NewTokenizer(strings.NewReader(string(p.Brief))) for { tt := z.Next() if tt == html.ErrorToken { break } else if tt == html.StartTagToken { tag := z.Token() if tag.Data == "h1" { if tt = z.Next(); tt == html.TextToken { title = z.Token().Data } } } } feed.Items = append(feed.Items, Item{ Title: title, Link: "https://" + ctx.serv + "/post?id=" + strconv.Itoa(p.Id), Guid: "https://" + ctx.serv + "/post?id=" + strconv.Itoa(p.Id), Description: "", PubDate: p.FormattedTime, Content: "", }) } out, err := xml.MarshalIndent(feed, "", " ") if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("Internal Error")) ctx.err.Printf("Could not create RSS feed: %v\n", err) return } // Really stupid workaround. // Basically, encoders will escape HTML automatically. // This can be configured on the JSON encoder. // It cannot on the XML. // God only knows why. out = bytes.Replace(out, []byte("&"), []byte("&"), -1) out = bytes.Replace(out, []byte(">"), []byte(">"), -1) out = bytes.Replace(out, []byte("<"), []byte("<"), -1) w.Write([]byte(xml.Header + `` + string(out) + ``)) }