all repos — honk @ 4cf0d95097fb3366550ed3031d15e629a456ccba

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	HTAbout template.HTML
 44	Onts    []string
 45	Key     string
 46	URL     string
 47	Options UserOptions
 48	SecKey  httpsig.PrivateKey
 49}
 50
 51type UserOptions struct {
 52	SkinnyCSS  bool   `json:",omitempty"`
 53	OmitImages bool   `json:",omitempty"`
 54	Avahex     bool   `json:",omitempty"`
 55	MentionAll bool   `json:",omitempty"`
 56	Avatar     string `json:",omitempty"`
 57	MapLink    string `json:",omitempty"`
 58	Reaction   string `json:",omitempty"`
 59	Chats      int
 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	args := flag.Args()
267	cmd := "run"
268	if len(args) > 0 {
269		cmd = args[0]
270	}
271	switch cmd {
272	case "init":
273		initdb()
274	case "upgrade":
275		upgradedb()
276	case "version":
277		fmt.Println(softwareVersion)
278		os.Exit(0)
279	}
280	db := opendatabase()
281	dbversion := 0
282	getconfig("dbversion", &dbversion)
283	if dbversion != myVersion {
284		log.Fatal("incorrect database version. run upgrade.")
285	}
286	getconfig("servermsg", &serverMsg)
287	getconfig("aboutmsg", &aboutMsg)
288	getconfig("loginmsg", &loginMsg)
289	getconfig("servername", &serverName)
290	getconfig("masqname", &masqName)
291	if masqName == "" {
292		masqName = serverName
293	}
294	serverPrefix = fmt.Sprintf("https://%s/", serverName)
295	getconfig("usersep", &userSep)
296	getconfig("honksep", &honkSep)
297	getconfig("debug", &debugMode)
298	prepareStatements(db)
299	switch cmd {
300	case "admin":
301		adminscreen()
302	case "import":
303		if len(args) != 4 {
304			log.Fatal("import username mastodon|twitter srcdir")
305		}
306		importMain(args[1], args[2], args[3])
307	case "debug":
308		if len(args) != 2 {
309			log.Fatal("need an argument: debug (on|off)")
310		}
311		switch args[1] {
312		case "on":
313			setconfig("debug", 1)
314		case "off":
315			setconfig("debug", 0)
316		default:
317			log.Fatal("argument must be on or off")
318		}
319	case "adduser":
320		adduser()
321	case "deluser":
322		if len(args) < 2 {
323			fmt.Printf("usage: honk deluser username\n")
324			return
325		}
326		deluser(args[1])
327	case "chpass":
328		chpass()
329	case "cleanup":
330		arg := "30"
331		if len(args) > 1 {
332			arg = args[1]
333		}
334		cleanupdb(arg)
335	case "unplug":
336		if len(args) < 2 {
337			fmt.Printf("usage: honk unplug servername\n")
338			return
339		}
340		name := args[1]
341		unplugserver(name)
342	case "backup":
343		if len(args) < 2 {
344			fmt.Printf("usage: honk backup dirname\n")
345			return
346		}
347		name := args[1]
348		svalbard(name)
349	case "ping":
350		if len(args) < 3 {
351			fmt.Printf("usage: honk ping (from username) (to username or url)\n")
352			return
353		}
354		name := args[1]
355		targ := args[2]
356		user, err := butwhatabout(name)
357		if err != nil {
358			log.Printf("unknown user")
359			return
360		}
361		ping(user, targ)
362	case "run":
363		serve()
364	case "backend":
365		backendServer()
366	case "test":
367		ElaborateUnitTests()
368	default:
369		log.Fatal("unknown command")
370	}
371}