all repos — honk @ ca598399f31e6d1b408791d588048d4bac294c69

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