all repos — honk @ aec7a0851ef9ed273809e7a1778aa6e84de35e3e

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 main() {
263	flag.StringVar(&dataDir, "datadir", dataDir, "data directory")
264	flag.StringVar(&viewDir, "viewdir", viewDir, "view directory")
265	flag.Parse()
266
267	if alllogname != "stderr" {
268		elogname = alllogname
269		ilogname = alllogname
270		dlogname = alllogname
271	}
272	initLogging(elogname, ilogname, dlogname)
273
274	args := flag.Args()
275	cmd := "run"
276	if len(args) > 0 {
277		cmd = args[0]
278	}
279	switch cmd {
280	case "init":
281		initdb()
282	case "upgrade":
283		upgradedb()
284	case "version":
285		fmt.Println(softwareVersion)
286		os.Exit(0)
287	}
288	db := opendatabase()
289	dbversion := 0
290	getconfig("dbversion", &dbversion)
291	if dbversion != myVersion {
292		elog.Fatal("incorrect database version. run upgrade.")
293	}
294	getconfig("servermsg", &serverMsg)
295	getconfig("aboutmsg", &aboutMsg)
296	getconfig("loginmsg", &loginMsg)
297	getconfig("servername", &serverName)
298	getconfig("masqname", &masqName)
299	if masqName == "" {
300		masqName = serverName
301	}
302	serverPrefix = fmt.Sprintf("https://%s/", serverName)
303	getconfig("usersep", &userSep)
304	getconfig("honksep", &honkSep)
305	getconfig("debug", &debugMode)
306	prepareStatements(db)
307	switch cmd {
308	case "admin":
309		adminscreen()
310	case "import":
311		if len(args) != 4 {
312			elog.Fatal("import username mastodon|twitter srcdir")
313		}
314		importMain(args[1], args[2], args[3])
315	case "debug":
316		if len(args) != 2 {
317			elog.Fatal("need an argument: debug (on|off)")
318		}
319		switch args[1] {
320		case "on":
321			setconfig("debug", 1)
322		case "off":
323			setconfig("debug", 0)
324		default:
325			elog.Fatal("argument must be on or off")
326		}
327	case "adduser":
328		adduser()
329	case "deluser":
330		if len(args) < 2 {
331			fmt.Printf("usage: honk deluser username\n")
332			return
333		}
334		deluser(args[1])
335	case "chpass":
336		chpass()
337	case "cleanup":
338		arg := "30"
339		if len(args) > 1 {
340			arg = args[1]
341		}
342		cleanupdb(arg)
343	case "unplug":
344		if len(args) < 2 {
345			fmt.Printf("usage: honk unplug servername\n")
346			return
347		}
348		name := args[1]
349		unplugserver(name)
350	case "backup":
351		if len(args) < 2 {
352			fmt.Printf("usage: honk backup dirname\n")
353			return
354		}
355		name := args[1]
356		svalbard(name)
357	case "ping":
358		if len(args) < 3 {
359			fmt.Printf("usage: honk ping (from username) (to username or url)\n")
360			return
361		}
362		name := args[1]
363		targ := args[2]
364		user, err := butwhatabout(name)
365		if err != nil {
366			elog.Printf("unknown user")
367			return
368		}
369		ping(user, targ)
370	case "run":
371		serve()
372	case "backend":
373		backendServer()
374	case "test":
375		ElaborateUnitTests()
376	default:
377		elog.Fatal("unknown command")
378	}
379}