all repos — honk @ 489528e3dcf861b5a4747abe25bf88cf62304a81

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