Remove webmentions

master
Jan-Lukas Else 2020-07-21 20:31:20 +02:00
parent 9aebddd251
commit 7cbaf6ad31
3 changed files with 0 additions and 196 deletions

View File

@ -16,6 +16,5 @@ func main() {
log.Println("Blog URL: " + BlogUrl) log.Println("Blog URL: " + BlogUrl)
http.HandleFunc("/micropub", HandleMicroPub) http.HandleFunc("/micropub", HandleMicroPub)
http.HandleFunc("/media", HandleMedia) http.HandleFunc("/media", HandleMedia)
http.HandleFunc("/webmention", HandleWebmention)
log.Fatal(http.ListenAndServe(":5555", nil)) log.Fatal(http.ListenAndServe(":5555", nil))
} }

View File

@ -1,161 +0,0 @@
package main
import (
"crypto/md5"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/url"
"strings"
"time"
"willnorris.com/go/webmention"
)
type Mention struct {
Source string `json:"source"`
Target string `json:"target"`
Date string `json:"date"`
}
func HandleWebmention(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
w.WriteHeader(http.StatusMethodNotAllowed)
_, _ = w.Write([]byte("The HTTP method is not allowed, make a POST request"))
return
}
sourceUrl, err := url.Parse(r.FormValue("source"))
if err != nil || !(sourceUrl.Scheme == "http" || sourceUrl.Scheme == "https") {
err = errors.New("failed to parse webmention source URL")
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(err.Error()))
return
}
targetUrl, err := url.Parse(r.FormValue("target"))
if err != nil || !(sourceUrl.Scheme == "http" || sourceUrl.Scheme == "https") {
err = errors.New("failed to parse webmention target URL")
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(err.Error()))
return
}
// Check if urls don't equal
if sourceUrl.String() == targetUrl.String() {
err = errors.New("source and target equal")
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(err.Error()))
return
}
// Check if target is blog
if !strings.HasPrefix(targetUrl.String(), strings.TrimSuffix(BlogUrl, "/")) {
err = errors.New("wrong webmention target")
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(err.Error()))
return
}
// Check response code for source
respCode, err := responseCodeForSource(sourceUrl.String())
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
_, _ = w.Write([]byte(err.Error()))
return
}
if respCode < 200 || respCode >= 300 {
if respCode == 410 || respCode == 404 {
// Delete mention, because source is gone
go func() {
e := deleteWebmention(sourceUrl.String(), targetUrl.String())
if e != nil {
fmt.Print("Tried to delete webmention", sourceUrl.String(), "but failed:", e.Error())
}
}()
returnSuccess(targetUrl.String(), w, r)
return
} else {
err = errors.New("source returned error")
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(err.Error()))
return
}
}
// Check if source mentions target
if !sourceMentionsTarget(sourceUrl.String(), targetUrl.String()) {
err = errors.New("source doesn't mention target")
go func() {
// Try to delete webmention nevertheless
e := deleteWebmention(sourceUrl.String(), targetUrl.String())
if e != nil {
fmt.Print("Tried to delete webmention (source doesn't mention target) ", sourceUrl.String(), "but failed:", e.Error())
}
}()
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(err.Error()))
return
}
go func() {
e := saveWebmention(&Mention{
Source: sourceUrl.String(),
Target: targetUrl.String(),
Date: time.Now().Format("2006-01-02"),
})
if e != nil {
fmt.Println("Failed to save webmention:", e.Error())
}
}()
returnSuccess(targetUrl.String(), w, r)
go func() {
if SelectedNotificationServices != nil {
SelectedNotificationServices.Post("New webmention: " + sourceUrl.String())
}
}()
return
}
func responseCodeForSource(source string) (int, error) {
client := http.DefaultClient
resp, err := client.Get(source)
if err != nil || resp == nil {
return 0, err
}
return resp.StatusCode, nil
}
func sourceMentionsTarget(source string, target string) bool {
client := webmention.New(nil)
dl, err := client.DiscoverLinks(source, "")
if err != nil {
return false
}
for _, link := range dl {
if link == target {
return true
}
}
return false
}
func saveWebmention(mention *Mention) (err error) {
bytesRepresentation, err := json.Marshal(mention)
if err != nil {
return errors.New("failed to marshal json before committing")
}
filePath := fmt.Sprintf("data/mentions/%x/%x.json", md5.Sum([]byte(strings.ReplaceAll(mention.Target, "/", ""))), md5.Sum([]byte(mention.Source)))
err = SelectedStorage.UpdateFile(filePath, string(bytesRepresentation), "New webmention from "+mention.Source)
return
}
func deleteWebmention(source string, target string) (err error) {
filePath := fmt.Sprintf("data/mentions/%x/%x.json", md5.Sum([]byte(strings.ReplaceAll(target, "/", ""))), md5.Sum([]byte(source)))
err = SelectedStorage.DeleteFile(filePath, "Delete webmention from "+source)
return
}
func returnSuccess(target string, w http.ResponseWriter, r *http.Request) {
w.Header().Add("Location", target)
if strings.Contains(r.Header.Get("Accept"), "text/html") {
// Redirect browser
w.WriteHeader(http.StatusSeeOther)
} else {
w.WriteHeader(http.StatusCreated)
}
return
}

View File

@ -1,34 +0,0 @@
package main
import (
"net/http"
"net/http/httptest"
"strconv"
"testing"
)
func Test_responseCodeForSource(t *testing.T) {
for _, code := range []int{200, 404} {
t.Run(strconv.Itoa(code), func(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(code)
}))
defer ts.Close()
got, err := responseCodeForSource(ts.URL)
if err != nil || got != code {
t.Errorf("Wrong response code: Got %d, but expected %d", got, code)
}
})
}
t.Run("Error", func(t *testing.T) {
ts := httptest.NewUnstartedServer(nil)
defer ts.Close()
got, err := responseCodeForSource(ts.URL)
if err == nil {
t.Errorf("Error is nil")
}
if got != 0 {
t.Errorf("Wrong response code: Got %d, but expected %d", got, 0)
}
})
}