all repos — honk @ 47529122da6f06a62dd609325abdee656ba7de64

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	prepareStatements(db)
324	switch cmd {
325	case "admin":
326		adminscreen()
327	case "import":
328		if len(args) != 4 {
329			elog.Fatal("import username mastodon|twitter srcdir")
330		}
331		importMain(args[1], args[2], args[3])
332	case "devel":
333		if len(args) != 2 {
334			elog.Fatal("need an argument: devel (on|off)")
335		}
336		switch args[1] {
337		case "on":
338			setconfig("devel", 1)
339		case "off":
340			setconfig("devel", 0)
341		default:
342			elog.Fatal("argument must be on or off")
343		}
344	case "adduser":
345		adduser()
346	case "deluser":
347		if len(args) < 2 {
348			fmt.Printf("usage: honk deluser username\n")
349			return
350		}
351		deluser(args[1])
352	case "chpass":
353		chpass()
354	case "cleanup":
355		arg := "30"
356		if len(args) > 1 {
357			arg = args[1]
358		}
359		cleanupdb(arg)
360	case "unplug":
361		if len(args) < 2 {
362			fmt.Printf("usage: honk unplug servername\n")
363			return
364		}
365		name := args[1]
366		unplugserver(name)
367	case "backup":
368		if len(args) < 2 {
369			fmt.Printf("usage: honk backup dirname\n")
370			return
371		}
372		name := args[1]
373		svalbard(name)
374	case "ping":
375		if len(args) < 3 {
376			fmt.Printf("usage: honk ping (from username) (to username or url)\n")
377			return
378		}
379		name := args[1]
380		targ := args[2]
381		user, err := butwhatabout(name)
382		if err != nil {
383			elog.Printf("unknown user")
384			return
385		}
386		ping(user, targ)
387	case "run":
388		serve()
389	case "backend":
390		backendServer()
391	case "test":
392		ElaborateUnitTests()
393	default:
394		elog.Fatal("unknown command")
395	}
396}