all repos — honk @ 51747f54012e779a0354077d3dd883655adb896a

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