all repos — honk @ ebd4ff4fb985f911747ee0bdc02f5f69c96bcf42

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