all repos — honk @ 560c50589cd44efd7e0f62bfa9e08f3c4aa0af33

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