all repos — honk @ 8ae9dd59981005d89ff88afb96b46d26401027eb

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