all repos — honk @ e7b725fabdc66d45df72fe053e0dbcef55aa2c27

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