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}