all repos — honk @ 6edbe6d79aa661edf593d6fda404d231da603687

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