all repos — honk @ 8a0d7362a4559a898070f54293d6062e4983906c

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