all repos — honk @ dfad4879c8226e3a8cd776b1ee9caf5d83f9af84

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}
167
168type Place struct {
169	Name      string
170	Latitude  float64
171	Longitude float64
172	Url       string
173}
174
175type Duration int64
176
177func (d Duration) String() string {
178	s := time.Duration(d).String()
179	if strings.HasSuffix(s, "m0s") {
180		s = s[:len(s)-2]
181	}
182	if strings.HasSuffix(s, "h0m") {
183		s = s[:len(s)-2]
184	}
185	return s
186}
187
188func parseDuration(s string) time.Duration {
189	didx := strings.IndexByte(s, 'd')
190	if didx != -1 {
191		days, _ := strconv.ParseInt(s[:didx], 10, 0)
192		dur, _ := time.ParseDuration(s[didx:])
193		return dur + 24*time.Hour*time.Duration(days)
194	}
195	dur, _ := time.ParseDuration(s)
196	return dur
197}
198
199type Time struct {
200	StartTime time.Time
201	EndTime   time.Time
202	Duration  Duration
203}
204
205type Honker struct {
206	ID     int64
207	UserID int64
208	Name   string
209	XID    string
210	Handle string
211	Flavor string
212	Combos []string
213	Meta   HonkerMeta
214}
215
216type HonkerMeta struct {
217	Notes string
218}
219
220type SomeThing struct {
221	What  int
222	XID   string
223	Owner string
224	Name  string
225}
226
227const (
228	SomeNothing int = iota
229	SomeActor
230	SomeCollection
231)
232
233var serverName string
234var masqName string
235var dataDir = "."
236var viewDir = "."
237var iconName = "icon.png"
238var serverMsg template.HTML
239var aboutMsg template.HTML
240var loginMsg template.HTML
241
242func ElaborateUnitTests() {
243}
244
245func unplugserver(hostname string) {
246	db := opendatabase()
247	xid := fmt.Sprintf("%%https://%s/%%", hostname)
248	db.Exec("delete from honkers where xid like ? and flavor = 'dub'", xid)
249	db.Exec("delete from doovers where rcpt like ?", xid)
250}
251
252func main() {
253	flag.StringVar(&dataDir, "datadir", dataDir, "data directory")
254	flag.StringVar(&viewDir, "viewdir", viewDir, "view directory")
255	flag.Parse()
256	args := flag.Args()
257	cmd := "run"
258	if len(args) > 0 {
259		cmd = args[0]
260	}
261	switch cmd {
262	case "init":
263		initdb()
264	case "upgrade":
265		upgradedb()
266	case "version":
267		fmt.Println(softwareVersion)
268		os.Exit(0)
269	}
270	db := opendatabase()
271	dbversion := 0
272	getconfig("dbversion", &dbversion)
273	if dbversion != myVersion {
274		log.Fatal("incorrect database version. run upgrade.")
275	}
276	getconfig("servermsg", &serverMsg)
277	getconfig("aboutmsg", &aboutMsg)
278	getconfig("loginmsg", &loginMsg)
279	getconfig("servername", &serverName)
280	getconfig("masqname", &masqName)
281	if masqName == "" {
282		masqName = serverName
283	}
284	getconfig("usersep", &userSep)
285	getconfig("honksep", &honkSep)
286	getconfig("debug", &debugMode)
287	prepareStatements(db)
288	switch cmd {
289	case "admin":
290		adminscreen()
291	case "import":
292		if len(args) != 4 {
293			log.Fatal("import username mastodon|twitter srcdir")
294		}
295		importMain(args[1], args[2], args[3])
296	case "debug":
297		if len(args) != 2 {
298			log.Fatal("need an argument: debug (on|off)")
299		}
300		switch args[1] {
301		case "on":
302			setconfig("debug", 1)
303		case "off":
304			setconfig("debug", 0)
305		default:
306			log.Fatal("argument must be on or off")
307		}
308	case "adduser":
309		adduser()
310	case "deluser":
311		if len(args) < 2 {
312			fmt.Printf("usage: honk deluser username\n")
313			return
314		}
315		deluser(args[1])
316	case "chpass":
317		chpass()
318	case "cleanup":
319		arg := "30"
320		if len(args) > 1 {
321			arg = args[1]
322		}
323		cleanupdb(arg)
324	case "unplug":
325		if len(args) < 2 {
326			fmt.Printf("usage: honk unplug servername\n")
327			return
328		}
329		name := args[1]
330		unplugserver(name)
331	case "backup":
332		if len(args) < 2 {
333			fmt.Printf("usage: honk backup dirname\n")
334			return
335		}
336		name := args[1]
337		svalbard(name)
338	case "ping":
339		if len(args) < 3 {
340			fmt.Printf("usage: honk ping from to\n")
341			return
342		}
343		name := args[1]
344		targ := args[2]
345		user, err := butwhatabout(name)
346		if err != nil {
347			log.Printf("unknown user")
348			return
349		}
350		ping(user, targ)
351	case "run":
352		serve()
353	case "backend":
354		backendServer()
355	case "test":
356		ElaborateUnitTests()
357	default:
358		log.Fatal("unknown command")
359	}
360}