21 Commits

Author SHA1 Message Date
ai a13c4b63f0 изменение версии v1.5.7 2024-10-27 15:57:02 +03:00
ai 2f5fc86d3d go vet с поддиректориями 2024-10-27 15:45:09 +03:00
ai 008f82f0f4 обновление модулей 2024-10-27 15:41:44 +03:00
ai 65a41c26db добавлено условие update 2024-10-27 15:39:55 +03:00
ai 9384b91fb7 изменение версии 2024-07-14 13:29:17 +03:00
ai ce3894864a добавлено сообщение в лог для action discard 2024-06-18 12:13:22 +03:00
ai ed96f0c66f добавлена обработка сообщений, полученных через IMAP (MAPI) 2023-03-15 22:31:42 +03:00
ai aae34c04f4 добавить заголовок seen для mirror-to 2023-03-05 14:20:01 +03:00
ai d0f6547da9 добавить заголовки для mirror-to 2023-03-05 12:42:54 +03:00
ai 1987653574 добавить заголовки 2023-03-05 12:35:03 +03:00
ai eb9d4943e8 fix format string 2023-02-25 21:22:24 +03:00
ai 5bcfec0eaa fix format string 2023-02-25 21:02:57 +03:00
ai aa8f9a4987 константы вместо переменных 2023-02-23 22:59:24 +03:00
ai 6f9ef2c770 исправлено описание 2023-02-23 22:32:14 +03:00
ai 41d9f0df73 удалена ссылка на старый сайт 2023-02-23 21:34:32 +03:00
ai 279bdd003b замена константы на переменную 2023-02-23 16:09:43 +03:00
ai 5f6631176d version 1.5.0 2023-02-23 02:37:27 +03:00
ai 21df0d8fd7 добавлена обработка rewrite subject 2023-02-23 02:37:00 +03:00
ai 3c41283fed кросс-компиляция 2023-02-23 02:20:46 +03:00
ai 1b0b9d7604 удалён reject-score 2023-02-20 16:32:39 +03:00
ai fd72237063 добавлен mirror then discard 2023-02-18 19:51:29 +03:00
7 changed files with 399 additions and 53 deletions
+5 -9
View File
@@ -1,12 +1,8 @@
Rspamd plugin for CommuniGate Pro 5.x, 6.x Rspamd helper for CommuniGate Pro 5.x, 6.x
Installation and usage instructions can be found at Copyright (C) 2017-2024 Andrey Igoshin <ai@vsu.ru>
http://www.communigate.com/CGPMcAfee/#Integrate Version 1.5.7
Copyright (C) 2017-2022 Andrey Igoshin <ai@vsu.ru>
Version 1.4.0
https://git.vsu.ru/ai/rspamd-cgp https://git.vsu.ru/ai/rspamd-cgp
@@ -16,9 +12,9 @@
Authentication Identifier (default CommuniGate Pro Main Domain) Authentication Identifier (default CommuniGate Pro Main Domain)
-host string -host string
Rspamd host to connect (default "localhost:11333") Rspamd host to connect (default "localhost:11333")
-mirror-discard
Mirror then discard selected messages
-mirror-to string -mirror-to string
Mirror selected messages to email Mirror selected messages to email
-reject-score float
Reject score to discard (default +Inf)
-timeout duration -timeout duration
Rspamd request timeout (default 15s) Rspamd request timeout (default 15s)
+9 -1
View File
@@ -2,13 +2,21 @@
export GOPATH="${HOME}/src/rspamd-cgp" export GOPATH="${HOME}/src/rspamd-cgp"
# если на целевой ОС не совпадает glibc, то собираем без зависимостей.
# результирующий файл, возможно, получится медленнее и большего размера.
export CGO_ENABLED=0
if [ "$1" == "fmt" ]; then if [ "$1" == "fmt" ]; then
go fmt $* go fmt $*
elif [ "$1" == "tidy" ]; then elif [ "$1" == "tidy" ]; then
go mod tidy go mod tidy
elif [ "$1" == "update" ]; then
echo "update..."
go get -u ./...
go mod tidy
elif [ "$1" == "vet" ]; then elif [ "$1" == "vet" ]; then
echo "vet..." echo "vet..."
go vet go vet ./...
else else
go build go build
fi fi
+344 -9
View File
@@ -2,14 +2,23 @@ package cgp
import ( import (
"bufio" "bufio"
"bytes"
"crypto/sha256"
"fmt" "fmt"
"io"
"os" "os"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"syscall" "syscall"
"unicode"
) )
const recvHdr = "Received:"
const seenHdr = "X-Rspamd-Seen:"
const subjHdr = "Subject:"
const submitDir = "Submitted"
var MainDomain string var MainDomain string
var reMD *regexp.Regexp var reMD *regexp.Regexp
var reSELF *regexp.Regexp var reSELF *regexp.Regexp
@@ -19,7 +28,7 @@ var protocol int
func init() { func init() {
reMD = regexp.MustCompile(`^\s+DomainName\s+=\s+([^;]+);`) reMD = regexp.MustCompile(`^\s+DomainName\s+=\s+([^;]+);`)
reSELF = regexp.MustCompile(`^S (?:<([^>]+)> )?(?:DSN|GROUP|LIST|PBX|PIPE|RULE) \[0\.0\.0\.0\]`) reSELF = regexp.MustCompile(`^S (?:<([^>]+)> )?(?:DSN|GROUP|LIST|PBX|PIPE|RULE) \[0\.0\.0\.0\]`)
reSMTP = regexp.MustCompile(`^S (?:<([^>]+)> )?(?:SMTP|HTTPU?|AIRSYNC|XIMSS) \[([0-9a-f.:]+)\]`) reSMTP = regexp.MustCompile(`^S (?:<([^>]+)> )?(?:SMTP|HTTPU?|AIRSYNC|XIMSS|IMAP) \[([0-9a-f.:]+)\]`)
err := setMainDomain() err := setMainDomain()
if err != nil { if err != nil {
@@ -38,7 +47,8 @@ func AddHeader(seq int, headers []string) {
} }
} }
func Discard(seq int) { func Discard(seq int, qid int, from string, rcpts []string) {
Putline("* %d [%d]: Action: discard; from %s, rcpts %s\n", seq, qid, from, strings.Join(rcpts, ","))
Putline("%d DISCARD\n", seq) Putline("%d DISCARD\n", seq)
} }
@@ -52,7 +62,7 @@ func Intf(seq int, ver string) {
Putline("%d INTF %d\n", seq, protocol) Putline("%d INTF %d\n", seq, protocol)
} }
func Message(filename string) (from string, rcpts []string, auth string, ip string, qid int, body []byte, err error) { func Message(filename string) (from string, rcpts []string, auth string, ip string, qid int, body []byte, seen bool, err error) {
qid, err = strconv.Atoi((filename)[strings.LastIndexByte(filename, '/')+1 : strings.LastIndexByte(filename, '.')]) qid, err = strconv.Atoi((filename)[strings.LastIndexByte(filename, '/')+1 : strings.LastIndexByte(filename, '.')])
if err != nil { if err != nil {
@@ -68,7 +78,9 @@ func Message(filename string) (from string, rcpts []string, auth string, ip stri
var line []byte var line []byte
var pos int64 var pos int64
for m := bufio.NewReader(h); ; { m := bufio.NewReader(h)
for {
line, err = m.ReadSlice('\n') line, err = m.ReadSlice('\n')
if err != nil { if err != nil {
@@ -101,7 +113,14 @@ func Message(filename string) (from string, rcpts []string, auth string, ip stri
} }
} }
rcpts = uniqueNonEmptyElementsOf(rcpts) seen, err = isSeen(m)
if err != nil {
return
}
if seen {
return
}
fi, err := h.Stat() fi, err := h.Stat()
if err != nil { if err != nil {
@@ -119,6 +138,8 @@ func Message(filename string) (from string, rcpts []string, auth string, ip stri
return return
} }
rcpts = uniqueNonEmptyElementsOf(rcpts)
if from == "" || len(rcpts) == 0 || n < len(body) { if from == "" || len(rcpts) == 0 || n < len(body) {
err = fmt.Errorf("cgp.Message() error: from='%s', len(to)=%d, auth='%s' ip='%s', size=%d/%d", from, len(rcpts), auth, ip, len(body), n) err = fmt.Errorf("cgp.Message() error: from='%s', len(to)=%d, auth='%s' ip='%s', size=%d/%d", from, len(rcpts), auth, ip, len(body), n)
} }
@@ -126,26 +147,39 @@ func Message(filename string) (from string, rcpts []string, auth string, ip stri
return return
} }
func MirrorTo(seq int, to []string, headers []string) { func MirrorTo(seq int, qid int, to []string, headers []string, body []byte, mirrorDiscard bool) {
hdrs := replaceSpecChars(strings.Join(headers, "\n"))
if protocol >= 4 { if protocol >= 4 {
if len(to) > 0 { if len(to) > 0 {
seenHdr, err := makeSeen(body)
if err != nil {
Failure(seq, qid, err)
return
}
headers = append(headers, seenHdr)
hdrs := replaceSpecChars(strings.Join(headers, "\n"))
mirrorTo := []string{} mirrorTo := []string{}
for _, m := range to { for _, m := range to {
mirrorTo = append(mirrorTo, fmt.Sprintf("MIRRORTO \"%s\"", m)) mirrorTo = append(mirrorTo, fmt.Sprintf("MIRRORTO \"%s\"", m))
} }
Putline("%d %s ADDHEADER \"%s\" OK\n", seq, strings.Join(mirrorTo, " "), hdrs) if mirrorDiscard {
Putline("%d ADDHEADER \"%s\" %s DISCARD\n", seq, hdrs, strings.Join(mirrorTo, " "))
} else {
Putline("%d ADDHEADER \"%s\" %s OK\n", seq, hdrs, strings.Join(mirrorTo, " "))
}
} else { } else {
hdrs := replaceSpecChars(strings.Join(headers, "\n"))
Putline("%d ADDHEADER \"%s\" OK\n", seq, hdrs) Putline("%d ADDHEADER \"%s\" OK\n", seq, hdrs)
} }
} else { } else {
hdrs := replaceSpecChars(strings.Join(headers, "\n"))
Putline("%d ADDHEADER \"%s\"\n", seq, hdrs) Putline("%d ADDHEADER \"%s\"\n", seq, hdrs)
} }
} }
@@ -154,6 +188,11 @@ func Ok(seq int) {
Putline("%d OK\n", seq) Putline("%d OK\n", seq)
} }
func OkSeen(seq, qid int) {
Putline("* %d [%d]: Already seen by Rspamd\n", seq, qid)
Putline("%d OK\n", seq)
}
func Putline(format string, a ...interface{}) { func Putline(format string, a ...interface{}) {
s := fmt.Sprintf(format, a...) s := fmt.Sprintf(format, a...)
syscall.Write(int(os.Stdout.Fd()), []byte(s)) syscall.Write(int(os.Stdout.Fd()), []byte(s))
@@ -163,6 +202,302 @@ func Reject(seq int) {
Putline("%d REJECT Try again later\n", seq) Putline("%d REJECT Try again later\n", seq)
} }
func RewriteSubject(seq int, headers []string, subject string, qid int, from string, rcpts []string, body []byte) {
var err error
var firstRecv bool = true
var line []byte
var m *bufio.Reader
var hdr string
filename := fmt.Sprintf("%s/%drs.sub", submitDir, qid)
filetemp := strings.Replace(filename, "sub", "tmp", 1)
fh, err := os.Create(filetemp)
if err != nil {
goto fin
}
defer fh.Close()
_, err = fh.WriteString(strings.Join([]string{"Return-Path: ", from, "\n"}, ""))
if err != nil {
goto fin
}
for _, rcpt := range rcpts {
_, err = fh.WriteString(strings.Join([]string{"Envelope-To: ", rcpt, "\n"}, ""))
if err != nil {
goto fin
}
}
for _, hdr = range headers {
_, err = fh.WriteString(hdr)
if err != nil {
goto fin
}
}
m = bufio.NewReader(bytes.NewReader(body))
for {
hdr, err = getHeader(m)
if err == io.EOF {
err = nil
if len(hdr) == 0 {
break
}
}
if err != nil {
goto fin
}
if hdr == "\n" {
// конец заголовка
_, err = fh.WriteString(hdr)
if err != nil {
goto fin
}
break
}
if firstRecv && strings.HasPrefix(hdr, recvHdr) {
sum := fmt.Sprintf("%x", sha256.Sum224([]byte(nospace(hdr))))
_, err = fh.WriteString(strings.Join([]string{seenHdr, " ", sum, "\n"}, ""))
if err != nil {
goto fin
}
_, err = fh.WriteString(hdr)
if err != nil {
goto fin
}
firstRecv = false
continue
}
if strings.HasPrefix(hdr, subjHdr) {
_, err = fh.WriteString(strings.Join([]string{subjHdr, " ", subject, "\n"}, ""))
if err != nil {
goto fin
}
continue
}
_, err = fh.WriteString(hdr)
if err != nil {
goto fin
}
}
for {
line, err = m.ReadSlice('\n')
if err == io.EOF {
err = nil
break
}
if err != nil {
goto fin
}
_, err = fh.Write(line)
if err != nil {
goto fin
}
}
if err = fh.Close(); err != nil {
goto fin
}
err = os.Rename(filetemp, filename)
fin:
if err != nil {
Failure(seq, qid, err)
} else {
Discard(seq, qid, from, rcpts)
}
}
func getHeader(m *bufio.Reader) (hdr string, err error) {
var c byte
var line []byte
var b strings.Builder
b.Grow(384)
for {
c, err = m.ReadByte()
if err == io.EOF {
if c == 0 {
break
} else {
err = nil
}
}
if err != nil {
return
}
if b.Len() == 0 {
if c == ' ' || c == '\t' {
err = m.UnreadByte()
if err != nil {
return
}
err = fmt.Errorf("bad header")
return
} else if c == '\n' {
b.WriteByte(c)
break
} else {
b.WriteByte(c)
line, err = m.ReadSlice('\n')
if err == io.EOF {
err = nil
}
if err != nil {
return
}
b.Write(line)
}
} else {
if c == ' ' || c == '\t' {
b.WriteByte(c)
line, err = m.ReadSlice('\n')
if err == io.EOF {
err = nil
}
if err != nil {
return
}
b.Write(line)
} else {
err = m.UnreadByte()
if err != nil {
return
}
break
}
}
}
hdr = b.String()
return
}
func isSeen(m *bufio.Reader) (seen bool, err error) {
var found bool
var seenSum string
var hdr string
for {
hdr, err = getHeader(m)
if err == io.EOF {
err = nil
if len(hdr) == 0 {
break
}
}
if err != nil {
return
}
if hdr == "\n" {
// конец RFC5322 заголовка
break
}
if !found {
if seenSum, found = strings.CutPrefix(hdr, seenHdr); found {
seenSum = strings.TrimSpace(seenSum)
}
} else if strings.HasPrefix(hdr, recvHdr) {
sum := fmt.Sprintf("%x", sha256.Sum224([]byte(nospace(hdr))))
if seenSum == sum {
seen = true
}
break
}
}
return
}
func makeSeen(body []byte) (seenhdr string, err error) {
var hdr string
m := bufio.NewReader(bytes.NewReader(body))
for {
hdr, err = getHeader(m)
if err == io.EOF {
err = nil
if len(hdr) == 0 {
break
}
}
if err != nil {
return
}
if hdr == "\n" {
break
}
if strings.HasPrefix(hdr, recvHdr) {
seenhdr = fmt.Sprintf("%s %x", seenHdr, sha256.Sum224([]byte(nospace(hdr))))
break
}
}
return
}
func nospace(s string) string {
var b strings.Builder
b.Grow(len(s))
for _, c := range s {
if !unicode.IsSpace(c) {
b.WriteRune(c)
}
}
return b.String()
}
func replaceSpecChars(msg string) string { func replaceSpecChars(msg string) string {
var sb strings.Builder var sb strings.Builder
+11 -8
View File
@@ -2,18 +2,17 @@ package config
import ( import (
"flag" "flag"
"math"
"strings" "strings"
"time" "time"
) )
type Config struct { type Config struct {
AuthservId string AuthservId string
Debug bool Debug bool
Host string Host string
MirrorTo []string MirrorDiscard bool
RejectScore float64 MirrorTo []string
Timeout time.Duration Timeout time.Duration
} }
func New() *Config { func New() *Config {
@@ -24,8 +23,8 @@ func New() *Config {
flag.StringVar(&config.AuthservId, "authserv-id", "", "Authentication Identifier (default CommuniGate Pro Main Domain)") flag.StringVar(&config.AuthservId, "authserv-id", "", "Authentication Identifier (default CommuniGate Pro Main Domain)")
flag.StringVar(&config.Host, "host", "localhost:11333", "Rspamd host to connect") flag.StringVar(&config.Host, "host", "localhost:11333", "Rspamd host to connect")
flag.BoolVar(&config.MirrorDiscard, "mirror-discard", false, "Mirror then discard selected messages")
flag.StringVar(&mirrorTo, "mirror-to", "", "Mirror selected messages to email") flag.StringVar(&mirrorTo, "mirror-to", "", "Mirror selected messages to email")
flag.Float64Var(&config.RejectScore, "reject-score", math.Inf(1), "Reject score to discard")
flag.DurationVar(&config.Timeout, "timeout", 15*time.Second, "Rspamd request timeout") flag.DurationVar(&config.Timeout, "timeout", 15*time.Second, "Rspamd request timeout")
flag.BoolVar(&config.Debug, "debug", false, "Export debug information (for developers)") flag.BoolVar(&config.Debug, "debug", false, "Export debug information (for developers)")
@@ -35,6 +34,10 @@ func New() *Config {
config.MirrorTo = strings.Split(strings.ReplaceAll(mirrorTo, " ", ""), ",") config.MirrorTo = strings.Split(strings.ReplaceAll(mirrorTo, " ", ""), ",")
} }
if config.MirrorDiscard && len(config.MirrorTo) == 0 {
config.MirrorDiscard = false
}
if config.Timeout < time.Second { if config.Timeout < time.Second {
config.Timeout *= time.Second config.Timeout *= time.Second
} }
+2 -2
View File
@@ -1,10 +1,10 @@
module git.vsu.ru/ai/rspamd-cgp module git.vsu.ru/ai/rspamd-cgp
go 1.18 go 1.23
require github.com/json-iterator/go v1.1.12 require github.com/json-iterator/go v1.1.12
require ( require (
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
) )
+2 -1
View File
@@ -4,8 +4,9 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+26 -23
View File
@@ -16,7 +16,7 @@ import (
"git.vsu.ru/ai/rspamd-cgp/config" "git.vsu.ru/ai/rspamd-cgp/config"
) )
var ( const (
headerJunkG string = "X-Junk-Score: [XX]" headerJunkG string = "X-Junk-Score: [XX]"
headerJunkA string = "X-Junk-Score: [XXXX]" headerJunkA string = "X-Junk-Score: [XXXX]"
headerJunkR string = "X-Junk-Score: [XXXXXXXXXX]" headerJunkR string = "X-Junk-Score: [XXXXXXXXXX]"
@@ -24,8 +24,8 @@ var (
var authservId string var authservId string
var client *http.Client var client *http.Client
var mirrorDiscard bool
var mirrorTo []string var mirrorTo []string
var rejectScore float64
var host string var host string
var debug bool var debug bool
@@ -39,8 +39,8 @@ func init() {
authservId = cgp.MainDomain authservId = cgp.MainDomain
} }
mirrorDiscard = config.MirrorDiscard
mirrorTo = config.MirrorTo mirrorTo = config.MirrorTo
rejectScore = config.RejectScore
host = "http://" + config.Host + "/checkv2" host = "http://" + config.Host + "/checkv2"
debug = config.Debug debug = config.Debug
@@ -61,12 +61,17 @@ func printResponse(v any) {
func Scan(seq int, filename string) { func Scan(seq int, filename string) {
from, rcpts, auth, ip, qid, body, err := cgp.Message(filename) from, rcpts, auth, ip, qid, body, seen, err := cgp.Message(filename)
if err != nil { if err != nil {
cgp.Failure(seq, qid, err) cgp.Failure(seq, qid, err)
return return
} }
if seen {
cgp.OkSeen(seq, qid)
return
}
req, err := http.NewRequest("POST", host, bytes.NewReader(body)) req, err := http.NewRequest("POST", host, bytes.NewReader(body))
if err != nil { if err != nil {
cgp.Failure(seq, qid, err) cgp.Failure(seq, qid, err)
@@ -100,23 +105,23 @@ func Scan(seq int, filename string) {
return return
} }
var js map[string]interface{} var res map[string]interface{}
if err := json.Unmarshal(rbody, &js); err != nil { if err := json.Unmarshal(rbody, &res); err != nil {
cgp.Failure(seq, qid, err) cgp.Failure(seq, qid, err)
return return
} }
if debug { if debug {
printResponse(js) printResponse(res)
} }
var headers []string var headers []string
if _, ok := js["dkim-signature"]; ok { if _, ok := res["dkim-signature"]; ok {
headers = append(headers, strings.Join([]string{"DKIM-Signature: ", js["dkim-signature"].(string)}, "")) headers = append(headers, strings.Join([]string{"DKIM-Signature: ", res["dkim-signature"].(string)}, ""))
} }
if milter, ok := js["milter"]; ok { if milter, ok := res["milter"]; ok {
if hdrs, ok := milter.(map[string]interface{})["add_headers"]; ok { if hdrs, ok := milter.(map[string]interface{})["add_headers"]; ok {
if reflect.TypeOf(hdrs).String() == "map[string]interface {}" { if reflect.TypeOf(hdrs).String() == "map[string]interface {}" {
for h, vh := range hdrs.(map[string]interface{}) { for h, vh := range hdrs.(map[string]interface{}) {
@@ -130,11 +135,10 @@ func Scan(seq int, filename string) {
} }
} }
action := js["action"] action := res["action"]
score := (js["score"]).(float64)
cgp.Putline("* %d [%d]: Action: %s; Score: %.2f/%.2f; Time elapsed: %.3fs\n", cgp.Putline("* %d [%d]: Action: %s; Score: %.2f/%.2f; Time elapsed: %.3fs\n",
seq, qid, action, score, js["required_score"], js["time_real"]) seq, qid, action, res["score"], res["required_score"], res["time_real"])
switch action { switch action {
case "no action": case "no action":
@@ -145,22 +149,21 @@ func Scan(seq int, filename string) {
} }
case "discard": case "discard":
cgp.Discard(seq) cgp.Discard(seq, qid, from, rcpts)
case "quarantine": case "quarantine":
cgp.MirrorTo(seq, mirrorTo, append(headers, headerJunkR)) cgp.MirrorTo(seq, qid, mirrorTo, append(headers, headerJunkR), body, mirrorDiscard)
case "reject": case "reject":
if score >= rejectScore { cgp.AddHeader(seq, append(headers, headerJunkR))
cgp.Putline("* %d [%d]: Action set to discard due to Score(%.2f) >= rejectScore(%d)\n",
seq, qid, score, rejectScore)
cgp.Discard(seq)
} else {
cgp.AddHeader(seq, append(headers, headerJunkR))
}
case "rewrite subject": case "rewrite subject":
fallthrough if subject, ok := res["subject"]; ok {
cgp.RewriteSubject(seq, append(headers, headerJunkA), subject.(string), qid, from, rcpts, body)
} else {
cgp.AddHeader(seq, append(headers, headerJunkA))
}
case "add header": case "add header":
cgp.AddHeader(seq, append(headers, headerJunkA)) cgp.AddHeader(seq, append(headers, headerJunkA))