diff --git a/Gopkg.lock b/Gopkg.lock deleted file mode 100644 index ef3dfba..0000000 --- a/Gopkg.lock +++ /dev/null @@ -1,39 +0,0 @@ -# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. - - -[[projects]] - name = "github.com/davecgh/go-spew" - packages = ["spew"] - revision = "346938d642f2ec3594ed81d874461961cd0faa76" - version = "v1.1.0" - -[[projects]] - name = "github.com/pkg/errors" - packages = ["."] - revision = "645ef00459ed84a119197bfb8d8205042c6df63d" - version = "v0.8.0" - -[[projects]] - name = "github.com/pmezard/go-difflib" - packages = ["difflib"] - revision = "792786c7400a136282c1664665ae0a8db921c6c2" - version = "v1.0.0" - -[[projects]] - branch = "master" - name = "github.com/teamwork/test" - packages = [".","diff","fakeconn"] - revision = "559f1f9ef83a9d1858c16909fabb14abb61154a9" - -[[projects]] - branch = "master" - name = "github.com/teamwork/utils" - packages = ["mathutil"] - revision = "cb6abdaad548c37166283f60e501823b6427eaf7" - -[solve-meta] - analyzer-name = "dep" - analyzer-version = 1 - inputs-digest = "98c5b3796c07ce2c60082812bf0bce90b43b35a9cec4382cd68d4da43e848f7c" - solver-name = "gps-cdcl" - solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml deleted file mode 100644 index f4cee38..0000000 --- a/Gopkg.toml +++ /dev/null @@ -1,34 +0,0 @@ - -# Gopkg.toml example -# -# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md -# for detailed Gopkg.toml documentation. -# -# required = ["github.com/user/thing/cmd/thing"] -# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] -# -# [[constraint]] -# name = "github.com/user/project" -# version = "1.0.0" -# -# [[constraint]] -# name = "github.com/user/project2" -# branch = "dev" -# source = "github.com/myfork/project2" -# -# [[override]] -# name = "github.com/x/y" -# version = "2.4.0" - - -[[constraint]] - branch = "master" - name = "github.com/teamwork/test" - -[[constraint]] - branch = "master" - name = "github.com/teamwork/utils" - -[[constraint]] - name = "github.com/pkg/errors" - version = "0.8.0" diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..cb4b091 --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module github.com/teamwork/spamc + +go 1.16 + +require ( + github.com/pkg/errors v0.9.1 + github.com/teamwork/test v0.0.0-20200108114543-02621bae84ad + github.com/teamwork/utils v0.0.0-20210422143242-99315371ead6 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..d857fa6 --- /dev/null +++ b/go.sum @@ -0,0 +1,12 @@ +github.com/Strum355/go-difflib v1.1.0 h1:+rR2X3UuvIbe1Jmhx8WA7gkgjMNRscFWbHchk2RB8I4= +github.com/Strum355/go-difflib v1.1.0/go.mod h1:r1cVg1JkGsTWkaR7At56v7hfuMgiUL8meTLwxFzOmvE= +github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/teamwork/test v0.0.0-20190410143529-8897d82f8d46/go.mod h1:TIbx7tx6WHBjQeLRM4eWQZBL7kmBZ7/KI4x4v7Y5YmA= +github.com/teamwork/test v0.0.0-20200108114543-02621bae84ad h1:25sEr0awm0ZPancg5W5H5VvN7PWsJloUBpii10a9isw= +github.com/teamwork/test v0.0.0-20200108114543-02621bae84ad/go.mod h1:TIbx7tx6WHBjQeLRM4eWQZBL7kmBZ7/KI4x4v7Y5YmA= +github.com/teamwork/utils v0.0.0-20210422143242-99315371ead6 h1:l7CEV+qJRWPFhPu7ZVI6uantfRGFrBE47lEhgn+wtrs= +github.com/teamwork/utils v0.0.0-20210422143242-99315371ead6/go.mod h1:3Fn0qxFeRNpvsg/9T1+btOOOKkd1qG2nPYKKcOmNpcs= diff --git a/spamc.go b/spamc.go index ea6b07f..c54ddb2 100644 --- a/spamc.go +++ b/spamc.go @@ -417,26 +417,27 @@ func parseReport(tp *textproto.Reader) (Report, error) { case table: s := reTableLine.FindAllStringSubmatch(line, -1) - if len(s) != 1 { - continue + if len(s) != 0 { + points, err := strconv.ParseFloat(s[0][1], 64) + if err != nil { + continue + } + + report.Table = append(report.Table, struct { + Points float64 + Rule string + Description string + }{ + points, s[0][2], s[0][3], + }) + } else { + + last := len(report.Table) - 1 + if last >= 0 { + line = strings.TrimSpace(line) + report.Table[last].Description += "\n" + strings.Repeat(" ", 28) + strings.TrimSpace(line) + } } - - if len(s[0]) != 4 { - continue - } - - points, err := strconv.ParseFloat(s[0][1], 64) - if err != nil { - continue - } - - report.Table = append(report.Table, struct { - Points float64 - Rule string - Description string - }{ - points, s[0][2], s[0][3], - }) } } diff --git a/spamc_test.go b/spamc_test.go index 1361c7f..c9a494f 100644 --- a/spamc_test.go +++ b/spamc_test.go @@ -248,6 +248,98 @@ func TestParseSpamHeader(t *testing.T) { } } +func TestParseReportWithNewlines(t *testing.T) { + cases := []struct { + in string + want Report + }{ + { + normalizeSpace(` + Spam detection software, running on the system "d311d8df23f8", + has NOT identified this incoming email as spam. + + Content preview: the body [...] + + Content analysis details: (4.4 points, 5.0 required) + + pts rule name description + ---- ---------------------- -------------------------------------------------- + -0.0 NO_RELAYS Informational: message was not relayed via SMTP + 0.0 HEADER_FROM_DIFFERENT_DOMAINS From and EnvelopeFrom 2nd level mail are different + 1.9 URIBL_ABUSE_SURBL Contains an URL listed in the ABUSE SURBL blocklist + [URIs: blacklistedurl.com] + 2.5 URIBL_DBL_SPAM Contains a spam URL listed in the Spamhaus DBL + blocklist + [URIs: blablablabla.com] + `), + Report{ + Intro: normalizeSpace(` + Spam detection software, running on the system "d311d8df23f8", + has NOT identified this incoming email as spam. + + Content preview: the body [...] + + Content analysis details: (4.4 points, 5.0 required) + `), + Table: []struct { + Points float64 + Rule string + Description string + }{ + { + Points: -0.0, + Rule: "NO_RELAYS", + Description: "Informational: message was not relayed via SMTP", + }, + { + Points: 0.0, + Rule: "HEADER_FROM_DIFFERENT_DOMAINS", + Description: "From and EnvelopeFrom 2nd level mail are different", + }, + { + Points: 1.9, + Rule: "URIBL_ABUSE_SURBL", + Description: "Contains an URL listed in the ABUSE SURBL blocklist\n [URIs: blacklistedurl.com]", + }, + { + Points: 2.5, + Rule: "URIBL_DBL_SPAM", + Description: "Contains a spam URL listed in the Spamhaus DBL\n blocklist\n [URIs: blablablabla.com]", + }, + }, + }, + }, + } + + for i, tc := range cases { + t.Run(fmt.Sprintf("%v", i), func(t *testing.T) { + tp := textproto.NewReader(bufio.NewReader(strings.NewReader(tc.in))) + + out, err := parseReport(tp) + if err != nil { + t.Fatal(err) + } + + if d := diff.TextDiff(tc.want.Intro, out.Intro); d != "" { + t.Errorf("intro wrong\n%v", d) + } + + if !reflect.DeepEqual(out.Table, tc.want.Table) { + t.Errorf("wrong table\nout: %#v\nwant: %#v\n", + out.Table, tc.want.Table) + } + + if !t.Failed() { + tc.in += "\n" + s := out.String() + if d := diff.TextDiff(s, tc.in); d != "" { + t.Errorf("String() not the same\n%v", d) + } + } + }) + } +} + func TestParseReport(t *testing.T) { cases := []struct { in string