all repos — honk @ 2d2594339703492189d3a2b90b7b0f039d44f09a

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