diff --git a/client.go b/client.go new file mode 100644 index 0000000..1fdb410 --- /dev/null +++ b/client.go @@ -0,0 +1,511 @@ +package spamc + +import ( + "bufio" + "errors" + "fmt" + "io" + "net" + "regexp" + "strconv" + "strings" + "time" +) + +//Error Types + +const EX_OK = 0 //no problems +const EX_USAGE = 64 // command line usage error +const EX_DATAERR = 65 // data format error +const EX_NOINPUT = 66 // cannot open input +const EX_NOUSER = 67 // addressee unknown +const EX_NOHOST = 68 // host name unknown +const EX_UNAVAILABLE = 69 // service unavailable +const EX_SOFTWARE = 70 // internal software error +const EX_OSERR = 71 // system error (e.g., can't fork) +const EX_OSFILE = 72 // critical OS file missing +const EX_CANTCREAT = 73 // can't create (user) output file +const EX_IOERR = 74 // input/output error +const EX_TEMPFAIL = 75 // temp failure; user is invited to retry +const EX_PROTOCOL = 76 // remote error in protocol +const EX_NOPERM = 77 // permission denied +const EX_CONFIG = 78 // configuration error +const EX_TIMEOUT = 79 // read timeout + +//map of default spamD protocol errors v1.5 +var SpamDError = map[int]string{ + EX_USAGE: "Command line usage error", + EX_DATAERR: "Data format error", + EX_NOINPUT: "Cannot open input", + EX_NOUSER: "Addressee unknown", + EX_NOHOST: "Host name unknown", + EX_UNAVAILABLE: "Service unavailable", + EX_SOFTWARE: "Internal software error", + EX_OSERR: "System error", + EX_OSFILE: "Critical OS file missing", + EX_CANTCREAT: "Can't create (user) output file", + EX_IOERR: "Input/output error", + EX_TEMPFAIL: "Temp failure; user is invited to retry", + EX_PROTOCOL: "Remote error in protocol", + EX_NOPERM: "Permission denied", + EX_CONFIG: "Configuration error", + EX_TIMEOUT: "Read timeout", +} + +//Default parameters +const PROTOCOL_VERSION = "1.5" +const DEFAULT_TIMEOUT = 10 + +//Command types +const CHECK = "CHECK" +const SYMBOLS = "SYMBOLS" +const REPORT = "REPORT" +const REPORT_IGNOREWARNING = "REPORT_IGNOREWARNING" +const REPORT_IFSPAM = "REPORT_IFSPAM" +const SKIP = "SKIP" +const PING = "PING" +const TELL = "TELL" +const PROCESS = "PROCESS" +const HEADERS = "HEADERS" + +//Learn types +const LEARN_SPAM = "SPAM" +const LEARN_HAM = "HAM" +const LEARN_NOTSPAM = "NOTSPAM" +const LEARN_NOT_SPAM = "NOT_SPAM" +const LEARN_FORGET = "FORGET" + +//Test Types +const TEST_INFO = "info" +const TEST_BODY = "body" +const TEST_RAWBODY = "rawbody" +const TEST_HEADER = "header" +const TEST_FULL = "full" +const TEST_URI = "uri" +const TEST_TXT = "text" + +//only for parse use !important +const SPLIT = "ยง" +const TABLE_MARK = "----" + +//Types +type Client struct { + ConnTimoutSecs int + ProtocolVersion string + Host string + User string +} + +//Default response struct +type SpamDOut struct { + Code int + Message string + Vars map[string]interface{} +} + +//Default callback to SpanD response +type FnCallback func(*bufio.Reader) (*SpamDOut, error) + +//new instance of Client +func New(host string, timeout int) *Client { + return &Client{timeout, PROTOCOL_VERSION, host, ""} +} + +func (s *Client) SetUnixUser(user string) { + s.User = user +} + +// Return a confirmation that spamd is alive. +func (s *Client) Ping() (r *SpamDOut, err error) { + return s.simpleCall(PING, []string{}) +} + +// Just check if the passed message is spam or not and return score +func (s *Client) Check(msgpars ...string) (reply *SpamDOut, err error) { + return s.simpleCall(CHECK, msgpars) +} + +//Ignore this message -- client opened connection then changed its mind +func (s *Client) Skip(msgpars ...string) (reply *SpamDOut, err error) { + return s.simpleCall(SKIP, msgpars) +} + +//Check if message is spam or not, and return score plus list of symbols hit +func (s *Client) Symbols(msgpars ...string) (reply *SpamDOut, err error) { + return s.simpleCall(SYMBOLS, msgpars) +} + +//Check if message is spam or not, and return score plus report +func (s *Client) Report(msgpars ...string) (reply *SpamDOut, err error) { + return s.simpleCall(REPORT, msgpars) +} + +//Check if message is spam or not, and return score plus report +func (s *Client) ReportIgnoreWarning(msgpars ...string) (reply *SpamDOut, err error) { + return s.simpleCall(REPORT_IGNOREWARNING, msgpars) +} + +//Check if message is spam or not, and return score plus report if the message is spam +func (s *Client) ReportIfSpam(msgpars ...string) (reply *SpamDOut, err error) { + return s.simpleCall(REPORT_IFSPAM, msgpars) +} + +//Process this message and return a modified message - on deloy +func (s *Client) Process(msgpars ...string) (reply *SpamDOut, err error) { + return s.simpleCall(PROCESS, msgpars) +} + +//Same as PROCESS, but return only modified headers, not body (new in protocol 1.4) +func (s *Client) Headers(msgpars ...string) (reply *SpamDOut, err error) { + return s.simpleCall(HEADERS, msgpars) +} + +//Sign the message as spam +func (s *Client) ReportingSpam(msgpars ...string) (reply *SpamDOut, err error) { + headers := map[string]string{ + "Message-class": "spam", + "Set": "local,remote", + } + return s.Tell(msgpars, &headers) +} + +//Sign the message as false-positive +func (s *Client) RevokeSpam(msgpars ...string) (reply *SpamDOut, err error) { + headers := map[string]string{ + "Message-class": "ham", + "Set": "local,remote", + } + return s.Tell(msgpars, &headers) +} + +//Learn if a message is spam or not +func (s *Client) Learn(learnType string, msgpars ...string) (reply *SpamDOut, err error) { + headers := make(map[string]string) + switch strings.ToUpper(learnType) { + case LEARN_SPAM: + headers["Message-class"] = "spam" + headers["Set"] = "local" + case LEARN_HAM, LEARN_NOTSPAM, LEARN_NOT_SPAM: + headers["Message-class"] = "ham" + headers["Set"] = "local" + case LEARN_FORGET: + headers["Remove"] = "local" + default: + err = errors.New("Learn Type Not Found") + return + } + return s.Tell(msgpars, &headers) +} + +//wrapper to simple calls +func (s *Client) simpleCall(cmd string, msgpars []string) (reply *SpamDOut, err error) { + return s.call(cmd, msgpars, func(data *bufio.Reader) (r *SpamDOut, e error) { + r, e = processResponse(cmd, data) + if r.Code == EX_OK { + e = nil + } + return + }, nil) +} + +//external wrapper to simple call +func (s *Client) SimpleCall(cmd string, msgpars ...string) (reply *SpamDOut, err error) { + return s.simpleCall(strings.ToUpper(cmd), msgpars) +} + +//Tell what type of we are to process and what should be done +//with that message. This includes setting or removing a local +//or a remote database (learning, reporting, forgetting, revoking) +func (s *Client) Tell(msgpars []string, headers *map[string]string) (reply *SpamDOut, err error) { + return s.call(TELL, msgpars, func(data *bufio.Reader) (r *SpamDOut, e error) { + r, e = processResponse(TELL, data) + + if r.Code == EX_UNAVAILABLE { + e = errors.New("TELL commands are not enabled, set the --allow-tell switch.") + return + } + if r.Code == EX_OK { + e = nil + return + } + return + }, headers) +} + +//here a TCP socket is created to call SPAMD +func (s *Client) call(cmd string, msgpars []string, onData FnCallback, extraHeaders *map[string]string) (reply *SpamDOut, err error) { + + if extraHeaders == nil { + extraHeaders = &map[string]string{} + } + + switch len(msgpars) { + case 1: + if s.User != "" { + x := *extraHeaders + x["User"] = s.User + *extraHeaders = x + } + case 2: + x := *extraHeaders + x["User"] = msgpars[1] + *extraHeaders = x + default: + if cmd != PING { + err = errors.New("Message parameters wrong size") + } else { + msgpars = []string{""} + } + return + } + + if cmd == REPORT_IGNOREWARNING { + cmd = REPORT + } + + // Create a new connection + stream, err := net.Dial("tcp", s.Host) + + if err != nil { + err = errors.New("Connection dial error to spamd: " + err.Error()) + return + } + // Set connection timeout + timeout := time.Now().Add(time.Duration(s.ConnTimoutSecs) * time.Duration(time.Second)) + errTimeout := stream.SetDeadline(timeout) + if errTimeout != nil { + err = errors.New("Connection to spamd Timed Out:" + errTimeout.Error()) + return + } + defer stream.Close() + + // Create Command to Send to spamd + cmd += " SPAMC/" + s.ProtocolVersion + "\r\n" + cmd += "Content-length: " + fmt.Sprintf("%v\r\n", len(msgpars[0])+2) + //Process Extra Headers if Any + if len(*extraHeaders) > 0 { + for hname, hvalue := range *extraHeaders { + cmd = cmd + hname + ": " + hvalue + "\r\n" + } + } + cmd += "\r\n" + msgpars[0] + "\r\n\r\n" + + _, errwrite := stream.Write([]byte(cmd)) + if errwrite != nil { + err = errors.New("spamd returned a error: " + errwrite.Error()) + return + } + + // Execute onData callback throwing the buffer like parameter + reply, err = onData(bufio.NewReader(stream)) + return +} + +//SpamD reply processor +func processResponse(cmd string, data *bufio.Reader) (returnObj *SpamDOut, err error) { + defer func() { + data.UnreadByte() + }() + + returnObj = new(SpamDOut) + returnObj.Code = -1 + //read the first line + line, _, _ := data.ReadLine() + lineStr := string(line) + + r := regexp.MustCompile(`(?i)SPAMD\/([0-9\.]+)\s([0-9]+)\s([0-9A-Z_]+)`) + var result = r.FindStringSubmatch(lineStr) + if len(result) < 4 { + if cmd != "SKIP" { + err = errors.New("spamd unreconized reply:" + lineStr) + } else { + returnObj.Code = EX_OK + returnObj.Message = "SKIPPED" + } + return + } + returnObj.Code, _ = strconv.Atoi(result[2]) + returnObj.Message = result[3] + + //verify a mapped error... + if SpamDError[returnObj.Code] != "" { + err = errors.New(SpamDError[returnObj.Code]) + returnObj.Vars = make(map[string]interface{}) + returnObj.Vars["error_description"] = SpamDError[returnObj.Code] + return + } + returnObj.Vars = make(map[string]interface{}) + + //start didSet + if cmd == TELL { + returnObj.Vars["didSet"] = false + returnObj.Vars["didRemove"] = false + for { + line, _, err = data.ReadLine() + + if err == io.EOF || err != nil { + if err == io.EOF { + err = nil + } + break + } + if strings.Contains(string(line), "DidRemove") { + returnObj.Vars["didRemove"] = true + } + if strings.Contains(string(line), "DidSet") { + returnObj.Vars["didSet"] = true + } + + } + return + } + //read the second line + line, _, err = data.ReadLine() + + //finish here if line is empty + if len(line) == 0 { + if err == io.EOF { + err = nil + } + return + } + + //ignore content-length header.. + lineStr = string(line) + switch cmd { + + case SYMBOLS, CHECK, REPORT, REPORT_IFSPAM, REPORT_IGNOREWARNING, PROCESS, HEADERS: + + switch cmd { + case SYMBOLS, REPORT, REPORT_IFSPAM, REPORT_IGNOREWARNING, PROCESS, HEADERS: + //ignore content-length header.. + line, _, err = data.ReadLine() + lineStr = string(line) + } + + r := regexp.MustCompile(`(?i)Spam:\s(True|False|Yes|No)\s;\s([0-9\.]+)\s\/\s([0-9\.]+)`) + var result = r.FindStringSubmatch(lineStr) + + if len(result) > 0 { + returnObj.Vars["isSpam"] = false + switch result[1][0:1] { + case "T", "t", "Y", "y": + returnObj.Vars["isSpam"] = true + } + returnObj.Vars["spamScore"], _ = strconv.ParseFloat(result[2], 64) + returnObj.Vars["baseSpamScore"], _ = strconv.ParseFloat(result[3], 64) + } + + switch cmd { + case PROCESS, HEADERS: + lines := "" + for { + line, _, err = data.ReadLine() + if err == io.EOF || err != nil { + if err == io.EOF { + err = nil + } + return + } + lines += string(line) + "\r\n" + returnObj.Vars["body"] = lines + } + return + case SYMBOLS: + //ignore line break... + data.ReadLine() + //read + line, _, err = data.ReadLine() + returnObj.Vars["symbolList"] = strings.Split(string(line), ",") + + case REPORT, REPORT_IFSPAM, REPORT_IGNOREWARNING: + //ignore line break... + data.ReadLine() + + for { + line, _, err = data.ReadLine() + + if len(line) > 0 { + lineStr = string(line) + + //TXT Table found, prepare to parse.. + if lineStr[0:4] == TABLE_MARK { + + section := []map[string]interface{}{} + tt := 0 + for { + line, _, err = data.ReadLine() + //Stop read the text table if last line or Void line + if err == io.EOF || err != nil || len(line) == 0 { + if err == io.EOF { + err = nil + } + break + } + //Parsing + lineStr = string(line) + spc := 2 + if lineStr[0:1] == "-" { + spc = 1 + } + lineStr = strings.Replace(lineStr, " ", SPLIT, spc) + lineStr = strings.Replace(lineStr, " ", SPLIT, 1) + if spc > 1 { + lineStr = " " + lineStr[2:] + } + x := strings.Split(lineStr, SPLIT) + if lineStr[1:3] == SPLIT { + section[tt-1]["message"] = fmt.Sprintf("%v %v", section[tt-1]["message"], strings.TrimSpace(lineStr[5:])) + } else { + if len(x) != 0 { + message := strings.TrimSpace(x[2]) + score, _ := strconv.ParseFloat(strings.TrimSpace(x[0]), 64) + + section = append(section, map[string]interface{}{ + "score": score, + "symbol": x[1], + "message": message, + }) + + tt++ + } + } + } + if REPORT_IGNOREWARNING == cmd { + nsection := []map[string]interface{}{} + for _, c := range section { + if c["score"].(float64) != 0 { + nsection = append(nsection, c) + } + } + section = nsection + } + + returnObj.Vars["report"] = section + break + } + } + + if err == io.EOF || err != nil { + if err == io.EOF { + err = nil + } + break + } + } + } + } + + if err != io.EOF { + for { + line, _, err = data.ReadLine() + if err == io.EOF || err != nil { + if err == io.EOF { + err = nil + } + break + } + } + } + return +} diff --git a/examples/report.go b/examples/report.go new file mode 100644 index 0000000..e3af45d --- /dev/null +++ b/examples/report.go @@ -0,0 +1,44 @@ +package main + +import ( + "fmt" + "go-spamc" +) + +func main() { + + html := "TestHello world. I'm not a Spam, don't kill me spamassassin!" + + client := spamc.New("127.0.0.1:783", 10) + + //the 2nd parameter is optional, you can set who (the unix user) do the call + //looks like client.Report(html, "saintienn") + + reply, err := client.Report(html) + + if err == nil { + fmt.Println(reply) + } else { + fmt.Println(reply, err) + } + +} + +/* Example Response + { + Code: 0, + Message: 'EX_OK', + Vars:{ + isSpam: true, + spamScore: 6.9, + baseSpamScore: 5, + report:[ + { + "score": score, + "symbol": x[1], + "message": message, + } + ] + } + } +*/ diff --git a/examples/report.js b/examples/report.js deleted file mode 100644 index a6d84b5..0000000 --- a/examples/report.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * User: Carl Glaysher - * Date: 17/03/2012 - * Time: 08:46 - * Description: Front end to check spamc client - */ -var spamc = require('./spamc'); -var client = new spamc(); - -client.report('My Message as String',function(result){ - console.log(result); -}); - -/* Example Response - { - responseCode: 0, - responseMessage: 'EX_OK', - isSpam: true, - spamScore: 6.9, - baseSpamScore: 5, - report:[ - { - score: '0.0', - name: 'NO_RELAYS', - description: 'Informational', - type: 'message' - } - ] - } -*/ \ No newline at end of file diff --git a/index.js b/index.js deleted file mode 100644 index 7017b6c..0000000 --- a/index.js +++ /dev/null @@ -1,334 +0,0 @@ -/** - * Author: Carl Glaysher - * Date Created: 17/03/2012 - * Description: Module to emulate SPAMC client in a node way - * Available Commands: - * - * ping - returns boolean - * check - returns object - * symbols - returns object with matches - * report - returns objects with matches and descriptions - * reportIfSpam - returns object with matches and descriptions - * process - returns object with modified message - * headers - returns object with modified headers only - * learn - TELL spamassassin message is Spam or Ham - * tell - TELL spamassassin message is Spam - * revoke - remove Spammed Message as being spam from spamassassin - * - */ -var net = require('net'); - -var spamc = function (host, port, timeout) { - var self = this; - var protocolVersion = 1.5; - var host = (host == undefined) ? '127.0.0.1' : host; - var port = (port == undefined) ? 783 : port; - var connTimoutSecs = (timeout == undefined) ? 10 : timeout; - /* - * Description: Sends a Ping to spamd and returns Pong on response - * Param: onResponse {function} - * Returns: self - */ - this.ping = function(onResponse){ - exec('PING',null,function(data){ - /* Check Response has the word PONG */ - if(data[0].indexOf('PONG')>0){ - onResponse(true); - }else{ - onResponse(false); - } - }); - return self; - }; - /* - * Description: returns spam score - * Param: message {string} - * Param: onResponse {function} - * Returns: self - */ - this.check = function(message,onResponse){ - exec('CHECK',message,function(data){ - var response = processResponse('CHECK',data); - if(typeof(onResponse)=='function') onResponse(response); - }); - return self; - }; - /* - * Description: Returns Spam Score and Matches - * Param: message {string} - * Param: onResponse {function} - * Returns: self - */ - this.symbols = function(message,onResponse){ - exec('SYMBOLS',message,function(data){ - var response = processResponse('SYMBOLS',data); - if(typeof(onResponse)=='function') onResponse(response); - }); - return self; - }; - /* - * Description: Returns an object report - * Param: message {string} - * Param: onResponse {function} - * Returns: self - */ - this.report = function(message,onResponse){ - exec('REPORT',message,function(data){ - var response = processResponse('REPORT',data); - if(typeof(onResponse)=='function') onResponse(response); - }); - return self; - }; - /* - * Description: Returns Object Report if is spam - * Param: message {string} - * Param: onResponse {function} - * Returns: self - */ - this.reportIfSpam = function(message,onResponse){ - exec('REPORT_IFSPAM',message,function(data){ - var response = processResponse('REPORT_IFSPAM',data); - if(typeof(onResponse)=='function') onResponse(response); - }); - return self; - }; - /* - * Description: Returns back a report for the message + the message - * Param: message {string} - * Param: onResponse {function} - * Returns: self - */ - this.process = function(message,onResponse){ - exec('PROCESS',message,function(data){ - var response = processResponse('PROCESS',data); - if(typeof(onResponse)=='function') onResponse(response); - }); - return self; - }; - /* - * Description: Returns headers for the message - * Param: message {string} - * Param: onResponse {function} - * Returns: self - */ - this.headers = function(message,onResponse){ - exec('HEADERS',message,function(data){ - var response = processResponse('HEADERS',data); - if(typeof(onResponse)=='function') onResponse(response); - }); - return self; - }; - - /* - * Description: Tell spamd to learn message is spam/ham or forget - * Param: message {string} - * Param: learnType {string} - * Param: onResponse {function} - * Returns: self - */ - this.learn = function(message,learnType,onResponse){ - var headers; - switch(learnType.toUpperCase()){ - case 'SPAM': - headers=[ - {name:'Message-class','value':'spam'}, - {name:'Set','value':'local'} - ]; - break; - case 'HAM': - case 'NOTSPAM': - case 'NOT_SPAM': - headers=[ - {name:'Message-class','value':'ham'}, - {name:'Set','value':'local'} - ]; - break; - case 'FORGET': - headers=[ - {name:'Remove','value':'local'} - ]; - break; - default: - throw new Error('Learn Type Not Found'); - } - exec('TELL',message,function(data){ - var response = processResponse('HEADERS',data); - if(response.responseCode==69){ - throw new Error('TELL commands are not enabled, set the --allow-tell switch.'); - } - if(typeof(onResponse)=='function') onResponse(response); - },headers); - return self; - }; - /* - * Description: tell spamd message is not spam - * Param: message {string} - * Param: onResponse {function} - * Returns: self - */ - this.revoke = function(message,onResponse){ - headers=[ - {name:'Message-class','value':'ham'}, - {name:'Set','value':'local,remote'} - ]; - exec('TELL',message,function(data){ - var response = processResponse('HEADERS',data); - if(response.responseCode==69){ - throw new Error('TELL commands are not enabled, set the --allow-tell switch.'); - } - if(typeof(onResponse)=='function') onResponse(response); - },headers); - return self; - }; - /* - * Description: Tell spamd message is spam - * Param: message {string} - * Param: onResponse {function} - * Returns: self - */ - this.tell = function(message,onResponse){ - headers=[ - {name:'Message-class','value':'spam'}, - {name:'Set','value':'local,remote'} - ]; - exec('TELL',message,function(data){ - var response = processResponse('HEADERS',data); - if(response.responseCode==69){ - throw new Error('TELL commands are not enabled, set the --allow-tell switch.'); - } - if(typeof(onResponse)=='function') onResponse(response); - },headers); - return self; - }; - /* - * Description: Sends a command to spamd - * Param: cmd {string} - * Param: message {string} - * Param: onData {function(data)} - */ - var exec = function(cmd,message,onData,extraHeaders){ - var responseData = []; - var stream = net.createConnection(port,host); - stream.setTimeout(connTimoutSecs*1000,function(){ - throw new Error('Connection to spamd Timed Out'); - }); - stream.on('connect',function(){ - /* Create Command to Send to spamd */ - cmd = cmd+" SPAMC/"+protocolVersion+"\r\n"; - if(typeof(message)=='string'){ - message = message+'\r\n'; - cmd = cmd+"Content-length: "+(message.length)+"\r\n"; - /* Process Extra Headers if Any */ - if(typeof(extraHeaders)=='object'){ - for(var i=0;i0){ - responseData[responseData.length]=data[i]; - } - } - }); - stream.on('close',function(){ - onData(responseData); - }) - }; - /* - * Description: Processes Response from spamd and put into a formatted object - * Param: cmd {string} - * Param: lines {array[string]} - * Return: {object} - */ - var processResponse = function(cmd,lines){ - var returnObj = {}; - var result = lines[0].match(/SPAMD\/([0-9\.]+)\s([0-9]+)\s([0-9A-Z_]+)/); - if(result==null){ - throw new Error('spamd unreconized response:'+lines[0]); - } - returnObj.responseCode = parseInt(result[2]); - returnObj.responseMessage = result[3]; - if(cmd=='TELL'){ - returnObj.didSet=false; - returnObj.didRemove=false; - } - for(var i=0;i=0){ - returnObj.didSet=true; - } - if(lines[i].indexOf('DidRemove:')>=0){ - returnObj.didRemove=true; - } - } - if(cmd=='PROCESS'){ - returnObj.message = ''; - for(var i=3;i", - "description":"Connect to spamassassin via spamd daemon and check spam score on a email", - "contributors":[ - { - "name":"Carl Glaysher", - "email":"carl@coxeh.com" - } - ], - "repository": { - "type": "git", - "url": "https://github.com/coxeh/node-spamc" - }, - "main":"./index.js", - "keywords":["spamd","spamc","spamassassin","email","spam"] -} \ No newline at end of file diff --git a/readme.md b/readme.md index 7adb091..52e2941 100644 --- a/readme.md +++ b/readme.md @@ -1,34 +1,55 @@ -# node-spamc +# go-spamc -node-spamc is a nodejs module that connects to spamassassin's spamd daemon. You are able to: +go-spamc is a golang package that connects to spamassassin's spamd daemon. +Is a code port of nodejs module node-spamc(https://github.com/coxeh/node-spamc) + +Thanks for your amazing code Carl Glaysher ;) + + + +You are able to: - Check a message for a spam score and return back what spamassassin matched on - Ability to send messages to spamassassin to learn from - Ability to do everything that `spamc` is capable of -## Commands Available - - - `check` checks a message for a spam score and returns an object of information - - `symbols` like `check` but also returns what the message matched on - - `report` like `symbols` but matches also includes a small description - - `reportIfSpam` only returns a result if message is spam - - `process` like `check` but also returns a processed message with extra headers - - `headers` like `check` but also returns the message headers in a array - - `learn` abilty to parse a message to spamassassin and learn it as spam or ham - - `tell` ability to tell spamassassin that the message is spam - - `revoke` abilty to tell spamassassin that the message is not spam +## Methods Available + - `Check` checks a message for a spam score and returns an object of information + - `Symbols` like `check` but also returns what the message matched on + - `Report` like `symbols` but matches also includes a small description + - `ReportIfSpam` only returns a result if message is spam + - `ReportIgnoreWarning` like report but matches only symbols with score > 0 "New" + - `Process` like `check` but also returns a processed message with extra headers + - `Headers` like `check` but also returns the message headers in a array + - `Learn` abilty to parse a message to spamassassin and learn it as spam or ham + - `ReportingSpam` ability to tell spamassassin that the message is spam + - `RevokeSpam` abilty to tell spamassassin that the message is not spam + ## Example +example.go -This example will parse a message to spamassassin to perform a report and will callback on success. + package main + + import ( + "fmt" + "spamc" + ) - var spamc = require('./spamc'); - var client = new spamc(); + func main() { + + html := "Hello world. I'm not a Spam, don't kill me SpamAssassin!" + client := spamc.New() + + //the 2nd parameter is optional, you can set who (the unix user) do the call + reply, err := client.Check(html, "saintienn") + + fmt.Println(reply.Code) + fmt.Println(reply.Message) + fmt.Println(reply.Vars) + } - client.report('My Message as String',function(result){ - console.log(result); - }); \ No newline at end of file