web.go (view raw)
1//
2// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com>
3//
4// Permission to use, copy, modify, and distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
16package main
17
18import (
19 "bytes"
20 "fmt"
21 "html"
22 "html/template"
23 "io"
24 "log"
25 notrand "math/rand"
26 "net/http"
27 "net/url"
28 "os"
29 "sort"
30 "strconv"
31 "strings"
32 "time"
33
34 "github.com/gorilla/mux"
35 "humungus.tedunangst.com/r/webs/css"
36 "humungus.tedunangst.com/r/webs/htfilter"
37 "humungus.tedunangst.com/r/webs/httpsig"
38 "humungus.tedunangst.com/r/webs/image"
39 "humungus.tedunangst.com/r/webs/junk"
40 "humungus.tedunangst.com/r/webs/login"
41 "humungus.tedunangst.com/r/webs/rss"
42 "humungus.tedunangst.com/r/webs/templates"
43)
44
45var readviews *templates.Template
46
47var userSep = "u"
48var honkSep = "h"
49
50func getuserstyle(u *login.UserInfo) template.CSS {
51 if u == nil {
52 return ""
53 }
54 user, _ := butwhatabout(u.Username)
55 if user.SkinnyCSS {
56 return "main { max-width: 700px; }"
57 }
58 return ""
59}
60
61func getInfo(r *http.Request) map[string]interface{} {
62 u := login.GetUserInfo(r)
63 templinfo := make(map[string]interface{})
64 templinfo["StyleParam"] = getassetparam("views/style.css")
65 templinfo["LocalStyleParam"] = getassetparam("views/local.css")
66 templinfo["JSParam"] = getassetparam("views/honkpage.js")
67 templinfo["UserStyle"] = getuserstyle(u)
68 templinfo["ServerName"] = serverName
69 templinfo["IconName"] = iconName
70 templinfo["UserInfo"] = u
71 templinfo["UserSep"] = userSep
72 if u != nil {
73 var combos []string
74 combocache.Get(u.UserID, &combos)
75 templinfo["Combos"] = combos
76 }
77 return templinfo
78}
79
80func homepage(w http.ResponseWriter, r *http.Request) {
81 templinfo := getInfo(r)
82 u := login.GetUserInfo(r)
83 var honks []*Honk
84 var userid int64 = -1
85 if r.URL.Path == "/front" || u == nil {
86 honks = getpublichonks()
87 } else {
88 userid = u.UserID
89 if r.URL.Path == "/atme" {
90 templinfo["PageName"] = "atme"
91 honks = gethonksforme(userid)
92 } else if r.URL.Path == "/first" {
93 templinfo["PageName"] = "first"
94 honks = gethonksforuser(userid)
95 honks = osmosis(honks, userid)
96 } else {
97 templinfo["PageName"] = "home"
98 honks = gethonksforuser(userid)
99 honks = osmosis(honks, userid)
100 }
101 if len(honks) > 0 {
102 templinfo["TopXID"] = honks[0].XID
103 }
104 templinfo["HonkCSRF"] = login.GetCSRF("honkhonk", r)
105 }
106
107 templinfo["ShowRSS"] = true
108 templinfo["ServerMessage"] = serverMsg
109 honkpage(w, u, honks, templinfo)
110}
111
112func showfunzone(w http.ResponseWriter, r *http.Request) {
113 var emunames, memenames []string
114 dir, err := os.Open("emus")
115 if err == nil {
116 emunames, _ = dir.Readdirnames(0)
117 dir.Close()
118 }
119 for i, e := range emunames {
120 if len(e) > 4 {
121 emunames[i] = e[:len(e)-4]
122 }
123 }
124 dir, err = os.Open("memes")
125 if err == nil {
126 memenames, _ = dir.Readdirnames(0)
127 dir.Close()
128 }
129 templinfo := getInfo(r)
130 templinfo["Emus"] = emunames
131 templinfo["Memes"] = memenames
132 err = readviews.Execute(w, "funzone.html", templinfo)
133 if err != nil {
134 log.Print(err)
135 }
136}
137
138func showrss(w http.ResponseWriter, r *http.Request) {
139 name := mux.Vars(r)["name"]
140
141 var honks []*Honk
142 if name != "" {
143 honks = gethonksbyuser(name, false)
144 } else {
145 honks = getpublichonks()
146 }
147 if len(honks) > 20 {
148 honks = honks[0:20]
149 }
150 reverbolate(-1, honks)
151
152 home := fmt.Sprintf("https://%s/", serverName)
153 base := home
154 if name != "" {
155 home += "u/" + name
156 name += " "
157 }
158 feed := rss.Feed{
159 Title: name + "honk",
160 Link: home,
161 Description: name + "honk rss",
162 Image: &rss.Image{
163 URL: base + "icon.png",
164 Title: name + "honk rss",
165 Link: home,
166 },
167 }
168 var modtime time.Time
169 for _, honk := range honks {
170 if !firstclass(honk) {
171 continue
172 }
173 desc := string(honk.HTML)
174 for _, d := range honk.Donks {
175 desc += fmt.Sprintf(`<p><a href="%s">Attachment: %s</a>`,
176 d.URL, html.EscapeString(d.Name))
177 }
178 if t := honk.Time; t != nil {
179 desc += fmt.Sprintf(`<p>Time: %s`, t.StartTime.Local().Format("03:04PM EDT Mon Jan 02"))
180 if t.Duration != 0 {
181 desc += fmt.Sprintf(`<br>Duration: %s`, t.Duration)
182 }
183 }
184 if p := honk.Place; p != nil {
185 desc += fmt.Sprintf(`<p>Location: <a href="%s">%s</a> %f %f`,
186 html.EscapeString(p.Url), html.EscapeString(p.Name), p.Latitude, p.Longitude)
187 }
188
189 feed.Items = append(feed.Items, &rss.Item{
190 Title: fmt.Sprintf("%s %s %s", honk.Username, honk.What, honk.XID),
191 Description: rss.CData{desc},
192 Link: honk.URL,
193 PubDate: honk.Date.Format(time.RFC1123),
194 Guid: &rss.Guid{IsPermaLink: true, Value: honk.URL},
195 })
196 if honk.Date.After(modtime) {
197 modtime = honk.Date
198 }
199 }
200 w.Header().Set("Cache-Control", "max-age=300")
201 w.Header().Set("Last-Modified", modtime.Format(http.TimeFormat))
202
203 err := feed.Write(w)
204 if err != nil {
205 log.Printf("error writing rss: %s", err)
206 }
207}
208
209func crappola(j junk.Junk) bool {
210 t, _ := j.GetString("type")
211 a, _ := j.GetString("actor")
212 o, _ := j.GetString("object")
213 if t == "Delete" && a == o {
214 log.Printf("crappola from %s", a)
215 return true
216 }
217 return false
218}
219
220func ping(user *WhatAbout, who string) {
221 box, err := getboxes(who)
222 if err != nil {
223 log.Printf("no inbox for ping: %s", err)
224 return
225 }
226 j := junk.New()
227 j["@context"] = itiswhatitis
228 j["type"] = "Ping"
229 j["id"] = user.URL + "/ping/" + xfiltrate()
230 j["actor"] = user.URL
231 j["to"] = who
232 keyname, key := ziggy(user.Name)
233 err = PostJunk(keyname, key, box.In, j)
234 if err != nil {
235 log.Printf("can't send ping: %s", err)
236 return
237 }
238 log.Printf("sent ping to %s: %s", who, j["id"])
239}
240
241func pong(user *WhatAbout, who string, obj string) {
242 box, err := getboxes(who)
243 if err != nil {
244 log.Printf("no inbox for pong %s : %s", who, err)
245 return
246 }
247 j := junk.New()
248 j["@context"] = itiswhatitis
249 j["type"] = "Pong"
250 j["id"] = user.URL + "/pong/" + xfiltrate()
251 j["actor"] = user.URL
252 j["to"] = who
253 j["object"] = obj
254 keyname, key := ziggy(user.Name)
255 err = PostJunk(keyname, key, box.In, j)
256 if err != nil {
257 log.Printf("can't send pong: %s", err)
258 return
259 }
260}
261
262func inbox(w http.ResponseWriter, r *http.Request) {
263 name := mux.Vars(r)["name"]
264 user, err := butwhatabout(name)
265 if err != nil {
266 http.NotFound(w, r)
267 return
268 }
269 if stealthmode(user.ID, r) {
270 http.NotFound(w, r)
271 return
272 }
273 var buf bytes.Buffer
274 io.Copy(&buf, r.Body)
275 payload := buf.Bytes()
276 j, err := junk.Read(bytes.NewReader(payload))
277 if err != nil {
278 log.Printf("bad payload: %s", err)
279 io.WriteString(os.Stdout, "bad payload\n")
280 os.Stdout.Write(payload)
281 io.WriteString(os.Stdout, "\n")
282 return
283 }
284 if crappola(j) {
285 return
286 }
287 keyname, err := httpsig.VerifyRequest(r, payload, zaggy)
288 if err != nil {
289 log.Printf("inbox message failed signature: %s", err)
290 if keyname != "" {
291 keyname, err = makeitworksomehowwithoutregardforkeycontinuity(keyname, r, payload)
292 if err != nil {
293 log.Printf("still failed: %s", err)
294 }
295 }
296 if err != nil {
297 return
298 }
299 }
300 what, _ := j.GetString("type")
301 if what == "Like" {
302 return
303 }
304 who, _ := j.GetString("actor")
305 origin := keymatch(keyname, who)
306 if origin == "" {
307 log.Printf("keyname actor mismatch: %s <> %s", keyname, who)
308 return
309 }
310 objid, _ := j.GetString("id")
311 if thoudostbitethythumb(user.ID, []string{who}, objid) {
312 log.Printf("ignoring thumb sucker %s", who)
313 return
314 }
315 switch what {
316 case "Ping":
317 obj, _ := j.GetString("id")
318 log.Printf("ping from %s: %s", who, obj)
319 pong(user, who, obj)
320 case "Pong":
321 obj, _ := j.GetString("object")
322 log.Printf("pong from %s: %s", who, obj)
323 case "Follow":
324 obj, _ := j.GetString("object")
325 if obj == user.URL {
326 log.Printf("updating honker follow: %s", who)
327 stmtSaveDub.Exec(user.ID, who, who, "dub")
328 go rubadubdub(user, j)
329 } else {
330 log.Printf("can't follow %s", obj)
331 }
332 case "Accept":
333 log.Printf("updating honker accept: %s", who)
334 _, err = stmtUpdateFlavor.Exec("sub", user.ID, who, "presub")
335 if err != nil {
336 log.Printf("error updating honker: %s", err)
337 return
338 }
339 case "Update":
340 obj, ok := j.GetMap("object")
341 if ok {
342 what, _ := obj.GetString("type")
343 switch what {
344 case "Person":
345 return
346 case "Question":
347 return
348 case "Note":
349 go xonksaver(user, j, origin)
350 return
351 }
352 }
353 log.Printf("unknown Update activity")
354 fd, _ := os.OpenFile("savedinbox.json", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
355 j.Write(fd)
356 io.WriteString(fd, "\n")
357 fd.Close()
358
359 case "Undo":
360 obj, ok := j.GetMap("object")
361 if !ok {
362 log.Printf("unknown undo no object")
363 } else {
364 what, _ := obj.GetString("type")
365 switch what {
366 case "Follow":
367 log.Printf("updating honker undo: %s", who)
368 _, err = stmtUpdateFlavor.Exec("undub", user.ID, who, "dub")
369 if err != nil {
370 log.Printf("error updating honker: %s", err)
371 return
372 }
373 case "Announce":
374 xid, _ := obj.GetString("object")
375 log.Printf("undo announce: %s", xid)
376 case "Like":
377 default:
378 log.Printf("unknown undo: %s", what)
379 }
380 }
381 default:
382 go xonksaver(user, j, origin)
383 }
384}
385
386func ximport(w http.ResponseWriter, r *http.Request) {
387 u := login.GetUserInfo(r)
388 xid := r.FormValue("xid")
389 xonk := getxonk(u.UserID, xid)
390 if xonk == nil {
391 p, _ := investigate(xid)
392 if p != nil {
393 xid = p.XID
394 }
395 j, err := GetJunk(xid)
396 if err != nil {
397 http.Error(w, "error getting external object", http.StatusInternalServerError)
398 log.Printf("error getting external object: %s", err)
399 return
400 }
401 log.Printf("importing %s", xid)
402 user, _ := butwhatabout(u.Username)
403
404 what, _ := j.GetString("type")
405 if isactor(what) {
406 outbox, _ := j.GetString("outbox")
407 gimmexonks(user, outbox)
408 http.Redirect(w, r, "/h?xid="+url.QueryEscape(xid), http.StatusSeeOther)
409 return
410 }
411 xonk = xonksaver(user, j, originate(xid))
412 }
413 convoy := ""
414 if xonk != nil {
415 convoy = xonk.Convoy
416 }
417 http.Redirect(w, r, "/t?c="+url.QueryEscape(convoy), http.StatusSeeOther)
418}
419
420func xzone(w http.ResponseWriter, r *http.Request) {
421 u := login.GetUserInfo(r)
422 rows, err := stmtRecentHonkers.Query(u.UserID, u.UserID)
423 if err != nil {
424 log.Printf("query err: %s", err)
425 return
426 }
427 defer rows.Close()
428 var honkers []Honker
429 for rows.Next() {
430 var xid string
431 rows.Scan(&xid)
432 honkers = append(honkers, Honker{XID: xid})
433 }
434 rows.Close()
435 for i, _ := range honkers {
436 _, honkers[i].Handle = handles(honkers[i].XID)
437 }
438 templinfo := getInfo(r)
439 templinfo["XCSRF"] = login.GetCSRF("ximport", r)
440 templinfo["Honkers"] = honkers
441 err = readviews.Execute(w, "xzone.html", templinfo)
442 if err != nil {
443 log.Print(err)
444 }
445}
446
447func outbox(w http.ResponseWriter, r *http.Request) {
448 name := mux.Vars(r)["name"]
449 user, err := butwhatabout(name)
450 if err != nil {
451 http.NotFound(w, r)
452 return
453 }
454 if stealthmode(user.ID, r) {
455 http.NotFound(w, r)
456 return
457 }
458
459 honks := gethonksbyuser(name, false)
460 if len(honks) > 20 {
461 honks = honks[0:20]
462 }
463
464 var jonks []junk.Junk
465 for _, h := range honks {
466 j, _ := jonkjonk(user, h)
467 jonks = append(jonks, j)
468 }
469
470 j := junk.New()
471 j["@context"] = itiswhatitis
472 j["id"] = user.URL + "/outbox"
473 j["type"] = "OrderedCollection"
474 j["totalItems"] = len(jonks)
475 j["orderedItems"] = jonks
476
477 w.Header().Set("Content-Type", theonetruename)
478 j.Write(w)
479}
480
481func emptiness(w http.ResponseWriter, r *http.Request) {
482 name := mux.Vars(r)["name"]
483 user, err := butwhatabout(name)
484 if err != nil {
485 http.NotFound(w, r)
486 return
487 }
488 if stealthmode(user.ID, r) {
489 http.NotFound(w, r)
490 return
491 }
492 colname := "/followers"
493 if strings.HasSuffix(r.URL.Path, "/following") {
494 colname = "/following"
495 }
496 j := junk.New()
497 j["@context"] = itiswhatitis
498 j["id"] = user.URL + colname
499 j["type"] = "OrderedCollection"
500 j["totalItems"] = 0
501 j["orderedItems"] = []junk.Junk{}
502
503 w.Header().Set("Content-Type", theonetruename)
504 j.Write(w)
505}
506
507func showuser(w http.ResponseWriter, r *http.Request) {
508 name := mux.Vars(r)["name"]
509 user, err := butwhatabout(name)
510 if err != nil {
511 log.Printf("user not found %s: %s", name, err)
512 http.NotFound(w, r)
513 return
514 }
515 if stealthmode(user.ID, r) {
516 http.NotFound(w, r)
517 return
518 }
519 if friendorfoe(r.Header.Get("Accept")) {
520 j := asjonker(user)
521 w.Header().Set("Content-Type", theonetruename)
522 j.Write(w)
523 return
524 }
525 u := login.GetUserInfo(r)
526 honks := gethonksbyuser(name, u != nil && u.Username == name)
527 templinfo := getInfo(r)
528 filt := htfilter.New()
529 templinfo["Name"] = user.Name
530 whatabout := user.About
531 whatabout = obfusbreak(user.About)
532 templinfo["WhatAbout"], _ = filt.String(whatabout)
533 templinfo["ServerMessage"] = ""
534 templinfo["HonkCSRF"] = login.GetCSRF("honkhonk", r)
535 honkpage(w, u, honks, templinfo)
536}
537
538func showhonker(w http.ResponseWriter, r *http.Request) {
539 u := login.GetUserInfo(r)
540 name := mux.Vars(r)["name"]
541 var honks []*Honk
542 if name == "" {
543 name = r.FormValue("xid")
544 honks = gethonksbyxonker(u.UserID, name)
545 } else {
546 honks = gethonksbyhonker(u.UserID, name)
547 }
548 name = html.EscapeString(name)
549 msg := fmt.Sprintf(`honks by honker: <a href="%s" ref="noreferrer">%s</a>`, name, name)
550 templinfo := getInfo(r)
551 templinfo["PageName"] = "honker"
552 templinfo["ServerMessage"] = template.HTML(msg)
553 templinfo["HonkCSRF"] = login.GetCSRF("honkhonk", r)
554 honkpage(w, u, honks, templinfo)
555}
556
557func showcombo(w http.ResponseWriter, r *http.Request) {
558 name := mux.Vars(r)["name"]
559 u := login.GetUserInfo(r)
560 honks := gethonksbycombo(u.UserID, name)
561 honks = osmosis(honks, u.UserID)
562 templinfo := getInfo(r)
563 templinfo["PageName"] = "combo"
564 templinfo["PageArg"] = "name"
565 templinfo["ServerMessage"] = "honks by combo: " + name
566 templinfo["HonkCSRF"] = login.GetCSRF("honkhonk", r)
567 if len(honks) > 0 {
568 templinfo["TopXID"] = honks[0].XID
569 }
570 honkpage(w, u, honks, templinfo)
571}
572func showconvoy(w http.ResponseWriter, r *http.Request) {
573 c := r.FormValue("c")
574 u := login.GetUserInfo(r)
575 honks := gethonksbyconvoy(u.UserID, c)
576 templinfo := getInfo(r)
577 templinfo["ServerMessage"] = "honks in convoy: " + c
578 templinfo["HonkCSRF"] = login.GetCSRF("honkhonk", r)
579 honkpage(w, u, honks, templinfo)
580}
581func showsearch(w http.ResponseWriter, r *http.Request) {
582 q := r.FormValue("q")
583 u := login.GetUserInfo(r)
584 honks := gethonksbysearch(u.UserID, q)
585 templinfo := getInfo(r)
586 templinfo["ServerMessage"] = "honks for search: " + q
587 templinfo["HonkCSRF"] = login.GetCSRF("honkhonk", r)
588 honkpage(w, u, honks, templinfo)
589}
590func showontology(w http.ResponseWriter, r *http.Request) {
591 name := mux.Vars(r)["name"]
592 u := login.GetUserInfo(r)
593 var userid int64 = -1
594 if u != nil {
595 userid = u.UserID
596 }
597 honks := gethonksbyontology(userid, "#"+name)
598 templinfo := getInfo(r)
599 templinfo["ServerMessage"] = "honks by ontology: " + name
600 templinfo["HonkCSRF"] = login.GetCSRF("honkhonk", r)
601 honkpage(w, u, honks, templinfo)
602}
603
604func thelistingoftheontologies(w http.ResponseWriter, r *http.Request) {
605 u := login.GetUserInfo(r)
606 var userid int64 = -1
607 if u != nil {
608 userid = u.UserID
609 }
610 rows, err := stmtSelectOnts.Query(userid)
611 if err != nil {
612 log.Printf("selection error: %s", err)
613 return
614 }
615 defer rows.Close()
616 var onts [][]string
617 for rows.Next() {
618 var o string
619 err := rows.Scan(&o)
620 if err != nil {
621 log.Printf("error scanning ont: %s", err)
622 continue
623 }
624 onts = append(onts, []string{o, o[1:]})
625 }
626 if u == nil {
627 w.Header().Set("Cache-Control", "max-age=300")
628 }
629 templinfo := getInfo(r)
630 templinfo["Onts"] = onts
631 err = readviews.Execute(w, "onts.html", templinfo)
632 if err != nil {
633 log.Print(err)
634 }
635}
636
637func showhonk(w http.ResponseWriter, r *http.Request) {
638 name := mux.Vars(r)["name"]
639 user, err := butwhatabout(name)
640 if err != nil {
641 http.NotFound(w, r)
642 return
643 }
644 if stealthmode(user.ID, r) {
645 http.NotFound(w, r)
646 return
647 }
648
649 xid := fmt.Sprintf("https://%s%s", serverName, r.URL.Path)
650 honk := getxonk(user.ID, xid)
651 if honk == nil {
652 http.NotFound(w, r)
653 return
654 }
655 u := login.GetUserInfo(r)
656 if u != nil && u.UserID != user.ID {
657 u = nil
658 }
659 if !honk.Public {
660 if u == nil {
661 http.NotFound(w, r)
662 return
663
664 }
665 templinfo := getInfo(r)
666 templinfo["ServerMessage"] = "one honk maybe more"
667 templinfo["HonkCSRF"] = login.GetCSRF("honkhonk", r)
668 honkpage(w, u, []*Honk{honk}, templinfo)
669 return
670 }
671 rawhonks := gethonksbyconvoy(honk.UserID, honk.Convoy)
672 if friendorfoe(r.Header.Get("Accept")) {
673 for _, h := range rawhonks {
674 if h.RID == honk.XID && h.Public && (h.Whofore == 2 || h.IsAcked()) {
675 honk.Replies = append(honk.Replies, h)
676 }
677 }
678 donksforhonks([]*Honk{honk})
679 _, j := jonkjonk(user, honk)
680 j["@context"] = itiswhatitis
681 w.Header().Set("Content-Type", theonetruename)
682 j.Write(w)
683 return
684 }
685 var honks []*Honk
686 for _, h := range rawhonks {
687 if h.Public && (h.Whofore == 2 || h.IsAcked()) {
688 honks = append(honks, h)
689 }
690 }
691
692 templinfo := getInfo(r)
693 templinfo["ServerMessage"] = "one honk maybe more"
694 templinfo["HonkCSRF"] = login.GetCSRF("honkhonk", r)
695 honkpage(w, u, honks, templinfo)
696}
697
698func honkpage(w http.ResponseWriter, u *login.UserInfo, honks []*Honk, templinfo map[string]interface{}) {
699 var userid int64 = -1
700 if u != nil {
701 userid = u.UserID
702 }
703 if u == nil {
704 w.Header().Set("Cache-Control", "max-age=60")
705 }
706 reverbolate(userid, honks)
707 templinfo["Honks"] = honks
708 err := readviews.Execute(w, "honkpage.html", templinfo)
709 if err != nil {
710 log.Print(err)
711 }
712}
713
714func saveuser(w http.ResponseWriter, r *http.Request) {
715 whatabout := r.FormValue("whatabout")
716 u := login.GetUserInfo(r)
717 db := opendatabase()
718 options := ""
719 if r.FormValue("skinny") == "skinny" {
720 options += " skinny "
721 }
722 _, err := db.Exec("update users set about = ?, options = ? where username = ?", whatabout, options, u.Username)
723 if err != nil {
724 log.Printf("error bouting what: %s", err)
725 }
726
727 http.Redirect(w, r, "/account", http.StatusSeeOther)
728}
729
730func submitbonk(w http.ResponseWriter, r *http.Request) {
731 xid := r.FormValue("xid")
732 userinfo := login.GetUserInfo(r)
733 user, _ := butwhatabout(userinfo.Username)
734
735 log.Printf("bonking %s", xid)
736
737 xonk := getxonk(userinfo.UserID, xid)
738 if xonk == nil {
739 return
740 }
741 if !xonk.Public {
742 return
743 }
744 donksforhonks([]*Honk{xonk})
745
746 _, err := stmtUpdateFlags.Exec(flagIsBonked, xonk.ID)
747 if err != nil {
748 log.Printf("error acking bonk: %s", err)
749 }
750
751 oonker := xonk.Oonker
752 if oonker == "" {
753 oonker = xonk.Honker
754 }
755 dt := time.Now().UTC()
756 bonk := Honk{
757 UserID: userinfo.UserID,
758 Username: userinfo.Username,
759 What: "bonk",
760 Honker: user.URL,
761 Oonker: oonker,
762 XID: xonk.XID,
763 RID: xonk.RID,
764 Noise: xonk.Noise,
765 Precis: xonk.Precis,
766 URL: xonk.URL,
767 Date: dt,
768 Donks: xonk.Donks,
769 Whofore: 2,
770 Convoy: xonk.Convoy,
771 Audience: []string{thewholeworld, oonker},
772 Public: true,
773 }
774
775 bonk.Format = "html"
776
777 err = savehonk(&bonk)
778 if err != nil {
779 log.Printf("uh oh")
780 return
781 }
782
783 go honkworldwide(user, &bonk)
784}
785
786func sendzonkofsorts(xonk *Honk, user *WhatAbout, what string) {
787 zonk := Honk{
788 What: what,
789 XID: xonk.XID,
790 Date: time.Now().UTC(),
791 Audience: oneofakind(xonk.Audience),
792 }
793 zonk.Public = !keepitquiet(zonk.Audience)
794
795 log.Printf("announcing %sed honk: %s", what, xonk.XID)
796 go honkworldwide(user, &zonk)
797}
798
799func zonkit(w http.ResponseWriter, r *http.Request) {
800 wherefore := r.FormValue("wherefore")
801 what := r.FormValue("what")
802 userinfo := login.GetUserInfo(r)
803 user, _ := butwhatabout(userinfo.Username)
804
805 if wherefore == "ack" {
806 xonk := getxonk(userinfo.UserID, what)
807 if xonk != nil {
808 _, err := stmtUpdateFlags.Exec(flagIsAcked, xonk.ID)
809 if err != nil {
810 log.Printf("error acking: %s", err)
811 }
812 sendzonkofsorts(xonk, user, "ack")
813 }
814 return
815 }
816
817 if wherefore == "deack" {
818 xonk := getxonk(userinfo.UserID, what)
819 if xonk != nil {
820 _, err := stmtClearFlags.Exec(flagIsAcked, xonk.ID)
821 if err != nil {
822 log.Printf("error deacking: %s", err)
823 }
824 sendzonkofsorts(xonk, user, "deack")
825 }
826 return
827 }
828
829 if wherefore == "unbonk" {
830 xonk := getbonk(userinfo.UserID, what)
831 if xonk != nil {
832 deletehonk(xonk.ID)
833 xonk = getxonk(userinfo.UserID, what)
834 _, err := stmtClearFlags.Exec(flagIsBonked, xonk.ID)
835 if err != nil {
836 log.Printf("error unbonking: %s", err)
837 }
838 sendzonkofsorts(xonk, user, "unbonk")
839 }
840 return
841 }
842
843 log.Printf("zonking %s %s", wherefore, what)
844 if wherefore == "zonk" {
845 xonk := getxonk(userinfo.UserID, what)
846 if xonk != nil {
847 deletehonk(xonk.ID)
848 if xonk.Whofore == 2 || xonk.Whofore == 3 {
849 sendzonkofsorts(xonk, user, "zonk")
850 }
851 }
852 }
853 _, err := stmtSaveZonker.Exec(userinfo.UserID, what, wherefore)
854 if err != nil {
855 log.Printf("error saving zonker: %s", err)
856 return
857 }
858}
859
860func edithonkpage(w http.ResponseWriter, r *http.Request) {
861 u := login.GetUserInfo(r)
862 user, _ := butwhatabout(u.Username)
863 xid := r.FormValue("xid")
864 honk := getxonk(u.UserID, xid)
865 if honk == nil || honk.Honker != user.URL || honk.What != "honk" {
866 log.Printf("no edit")
867 return
868 }
869
870 noise := honk.Noise
871 if honk.Precis != "" {
872 noise = honk.Precis + "\n\n" + noise
873 }
874
875 honks := []*Honk{honk}
876 donksforhonks(honks)
877 reverbolate(u.UserID, honks)
878 templinfo := getInfo(r)
879 templinfo["HonkCSRF"] = login.GetCSRF("honkhonk", r)
880 templinfo["Honks"] = honks
881 templinfo["Noise"] = noise
882 templinfo["ServerMessage"] = "honk edit"
883 templinfo["UpdateXID"] = honk.XID
884 if len(honk.Donks) > 0 {
885 templinfo["SavedFile"] = honk.Donks[0].XID
886 }
887 err := readviews.Execute(w, "honkpage.html", templinfo)
888 if err != nil {
889 log.Print(err)
890 }
891}
892
893// what a hot mess this function is
894func submithonk(w http.ResponseWriter, r *http.Request) {
895 rid := r.FormValue("rid")
896 noise := r.FormValue("noise")
897
898 userinfo := login.GetUserInfo(r)
899 user, _ := butwhatabout(userinfo.Username)
900
901 dt := time.Now().UTC()
902 updatexid := r.FormValue("updatexid")
903 var honk *Honk
904 if updatexid != "" {
905 honk = getxonk(userinfo.UserID, updatexid)
906 if honk == nil || honk.Honker != user.URL || honk.What != "honk" {
907 log.Printf("not saving edit")
908 return
909 }
910 honk.Date = dt
911 honk.What = "update"
912 honk.Format = "markdown"
913 } else {
914 xid := fmt.Sprintf("%s/%s/%s", user.URL, honkSep, xfiltrate())
915 what := "honk"
916 if rid != "" {
917 what = "tonk"
918 }
919 honk = &Honk{
920 UserID: userinfo.UserID,
921 Username: userinfo.Username,
922 What: what,
923 Honker: user.URL,
924 XID: xid,
925 Date: dt,
926 Format: "markdown",
927 }
928 }
929
930 noise = hooterize(noise)
931 honk.Noise = noise
932 translate(honk)
933
934 var convoy string
935 if rid != "" {
936 xonk := getxonk(userinfo.UserID, rid)
937 if xonk != nil {
938 if xonk.Public {
939 honk.Audience = append(honk.Audience, xonk.Audience...)
940 }
941 convoy = xonk.Convoy
942 } else {
943 xonkaud, c := whosthere(rid)
944 honk.Audience = append(honk.Audience, xonkaud...)
945 convoy = c
946 }
947 for i, a := range honk.Audience {
948 if a == thewholeworld {
949 honk.Audience[0], honk.Audience[i] = honk.Audience[i], honk.Audience[0]
950 break
951 }
952 }
953 honk.RID = rid
954 } else {
955 honk.Audience = []string{thewholeworld}
956 }
957 if honk.Noise != "" && honk.Noise[0] == '@' {
958 honk.Audience = append(grapevine(honk.Noise), honk.Audience...)
959 } else {
960 honk.Audience = append(honk.Audience, grapevine(honk.Noise)...)
961 }
962
963 if convoy == "" {
964 convoy = "data:,electrichonkytonk-" + xfiltrate()
965 }
966 butnottooloud(honk.Audience)
967 honk.Audience = oneofakind(honk.Audience)
968 if len(honk.Audience) == 0 {
969 log.Printf("honk to nowhere")
970 http.Error(w, "honk to nowhere...", http.StatusNotFound)
971 return
972 }
973 honk.Public = !keepitquiet(honk.Audience)
974 honk.Convoy = convoy
975
976 donkxid := r.FormValue("donkxid")
977 if donkxid == "" {
978 file, filehdr, err := r.FormFile("donk")
979 if err == nil {
980 var buf bytes.Buffer
981 io.Copy(&buf, file)
982 file.Close()
983 data := buf.Bytes()
984 xid := xfiltrate()
985 var media, name string
986 img, err := image.Vacuum(&buf, image.Params{MaxWidth: 2048, MaxHeight: 2048})
987 if err == nil {
988 data = img.Data
989 format := img.Format
990 media = "image/" + format
991 if format == "jpeg" {
992 format = "jpg"
993 }
994 name = xid + "." + format
995 xid = name
996 } else {
997 maxsize := 100000
998 if len(data) > maxsize {
999 log.Printf("bad image: %s too much text: %d", err, len(data))
1000 http.Error(w, "didn't like your attachment", http.StatusUnsupportedMediaType)
1001 return
1002 }
1003 for i := 0; i < len(data); i++ {
1004 if data[i] < 32 && data[i] != '\t' && data[i] != '\r' && data[i] != '\n' {
1005 log.Printf("bad image: %s not text: %d", err, data[i])
1006 http.Error(w, "didn't like your attachment", http.StatusUnsupportedMediaType)
1007 return
1008 }
1009 }
1010 media = "text/plain"
1011 name = filehdr.Filename
1012 if name == "" {
1013 name = xid + ".txt"
1014 }
1015 xid += ".txt"
1016 }
1017 desc := r.FormValue("donkdesc")
1018 if desc == "" {
1019 desc = name
1020 }
1021 url := fmt.Sprintf("https://%s/d/%s", serverName, xid)
1022 fileid, err := savefile(xid, name, desc, url, media, true, data)
1023 if err != nil {
1024 log.Printf("unable to save image: %s", err)
1025 return
1026 }
1027 var d Donk
1028 d.FileID = fileid
1029 honk.Donks = append(honk.Donks, &d)
1030 donkxid = d.XID
1031 }
1032 } else {
1033 xid := donkxid
1034 url := fmt.Sprintf("https://%s/d/%s", serverName, xid)
1035 donk := finddonk(url)
1036 if donk != nil {
1037 honk.Donks = append(honk.Donks, donk)
1038 } else {
1039 log.Printf("can't find file: %s", xid)
1040 }
1041 }
1042 herd := herdofemus(honk.Noise)
1043 for _, e := range herd {
1044 donk := savedonk(e.ID, e.Name, e.Name, "image/png", true)
1045 if donk != nil {
1046 donk.Name = e.Name
1047 honk.Donks = append(honk.Donks, donk)
1048 }
1049 }
1050 memetize(honk)
1051
1052 placename := r.FormValue("placename")
1053 placelat := r.FormValue("placelat")
1054 placelong := r.FormValue("placelong")
1055 if placename != "" || placelat != "" || placelong != "" {
1056 p := new(Place)
1057 p.Name = placename
1058 p.Latitude, _ = strconv.ParseFloat(placelat, 64)
1059 p.Longitude, _ = strconv.ParseFloat(placelong, 64)
1060 p.Url = r.FormValue("placeurl")
1061 honk.Place = p
1062 }
1063 timestart := r.FormValue("timestart")
1064 if timestart != "" {
1065 t := new(Time)
1066 now := time.Now().Local()
1067 for _, layout := range []string{"2006-01-02 3:04pm", "2006-01-02 15:04", "3:04pm", "15:04"} {
1068 start, err := time.ParseInLocation(layout, timestart, now.Location())
1069 if err == nil {
1070 if start.Year() == 0 {
1071 start = time.Date(now.Year(), now.Month(), now.Day(), start.Hour(), start.Minute(), 0, 0, now.Location())
1072 }
1073 t.StartTime = start
1074 break
1075 }
1076 }
1077 timeend := r.FormValue("timeend")
1078 dur, err := time.ParseDuration(timeend)
1079 if err == nil {
1080 t.Duration = Duration(dur)
1081 }
1082 if !t.StartTime.IsZero() {
1083 honk.What = "event"
1084 honk.Time = t
1085 }
1086 }
1087
1088 if honk.Public {
1089 honk.Whofore = 2
1090 } else {
1091 honk.Whofore = 3
1092 }
1093
1094 // back to markdown
1095 honk.Noise = noise
1096
1097 if r.FormValue("preview") == "preview" {
1098 honks := []*Honk{honk}
1099 reverbolate(userinfo.UserID, honks)
1100 templinfo := getInfo(r)
1101 templinfo["HonkCSRF"] = login.GetCSRF("honkhonk", r)
1102 templinfo["Honks"] = honks
1103 templinfo["InReplyTo"] = r.FormValue("rid")
1104 templinfo["Noise"] = r.FormValue("noise")
1105 templinfo["SavedFile"] = donkxid
1106 templinfo["ServerMessage"] = "honk preview"
1107 err := readviews.Execute(w, "honkpage.html", templinfo)
1108 if err != nil {
1109 log.Print(err)
1110 }
1111 return
1112 }
1113
1114 if updatexid != "" {
1115 updatehonk(honk)
1116 } else {
1117 err := savehonk(honk)
1118 if err != nil {
1119 log.Printf("uh oh")
1120 return
1121 }
1122 }
1123
1124 // reload for consistency
1125 honk.Donks = nil
1126 donksforhonks([]*Honk{honk})
1127
1128 go honkworldwide(user, honk)
1129
1130 http.Redirect(w, r, honk.XID, http.StatusSeeOther)
1131}
1132
1133func showhonkers(w http.ResponseWriter, r *http.Request) {
1134 userinfo := login.GetUserInfo(r)
1135 templinfo := getInfo(r)
1136 templinfo["Honkers"] = gethonkers(userinfo.UserID)
1137 templinfo["HonkerCSRF"] = login.GetCSRF("submithonker", r)
1138 err := readviews.Execute(w, "honkers.html", templinfo)
1139 if err != nil {
1140 log.Print(err)
1141 }
1142}
1143
1144var combocache = cacheNew(func(key interface{}) (interface{}, bool) {
1145 userid := key.(int64)
1146 honkers := gethonkers(userid)
1147 var combos []string
1148 for _, h := range honkers {
1149 combos = append(combos, h.Combos...)
1150 }
1151 for i, c := range combos {
1152 if c == "-" {
1153 combos[i] = ""
1154 }
1155 }
1156 combos = oneofakind(combos)
1157 sort.Strings(combos)
1158 return combos, true
1159})
1160
1161func showcombos(w http.ResponseWriter, r *http.Request) {
1162 userinfo := login.GetUserInfo(r)
1163 var combos []string
1164 combocache.Get(userinfo.UserID, &combos)
1165 templinfo := getInfo(r)
1166 err := readviews.Execute(w, "combos.html", templinfo)
1167 if err != nil {
1168 log.Print(err)
1169 }
1170}
1171
1172func submithonker(w http.ResponseWriter, r *http.Request) {
1173 u := login.GetUserInfo(r)
1174 name := r.FormValue("name")
1175 url := r.FormValue("url")
1176 peep := r.FormValue("peep")
1177 combos := r.FormValue("combos")
1178 honkerid, _ := strconv.ParseInt(r.FormValue("honkerid"), 10, 0)
1179
1180 defer combocache.Clear(u.UserID)
1181
1182 if honkerid > 0 {
1183 goodbye := r.FormValue("goodbye")
1184 if goodbye == "F" {
1185 db := opendatabase()
1186 row := db.QueryRow("select xid from honkers where honkerid = ? and userid = ?",
1187 honkerid, u.UserID)
1188 var xid string
1189 err := row.Scan(&xid)
1190 if err != nil {
1191 log.Printf("can't get honker xid: %s", err)
1192 return
1193 }
1194 log.Printf("unsubscribing from %s", xid)
1195 user, _ := butwhatabout(u.Username)
1196 go itakeitallback(user, xid)
1197 _, err = stmtUpdateFlavor.Exec("unsub", u.UserID, xid, "sub")
1198 if err != nil {
1199 log.Printf("error updating honker: %s", err)
1200 return
1201 }
1202
1203 http.Redirect(w, r, "/honkers", http.StatusSeeOther)
1204 return
1205 }
1206 combos = " " + strings.TrimSpace(combos) + " "
1207 _, err := stmtUpdateCombos.Exec(combos, honkerid, u.UserID)
1208 if err != nil {
1209 log.Printf("update honker err: %s", err)
1210 return
1211 }
1212 http.Redirect(w, r, "/honkers", http.StatusSeeOther)
1213 return
1214 }
1215
1216 flavor := "presub"
1217 if peep == "peep" {
1218 flavor = "peep"
1219 }
1220 p, err := investigate(url)
1221 if err != nil {
1222 http.Error(w, "error investigating: "+err.Error(), http.StatusInternalServerError)
1223 log.Printf("failed to investigate honker")
1224 return
1225 }
1226 url = p.XID
1227 if name == "" {
1228 name = p.Handle
1229 }
1230 _, err = stmtSaveHonker.Exec(u.UserID, name, url, flavor, combos)
1231 if err != nil {
1232 log.Print(err)
1233 return
1234 }
1235 if flavor == "presub" {
1236 user, _ := butwhatabout(u.Username)
1237 go subsub(user, url)
1238 }
1239 http.Redirect(w, r, "/honkers", http.StatusSeeOther)
1240}
1241
1242func zonkzone(w http.ResponseWriter, r *http.Request) {
1243 userinfo := login.GetUserInfo(r)
1244 rows, err := stmtGetZonkers.Query(userinfo.UserID)
1245 if err != nil {
1246 log.Printf("err: %s", err)
1247 return
1248 }
1249 defer rows.Close()
1250 var zonkers []Zonker
1251 for rows.Next() {
1252 var z Zonker
1253 rows.Scan(&z.ID, &z.Name, &z.Wherefore)
1254 zonkers = append(zonkers, z)
1255 }
1256 sort.Slice(zonkers, func(i, j int) bool {
1257 w1 := zonkers[i].Wherefore
1258 w2 := zonkers[j].Wherefore
1259 if w1 == w2 {
1260 return zonkers[i].Name < zonkers[j].Name
1261 }
1262 if w1 == "zonvoy" {
1263 w1 = "zzzzzzz"
1264 }
1265 if w2 == "zonvoy" {
1266 w2 = "zzzzzzz"
1267 }
1268 return w1 < w2
1269 })
1270
1271 templinfo := getInfo(r)
1272 templinfo["Zonkers"] = zonkers
1273 templinfo["ZonkCSRF"] = login.GetCSRF("zonkzonk", r)
1274 err = readviews.Execute(w, "zonkers.html", templinfo)
1275 if err != nil {
1276 log.Print(err)
1277 }
1278}
1279
1280func zonkzonk(w http.ResponseWriter, r *http.Request) {
1281 userinfo := login.GetUserInfo(r)
1282 itsok := r.FormValue("itsok")
1283 if itsok == "iforgiveyou" {
1284 zonkerid, _ := strconv.ParseInt(r.FormValue("zonkerid"), 10, 0)
1285 db := opendatabase()
1286 db.Exec("delete from zonkers where userid = ? and zonkerid = ?",
1287 userinfo.UserID, zonkerid)
1288 bitethethumbs()
1289 http.Redirect(w, r, "/zonkzone", http.StatusSeeOther)
1290 return
1291 }
1292 wherefore := r.FormValue("wherefore")
1293 name := r.FormValue("name")
1294 if name == "" {
1295 return
1296 }
1297 switch wherefore {
1298 case "zonker":
1299 case "zomain":
1300 case "zonvoy":
1301 case "zord":
1302 case "zilence":
1303 case "zoggle":
1304 default:
1305 return
1306 }
1307 db := opendatabase()
1308 db.Exec("insert into zonkers (userid, name, wherefore) values (?, ?, ?)",
1309 userinfo.UserID, name, wherefore)
1310
1311 if wherefore != "zonvoy" {
1312 bitethethumbs()
1313 }
1314
1315 http.Redirect(w, r, "/zonkzone", http.StatusSeeOther)
1316}
1317
1318func accountpage(w http.ResponseWriter, r *http.Request) {
1319 u := login.GetUserInfo(r)
1320 user, _ := butwhatabout(u.Username)
1321 templinfo := getInfo(r)
1322 templinfo["UserCSRF"] = login.GetCSRF("saveuser", r)
1323 templinfo["LogoutCSRF"] = login.GetCSRF("logout", r)
1324 templinfo["User"] = user
1325 err := readviews.Execute(w, "account.html", templinfo)
1326 if err != nil {
1327 log.Print(err)
1328 }
1329}
1330
1331func dochpass(w http.ResponseWriter, r *http.Request) {
1332 err := login.ChangePassword(w, r)
1333 if err != nil {
1334 log.Printf("error changing password: %s", err)
1335 }
1336 http.Redirect(w, r, "/account", http.StatusSeeOther)
1337}
1338
1339func fingerlicker(w http.ResponseWriter, r *http.Request) {
1340 orig := r.FormValue("resource")
1341
1342 log.Printf("finger lick: %s", orig)
1343
1344 if strings.HasPrefix(orig, "acct:") {
1345 orig = orig[5:]
1346 }
1347
1348 name := orig
1349 idx := strings.LastIndexByte(name, '/')
1350 if idx != -1 {
1351 name = name[idx+1:]
1352 if fmt.Sprintf("https://%s/%s/%s", serverName, userSep, name) != orig {
1353 log.Printf("foreign request rejected")
1354 name = ""
1355 }
1356 } else {
1357 idx = strings.IndexByte(name, '@')
1358 if idx != -1 {
1359 name = name[:idx]
1360 if name+"@"+serverName != orig {
1361 log.Printf("foreign request rejected")
1362 name = ""
1363 }
1364 }
1365 }
1366 user, err := butwhatabout(name)
1367 if err != nil {
1368 http.NotFound(w, r)
1369 return
1370 }
1371 if stealthmode(user.ID, r) {
1372 http.NotFound(w, r)
1373 return
1374 }
1375
1376 j := junk.New()
1377 j["subject"] = fmt.Sprintf("acct:%s@%s", user.Name, serverName)
1378 j["aliases"] = []string{user.URL}
1379 var links []junk.Junk
1380 l := junk.New()
1381 l["rel"] = "self"
1382 l["type"] = `application/activity+json`
1383 l["href"] = user.URL
1384 links = append(links, l)
1385 j["links"] = links
1386
1387 w.Header().Set("Cache-Control", "max-age=3600")
1388 w.Header().Set("Content-Type", "application/jrd+json")
1389 j.Write(w)
1390}
1391
1392func somedays() string {
1393 secs := 432000 + notrand.Int63n(432000)
1394 return fmt.Sprintf("%d", secs)
1395}
1396
1397func avatate(w http.ResponseWriter, r *http.Request) {
1398 n := r.FormValue("a")
1399 a := avatar(n)
1400 w.Header().Set("Cache-Control", "max-age="+somedays())
1401 w.Write(a)
1402}
1403
1404func servecss(w http.ResponseWriter, r *http.Request) {
1405 fd, err := os.Open("views" + r.URL.Path)
1406 if err != nil {
1407 http.NotFound(w, r)
1408 return
1409 }
1410 w.Header().Set("Cache-Control", "max-age=0")
1411 w.Header().Set("Content-Type", "text/css; charset=utf-8")
1412 err = css.Filter(fd, w)
1413 if err != nil {
1414 log.Printf("error filtering css: %s", err)
1415 }
1416}
1417func serveasset(w http.ResponseWriter, r *http.Request) {
1418 w.Header().Set("Cache-Control", "max-age=7776000")
1419 http.ServeFile(w, r, "views"+r.URL.Path)
1420}
1421func servehtml(w http.ResponseWriter, r *http.Request) {
1422 templinfo := getInfo(r)
1423 err := readviews.Execute(w, r.URL.Path[1:]+".html", templinfo)
1424 if err != nil {
1425 log.Print(err)
1426 }
1427}
1428func serveemu(w http.ResponseWriter, r *http.Request) {
1429 xid := mux.Vars(r)["xid"]
1430 w.Header().Set("Cache-Control", "max-age="+somedays())
1431 http.ServeFile(w, r, "emus/"+xid)
1432}
1433func servememe(w http.ResponseWriter, r *http.Request) {
1434 xid := mux.Vars(r)["xid"]
1435 w.Header().Set("Cache-Control", "max-age="+somedays())
1436 http.ServeFile(w, r, "memes/"+xid)
1437}
1438
1439func servefile(w http.ResponseWriter, r *http.Request) {
1440 xid := mux.Vars(r)["xid"]
1441 row := stmtGetFileData.QueryRow(xid)
1442 var media string
1443 var data []byte
1444 err := row.Scan(&media, &data)
1445 if err != nil {
1446 log.Printf("error loading file: %s", err)
1447 http.NotFound(w, r)
1448 return
1449 }
1450 w.Header().Set("Content-Type", media)
1451 w.Header().Set("X-Content-Type-Options", "nosniff")
1452 w.Header().Set("Cache-Control", "max-age="+somedays())
1453 w.Write(data)
1454}
1455
1456func nomoroboto(w http.ResponseWriter, r *http.Request) {
1457 io.WriteString(w, "User-agent: *\n")
1458 io.WriteString(w, "Disallow: /a\n")
1459 io.WriteString(w, "Disallow: /d\n")
1460 io.WriteString(w, "Disallow: /meme\n")
1461 for _, u := range allusers() {
1462 fmt.Fprintf(w, "Disallow: /%s/%s/%s/\n", userSep, u.Username, honkSep)
1463 }
1464}
1465
1466func webhydra(w http.ResponseWriter, r *http.Request) {
1467 u := login.GetUserInfo(r)
1468 userid := u.UserID
1469 templinfo := getInfo(r)
1470 templinfo["HonkCSRF"] = login.GetCSRF("honkhonk", r)
1471 page := r.FormValue("page")
1472 var honks []*Honk
1473 switch page {
1474 case "atme":
1475 honks = gethonksforme(userid)
1476 case "home":
1477 honks = gethonksforuser(userid)
1478 honks = osmosis(honks, userid)
1479 case "first":
1480 honks = gethonksforuserfirstclass(userid)
1481 honks = osmosis(honks, userid)
1482 case "combo":
1483 c := r.FormValue("c")
1484 honks = gethonksbycombo(userid, c)
1485 case "convoy":
1486 c := r.FormValue("c")
1487 honks = gethonksbyconvoy(userid, c)
1488 case "honker":
1489 xid := r.FormValue("xid")
1490 if strings.IndexByte(xid, '@') != -1 {
1491 xid = gofish(xid)
1492 }
1493 honks = gethonksbyxonker(userid, xid)
1494 default:
1495 http.NotFound(w, r)
1496 }
1497 if len(honks) > 0 {
1498 templinfo["TopXID"] = honks[0].XID
1499 }
1500 if topxid := r.FormValue("topxid"); topxid != "" {
1501 for i, h := range honks {
1502 if h.XID == topxid {
1503 honks = honks[0:i]
1504 break
1505 }
1506 }
1507 log.Printf("topxid %d frags", len(honks))
1508 }
1509 reverbolate(userid, honks)
1510 templinfo["Honks"] = honks
1511 w.Header().Set("Content-Type", "text/html; charset=utf-8")
1512 err := readviews.Execute(w, "honkfrags.html", templinfo)
1513 if err != nil {
1514 log.Printf("frag error: %s", err)
1515 }
1516}
1517
1518func serve() {
1519 db := opendatabase()
1520 login.Init(db)
1521
1522 listener, err := openListener()
1523 if err != nil {
1524 log.Fatal(err)
1525 }
1526 go redeliverator()
1527
1528 debug := false
1529 getconfig("debug", &debug)
1530 readviews = templates.Load(debug,
1531 "views/honkpage.html",
1532 "views/honkfrags.html",
1533 "views/honkers.html",
1534 "views/zonkers.html",
1535 "views/combos.html",
1536 "views/honkform.html",
1537 "views/honk.html",
1538 "views/account.html",
1539 "views/about.html",
1540 "views/funzone.html",
1541 "views/login.html",
1542 "views/xzone.html",
1543 "views/header.html",
1544 "views/onts.html",
1545 "views/honkpage.js",
1546 )
1547 if !debug {
1548 assets := []string{"views/style.css", "views/local.css", "views/honkpage.js"}
1549 for _, s := range assets {
1550 savedassetparams[s] = getassetparam(s)
1551 }
1552 }
1553
1554 bitethethumbs()
1555
1556 mux := mux.NewRouter()
1557 mux.Use(login.Checker)
1558
1559 posters := mux.Methods("POST").Subrouter()
1560 getters := mux.Methods("GET").Subrouter()
1561
1562 getters.HandleFunc("/", homepage)
1563 getters.HandleFunc("/home", homepage)
1564 getters.HandleFunc("/front", homepage)
1565 getters.HandleFunc("/robots.txt", nomoroboto)
1566 getters.HandleFunc("/rss", showrss)
1567 getters.HandleFunc("/"+userSep+"/{name:[[:alnum:]]+}", showuser)
1568 getters.HandleFunc("/"+userSep+"/{name:[[:alnum:]]+}/"+honkSep+"/{xid:[[:alnum:]]+}", showhonk)
1569 getters.HandleFunc("/"+userSep+"/{name:[[:alnum:]]+}/rss", showrss)
1570 posters.HandleFunc("/"+userSep+"/{name:[[:alnum:]]+}/inbox", inbox)
1571 getters.HandleFunc("/"+userSep+"/{name:[[:alnum:]]+}/outbox", outbox)
1572 getters.HandleFunc("/"+userSep+"/{name:[[:alnum:]]+}/followers", emptiness)
1573 getters.HandleFunc("/"+userSep+"/{name:[[:alnum:]]+}/following", emptiness)
1574 getters.HandleFunc("/a", avatate)
1575 getters.HandleFunc("/o", thelistingoftheontologies)
1576 getters.HandleFunc("/o/{name:.+}", showontology)
1577 getters.HandleFunc("/d/{xid:[[:alnum:].]+}", servefile)
1578 getters.HandleFunc("/emu/{xid:[[:alnum:]_.-]+}", serveemu)
1579 getters.HandleFunc("/meme/{xid:[[:alnum:]_.-]+}", servememe)
1580 getters.HandleFunc("/.well-known/webfinger", fingerlicker)
1581
1582 getters.HandleFunc("/style.css", servecss)
1583 getters.HandleFunc("/local.css", servecss)
1584 getters.HandleFunc("/honkpage.js", serveasset)
1585 getters.HandleFunc("/about", servehtml)
1586 getters.HandleFunc("/login", servehtml)
1587 posters.HandleFunc("/dologin", login.LoginFunc)
1588 getters.HandleFunc("/logout", login.LogoutFunc)
1589
1590 loggedin := mux.NewRoute().Subrouter()
1591 loggedin.Use(login.Required)
1592 loggedin.HandleFunc("/account", accountpage)
1593 loggedin.HandleFunc("/funzone", showfunzone)
1594 loggedin.HandleFunc("/chpass", dochpass)
1595 loggedin.HandleFunc("/atme", homepage)
1596 loggedin.HandleFunc("/zonkzone", zonkzone)
1597 loggedin.HandleFunc("/xzone", xzone)
1598 loggedin.HandleFunc("/edit", edithonkpage)
1599 loggedin.Handle("/honk", login.CSRFWrap("honkhonk", http.HandlerFunc(submithonk)))
1600 loggedin.Handle("/bonk", login.CSRFWrap("honkhonk", http.HandlerFunc(submitbonk)))
1601 loggedin.Handle("/zonkit", login.CSRFWrap("honkhonk", http.HandlerFunc(zonkit)))
1602 loggedin.Handle("/zonkzonk", login.CSRFWrap("zonkzonk", http.HandlerFunc(zonkzonk)))
1603 loggedin.Handle("/saveuser", login.CSRFWrap("saveuser", http.HandlerFunc(saveuser)))
1604 loggedin.Handle("/ximport", login.CSRFWrap("ximport", http.HandlerFunc(ximport)))
1605 loggedin.HandleFunc("/honkers", showhonkers)
1606 loggedin.HandleFunc("/h/{name:[[:alnum:]_.-]+}", showhonker)
1607 loggedin.HandleFunc("/h", showhonker)
1608 loggedin.HandleFunc("/c/{name:[[:alnum:]_.-]+}", showcombo)
1609 loggedin.HandleFunc("/c", showcombos)
1610 loggedin.HandleFunc("/t", showconvoy)
1611 loggedin.HandleFunc("/q", showsearch)
1612 loggedin.HandleFunc("/hydra", webhydra)
1613 loggedin.Handle("/submithonker", login.CSRFWrap("submithonker", http.HandlerFunc(submithonker)))
1614
1615 err = http.Serve(listener, mux)
1616 if err != nil {
1617 log.Fatal(err)
1618 }
1619}