all repos — honk @ c62665548885698c443c82502effb9fcf8d6abbf

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