Well, sSMTP is my dumb forwarder of choice, and as the original reporter appears to have moved on, and sSMTP has been dropped from testing due to this bug, and no one else seems to care, it looks like I'll have to step in.
Attached is a patch against the original 2.64 source that should address this bug, and hopefully not break anything. An overview of my changes: * Added code to standarise() to drop the trailing '\r' if the line originally ended with "\r\n". * Added a check to header_parse() that effectively converts an "\r\n" in the input into '\n'. * Added a conditional so that header_parse() doesn't pass the empty string to header_save()---a behavior I observed in testing, at the end of a header block with "\r\n" line endings. * Simplified the last if(in_header) conditional in header_parse(), because it erroneously assumes that if in_header == True, then c could have some value other than EOF. (See the condition on the previous "while" loop, and the lack of any other way to exit said loop.) header_parse() will now properly grab a header if fed a message without a body (i.e. no "\n\n" ending the header block), although this code will still drop a header if there is no newline at the end. Christoph, thank you for your excellent analysis, and the test cases. I made use of them, and with my changes sSMTP appears to do the right thing. I hope we can still count you as a Debian user, four years on! Aníbal, please test this patch and let me know if there are any issues. If everything looks good, I have two requests: 1. Convert all instances of (char)NULL in the source to '\0'. The former is silly, and if you add -Wall -Wextra to CFLAGS, GCC complains about it voluminously. (I followed that convention in my patch, but only so that this change could be applied across the board.) 2. Integrate all/most of debian/patches/* into the original upstream source, and bump the version to >= 2.65. It's about time.
--- ssmtp-2.64/ssmtp.c.orig 2009-11-23 04:55:11.000000000 -0500 +++ ssmtp-2.64/ssmtp.c 2014-06-19 01:21:01.907825184 -0400 @@ -365,6 +365,12 @@ if((p = strchr(str, '\n'))) { *p = (char)NULL; *linestart = True; + + /* If the line ended in "\r\n", then drop the '\r' too */ + sl = strlen(str); + if(sl >= 1 && str[sl - 1] == '\r') { + str[sl - 1] = (char)NULL; + } } return(leadingdot); } @@ -758,6 +764,14 @@ } len++; + if(l == '\r' && c == '\n') { + /* Properly handle input that already has "\r\n" + line endings; see https://bugs.debian.org/584162 */ + l = (len >= 2 ? *(q - 2) : '\n'); + q--; + len--; + } + if(l == '\n') { switch(c) { case ' ': @@ -780,7 +794,9 @@ if((q = strrchr(p, '\n'))) { *q = (char)NULL; } - header_save(p); + if(len > 0) { + header_save(p); + } q = p; len = 0; @@ -790,35 +806,12 @@ l = c; } - if(in_header) { - if(l == '\n') { - switch(c) { - case ' ': - case '\t': - /* Must insert '\r' before '\n's embedded in header - fields otherwise qmail won't accept our mail - because a bare '\n' violates some RFC */ - - *(q - 1) = '\r'; /* Replace previous \n with \r */ - *q++ = '\n'; /* Insert \n */ - len++; - - break; - - case '\n': - in_header = False; - - default: - *q = (char)NULL; - if((q = strrchr(p, '\n'))) { - *q = (char)NULL; - } - header_save(p); - - q = p; - len = 0; - } + if(in_header && l == '\n') { + /* Got EOF while reading the header */ + if((q = strrchr(p, '\n'))) { + *q = (char)NULL; } + header_save(p); } (void)free(p); }