From 0d4a1c9ffb636d84c180a703c4070be44bf95f51 Mon Sep 17 00:00:00 2001 From: John Denker Date: Fri, 13 Jul 2012 11:25:21 -0700 Subject: add skrewt to filter-chain --- tools/filters.conf | 1 + tools/makefile | 7 +- tools/skrewt.c | 198 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 204 insertions(+), 2 deletions(-) create mode 100644 tools/skrewt.c diff --git a/tools/filters.conf b/tools/filters.conf index 97b54d1..2a9e5d0 100644 --- a/tools/filters.conf +++ b/tools/filters.conf @@ -1,3 +1,4 @@ # configuration file for hi-q + /var/qmail/bin/skrewt /usr/local/bin/spamc -Y 2 /var/qmail/bin/qmail-queue diff --git a/tools/makefile b/tools/makefile index 256fa6e..c5b702b 100644 --- a/tools/makefile +++ b/tools/makefile @@ -10,10 +10,12 @@ CC= /usr/bin/g++ -Wall -g -I $(HOME)/lib/include .SECONDARY : # do not remove any intermediate files -all: pido hi-q +progs = pido hi-q skrewt + +all: $(progs) install: - install pido hi-q /var/qmail/bin/ + install $(progs) /var/qmail/bin/ cp filters.conf /var/qmail/control/ install -m700 -d /var/qmail/rbin chown qmaild /var/qmail/rbin @@ -27,6 +29,7 @@ install: chown qmaild /var/qmail/control/*.crtkey install qmail-tls-check_certs /var/qmail/bin/ install spamassassin /etc/init.d/ + install qmail /etc/init.d/ install spamassassin.default /etc/default/spamassassin install tcprules.make /etc/tcpserver/makefile diff --git a/tools/skrewt.c b/tools/skrewt.c new file mode 100644 index 0000000..117981b --- /dev/null +++ b/tools/skrewt.c @@ -0,0 +1,198 @@ +/////////////////// +// skrewt.c +// +// scrutinize email +// + +#include +#include /* for exit() */ +#include /* for strcmp() */ +#include /* toupper */ + +using namespace std; + +void usage(const int sts){ + (sts ? cerr : cout) << +"Usage: skrewt [options]\n" +"\n" +" Scrutinizes email. Reads stdin, copies it to stdout.\n" +" Exit result 0 means good, 1 means rejection (spam).\n" +" Writes reason for rejection to stderr.\n" +"\n" +" Typically used as a filter in a pipeline, along with spamc -E\n" +" Options\n" +" -h print this msg (and exit immediately).\n" +"\n" +" Messages containing the string '-please-bounce-this-' will be rejected.\n" +" Messages with no date will be rejected.\n" +; + exit(sts); +} + +///////////////////////////////////////////////////////// +// Case insensitive comparison of strings + +class lessthan_foldcase{ +public: + bool operator() (const std::string& a, const std::string& b) const { + size_t a_len = a.length(); + size_t b_len = b.length(); + + size_t lim = a_len < b_len ? a_len : b_len; + + for (size_t i=0; i chb) return false; + } + // here if one is an extension of the other + if ( a_len < b_len ) return true; + return false; + } +}; + + +// Returns negative if a is less than b in alphabetical order +// returns 0 if they are the same, or positive if a is greater. +// Like perl cmp operator, but ignores case. +int cmp_casefold(const std::string& a, const std::string& b) { + string::const_iterator aa, bb; + aa = a.begin(); + bb = b.begin(); + while (aa != a.end() && bb != b.end()){ + char ca = tolower(*aa++); + char cb = tolower(*bb++); + if (ca != cb) return ca < cb ? -2 : 2; + } + if (aa != a.end()) return 1; // a is longer + if (bb != b.end()) return -1; // b is longer + return 0; +} + + +string toLower(const std::string& a){ + string rslt = a; + string::iterator rr; + for (rr = rslt.begin(); rr != rslt.end(); rr++){ + *rr = tolower(*rr); + } + return rslt; +} + + +string ltrim(string foo){ + unsigned where = foo.find_first_not_of(" \t\r\n"); + if (where == foo.npos) return foo; + return foo.substr(where); +} + + + +int main(int argc, char** argv){ + if (argc > 1) { + if (argv[1] == string("-h")) usage(0); + usage(1); + } + + int inheads(1); + string boundary("x-xx-x"); + int text_type(1); + int textlines(0); + int gotdate(0); + for (;;){ + if (cin.eof()) break; + if (cin.bad()) return 1; + if (inheads) { + string header; + if (getline(cin, header).fail()) continue; + for (;;) { + if (cin.eof()) break; + if (cin.bad()) return 1; + char ch; + if (cin.get(ch).fail()) continue; + cin.putback(ch); + if (ch != ' ' && ch != '\t') break; + string line; + if (getline(cin, line).fail()) continue; + header += "\n" + line; + } + if (header.length() == 0) { + if (!gotdate) { + cerr << "skrewt rejection: no date" << endl; + exit(1); // disallow mail with no date + } + inheads = 0; + } + else { + string headword; + string rest; + unsigned int where = header.find(":"); + if (where != string::npos) { + headword = header.substr(0, where); + rest = ltrim(header.substr(1+where)); + } + headword = toLower(headword); + if (headword == "content-type") { + string the_type = rest; + unsigned int where = the_type.find_first_of(" \t;\n"); + if (where == string::npos) { + /* do nothing */ + } + else { + the_type = the_type.substr(0,where); + } + the_type = toLower(the_type); + text_type = (the_type.find("text/") == 0); + if (0) cerr << "type (" << the_type << ") " + << (text_type?"text":"nope") << endl; + string srch = "boundary="; + where = header.find(srch); + if (where != string::npos) { + where += srch.length(); + boundary = header.substr(where); + if (boundary[0] == '"') { + boundary = boundary.substr(1); + where = boundary.find_first_of("\""); + } else { + where = boundary.find_first_of(" \t;\n"); + } + if (where == string::npos) { + /* do nothing, boundary=boundary as a whole */ + } else { + boundary = boundary.substr(0, where); + } + } + } else if (headword == "date") { + gotdate++; + } else if (headword == "subject") { + if (rest.find("-please-bounce-this-") != string::npos) { + cerr << "skrewt rejection: by request" << endl; + exit(1); + } + } + } + cout << header << endl; + } else { + string line; + if (!getline(cin, line).fail()) { + if (line == "--" + boundary) { + inheads = 1; + } else { + if (text_type) { + if (ltrim(line).length()) textlines++; + } + } + cout << line << endl; + } + } + } + if (0) cerr << "textlines: " << textlines << endl; + if (!textlines) { + cerr << "skrewt rejection: no text" << endl; + exit(1); + } + return 0; +} -- cgit v1.2.3