all repos — honk @ 7fc65173054ff234db65c78f211c7369c567eeff

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