all repos — honk @ 76d8e301e74b46da13000eb4d775b3dd76c0e3c5

my fork of honk

honk.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	"flag"
 20	"fmt"
 21	"html/template"
 22	"log"
 23	notrand "math/rand"
 24	"os"
 25	"strconv"
 26	"strings"
 27	"time"
 28
 29	"humungus.tedunangst.com/r/webs/httpsig"
 30)
 31
 32var softwareVersion = "develop"
 33
 34func init() {
 35	notrand.Seed(time.Now().Unix())
 36}
 37
 38type WhatAbout struct {
 39	ID      int64
 40	Name    string
 41	Display string
 42	About   string
 43	Key     string
 44	URL     string
 45	Options UserOptions
 46	SecKey  httpsig.PrivateKey
 47}
 48
 49type UserOptions struct {
 50	SkinnyCSS  bool   `json:",omitempty"`
 51	OmitImages bool   `json:",omitempty"`
 52	Avatar     string `json:",omitempty"`
 53	MapLink    string `json:",omitempty"`
 54	Reaction   string `json:",omitempty"`
 55}
 56
 57type KeyInfo struct {
 58	keyname string
 59	seckey  httpsig.PrivateKey
 60}
 61
 62const serverUID int64 = -2
 63
 64type Honk struct {
 65	ID       int64
 66	UserID   int64
 67	Username string
 68	What     string
 69	Honker   string
 70	Handle   string
 71	Oonker   string
 72	Oondle   string
 73	XID      string
 74	RID      string
 75	Date     time.Time
 76	URL      string
 77	Noise    string
 78	Precis   string
 79	Format   string
 80	Convoy   string
 81	Audience []string
 82	Public   bool
 83	Whofore  int64
 84	Replies  []*Honk
 85	Flags    int64
 86	HTPrecis template.HTML
 87	HTML     template.HTML
 88	Style    string
 89	Open     string
 90	Donks    []*Donk
 91	Onts     []string
 92	Place    *Place
 93	Time     *Time
 94	Mentions []Mention
 95}
 96
 97type Chonk struct {
 98	ID     int64
 99	UserID int64
100	XID    string
101	Who    string
102	Target string
103	Date   time.Time
104	Noise  string
105	Format string
106	Donks  []*Donk
107	Handle string
108	HTML   template.HTML
109}
110
111type Mention struct {
112	Who   string
113	Where string
114}
115
116type OldRevision struct {
117	Precis string
118	Noise  string
119}
120
121const (
122	flagIsAcked    = 1
123	flagIsBonked   = 2
124	flagIsSaved    = 4
125	flagIsUntagged = 8
126	flagIsReacted  = 16
127)
128
129func (honk *Honk) IsAcked() bool {
130	return honk.Flags&flagIsAcked != 0
131}
132
133func (honk *Honk) IsBonked() bool {
134	return honk.Flags&flagIsBonked != 0
135}
136
137func (honk *Honk) IsSaved() bool {
138	return honk.Flags&flagIsSaved != 0
139}
140
141func (honk *Honk) IsUntagged() bool {
142	return honk.Flags&flagIsUntagged != 0
143}
144
145func (honk *Honk) IsReacted() bool {
146	return honk.Flags&flagIsReacted != 0
147}
148
149type Donk struct {
150	FileID int64
151	XID    string
152	Name   string
153	Desc   string
154	URL    string
155	Media  string
156	Local  bool
157}
158
159type Place struct {
160	Name      string
161	Latitude  float64
162	Longitude float64
163	Url       string
164}
165
166type Duration int64
167
168func (d Duration) String() string {
169	s := time.Duration(d).String()
170	if strings.HasSuffix(s, "m0s") {
171		s = s[:len(s)-2]
172	}
173	if strings.HasSuffix(s, "h0m") {
174		s = s[:len(s)-2]
175	}
176	return s
177}
178
179func parseDuration(s string) time.Duration {
180	didx := strings.IndexByte(s, 'd')
181	if didx != -1 {
182		days, _ := strconv.ParseInt(s[:didx], 10, 0)
183		dur, _ := time.ParseDuration(s[didx:])
184		return dur + 24*time.Hour*time.Duration(days)
185	}
186	dur, _ := time.ParseDuration(s)
187	return dur
188}
189
190type Time struct {
191	StartTime time.Time
192	EndTime   time.Time
193	Duration  Duration
194}
195
196type Honker struct {
197	ID     int64
198	UserID int64
199	Name   string
200	XID    string
201	Handle string
202	Flavor string
203	Combos []string
204	Meta   HonkerMeta
205}
206
207type HonkerMeta struct {
208	Notes string
209}
210
211type SomeThing struct {
212	What  int
213	XID   string
214	Owner string
215	Name  string
216}
217
218const (
219	SomeNothing int = iota
220	SomeActor
221	SomeCollection
222)
223
224var serverName string
225var masqName string
226var dataDir = "."
227var viewDir = "."
228var iconName = "icon.png"
229var serverMsg template.HTML
230var aboutMsg template.HTML
231var loginMsg template.HTML
232
233func ElaborateUnitTests() {
234}
235
236func unplugserver(hostname string) {
237	db := opendatabase()
238	xid := fmt.Sprintf("%%https://%s/%%", hostname)
239	db.Exec("delete from honkers where xid like ? and flavor = 'dub'", xid)
240	db.Exec("delete from doovers where rcpt like ?", xid)
241}
242
243func main() {
244	flag.StringVar(&dataDir, "datadir", dataDir, "data directory")
245	flag.StringVar(&viewDir, "viewdir", viewDir, "view directory")
246	flag.Parse()
247	args := flag.Args()
248	cmd := "run"
249	if len(args) > 0 {
250		cmd = args[0]
251	}
252	switch cmd {
253	case "init":
254		initdb()
255	case "upgrade":
256		upgradedb()
257	case "version":
258		fmt.Println(softwareVersion)
259		os.Exit(0)
260	}
261	db := opendatabase()
262	dbversion := 0
263	getconfig("dbversion", &dbversion)
264	if dbversion != myVersion {
265		log.Fatal("incorrect database version. run upgrade.")
266	}
267	getconfig("servermsg", &serverMsg)
268	getconfig("aboutmsg", &aboutMsg)
269	getconfig("loginmsg", &loginMsg)
270	getconfig("servername", &serverName)
271	getconfig("masqname", &masqName)
272	if masqName == "" {
273		masqName = serverName
274	}
275	getconfig("usersep", &userSep)
276	getconfig("honksep", &honkSep)
277	getconfig("debug", &debugMode)
278	prepareStatements(db)
279	switch cmd {
280	case "admin":
281		adminscreen()
282	case "import":
283		if len(args) != 4 {
284			log.Fatal("import username mastodon|twitter srcdir")
285		}
286		importMain(args[1], args[2], args[3])
287	case "debug":
288		if len(args) != 2 {
289			log.Fatal("need an argument: debug (on|off)")
290		}
291		switch args[1] {
292		case "on":
293			setconfig("debug", 1)
294		case "off":
295			setconfig("debug", 0)
296		default:
297			log.Fatal("argument must be on or off")
298		}
299	case "adduser":
300		adduser()
301	case "deluser":
302		if len(args) < 2 {
303			fmt.Printf("usage: honk deluser username\n")
304			return
305		}
306		deluser(args[1])
307	case "chpass":
308		chpass()
309	case "cleanup":
310		arg := "30"
311		if len(args) > 1 {
312			arg = args[1]
313		}
314		cleanupdb(arg)
315	case "unplug":
316		if len(args) < 2 {
317			fmt.Printf("usage: honk unplug servername\n")
318			return
319		}
320		name := args[1]
321		unplugserver(name)
322	case "ping":
323		if len(args) < 3 {
324			fmt.Printf("usage: honk ping from to\n")
325			return
326		}
327		name := args[1]
328		targ := args[2]
329		user, err := butwhatabout(name)
330		if err != nil {
331			log.Printf("unknown user")
332			return
333		}
334		ping(user, targ)
335	case "run":
336		serve()
337	case "backend":
338		backendServer()
339	case "test":
340		xid := args[1]
341		chatter := loadchatter(1)
342		for _, chonks := range chatter {
343			for _, ch := range chonks {
344				if ch.XID == xid {
345					var user *WhatAbout
346					somenumberedusers.Get(ch.UserID, &user)
347					msg := chonkifymsg(user, ch)
348					fmt.Println(string(msg))
349				}
350			}
351		}
352		ElaborateUnitTests()
353	default:
354		log.Fatal("unknown command")
355	}
356}