From 54b8b60edd5933116cab4c6130cdcb7bfc87a327 Mon Sep 17 00:00:00 2001 From: Jeff Becker Date: Tue, 8 Aug 2017 09:18:31 -0400 Subject: [PATCH] add initial local spam filter --- contrib/backends/srndv2/src/srnd/config.go | 36 +++++++++++++++++++ contrib/backends/srndv2/src/srnd/daemon.go | 10 ++++++ .../backends/srndv2/src/srnd/frontend_http.go | 2 +- contrib/backends/srndv2/src/srnd/nntp.go | 2 +- contrib/backends/srndv2/src/srnd/store.go | 8 +++-- 5 files changed, 54 insertions(+), 4 deletions(-) diff --git a/contrib/backends/srndv2/src/srnd/config.go b/contrib/backends/srndv2/src/srnd/config.go index 9eca5fa..645d30e 100644 --- a/contrib/backends/srndv2/src/srnd/config.go +++ b/contrib/backends/srndv2/src/srnd/config.go @@ -5,18 +5,45 @@ package srnd import ( + "bufio" + "bytes" "encoding/base32" "fmt" "github.com/majestrate/configparser" "github.com/majestrate/nacl" + "io/ioutil" "log" "net" "os" "path/filepath" + "regexp" "strings" "time" ) +type FilterConfig struct { + globalFilters []*regexp.Regexp +} + +func (fc *FilterConfig) LoadFile(fname string) (err error) { + var data []byte + data, err = ioutil.ReadFile(fname) + if err == nil { + r := bytes.NewReader(data) + sc := bufio.NewScanner(r) + for sc.Scan() { + txt := sc.Text() + idx := strings.Index(txt, "#") + if idx >= 0 { + txt = txt[:idx] + } + fc.globalFilters = append(fc.globalFilters, regexp.MustCompile(txt)) + } + + } + return +} + type FeedConfig struct { policy FeedPolicy quarks map[string]string @@ -70,6 +97,7 @@ type SRNdConfig struct { pprof *ProfilingConfig hooks []*HookConfig inboundPolicy *FeedPolicy + filter FilterConfig } // check for config files @@ -417,6 +445,14 @@ func ReadConfig() *SRNdConfig { } } + filterFile := "filters.txt" + + if CheckFile(filterFile) { + err = sconf.filter.LoadFile(filterFile) + if err != nil { + log.Fatalf("failed to load %s: %s", filterFile, err) + } + } return &sconf } diff --git a/contrib/backends/srndv2/src/srnd/daemon.go b/contrib/backends/srndv2/src/srnd/daemon.go index 60742be..0166510 100644 --- a/contrib/backends/srndv2/src/srnd/daemon.go +++ b/contrib/backends/srndv2/src/srnd/daemon.go @@ -131,6 +131,16 @@ type NNTPDaemon struct { article_lifetime time.Duration } +// return true if text passes all checks and is okay for posting +func (self *NNTPDaemon) CheckText(text string) bool { + for _, re := range self.conf.filter.globalFilters { + if re.MatchString(text) { + return false + } + } + return true +} + func (self NNTPDaemon) End() { if self.listener != nil { self.listener.Close() diff --git a/contrib/backends/srndv2/src/srnd/frontend_http.go b/contrib/backends/srndv2/src/srnd/frontend_http.go index 1f411a8..4ac4f15 100644 --- a/contrib/backends/srndv2/src/srnd/frontend_http.go +++ b/contrib/backends/srndv2/src/srnd/frontend_http.go @@ -393,7 +393,7 @@ func (self *httpFrontend) poll() { R: msg.Body, N: self.daemon.messageSizeLimitFor(nntp.Newsgroup()), } - err = self.daemon.store.ProcessMessageBody(f, textproto.MIMEHeader(msg.Header), body) + err = self.daemon.store.ProcessMessageBody(f, textproto.MIMEHeader(msg.Header), body, self.daemon.CheckText) } } } diff --git a/contrib/backends/srndv2/src/srnd/nntp.go b/contrib/backends/srndv2/src/srnd/nntp.go index 55d6572..e8821bf 100644 --- a/contrib/backends/srndv2/src/srnd/nntp.go +++ b/contrib/backends/srndv2/src/srnd/nntp.go @@ -565,7 +565,7 @@ func (self *nntpConnection) storeMessage(daemon *NNTPDaemon, hdr textproto.MIMEH // now store attachments and article err = writeMIMEHeader(f, hdr) if err == nil { - err = daemon.store.ProcessMessageBody(f, hdr, body) + err = daemon.store.ProcessMessageBody(f, hdr, body, daemon.CheckText) if err == nil { // tell daemon daemon.loadFromInfeed(msgid) diff --git a/contrib/backends/srndv2/src/srnd/store.go b/contrib/backends/srndv2/src/srnd/store.go index 06a0d0b..2686338 100644 --- a/contrib/backends/srndv2/src/srnd/store.go +++ b/contrib/backends/srndv2/src/srnd/store.go @@ -65,7 +65,7 @@ type ArticleStore interface { // process body of nntp message, register attachments and the article // write the body into writer as we go through the body // does NOT write mime header - ProcessMessageBody(wr io.Writer, hdr textproto.MIMEHeader, body *io.LimitedReader) error + ProcessMessageBody(wr io.Writer, hdr textproto.MIMEHeader, body *io.LimitedReader, spamfilter func(string) bool) error // register this post with the daemon RegisterPost(nntp NNTPMessage) error // register signed message @@ -437,8 +437,12 @@ func (self *articleStore) getMIMEHeader(messageID string) (hdr textproto.MIMEHea return hdr } -func (self *articleStore) ProcessMessageBody(wr io.Writer, hdr textproto.MIMEHeader, body *io.LimitedReader) (err error) { +func (self *articleStore) ProcessMessageBody(wr io.Writer, hdr textproto.MIMEHeader, body *io.LimitedReader, spamfilter func(string) bool) (err error) { err = read_message_body(body, hdr, self, wr, false, func(nntp NNTPMessage) { + if !spamfilter(nntp.Message()) { + err = errors.New("spam message") + return + } err = self.RegisterPost(nntp) if err == nil { pk := hdr.Get("X-PubKey-Ed25519")