all repos — honk @ 4dc42a9ed445ac4351aaa15f9742065dacce3558

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