# HG changeset patch # User Peter Sanchez # Date 1723553186 21600 # Tue Aug 13 06:46:26 2024 -0600 # Node ID 7de2e36da30eb43d40459f88b141457f945e6121 # Parent 17d9d1fbbc8e74f439d67e4d32f760cf423eaee0 # Parent 6f5c6b9858ba6b826a08003e50cf5a04f82c9255 merged tedu upstream diff --git a/.hgignore b/.hgignore --- a/.hgignore +++ b/.hgignore @@ -7,6 +7,7 @@ memes emus honk +avatarcache violations.json docs/activitypub.7.html docs/hfcs.1.html diff --git a/activity.go b/activity.go --- a/activity.go +++ b/activity.go @@ -186,6 +186,9 @@ Fixup: sign, Limit: 1 * 1024 * 1024, }) + if err != nil { + dlog.Printf("Outbound (GetJunkTimeout) Request: %v Failed! %v", url, err) + } return j, err } @@ -1983,6 +1986,18 @@ if info.Owner == "" { info.Owner = info.XID } + + iconInfo, ok := obj.GetMap("icon") + if ok { + mType, _ := iconInfo.GetString("mediaType") + if strings.HasPrefix(mType, "image/") { + AvatarUrl, ok := iconInfo.GetString("url") + if ok { + info.AvatarURL = AvatarUrl + } + } + } + return info, nil } diff --git a/database.go b/database.go --- a/database.go +++ b/database.go @@ -1077,6 +1077,30 @@ cleanupfiles() } +func getusercount() int { + row := stmtGetUserCount.QueryRow() + var count int + row.Scan(&count) + return count +} + +func getactiveusercount(monthsago int) int { + origin := time.Now().AddDate(0, -monthsago, 0).UTC().Format(dbtimeformat) + row := stmtGetActiveUserCount.QueryRow(origin) + + var count int + row.Scan(&count) + return count +} + +func getlocalhonkcount() int { + row := stmtGetLocalHonkCount.QueryRow() + + var count int + row.Scan(&count) + return count +} + var stmtHonkers, stmtDubbers, stmtNamedDubbers, stmtSaveHonker, stmtUpdateFlavor, stmtUpdateHonker *sql.Stmt var stmtDeleteHonker *sql.Stmt var stmtAnyXonk, stmtOneXonk, stmtPublicHonks, stmtUserHonks, stmtHonksByCombo, stmtHonksByConvoy *sql.Stmt @@ -1095,6 +1119,9 @@ var stmtHonksISaved, stmtGetFilters, stmtSaveFilter, stmtDeleteFilter *sql.Stmt var stmtGetTracks *sql.Stmt var stmtSaveChonk, stmtLoadChonks, stmtGetChatters *sql.Stmt +var stmtGetUserCount *sql.Stmt +var stmtGetActiveUserCount *sql.Stmt +var stmtGetLocalHonkCount *sql.Stmt var stmtDeliquentCheck, stmtDeliquentUpdate *sql.Stmt var stmtGetBlobData, stmtSaveBlobData *sql.Stmt @@ -1122,7 +1149,7 @@ } func prepareStatements(db *sql.DB) { - stmtHonkers = preparetodie(db, "select honkerid, userid, name, xid, flavor, combos, meta from honkers where userid = ? and (flavor = 'presub' or flavor = 'sub' or flavor = 'peep' or flavor = 'unsub') order by name") + stmtHonkers = preparetodie(db, "select honkerid, userid, name, xid, flavor, combos, meta from honkers where userid = ? and (flavor = 'presub' or flavor = 'sub' or flavor = 'peep' or flavor = 'unsub') order by name collate nocase") stmtSaveHonker = preparetodie(db, "insert into honkers (userid, name, xid, flavor, combos, owner, meta, folxid) values (?, ?, ?, ?, ?, ?, ?, '')") stmtUpdateFlavor = preparetodie(db, "update honkers set flavor = ?, folxid = ? where userid = ? and name = ? and xid = ? and flavor = ?") stmtUpdateHonker = preparetodie(db, "update honkers set name = ?, combos = ?, meta = ? where honkerid = ? and userid = ?") @@ -1206,6 +1233,9 @@ stmtSaveChonk = preparetodie(db, "insert into chonks (userid, xid, who, target, dt, noise, format) values (?, ?, ?, ?, ?, ?, ?)") stmtLoadChonks = preparetodie(db, "select chonkid, userid, xid, who, target, dt, noise, format from chonks where userid = ? and dt > ? and chonkid > ? order by chonkid asc") stmtGetChatters = preparetodie(db, "select distinct(target) from chonks where userid = ?") + stmtGetUserCount = preparetodie(db, "select count(*) from users where userid > 0") + stmtGetActiveUserCount = preparetodie(db, "select count(distinct honker) from honks where whofore = 2 and dt > ?") + stmtGetLocalHonkCount = preparetodie(db, "select count(*) from honks where whofore = 2") stmtDeliquentCheck = preparetodie(db, "select dooverid, msg from doovers where userid = ? and rcpt = ?") stmtDeliquentUpdate = preparetodie(db, "update doovers set msg = ? where dooverid = ?") g_blobdb = openblobdb() diff --git a/fun.go b/fun.go --- a/fun.go +++ b/fun.go @@ -471,7 +471,7 @@ var emucache = gencache.New(gencache.Options[string, *Emu]{Fill: func(ename string) (*Emu, bool) { fname := ename[1 : len(ename)-1] - exts := []string{".png", ".gif"} + exts := []string{".png", ".gif", ".svg"} for _, ext := range exts { _, err := os.Stat(dataDir + "/emus/" + fname + ext) if err != nil { @@ -481,7 +481,11 @@ if develMode { url = fmt.Sprintf("/emu/%s%s", fname, ext) } - return &Emu{ID: url, Name: ename, Type: "image/" + ext[1:]}, true + e := &Emu{ID: url, Name: ename, Type: "image/" + ext[1:]} + if ext == ".svg" { + e.Type = e.Type + "+xml" // image/svg+xml + } + return e, true } return nil, true }, Duration: 10 * time.Second}) diff --git a/honk.go b/honk.go --- a/honk.go +++ b/honk.go @@ -280,10 +280,11 @@ } type SomeThing struct { - What int - XID string - Owner string - Name string + What int + XID string + Owner string + Name string + AvatarURL string } const ( diff --git a/views/honk.html b/views/honk.html --- a/views/honk.html +++ b/views/honk.html @@ -10,7 +10,7 @@ {{ else }} {{ end }} - + {{ if .Oonker }} {{ if $bonkcsrf }} @@ -18,7 +18,7 @@ {{ else }} {{ end }} - + {{ end }}

@@ -85,7 +85,7 @@ {{ if eq .Media "video/mp4" }}

{{ else }} -

{{ .Desc }}

+

{{ .Desc }} {{ end }} {{ end }} {{ end }} diff --git a/views/honkers.html b/views/honkers.html --- a/views/honkers.html +++ b/views/honkers.html @@ -34,7 +34,7 @@ {{ end }}

-avatar +avatar

{{ .Name }}

diff --git a/views/honkform.html b/views/honkform.html --- a/views/honkform.html +++ b/views/honkform.html @@ -63,3 +63,23 @@ + diff --git a/web.go b/web.go --- a/web.go +++ b/web.go @@ -17,8 +17,10 @@ import ( "bytes" + "crypto/sha256" "crypto/sha512" "database/sql" + "encoding/hex" "errors" "fmt" "html/template" @@ -31,6 +33,7 @@ "net/url" "os" "os/signal" + gofilepath "path/filepath" "regexp" "runtime/pprof" "sort" @@ -2497,12 +2500,19 @@ var oldfingers = gencache.New(gencache.Options[string, []byte]{Fill: func(orig string) ([]byte, bool) { if strings.HasPrefix(orig, "acct:") { orig = orig[5:] + } else { + orig, _ = url.QueryUnescape(orig) } name := orig idx := strings.LastIndexByte(name, '/') if idx != -1 { name = name[idx+1:] - if serverURL("/%s/%s", userSep, name) != orig { + url := serverURL("/%s/%s", userSep, name) + if strings.HasPrefix(name, "@") { + url = fmt.Sprintf("https://%s/%s", serverName, name) + name = name[1:] + } + if url != orig { ilog.Printf("foreign request rejected") name = "" } @@ -2528,7 +2538,17 @@ l["rel"] = "self" l["type"] = `application/activity+json` l["href"] = user.URL - j["links"] = []junk.Junk{l} + + l3 := junk.New() + l3["rel"] = "http://webfinger.net/rel/avatar" + ext := gofilepath.Ext(user.Options.Avatar) + if ext[1:] == "jpg" { + l3["type"] = "image/jpeg" + } else if ext[1:] == "png" { + l3["type"] = "image/png" + } + l3["href"] = user.Options.Avatar + j["links"] = []junk.Junk{l, l3} return j.ToBytes(), true }}) @@ -2546,11 +2566,73 @@ } } +func knowninformation(w http.ResponseWriter, r *http.Request) { + j := junk.New() + l := junk.New() + + l["rel"] = `http://nodeinfo.diaspora.software/ns/schema/2.0` + l["href"] = fmt.Sprintf("https://%s/nodeinfo/2.0", serverName) + j["links"] = []junk.Junk{l} + + w.Header().Set("Content-Type", "application/json") + j.Write(w) +} + +func actualinformation(w http.ResponseWriter, r *http.Request) { + j := junk.New() + + soft := junk.New() + soft["name"] = "honk" + soft["version"] = softwareVersion + + services := junk.New() + services["inbound"] = []string{} + services["outbound"] = []string{"rss2.0"} + + users := junk.New() + users["total"] = getusercount() + users["activeHalfyear"] = getactiveusercount(6) + users["activeMonth"] = getactiveusercount(1) + + usage := junk.New() + usage["users"] = users + usage["localPosts"] = getlocalhonkcount() + + j["version"] = "2.0" + j["protocols"] = []string{"activitypub"} + j["software"] = soft + j["services"] = services + j["openRegistrations"] = false + j["usage"] = usage + + w.Header().Set("Content-Type", "application/json") + j.Write(w) +} + func somedays() string { secs := 432000 + notrand.Int63n(432000) return fmt.Sprintf("%d", secs) } +type avaKey struct { + uid int64 + url string +} + +func isURL(s string) bool { + u, err := url.Parse(s) + return err == nil && u.Scheme != "" && u.Host != "" +} + +func avatateautogen(w http.ResponseWriter, r *http.Request) { + n := r.FormValue("a") + a := genAvatar(n) + if !develMode { + w.Header().Set("Cache-Control", "max-age="+somedays()) + } + w.Write(a) +} + func lookatme(ava string) string { if strings.Contains(ava, serverName+"/"+userSep) { idx := strings.LastIndexByte(ava, '/') @@ -2569,16 +2651,75 @@ if develMode { loadAvatarColors() } + var uid int64 n := r.FormValue("a") + + if !isURL(n) { + avatateautogen(w, r) + return + } + + hasher := sha256.New() + hasher.Write([]byte(n)) + hashString := hex.EncodeToString(hasher.Sum(nil)) + + fileKey := fmt.Sprintf("%s/avatarcache/%s", dataDir, hashString) + s, err := os.Stat(fileKey) + if err == nil { + if time.Since(s.ModTime()) < (time.Hour * 24 * 7) { + b, _ := os.ReadFile(fileKey) + w.Header().Set("Content-Type", http.DetectContentType(b)) + if !develMode { + w.Header().Set("X-Content-Type-Options", "nosniff") + w.Header().Set("Cache-Control", "max-age="+somedays()) + } + w.Write(b) + return + } + } + if redir := lookatme(n); redir != "" { http.Redirect(w, r, redir, http.StatusSeeOther) return } - a := genAvatar(n) + if !develMode { w.Header().Set("Cache-Control", "max-age="+somedays()) } - w.Write(a) + + uinfo := login.GetUserInfo(r) + if uinfo != nil { + uid = uinfo.UserID + } else { + uid = 0 + } + + // Else, we fetch it now + xid := n + // j, err := GetJunk(u.UserID, xid) + j, err := GetJunk(UserID(uid), xid) + if err != nil { + avatateautogen(w, r) + return + } + + info, _ := somethingabout(j) + if info.AvatarURL == "" { + avatateautogen(w, r) + return + } + imageBytes, err := fetchsome(info.AvatarURL) + if err != nil { + avatateautogen(w, r) + return + } + w.Header().Set("Content-Type", http.DetectContentType(imageBytes)) + w.Write(imageBytes) + + go func() { + os.MkdirAll(fmt.Sprintf("%s/avatarcache/", dataDir), 0755) + os.WriteFile(fileKey, imageBytes, 0644) + }() } func serveviewasset(w http.ResponseWriter, r *http.Request) { @@ -3155,6 +3296,8 @@ getters.HandleFunc("/emu/{emu:[^.]*[^/]+}", serveemu) getters.HandleFunc("/meme/{meme:[^.]*[^/]+}", servememe) getters.HandleFunc("/.well-known/webfinger", fingerlicker) + getters.HandleFunc("/.well-known/nodeinfo", knowninformation) + getters.HandleFunc("/nodeinfo/2.0", actualinformation) getters.HandleFunc("/flag/{code:.+}", showflag)