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