/*
SGBlog -- Git-backed CGI/inetd blogging/phlogging engine
-Copyright (C) 2020 Sergey Matveev <stargrave@stargrave.org>
+Copyright (C) 2020-2021 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
"compress/gzip"
"crypto/sha1"
"encoding/hex"
- "encoding/json"
"encoding/xml"
"errors"
"fmt"
"hash"
"html"
"io"
- "io/ioutil"
"log"
"net/url"
"os"
"github.com/go-git/go-git/v5"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/object"
- "github.com/hjson/hjson-go"
"go.stargrave.org/sgblog"
"go.stargrave.org/sgblog/cmd/sgblog/atom"
"golang.org/x/crypto/blake2b"
{{end}}</pre>
<hr/>{{end}}
-{{if .Cfg.CommentsEmail}}[<a href="mailto:{{.Cfg.CommentsEmail}}?subject=Re:%20{{.TitleEscaped}}%20({{.Commit.Hash.String}})">leave comment</a>]{{end}}
+{{if .Cfg.CommentsEmail}}[<a href="mailto:{{.Cfg.CommentsEmail}}?subject={{.TitleEscaped}}">leave comment</a>]{{end}}
<dl>{{range $idx, $comment := .Comments}}
<dt><a name="comment{{$idx}}"><a href="#comment{{$idx}}">comment {{$idx}}</a>:</dt>
var (
renderableSchemes = map[string]struct{}{
- "ftp": struct{}{},
- "gopher": struct{}{},
- "http": struct{}{},
- "https": struct{}{},
- "telnet": struct{}{},
+ "finger": {},
+ "ftp": {},
+ "gemini": {},
+ "gopher": {},
+ "http": {},
+ "https": {},
+ "telnet": {},
}
)
func makeErr(err error) {
fmt.Print("Content-Type: text/plain; charset=UTF-8\n\n")
fmt.Println(err)
- panic(err)
+ log.Fatalln(err)
}
func checkETag(etag hash.Hash) {
if cfgPath == "" {
log.Fatalln("SGBLOG_CFG is not set")
}
- cfgRaw, err := ioutil.ReadFile(cfgPath)
+ cfg, err := readCfg(cfgPath)
if err != nil {
- makeErr(err)
- }
- var cfgGeneral map[string]interface{}
- if err = hjson.Unmarshal(cfgRaw, &cfgGeneral); err != nil {
- makeErr(err)
- }
- cfgRaw, err = json.Marshal(cfgGeneral)
- if err != nil {
- makeErr(err)
- }
- var cfg *Cfg
- if err = json.Unmarshal(cfgRaw, &cfg); err != nil {
- makeErr(err)
+ log.Fatalln(err)
}
+
pathInfo, exists := os.LookupEnv("PATH_INFO")
if !exists {
pathInfo = "/"
break
}
etagHash.Write(commit.Hash[:])
- commentsRaw := getNote(commentsTree, commit.Hash)
+ commentsRaw := sgblog.GetNote(repo, commentsTree, commit.Hash)
etagHash.Write(commentsRaw)
- topicsRaw := getNote(topicsTree, commit.Hash)
+ topicsRaw := sgblog.GetNote(repo, topicsTree, commit.Hash)
etagHash.Write(topicsRaw)
entries = append(entries, TableEntry{
Commit: commit,
}
entry.DomainURLs = append(entry.DomainURLs, makeA(line, u.Host))
}
- entry.CommentsNum = len(parseComments(entry.CommentsRaw))
- entry.Topics = parseTopics(entry.TopicsRaw)
+ entry.CommentsNum = len(sgblog.ParseComments(entry.CommentsRaw))
+ entry.Topics = sgblog.ParseTopics(entry.TopicsRaw)
entries[i] = entry
}
offsetPrev := offset - PageEntries
}
lines := msgSplit(commit.Message)
var categories []atom.Category
- for _, topic := range parseTopics(getNote(topicsTree, commit.Hash)) {
+ for _, topic := range sgblog.ParseTopics(sgblog.GetNote(repo, topicsTree, commit.Hash)) {
categories = append(categories, atom.Category{Term: topic})
}
+ htmlized := make([]string, 0, len(lines))
+ htmlized = append(htmlized, "<pre>")
+ for _, l := range lines[2:] {
+ htmlized = append(htmlized, lineURLize(cfg.AtomBaseURL+cfg.URLPrefix, l))
+ }
+ htmlized = append(htmlized, "</pre>")
feed.Entry = append(feed.Entry, &atom.Entry{
Title: lines[0],
ID: "urn:uuid:" + bytes2uuid(commit.Hash[:]),
Updated: atom.Time(commit.Author.When),
Summary: &atom.Text{Type: "text", Body: lines[0]},
Content: &atom.Text{
- Type: "text",
- Body: strings.Join(lines[2:], "\n"),
+ Type: "html",
+ Body: strings.Join(htmlized, "\n"),
},
Category: categories,
})
))
commit, err = repo.CommitObject(commentedHash)
if err != nil {
- makeErr(err)
+ continue
}
- comments := parseComments(getNote(t, commentedHash))
+ comments := sgblog.ParseComments(sgblog.GetNote(repo, t, commentedHash))
if len(comments) == 0 {
continue
}
lines := strings.Split(comments[len(comments)-1], "\n")
from := strings.TrimPrefix(lines[0], "From: ")
date := strings.TrimPrefix(lines[1], "Date: ")
+ htmlized := make([]string, 0, len(lines))
+ htmlized = append(htmlized, "<pre>")
+ for _, l := range lines[2:] {
+ htmlized = append(htmlized, lineURLize(cfg.AtomBaseURL+cfg.URLPrefix, l))
+ }
+ htmlized = append(htmlized, "</pre>")
idHasher.Reset()
idHasher.Write([]byte("COMMENT"))
idHasher.Write(commit.Hash[:])
commit.Hash.String(), "#comment", commentN,
}, ""),
}},
- Published: atom.TimeStr(date),
- Updated: atom.TimeStr(date),
+ Published: atom.TimeStr(strings.Replace(date, " ", "T", -1)),
+ Updated: atom.TimeStr(strings.Replace(date, " ", "T", -1)),
Content: &atom.Text{
- Type: "text",
- Body: strings.Join(lines[2:], "\n"),
+ Type: "html",
+ Body: strings.Join(htmlized, "\n"),
},
})
}
cfg.AtomBaseURL, cfg.URLPrefix, "/",
commit.Hash.String(), "/", AtomCommentsFeed,
}, "")
- commentsRaw := getNote(commentsTree, commit.Hash)
+ commentsRaw := sgblog.GetNote(repo, commentsTree, commit.Hash)
etagHash.Write(commentsRaw)
- topicsRaw := getNote(topicsTree, commit.Hash)
+ topicsRaw := sgblog.GetNote(repo, topicsTree, commit.Hash)
etagHash.Write(topicsRaw)
if strings.HasSuffix(pathInfo, AtomCommentsFeed) {
etagHash.Write([]byte("ATOM COMMENTS"))
date string
body []string
}
- commentsRaw := parseComments(commentsRaw)
+ commentsRaw := sgblog.ParseComments(commentsRaw)
var toSkip int
if len(commentsRaw) > PageEntries {
toSkip = len(commentsRaw) - PageEntries
idHasher.Write([]byte("COMMENT"))
idHasher.Write(commit.Hash[:])
idHasher.Write([]byte(comment.n))
+ htmlized := make([]string, 0, len(comment.body))
+ htmlized = append(htmlized, "<pre>")
+ for _, l := range comment.body {
+ htmlized = append(
+ htmlized,
+ lineURLize(cfg.AtomBaseURL+cfg.URLPrefix, l),
+ )
+ }
+ htmlized = append(htmlized, "</pre>")
feed.Entry = append(feed.Entry, &atom.Entry{
Title: fmt.Sprintf("Comment %s by %s", comment.n, comment.from),
Author: &atom.Person{Name: comment.from},
"#comment", comment.n,
}, ""),
}},
- Published: atom.TimeStr(comment.date),
- Updated: atom.TimeStr(comment.date),
+ Published: atom.TimeStr(
+ strings.Replace(comment.date, " ", "T", -1),
+ ),
+ Updated: atom.TimeStr(
+ strings.Replace(comment.date, " ", "T", -1),
+ ),
Content: &atom.Text{
- Type: "text",
- Body: strings.Join(comment.body, "\n"),
+ Type: "html",
+ Body: strings.Join(htmlized, "\n"),
},
})
}
out.Write(data)
goto AtomFinish
}
- notesRaw := getNote(notesTree, commit.Hash)
+ notesRaw := sgblog.GetNote(repo, notesTree, commit.Hash)
etagHash.Write(notesRaw)
checkETag(etagHash)
if len(commit.ParentHashes) > 0 {
parent = commit.ParentHashes[0].String()
}
- commentsParsed := parseComments(commentsRaw)
+ commentsParsed := sgblog.ParseComments(commentsRaw)
comments := make([]CommentEntry, 0, len(commentsParsed))
for _, comment := range commentsParsed {
lines := strings.Split(comment, "\n")
Version: sgblog.Version,
Cfg: cfg,
Title: title,
- TitleEscaped: strings.ReplaceAll(title, " ", "%20"),
+ TitleEscaped: url.PathEscape(fmt.Sprintf("Re: %s (%s)", title, commit.Hash)),
When: when,
AtomCommentsURL: atomCommentsURL,
Parent: parent,
Lines: lines[2:],
NoteLines: notesLines,
Comments: comments,
- Topics: parseTopics(topicsRaw),
+ Topics: sgblog.ParseTopics(topicsRaw),
})
if err != nil {
makeErr(err)
return
AtomFinish:
- os.Stdout.WriteString("Content-Type: text/xml; charset=UTF-8\n")
+ os.Stdout.WriteString("Content-Type: application/atom+xml; charset=UTF-8\n")
os.Stdout.WriteString("ETag: " + etagString(etagHash) + "\n")
if gzipWriter != nil {
os.Stdout.WriteString("Content-Encoding: gzip\n")