all repos — honk @ a4f701b48ee51be69f5feba0cb90e4f74328f010

my fork of honk

hfcs.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	"log"
 20	"net/http"
 21	"regexp"
 22)
 23
 24type Filter struct {
 25	ID              int64
 26	Actor           string
 27	IncludeAudience bool
 28	Text            string
 29	IsAnnounce      bool
 30	Reject          bool
 31	SkipMedia       bool
 32	Hide            bool
 33	Collapse        bool
 34	Rewrite         string
 35	re_rewrite      *regexp.Regexp
 36	Replace         string
 37}
 38
 39type filtType uint
 40
 41const (
 42	filtNone filtType = iota
 43	filtAny
 44	filtReject
 45	filtSkipMedia
 46	filtHide
 47	filtCollapse
 48	filtRewrite
 49)
 50
 51var filtNames = []string{"None", "Any", "Reject", "SkipMedia", "Hide", "Collapse", "Rewrite"}
 52
 53func (ft filtType) String() string {
 54	return filtNames[ft]
 55}
 56
 57type afiltermap map[filtType][]*Filter
 58
 59var filtcache = cacheNew(func(userid int64) (afiltermap, bool) {
 60	rows, err := stmtGetFilters.Query(userid)
 61	if err != nil {
 62		log.Printf("error querying filters: %s", err)
 63		return nil, false
 64	}
 65	defer rows.Close()
 66
 67	filtmap := make(afiltermap)
 68	for rows.Next() {
 69		filt := new(Filter)
 70		var j string
 71		var filterid int64
 72		err = rows.Scan(&filterid, &j)
 73		if err == nil {
 74			err = unjsonify(j, filt)
 75		}
 76		if err != nil {
 77			log.Printf("error scanning filter: %s", err)
 78			continue
 79		}
 80		filt.ID = filterid
 81		if filt.Reject {
 82			filtmap[filtReject] = append(filtmap[filtReject], filt)
 83		}
 84		if filt.SkipMedia {
 85			filtmap[filtSkipMedia] = append(filtmap[filtSkipMedia], filt)
 86		}
 87		if filt.Hide {
 88			filtmap[filtHide] = append(filtmap[filtHide], filt)
 89		}
 90		if filt.Collapse {
 91			filtmap[filtCollapse] = append(filtmap[filtCollapse], filt)
 92		}
 93		if filt.Rewrite != "" {
 94			filtmap[filtRewrite] = append(filtmap[filtRewrite], filt)
 95		}
 96	}
 97	return filtmap, true
 98})
 99
100func getfilters(userid int64, scope filtType) []*Filter {
101	var filtmap afiltermap
102	ok := filtcache.Get(userid, &filtmap)
103	if ok {
104		return filtmap[scope]
105	}
106	return nil
107}
108
109func rejectorigin(userid int64, origin string) bool {
110	if o := originate(origin); o != "" {
111		origin = o
112	}
113	filts := getfilters(userid, filtReject)
114	for _, f := range filts {
115		if f.Actor == origin {
116			log.Printf("rejecting origin: %s", origin)
117			return true
118		}
119	}
120	return false
121}
122
123func rejectactor(userid int64, actor string) bool {
124	origin := originate(actor)
125	filts := getfilters(userid, filtReject)
126	for _, f := range filts {
127		if f.Actor == actor || (origin != "" && f.Actor == origin) {
128			log.Printf("rejecting actor: %s", actor)
129			return true
130		}
131	}
132	return false
133}
134
135func stealthmode(userid int64, r *http.Request) bool {
136	agent := r.UserAgent()
137	agent = originate(agent)
138	if agent != "" {
139		fake := rejectorigin(userid, agent)
140		if fake {
141			log.Printf("faking 404 for %s", agent)
142			return fake
143		}
144	}
145	return false
146}
147
148// todo
149func matchfilter(h *Honk, f *Filter) bool {
150	match := true
151	if match && f.Actor != "" {
152		match = false
153		if f.Actor == h.Honker || f.Actor == h.Oonker {
154			match = true
155		}
156		if !match && (f.Actor == originate(h.Honker) ||
157			f.Actor == originate(h.Oonker) ||
158			f.Actor == originate(h.XID)) {
159			match = true
160		}
161		if !match && f.IncludeAudience {
162			for _, a := range h.Audience {
163				if f.Actor == a || f.Actor == originate(a) {
164					match = true
165					break
166				}
167			}
168		}
169	}
170	if match && f.Text != "" {
171		match = false
172		for _, d := range h.Donks {
173			if d.Desc == f.Text {
174				match = true
175			}
176		}
177	}
178	if match {
179		return true
180	}
181	return false
182}
183
184func rejectxonk(xonk *Honk) bool {
185	filts := getfilters(xonk.UserID, filtReject)
186	for _, f := range filts {
187		if matchfilter(xonk, filts) {
188			return true
189		}
190	}
191	return false
192}
193
194func skipMedia(xonk *Honk) bool {
195	filts := getfilters(xonk.UserID, filtSkipMedia)
196	for _, f := range filts {
197		if matchfilter(xonk, filts) {
198			return true
199		}
200	}
201	return false
202}
203
204// todo
205func unsee(filts []*Filter, h *Honk) string {
206	return ""
207}
208
209func osmosis(honks []*Honk, userid int64) []*Honk {
210	filts := getfilters(userid, filtHide)
211	j := 0
212outer:
213	for _, h := range honks {
214		if matchfilter(h, filts) {
215			continue outer
216		}
217		honks[j] = h
218		j++
219	}
220	honks = honks[0:j]
221	return honks
222}