honk/fun.go
Ted Unangst 90c7001e15 rework honk schema to store more information.
add original honker for bonks.
add separate precis field for summary.
add format to prepare for changing how html is saved.
2019-05-15 16:27:50 -04:00

432 lines
10 KiB
Go

//
// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
package main
import (
"crypto/rand"
"crypto/rsa"
"fmt"
"html"
"html/template"
"log"
"net/http"
"regexp"
"strings"
"sync"
)
func reverbolate(honks []*Honk) {
for _, h := range honks {
h.What += "ed"
if h.Honker == "" {
h.Honker = "https://" + serverName + "/u/" + h.Username
if strings.IndexByte(h.XID, '/') == -1 {
h.URL = h.Honker + "/h/" + h.XID
} else {
h.URL = h.XID
}
h.Noise = mentionize(h.Noise)
} else {
idx := strings.LastIndexByte(h.Honker, '/')
if idx != -1 {
h.Username = honkerhandle(h.Honker)
} else {
h.Username = h.Honker
}
if h.URL == "" {
h.URL = h.XID
}
}
zap := make(map[*Donk]bool)
h.Noise = unpucker(h.Noise)
precis := h.Precis
if precis != "" {
precis = "<p>summary: " + precis + "<p>"
}
h.HTML = cleanstring(precis + h.Noise)
emuxifier := func(e string) string {
for _, d := range h.Donks {
if d.Name == e {
zap[d] = true
return fmt.Sprintf(`<img class="emu" title="%s" src="/d/%s">`, d.Name, d.XID)
}
}
return e
}
h.HTML = template.HTML(re_emus.ReplaceAllStringFunc(string(h.HTML), emuxifier))
j := 0
for i := 0; i < len(h.Donks); i++ {
if !zap[h.Donks[i]] {
h.Donks[j] = h.Donks[i]
j++
}
}
h.Donks = h.Donks[:j]
}
}
func xfiltrate() string {
letters := "BCDFGHJKLMNPQRSTVWXYZbcdfghjklmnpqrstvwxyz1234567891234567891234"
for {
var b [18]byte
rand.Read(b[:])
for i, c := range b {
b[i] = letters[c&63]
}
s := string(b[:])
return s
}
}
type Mention struct {
who string
where string
}
var re_mentions = regexp.MustCompile(`@[[:alnum:]]+@[[:alnum:].]+`)
var re_urltions = regexp.MustCompile(`@https://\S+`)
func grapevine(s string) []string {
var mentions []string
m := re_mentions.FindAllString(s, -1)
for i := range m {
where := gofish(m[i])
if where != "" {
mentions = append(mentions, where)
}
}
m = re_urltions.FindAllString(s, -1)
for i := range m {
mentions = append(mentions, m[i][1:])
}
return mentions
}
func bunchofgrapes(s string) []Mention {
m := re_mentions.FindAllString(s, -1)
var mentions []Mention
for i := range m {
where := gofish(m[i])
if where != "" {
mentions = append(mentions, Mention{who: m[i], where: where})
}
}
m = re_urltions.FindAllString(s, -1)
for i := range m {
mentions = append(mentions, Mention{who: m[i][1:], where: m[i][1:]})
}
return mentions
}
type Emu struct {
ID string
Name string
}
var re_link = regexp.MustCompile(`@?https?://[^\s"]+[\w/)]`)
var re_emus = regexp.MustCompile(`:[[:alnum:]_]+:`)
func herdofemus(noise string) []Emu {
m := re_emus.FindAllString(noise, -1)
m = oneofakind(m)
var emus []Emu
for _, e := range m {
fname := e[1 : len(e)-1]
url := fmt.Sprintf("https://%s/emu/%s.png", serverName, fname)
emus = append(emus, Emu{ID: url, Name: e})
}
return emus
}
var re_bolder = regexp.MustCompile(`(^|\W)\*\*([\w\s,.!?'-]+)\*\*($|\W)`)
var re_italicer = regexp.MustCompile(`(^|\W)\*([\w\s,.!?'-]+)\*($|\W)`)
var re_bigcoder = regexp.MustCompile("```\n?((?s:.*?))\n?```")
var re_coder = regexp.MustCompile("`([^`]*)`")
func markitzero(s string) string {
var bigcodes []string
bigsaver := func(code string) string {
bigcodes = append(bigcodes, code)
return "``````"
}
s = re_bigcoder.ReplaceAllStringFunc(s, bigsaver)
var lilcodes []string
lilsaver := func(code string) string {
lilcodes = append(lilcodes, code)
return "`x`"
}
s = re_coder.ReplaceAllStringFunc(s, lilsaver)
s = re_bolder.ReplaceAllString(s, "$1<b>$2</b>$3")
s = re_italicer.ReplaceAllString(s, "$1<i>$2</i>$3")
lilun := func(s string) string {
code := lilcodes[0]
lilcodes = lilcodes[1:]
return code
}
s = re_coder.ReplaceAllStringFunc(s, lilun)
bigun := func(s string) string {
code := bigcodes[0]
bigcodes = bigcodes[1:]
return code
}
s = re_bigcoder.ReplaceAllStringFunc(s, bigun)
s = re_bigcoder.ReplaceAllString(s, "<pre><code>$1</code></pre>")
s = re_coder.ReplaceAllString(s, "<code>$1</code>")
return s
}
func obfusbreak(s string) string {
s = strings.TrimSpace(s)
s = strings.Replace(s, "\r", "", -1)
s = html.EscapeString(s)
// dammit go
s = strings.Replace(s, "&#39;", "'", -1)
linkfn := func(url string) string {
if url[0] == '@' {
return url
}
addparen := false
adddot := false
if strings.HasSuffix(url, ")") && strings.IndexByte(url, '(') == -1 {
url = url[:len(url)-1]
addparen = true
}
if strings.HasSuffix(url, ".") {
url = url[:len(url)-1]
adddot = true
}
url = fmt.Sprintf(`<a href="%s">%s</a>`, url, url)
if adddot {
url += "."
}
if addparen {
url += ")"
}
return url
}
s = re_link.ReplaceAllStringFunc(s, linkfn)
s = markitzero(s)
s = strings.Replace(s, "\n", "<br>", -1)
return s
}
func mentionize(s string) string {
s = re_mentions.ReplaceAllStringFunc(s, func(m string) string {
where := gofish(m)
if where == "" {
return m
}
who := m[0 : 1+strings.IndexByte(m[1:], '@')]
return fmt.Sprintf(`<span class="h-card"><a class="u-url mention" href="%s">%s</a></span>`,
html.EscapeString(where), html.EscapeString(who))
})
s = re_urltions.ReplaceAllStringFunc(s, func(m string) string {
return fmt.Sprintf(`<span class="h-card"><a class="u-url mention" href="%s">%s</a></span>`,
html.EscapeString(m[1:]), html.EscapeString(m))
})
return s
}
var re_unurl = regexp.MustCompile("https://([^/]+).*/([^/]+)")
func honkerhandle(h string) string {
m := re_unurl.FindStringSubmatch(h)
if len(m) > 2 {
return fmt.Sprintf("%s@%s", m[2], m[1])
}
return h
}
func prepend(s string, x []string) []string {
return append([]string{s}, x...)
}
// pleroma leaks followers addressed posts to followers
func butnottooloud(aud []string) {
for i, a := range aud {
if strings.HasSuffix(a, "/followers") {
aud[i] = ""
}
}
}
func oneofakind(a []string) []string {
var x []string
for n, s := range a {
if s != "" {
x = append(x, s)
for i := n + 1; i < len(a); i++ {
if a[i] == s {
a[i] = ""
}
}
}
}
return x
}
var ziggies = make(map[string]*rsa.PrivateKey)
var zaggies = make(map[string]*rsa.PublicKey)
var ziggylock sync.Mutex
func ziggy(username string) (keyname string, key *rsa.PrivateKey) {
ziggylock.Lock()
key = ziggies[username]
ziggylock.Unlock()
if key == nil {
db := opendatabase()
row := db.QueryRow("select seckey from users where username = ?", username)
var data string
row.Scan(&data)
var err error
key, _, err = pez(data)
if err != nil {
log.Printf("error decoding %s seckey: %s", username, err)
return
}
ziggylock.Lock()
ziggies[username] = key
ziggylock.Unlock()
}
keyname = fmt.Sprintf("https://%s/u/%s#key", serverName, username)
return
}
func zaggy(keyname string) (key *rsa.PublicKey) {
ziggylock.Lock()
key = zaggies[keyname]
ziggylock.Unlock()
if key != nil {
return
}
db := opendatabase()
row := db.QueryRow("select pubkey from xonkers where xid = ?", keyname)
var data string
err := row.Scan(&data)
if err != nil {
log.Printf("hitting the webs for missing pubkey: %s", keyname)
j, err := GetJunk(keyname)
if err != nil {
log.Printf("error getting %s pubkey: %s", keyname, err)
return
}
var ok bool
data, ok = jsonfindstring(j, []string{"publicKey", "publicKeyPem"})
if !ok {
log.Printf("error finding %s pubkey", keyname)
return
}
_, ok = jsonfindstring(j, []string{"publicKey", "owner"})
if !ok {
log.Printf("error finding %s pubkey owner", keyname)
return
}
_, key, err = pez(data)
if err != nil {
log.Printf("error decoding %s pubkey: %s", keyname, err)
return
}
_, err = stmtSaveBoxes.Exec(keyname, "", "", "", data)
if err != nil {
log.Printf("error saving key: %s", err)
}
} else {
_, key, err = pez(data)
if err != nil {
log.Printf("error decoding %s pubkey: %s", keyname, err)
return
}
}
ziggylock.Lock()
zaggies[keyname] = key
ziggylock.Unlock()
return
}
func makeitworksomehowwithoutregardforkeycontinuity(keyname string, r *http.Request, payload []byte) (string, error) {
db := opendatabase()
_, err := db.Exec("delete from xonkers where xid = ?", keyname)
if err != nil {
log.Printf("error deleting key: %s", err)
}
ziggylock.Lock()
delete(zaggies, keyname)
ziggylock.Unlock()
return zag(r, payload)
}
var thumbbiters map[int64]map[string]bool
var thumblock sync.Mutex
func bitethethumbs() {
rows, err := stmtThumbBiters.Query()
if err != nil {
log.Printf("error getting thumbbiters: %s", err)
return
}
defer rows.Close()
thumblock.Lock()
defer thumblock.Unlock()
thumbbiters = make(map[int64]map[string]bool)
for rows.Next() {
var userid int64
var name, wherefore string
err = rows.Scan(&userid, &name, &wherefore)
if err != nil {
log.Printf("error scanning zonker: %s", err)
continue
}
m := thumbbiters[userid]
if m == nil {
m = make(map[string]bool)
thumbbiters[userid] = m
}
m[name] = true
}
}
func thoudostbitethythumb(userid int64, who []string, objid string) bool {
thumblock.Lock()
biters := thumbbiters[userid]
thumblock.Unlock()
for _, w := range who {
if biters[w] {
return true
}
m := re_unurl.FindStringSubmatch(w)
if len(m) > 2 {
where := m[1]
if biters[where] {
return true
}
}
}
return false
}
func keymatch(keyname string, actor string, what string, userid int64) bool {
hash := strings.IndexByte(keyname, '#')
if hash == -1 {
hash = len(keyname)
}
owner := keyname[0:hash]
if owner == actor {
return true
}
return false
}