/*
-SGBlog -- Git-based CGI blogging engine
+SGBlog -- Git-backed CGI/inetd blogging/phlogging engine
Copyright (C) 2020 Sergey Matveev <stargrave@stargrave.org>
This program is free software: you can redistribute it and/or modify
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-// Git-based CGI blogging engine email to comments adder
+// Git-backed CGI/inetd blogging/phlogging engine
package main
import (
"fmt"
"io/ioutil"
"log"
+ "mime"
"net/mail"
"os"
"os/exec"
+ "regexp"
"strconv"
"strings"
"syscall"
"time"
- "go.cypherpunks.ru/netstring/v2"
+ "go.cypherpunks.ru/recfile"
+ "go.stargrave.org/sgblog"
)
-const WhenFmt = "2006-01-02 15:04:05Z07:00"
+var hashFinder = regexp.MustCompile("([0-9a-f]{40})")
+
+// Remove various whitespaces and excess lines, because git-notes-add
+// will remove and we have to know exact bytes count
+func cleanupBody(body string) []string {
+ lines := strings.Split(string(body), "\n")
+ for i, line := range lines {
+ line = strings.ReplaceAll(line, " ", " ")
+ line = strings.TrimRight(line, " \r")
+ lines[i] = line
+ }
+ for lines[0] == "" {
+ lines = lines[1:]
+ }
+ for lines[len(lines)-1] == "" {
+ lines = lines[:len(lines)-1]
+ }
+ withoutDups := make([]string, 0, len(lines))
+ emptyMet := false
+ for _, line := range lines {
+ if line == "" {
+ if emptyMet {
+ continue
+ }
+ emptyMet = true
+ } else {
+ emptyMet = false
+ }
+ withoutDups = append(withoutDups, line)
+ }
+ return withoutDups
+}
func main() {
gitCmd := flag.String("git-cmd", "/usr/local/bin/git", "Path to git executable")
notesRef := flag.String("ref", "comments", "notes reference name")
umask := flag.String("umask", "027", "umask value")
dryRun := flag.Bool("dryrun", false, "Show comment, do not add")
+ committerEmail := flag.String(
+ "committer-email",
+ "comment@blog.example.com",
+ "Git committer's email",
+ )
flag.Parse()
uid := syscall.Geteuid()
if err := syscall.Setuid(uid); err != nil {
if len(body) == 0 {
log.Fatal("no body")
}
+ from, err = new(mime.WordDecoder).DecodeHeader(from)
+ if err != nil {
+ log.Fatal(err)
+ }
+ subj = hashFinder.FindString(subj)
+ if subj == "" {
+ log.Fatal("no commit hash found in subject")
+ }
if h, err := hex.DecodeString(subj); err != nil || len(h) != sha1.Size {
os.Exit(0)
}
fromCols := strings.Fields(from)
- from = strings.Join(fromCols[:len(fromCols)-1], " ")
+ if len(fromCols) == 1 {
+ if idx := strings.Index(from, "@"); idx != -1 {
+ from = strings.Trim(from[:idx], "<>")
+ }
+ } else {
+ from = strings.Join(fromCols[:len(fromCols)-1], " ")
+ }
cmd := exec.Command(
*gitCmd, "--git-dir", *gitDir,
"notes", "--ref", *notesRef, "show", subj,
)
note, _ := cmd.Output()
- note = bytes.TrimSuffix(note, []byte{'\n'})
+ note = bytes.TrimRight(note, "\r\n")
- // Remove trailing whitespaces, because git-notes-add will remove
- // them anyway, and we have to know exact bytes count
- lines := strings.Split(string(body), "\n")
- for i, line := range lines {
- lines[i] = strings.TrimRight(line, " ")
+ buf := bytes.NewBuffer(note)
+ recfileW := recfile.NewWriter(buf)
+ if _, err = recfileW.RecordStart(); err != nil {
+ log.Fatal(err)
}
- for lines[len(lines)-1] == "" {
- lines = lines[:len(lines)-1]
+ // We trimmed newline, so have to start record twice
+ if _, err = recfileW.RecordStart(); err != nil {
+ log.Fatal(err)
+ }
+ if _, err = recfileW.WriteFields(
+ recfile.Field{"From", from},
+ recfile.Field{"Date", time.Now().UTC().Format(sgblog.WhenFmt)},
+ ); err != nil {
+ log.Fatal(err)
+ }
+ if _, err = recfileW.WriteFieldMultiline(
+ "Body", append([]string{""}, cleanupBody(string(body))...),
+ ); err != nil {
+ log.Fatal(err)
}
-
- buf := bytes.NewBuffer(note)
- w := netstring.NewWriter(buf)
- w.WriteChunk([]byte(fmt.Sprintf(
- "From: %s\nDate: %s\n\n%s",
- from,
- time.Now().Format(WhenFmt),
- strings.Join(lines, "\n"),
- )))
if *dryRun {
fmt.Print(buf.String())
"notes", "--ref", *notesRef, "add",
"-F", "-", "-f", subj,
)
+ cmd.Env = append(
+ cmd.Env,
+ "GIT_AUTHOR_NAME=SGBlog "+sgblog.Version,
+ "GIT_AUTHOR_EMAIL="+*committerEmail,
+ "GIT_COMMITTER_NAME=SGBlog "+sgblog.Version,
+ "GIT_COMMITTER_EMAIL="+*committerEmail,
+ )
cmd.Stdin = buf
if err = cmd.Run(); err != nil {
log.Fatal(err)