all repos — honk @ f51596b6f2f7798c58c40ef5186b02168f439445

my fork of honk

database.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	"database/sql"
 20	"fmt"
 21	"log"
 22	"strconv"
 23	"strings"
 24	"time"
 25
 26	"humungus.tedunangst.com/r/webs/login"
 27)
 28
 29func butwhatabout(name string) (*WhatAbout, error) {
 30	row := stmtWhatAbout.QueryRow(name)
 31	var user WhatAbout
 32	var options string
 33	err := row.Scan(&user.ID, &user.Name, &user.Display, &user.About, &user.Key, &options)
 34	user.URL = fmt.Sprintf("https://%s/%s/%s", serverName, userSep, user.Name)
 35	user.SkinnyCSS = strings.Contains(options, " skinny ")
 36	return &user, err
 37}
 38
 39func gethonkers(userid int64) []*Honker {
 40	rows, err := stmtHonkers.Query(userid)
 41	if err != nil {
 42		log.Printf("error querying honkers: %s", err)
 43		return nil
 44	}
 45	defer rows.Close()
 46	var honkers []*Honker
 47	for rows.Next() {
 48		var f Honker
 49		var combos string
 50		err = rows.Scan(&f.ID, &f.UserID, &f.Name, &f.XID, &f.Flavor, &combos)
 51		f.Combos = strings.Split(strings.TrimSpace(combos), " ")
 52		if err != nil {
 53			log.Printf("error scanning honker: %s", err)
 54			return nil
 55		}
 56		honkers = append(honkers, &f)
 57	}
 58	return honkers
 59}
 60
 61func getdubs(userid int64) []*Honker {
 62	rows, err := stmtDubbers.Query(userid)
 63	if err != nil {
 64		log.Printf("error querying dubs: %s", err)
 65		return nil
 66	}
 67	defer rows.Close()
 68	var honkers []*Honker
 69	for rows.Next() {
 70		var f Honker
 71		err = rows.Scan(&f.ID, &f.UserID, &f.Name, &f.XID, &f.Flavor)
 72		if err != nil {
 73			log.Printf("error scanning honker: %s", err)
 74			return nil
 75		}
 76		honkers = append(honkers, &f)
 77	}
 78	return honkers
 79}
 80
 81func allusers() []login.UserInfo {
 82	var users []login.UserInfo
 83	rows, _ := opendatabase().Query("select userid, username from users")
 84	defer rows.Close()
 85	for rows.Next() {
 86		var u login.UserInfo
 87		rows.Scan(&u.UserID, &u.Username)
 88		users = append(users, u)
 89	}
 90	return users
 91}
 92
 93func getxonk(userid int64, xid string) *Honk {
 94	h := new(Honk)
 95	var dt, aud, onts string
 96	row := stmtOneXonk.QueryRow(userid, xid)
 97	err := row.Scan(&h.ID, &h.UserID, &h.Username, &h.What, &h.Honker, &h.Oonker, &h.XID, &h.RID,
 98		&dt, &h.URL, &aud, &h.Noise, &h.Precis, &h.Convoy, &h.Whofore, &h.Flags, &onts)
 99	if err != nil {
100		if err != sql.ErrNoRows {
101			log.Printf("error scanning xonk: %s", err)
102		}
103		return nil
104	}
105	h.Date, _ = time.Parse(dbtimeformat, dt)
106	h.Audience = strings.Split(aud, " ")
107	h.Public = !keepitquiet(h.Audience)
108	if len(onts) > 0 {
109		h.Onts = strings.Split(onts, " ")
110	}
111	return h
112}
113
114func getbonk(userid int64, xid string) *Honk {
115	h := new(Honk)
116	var dt, aud, onts string
117	row := stmtOneBonk.QueryRow(userid, xid)
118	err := row.Scan(&h.ID, &h.UserID, &h.Username, &h.What, &h.Honker, &h.Oonker, &h.XID, &h.RID,
119		&dt, &h.URL, &aud, &h.Noise, &h.Precis, &h.Convoy, &h.Whofore, &h.Flags, &onts)
120	if err != nil {
121		if err != sql.ErrNoRows {
122			log.Printf("error scanning xonk: %s", err)
123		}
124		return nil
125	}
126	h.Date, _ = time.Parse(dbtimeformat, dt)
127	h.Audience = strings.Split(aud, " ")
128	h.Public = !keepitquiet(h.Audience)
129	if len(onts) > 0 {
130		h.Onts = strings.Split(onts, " ")
131	}
132	return h
133}
134
135func getpublichonks() []*Honk {
136	dt := time.Now().UTC().Add(-7 * 24 * time.Hour).Format(dbtimeformat)
137	rows, err := stmtPublicHonks.Query(dt)
138	return getsomehonks(rows, err)
139}
140func gethonksbyuser(name string, includeprivate bool) []*Honk {
141	dt := time.Now().UTC().Add(-7 * 24 * time.Hour).Format(dbtimeformat)
142	whofore := 2
143	if includeprivate {
144		whofore = 3
145	}
146	rows, err := stmtUserHonks.Query(whofore, name, dt)
147	return getsomehonks(rows, err)
148}
149func gethonksforuser(userid int64) []*Honk {
150	dt := time.Now().UTC().Add(-7 * 24 * time.Hour).Format(dbtimeformat)
151	rows, err := stmtHonksForUser.Query(userid, dt, userid, userid)
152	return getsomehonks(rows, err)
153}
154func gethonksforme(userid int64) []*Honk {
155	dt := time.Now().UTC().Add(-7 * 24 * time.Hour).Format(dbtimeformat)
156	rows, err := stmtHonksForMe.Query(userid, dt, userid)
157	return getsomehonks(rows, err)
158}
159func gethonksbyhonker(userid int64, honker string) []*Honk {
160	rows, err := stmtHonksByHonker.Query(userid, honker, userid)
161	return getsomehonks(rows, err)
162}
163func gethonksbyxonker(userid int64, xonker string) []*Honk {
164	rows, err := stmtHonksByXonker.Query(userid, xonker, xonker, userid)
165	return getsomehonks(rows, err)
166}
167func gethonksbycombo(userid int64, combo string) []*Honk {
168	combo = "% " + combo + " %"
169	rows, err := stmtHonksByCombo.Query(userid, combo, userid)
170	return getsomehonks(rows, err)
171}
172func gethonksbyconvoy(userid int64, convoy string) []*Honk {
173	rows, err := stmtHonksByConvoy.Query(userid, userid, convoy)
174	honks := getsomehonks(rows, err)
175	for i, j := 0, len(honks)-1; i < j; i, j = i+1, j-1 {
176		honks[i], honks[j] = honks[j], honks[i]
177	}
178	return honks
179}
180func gethonksbysearch(userid int64, q string) []*Honk {
181	q = "%" + q + "%"
182	rows, err := stmtHonksBySearch.Query(userid, q)
183	honks := getsomehonks(rows, err)
184	return honks
185}
186func gethonksbyontology(userid int64, name string) []*Honk {
187	rows, err := stmtHonksByOntology.Query(name, userid, userid)
188	honks := getsomehonks(rows, err)
189	return honks
190}
191
192func getsomehonks(rows *sql.Rows, err error) []*Honk {
193	if err != nil {
194		log.Printf("error querying honks: %s", err)
195		return nil
196	}
197	defer rows.Close()
198	var honks []*Honk
199	for rows.Next() {
200		var h Honk
201		var dt, aud, onts string
202		err = rows.Scan(&h.ID, &h.UserID, &h.Username, &h.What, &h.Honker, &h.Oonker, &h.XID,
203			&h.RID, &dt, &h.URL, &aud, &h.Noise, &h.Precis, &h.Convoy, &h.Whofore, &h.Flags, &onts)
204		if err != nil {
205			log.Printf("error scanning honks: %s", err)
206			return nil
207		}
208		h.Date, _ = time.Parse(dbtimeformat, dt)
209		h.Audience = strings.Split(aud, " ")
210		h.Public = !keepitquiet(h.Audience)
211		if len(onts) > 0 {
212			h.Onts = strings.Split(onts, " ")
213		}
214		honks = append(honks, &h)
215	}
216	rows.Close()
217	donksforhonks(honks)
218	return honks
219}
220
221func donksforhonks(honks []*Honk) {
222	db := opendatabase()
223	var ids []string
224	hmap := make(map[int64]*Honk)
225	for _, h := range honks {
226		ids = append(ids, fmt.Sprintf("%d", h.ID))
227		hmap[h.ID] = h
228	}
229	q := fmt.Sprintf("select honkid, donks.fileid, xid, name, description, url, media, local from donks join files on donks.fileid = files.fileid where honkid in (%s)", strings.Join(ids, ","))
230	rows, err := db.Query(q)
231	if err != nil {
232		log.Printf("error querying donks: %s", err)
233		return
234	}
235	defer rows.Close()
236	for rows.Next() {
237		var hid int64
238		var d Donk
239		err = rows.Scan(&hid, &d.FileID, &d.XID, &d.Name, &d.Desc, &d.URL, &d.Media, &d.Local)
240		if err != nil {
241			log.Printf("error scanning donk: %s", err)
242			continue
243		}
244		h := hmap[hid]
245		h.Donks = append(h.Donks, &d)
246	}
247}
248
249func savehonk(h *Honk) error {
250	dt := h.Date.UTC().Format(dbtimeformat)
251	aud := strings.Join(h.Audience, " ")
252
253	res, err := stmtSaveHonk.Exec(h.UserID, h.What, h.Honker, h.XID, h.RID, dt, h.URL,
254		aud, h.Noise, h.Convoy, h.Whofore, h.Format, h.Precis,
255		h.Oonker, h.Flags, strings.Join(h.Onts, " "))
256	if err != nil {
257		log.Printf("err saving honk: %s", err)
258		return err
259	}
260	h.ID, _ = res.LastInsertId()
261	for _, d := range h.Donks {
262		_, err = stmtSaveDonk.Exec(h.ID, d.FileID)
263		if err != nil {
264			log.Printf("err saving donk: %s", err)
265			return err
266		}
267	}
268	for _, o := range h.Onts {
269		_, err = stmtSaveOnts.Exec(strings.ToLower(o), h.ID)
270		if err != nil {
271			log.Printf("error saving ont: %s", err)
272			return err
273		}
274	}
275	return nil
276}
277
278func cleanupdb(arg string) {
279	db := opendatabase()
280	days, err := strconv.Atoi(arg)
281	if err != nil {
282		honker := arg
283		expdate := time.Now().UTC().Add(-3 * 24 * time.Hour).Format(dbtimeformat)
284		doordie(db, "delete from donks where honkid in (select honkid from honks where dt < ? and whofore = 0 and honker = ?)", expdate, honker)
285		doordie(db, "delete from honks where dt < ? and whofore = 0 and honker = ?", expdate, honker)
286	} else {
287		expdate := time.Now().UTC().Add(-time.Duration(days) * 24 * time.Hour).Format(dbtimeformat)
288		doordie(db, "delete from donks where honkid in (select honkid from honks where dt < ? and whofore = 0 and convoy not in (select convoy from honks where whofore = 2 or whofore = 3))", expdate)
289		doordie(db, "delete from honks where dt < ? and whofore = 0 and convoy not in (select convoy from honks where whofore = 2 or whofore = 3)", expdate)
290	}
291	doordie(db, "delete from files where fileid not in (select fileid from donks)")
292	for _, u := range allusers() {
293		doordie(db, "delete from zonkers where userid = ? and wherefore = 'zonvoy' and zonkerid < (select zonkerid from zonkers where userid = ? and wherefore = 'zonvoy' order by zonkerid desc limit 1 offset 200)", u.UserID, u.UserID)
294	}
295}
296
297var stmtHonkers, stmtDubbers, stmtSaveHonker, stmtUpdateFlavor, stmtUpdateCombos *sql.Stmt
298var stmtOneXonk, stmtPublicHonks, stmtUserHonks, stmtHonksByCombo, stmtHonksByConvoy *sql.Stmt
299var stmtHonksByOntology, stmtHonksForUser, stmtHonksForMe, stmtSaveDub, stmtHonksByXonker *sql.Stmt
300var stmtHonksBySearch, stmtHonksByHonker, stmtSaveHonk, stmtFileData, stmtWhatAbout *sql.Stmt
301var stmtOneBonk, stmtFindZonk, stmtFindXonk, stmtSaveDonk, stmtFindFile, stmtSaveFile *sql.Stmt
302var stmtAddDoover, stmtGetDoovers, stmtLoadDoover, stmtZapDoover, stmtOneHonker *sql.Stmt
303var stmtThumbBiters, stmtZonkIt, stmtZonkDonks, stmtSaveZonker *sql.Stmt
304var stmtGetZonkers, stmtRecentHonkers, stmtGetXonker, stmtSaveXonker, stmtDeleteXonker *sql.Stmt
305var stmtSelectOnts, stmtSaveOnts, stmtUpdateFlags, stmtClearFlags *sql.Stmt
306
307func preparetodie(db *sql.DB, s string) *sql.Stmt {
308	stmt, err := db.Prepare(s)
309	if err != nil {
310		log.Fatalf("error %s: %s", err, s)
311	}
312	return stmt
313}
314
315func prepareStatements(db *sql.DB) {
316	stmtHonkers = preparetodie(db, "select honkerid, userid, name, xid, flavor, combos from honkers where userid = ? and (flavor = 'sub' or flavor = 'peep' or flavor = 'unsub') order by name")
317	stmtSaveHonker = preparetodie(db, "insert into honkers (userid, name, xid, flavor, combos) values (?, ?, ?, ?, ?)")
318	stmtUpdateFlavor = preparetodie(db, "update honkers set flavor = ? where userid = ? and xid = ? and flavor = ?")
319	stmtUpdateCombos = preparetodie(db, "update honkers set combos = ? where honkerid = ? and userid = ?")
320	stmtOneHonker = preparetodie(db, "select xid from honkers where name = ? and userid = ?")
321	stmtDubbers = preparetodie(db, "select honkerid, userid, name, xid, flavor from honkers where userid = ? and flavor = 'dub'")
322
323	selecthonks := "select honks.honkid, honks.userid, username, what, honker, oonker, honks.xid, rid, dt, url, audience, noise, precis, convoy, whofore, flags, onts from honks join users on honks.userid = users.userid "
324	limit := " order by honks.honkid desc limit 250"
325	butnotthose := " and convoy not in (select name from zonkers where userid = ? and wherefore = 'zonvoy' order by zonkerid desc limit 100)"
326	stmtOneXonk = preparetodie(db, selecthonks+"where honks.userid = ? and xid = ?")
327	stmtOneBonk = preparetodie(db, selecthonks+"where honks.userid = ? and xid = ? and what = 'bonk' and whofore = 2")
328	stmtPublicHonks = preparetodie(db, selecthonks+"where whofore = 2 and dt > ?"+limit)
329	stmtUserHonks = preparetodie(db, selecthonks+"where (whofore = 2 or whofore = ?) and username = ? and dt > ?"+limit)
330	stmtHonksForUser = preparetodie(db, selecthonks+"where honks.userid = ? and dt > ? and honker in (select xid from honkers where userid = ? and flavor = 'sub' and combos not like '% - %')"+butnotthose+limit)
331	stmtHonksForMe = preparetodie(db, selecthonks+"where honks.userid = ? and dt > ? and whofore = 1"+butnotthose+limit)
332	stmtHonksByHonker = preparetodie(db, selecthonks+"join honkers on (honkers.xid = honks.honker or honkers.xid = honks.oonker) where honks.userid = ? and honkers.name = ?"+butnotthose+limit)
333	stmtHonksByXonker = preparetodie(db, selecthonks+" where honks.userid = ? and (honker = ? or oonker = ?)"+butnotthose+limit)
334	stmtHonksByCombo = preparetodie(db, selecthonks+"join honkers on honkers.xid = honks.honker where honks.userid = ? and honkers.combos like ?"+butnotthose+limit)
335	stmtHonksBySearch = preparetodie(db, selecthonks+"where honks.userid = ? and noise like ?"+limit)
336	stmtHonksByConvoy = preparetodie(db, selecthonks+"where (honks.userid = ? or (? = -1 and whofore = 2)) and convoy = ?"+limit)
337	stmtHonksByOntology = preparetodie(db, selecthonks+"join onts on honks.honkid = onts.honkid where onts.ontology = ? and (honks.userid = ? or (? = -1 and honks.whofore = 2))"+limit)
338
339	stmtSaveHonk = preparetodie(db, "insert into honks (userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags, onts) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
340	stmtSaveOnts = preparetodie(db, "insert into onts (ontology, honkid) values (?, ?)")
341	stmtFileData = preparetodie(db, "select media, content from files where xid = ?")
342	stmtFindXonk = preparetodie(db, "select honkid from honks where userid = ? and xid = ?")
343	stmtSaveDonk = preparetodie(db, "insert into donks (honkid, fileid) values (?, ?)")
344	stmtZonkIt = preparetodie(db, "delete from honks where honkid = ?")
345	stmtZonkDonks = preparetodie(db, "delete from donks where honkid = ?")
346	stmtFindFile = preparetodie(db, "select fileid from files where url = ? and local = 1")
347	stmtSaveFile = preparetodie(db, "insert into files (xid, name, description, url, media, local, content) values (?, ?, ?, ?, ?, ?, ?)")
348	stmtWhatAbout = preparetodie(db, "select userid, username, displayname, about, pubkey, options from users where username = ?")
349	stmtSaveDub = preparetodie(db, "insert into honkers (userid, name, xid, flavor) values (?, ?, ?, ?)")
350	stmtAddDoover = preparetodie(db, "insert into doovers (dt, tries, username, rcpt, msg) values (?, ?, ?, ?, ?)")
351	stmtGetDoovers = preparetodie(db, "select dooverid, dt from doovers")
352	stmtLoadDoover = preparetodie(db, "select tries, username, rcpt, msg from doovers where dooverid = ?")
353	stmtZapDoover = preparetodie(db, "delete from doovers where dooverid = ?")
354	stmtThumbBiters = preparetodie(db, "select userid, name, wherefore from zonkers where (wherefore = 'zonker' or wherefore = 'zomain' or wherefore = 'zord' or wherefore = 'zilence')")
355	stmtFindZonk = preparetodie(db, "select zonkerid from zonkers where userid = ? and name = ? and wherefore = 'zonk'")
356	stmtGetZonkers = preparetodie(db, "select zonkerid, name, wherefore from zonkers where userid = ? and wherefore <> 'zonk'")
357	stmtSaveZonker = preparetodie(db, "insert into zonkers (userid, name, wherefore) values (?, ?, ?)")
358	stmtGetXonker = preparetodie(db, "select info from xonkers where name = ? and flavor = ?")
359	stmtSaveXonker = preparetodie(db, "insert into xonkers (name, info, flavor) values (?, ?, ?)")
360	stmtDeleteXonker = preparetodie(db, "delete from xonkers where name = ? and flavor = ?")
361	stmtRecentHonkers = preparetodie(db, "select distinct(honker) from honks where userid = ? and honker not in (select xid from honkers where userid = ? and flavor = 'sub') order by honkid desc limit 100")
362	stmtUpdateFlags = preparetodie(db, "update honks set flags = flags | ? where honkid = ?")
363	stmtClearFlags = preparetodie(db, "update honks set flags = flags & ~ ? where honkid = ?")
364	stmtSelectOnts = preparetodie(db, "select distinct(ontology) from onts join honks on onts.honkid = honks.honkid where (honks.userid = ? or honks.whofore = 2)")
365}