all repos — honk @ 394d8b47c1d37bafad44318b77777af3cc64693a

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