summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Denker <jsd@av8n.com>2012-07-25 13:05:03 -0700
committerJohn Denker <jsd@av8n.com>2012-07-29 15:32:37 -0700
commit6e8083ff4ffe3fd2b6d337386637a2b5c1378cf7 (patch)
treea84885e65a856538acbe925645e1c67195bcb686
parent02bfae1f87c4693eb00bf943fb24886a5ac47a09 (diff)
fix a bunch of DOS-CR bugs
-rw-r--r--tools/filters.conf2
-rw-r--r--tools/mail-scan.c12
-rw-r--r--tools/skrewt.c121
3 files changed, 102 insertions, 33 deletions
diff --git a/tools/filters.conf b/tools/filters.conf
index bd8eb33..3cbd5bf 100644
--- a/tools/filters.conf
+++ b/tools/filters.conf
@@ -1,5 +1,5 @@
# configuration file for hi-q
-series /var/qmail/bin/skrewt
+series /var/qmail/bin/skrewt -err
stub /var/qmail/bin/greylist -check -v
sa /usr/local/bin/spamc -Y 0 -s 1000000 -x
qq /var/qmail/bin/qmail-queue
diff --git a/tools/mail-scan.c b/tools/mail-scan.c
index 1ea245f..dc8aa5c 100644
--- a/tools/mail-scan.c
+++ b/tools/mail-scan.c
@@ -183,6 +183,16 @@ public:
}
};
+string noCR(const string bar){
+ string foo(bar);
+ int len = foo.length();
+ if (len){
+ if (foo[len-1] == '\r') {
+ foo.erase(len-1);
+ }
+ }
+ return foo;
+}
////////////////////////////////////////////////////////////
int main(int _argc, const char** _argv){
@@ -268,6 +278,7 @@ int main(int _argc, const char** _argv){
return 1;
}
if (getline(infile, line).fail()) continue;
+ line = noCR(line);
Header.push_back(line);
msgsize += line.length()+1;
if (msgsize > maxsize) {
@@ -335,6 +346,7 @@ int main(int _argc, const char** _argv){
break;
}
} // end loop over matching records in this file
+
if (vflag && !foundsome_infile) {
cout << *file << endl;
didprint++;
diff --git a/tools/skrewt.c b/tools/skrewt.c
index ad99e67..a43fd13 100644
--- a/tools/skrewt.c
+++ b/tools/skrewt.c
@@ -1,4 +1,4 @@
-///////////////////
+//////////////////
// skrewt.c
//
// scrutinize email
@@ -13,6 +13,7 @@
#include <stdio.h> /* perror */
#include <sstream>
#include <vector>
+#include <list>
using namespace std;
@@ -28,6 +29,7 @@ void usage(const int sts){
" Options\n"
" -help print this msg (and exit immediately).\n"
" -maxsize ii msg size in bytes; anything bigger will be rejected.\n"
+" -error-exit exit early if errors have been detected.\n"
"\n"
" Messages containing the string '-please-bounce-this-' will be rejected.\n"
" Messages with no date will be rejected.\n"
@@ -36,7 +38,7 @@ void usage(const int sts){
}
// error exit codes, mostly as stated in qmail.c
-#define bar \
+#define ErrorCodes \
foo(good, 0) ;\
foo(spam, 21) ;\
foo(permerr, 31) ;\
@@ -46,7 +48,7 @@ foo(syserr, 71) ;\
foo(comerr, 74) ;
#define foo(name, num) const int ex_ ## name = num
-bar
+ErrorCodes
#undef foo
@@ -104,12 +106,23 @@ string toLower(const std::string& a){
}
////////////////
-string ltrim(string foo){
+string ltrim(const string foo){
size_t where = foo.find_first_not_of(" \t\r\n");
if (where == foo.npos) return foo;
return foo.substr(where);
}
+string noCR(const string bar){
+ string foo(bar);
+ int len = foo.length();
+ if (len){
+ if (foo[len-1] == '\r') {
+ foo.erase(len-1);
+ }
+ }
+ return foo;
+}
+
////////////////
// little utility to help with argument parsing:
//
@@ -117,7 +130,8 @@ int prefix(const string shorter, const string longer){
return shorter == longer.substr(0, shorter.length());
}
-void exeunt(const int sts){
+void maybe_exeunt(const int sts, const int really){
+ if (!really) return;
if (sts == ex_good) exit(sts);
const char* foo = getenv("HI_Q_GROUP");
@@ -136,6 +150,10 @@ void exeunt(const int sts){
exit(sts);
}
+void exeunt(const int sts){
+ maybe_exeunt(sts, 1);
+}
+
string basename(const string path){
size_t where = path.rfind("/");
if (where != string::npos) return path.substr(1+where);
@@ -192,6 +210,15 @@ void parse_content(const string type_spec_line, string &maintype, string &bounda
}
}
+string join(const string sep, const list<string> stuff){
+ string rslt;
+ for (list<string>::const_iterator ptr = stuff.begin();
+ ptr != stuff.end(); ptr++){
+ if (rslt.length()) rslt += sep;
+ rslt += *ptr;
+ }
+ return rslt;
+}
////////////////////////////////////////////////////////////
int main(int _argc, const char** _argv){
@@ -209,6 +236,8 @@ int main(int _argc, const char** _argv){
}
int maxsize(1000*1000);
+ int error_exit(0);
+ int mid_required(0);
while (argc) {
string arg(*argv); argv++; argc--;
@@ -216,14 +245,18 @@ int main(int _argc, const char** _argv){
if (prefix(arg, "-help")) {
usage(0);
}
- if (prefix(arg, "-maxsize")) {
+ if (0) {
+ } else if (prefix(arg, "-mid-required")) {
+ mid_required++;
+ } else if (prefix(arg, "-error-exit")) {
+ error_exit++;
+ } else if (prefix(arg, "-maxsize")) {
if (!argc) {
cerr << "Option -maxsize requires an argument" << endl;
exit(ex_usage);
}
maxsize = atoi(*argv); argv++; argc--;
- }
- if (arg.substr(0,1) == "-") {
+ } else if (arg.substr(0,1) == "-") {
cerr << "Unrecognized option '" << arg << "'" << endl;
cerr << "For help, try: " << progname << " -help" << endl;
exit(ex_usage);
@@ -236,28 +269,32 @@ int main(int _argc, const char** _argv){
int saw_blank_line(0);
string boundary("x-xx-x");
- string date;
+ string to;
+ string from;
string subject;
- string content_type;
+ string date;
string message_id;
+ string content_type;
int msgsize(0);
vector<string> bigbuf;
- cerr << "hi there" << endl;
+ int recno(0);
+ //xxxx cerr << progid << " begins" << endl;
for (;;){ // outer loop over all records in the header
if (cin.eof()) break;
if (cin.bad()) return 1;
- string headrec;
+ string line;
// on fail, go back to top of outer loop and check for eof versus bad
- if (getline(cin, headrec).fail()) continue;
- msgsize += headrec.length()+1;
+ if (getline(cin, line).fail()) continue;
+ msgsize += line.length()+1;
if (msgsize > maxsize) {
cerr << progid << " rejection: bigger than " << maxsize << endl;
exeunt(ex_spam);
}
- cout << headrec << endl;
- bigbuf.push_back(headrec); // for a folded record, this is the first line
+ cout << line << endl;
+ bigbuf.push_back(line);
+ string headrec = noCR(line); // for a folded record, this is the first line
for (;;) { // inner loop to build a multi-line record e.g. folded record:
if (cin.eof()) break;
@@ -276,12 +313,7 @@ int main(int _argc, const char** _argv){
}
cout << line << endl;
bigbuf.push_back(line);
- string cooked(line);
- if (cooked.length()){
- string::iterator ptr = cooked.end()-1;
- if (*ptr == '\r') cooked.erase(ptr);
- }
- headrec += "\n" + cooked;
+ headrec += "\n" + noCR(line);
}
// here with a fully assembled header record
int len = headrec.length();
@@ -301,6 +333,12 @@ int main(int _argc, const char** _argv){
}
headword = toLower(headword);
if (0){
+ } else if (headword == "from") {
+ from = rest;
+ } else if (headword == "to") {
+ to = rest;
+ } else if (headword == "message-id") {
+ message_id = rest;
} else if (headword == "date") {
date = rest;
} else if (headword == "subject") {
@@ -309,30 +347,49 @@ int main(int _argc, const char** _argv){
content_type = rest;
}
//xxxx cout << headrec.length() << " ... ";
+ recno++;
+ if (0) if (recno <= 6) cerr << progid << "#" << recno
+ << " " << headrec << endl;
}
- //xxx cerr << "headers are done. Delimited: " << saw_blank_line << endl;
+ cerr << progid <<" Mid '" << message_id << "'" << endl;
// Headers are done.
// Do some early-stage thinking.
+ list<string> badnews;
+
if (subject.find("-please-bounce-this-") != string::npos) {
- cerr << progid << " rejection: by request" << endl;
- exeunt(ex_spam);
+ badnews.push_back("by request");
}
if (!date.length()) {
- cerr << progid << " rejection: no date" << endl;
- exeunt(ex_spam); // disallow mail with no date
+ badnews.push_back("no date");
+ }
+
+ if (mid_required && !message_id.length()) {
+ badnews.push_back("no message-id");
+ }
+
+ if (badnews.size()){
+ cerr << progid << " " << join(", ", badnews) << endl;
+ if (error_exit){
+ cerr << progid << " '" << from
+ << "' to '" << to
+ << "'" << endl;
+ exeunt(ex_spam);
+ }
}
string main_contype;
- parse_content(content_type, main_contype, boundary);
+ if (content_type.length())
+ parse_content(content_type, main_contype, boundary);
+// some slightly-useful booleans:
int currently_text = main_contype == "text";
int main_multipart = main_contype == "multipart";
// early-stage thinking has been done.
// Now spew the rest of the message
- cerr << "body begins: " << main_contype << " " << currently_text << " " << boundary << endl;
+ //xxxx cerr << "body begins: " << main_contype << " " << currently_text << " " << boundary << endl;
int in_subheads(0);
int textlines(0);
@@ -345,7 +402,7 @@ int main(int _argc, const char** _argv){
msgsize += line.length()+1;
if (msgsize > maxsize) {
cerr << progid << " rejection: bigger than " << maxsize << endl;
- exeunt(ex_spam);
+ maybe_exeunt(ex_spam, error_exit);
}
bigbuf.push_back(line);
cout << line << endl;
@@ -380,10 +437,10 @@ int main(int _argc, const char** _argv){
}
}
- if (1) cerr << "textlines: " << textlines << endl;
+ if (0) cerr << "textlines: " << textlines << endl;
if (1 && !textlines) {
cerr << progid << " rejection: no text" << endl;
-// exeunt(ex_spam);
+// maybe_exeunt(ex_spam, error_exit);
}
cerr << progid << " normal completion" << endl;
exit(ex_good);