diff -u -r cyrus-imapd-2.2.12/imap/spool.c cyrus-imapd-2.2.12-patch/imap/spool.c --- cyrus-imapd-2.2.12/imap/spool.c 2004-10-27 22:40:50.000000000 +0200 +++ cyrus-imapd-2.2.12-patch/imap/spool.c 2005-05-08 11:31:33.000000000 +0200 @@ -52,6 +52,7 @@ #include #include #include +#include #include "spool.h" #include "prot.h" @@ -416,69 +417,117 @@ /* copies the message from fin to fout, massaging accordingly: . newlines are fiddled to \r\n . "." terminates - . embedded NULs are rejected + . embedded NULs are rejected or stripped according to rejectnul . bare \r are removed */ int spool_copy_msg(struct protstream *fin, FILE *fout) { char buf[8192], *p; int r = 0; + int n = 0; + int i = 0; + int sawcr = 0; + int sawnl = 0; + int nulsfound = 0; + int rejectnul = config_getswitch(IMAPOPT_REJECTNUL); + + /* reads in up to buffer size -2 chars from fin */ + while (n=prot_read(fin, buf, sizeof(buf)-2)) { + + /* a message should terminate with a . so if we run out of characters + it's an error */ + if (!n) { + syslog(LOG_ERR, "IOERROR: reading message: unexpected end of file"); + return IMAP_IOERROR; + } - /* -2: Might need room to add a \r\n\0 set */ - while (prot_fgets(buf, sizeof(buf)-2, fin)) { - p = buf + strlen(buf) - 1; - if (p < buf) { - /* buffer start with a \0 */ - r = IMAP_MESSAGE_CONTAINSNULL; - continue; /* need to eat the rest of the message */ - } - else if (buf[0] == '\r' && buf[1] == '\0') { - /* The message contained \r\0, and fgets is confusing us. */ - r = IMAP_MESSAGE_CONTAINSNULL; - continue; /* need to eat the rest of the message */ - } - else if (p[0] == '\r') { - /* - * We were unlucky enough to get a CR just before we ran - * out of buffer--put it back. - */ - prot_ungetc('\r', fin); - *p = '\0'; - } - else if (p[0] == '\n' && (p == buf || p[-1] != '\r')) { - /* found an \n without a \r */ - p[0] = '\r'; - p[1] = '\n'; - p[2] = '\0'; - } - else if (p[0] != '\n' && (strlen(buf) < sizeof(buf)-2)) { - /* line contained a \0 not at the end */ - r = IMAP_MESSAGE_CONTAINSNULL; - continue; - } + /* ensure that buffer is terminated and a known character is in the + following position, needed later on for comparisions */ + buf[n] = '\0'; + buf[n+1] = '\0'; + + /* if strlen of buffer is different to n then there are nuls. + if rejectnul option is set then these will cause an error + condition */ + if (n != strlen(buf)) { + if (rejectnul) { + r = IMAP_MESSAGE_CONTAINSNULL; + } + else { + /* if we don't reject nul then strip by copying following chars */ + p = (unsigned char *)buf; + for (i=0; i 0) { + *(p+i-nulsfound) = *(p+i); + } + } + } + /* reterminate buffer at new position and put known char in following + position needed later on for comparisons */ + buf[n-nulsfound] = '\0'; + buf[n-nulsfound+1] = '\0'; + } + } - /* Remove any lone CR characters */ - while ((p = strchr(buf, '\r')) && p[1] != '\n') { - /* Src/Target overlap, use memmove */ - /* strlen(p) will result in copying the NUL byte as well */ - memmove(p, p+1, strlen(p)); - } - - if (buf[0] == '.') { - if (buf[1] == '\r' && buf[2] == '\n') { - /* End of message */ - goto dot; - } - /* Remove the dot-stuffing */ - if (fout) fputs(buf+1, fout); - } else { - if (fout) fputs(buf, fout); - } + /* if there was an error then consume rest of stream if any and + return error */ + if (r) { + while (n==sizeof(buf)-2) n=prot_read (fin, buf, sizeof(buf)-2); + return r; + } + + /* output stream until .\r\n */ + for (p = (unsigned char *)buf; *p; p++) { + + /* drop bare \r */ + if (*p == '\r' && *(p+1) != '\n') { + continue; + } + /* correct bare \n to \r\n */ + if (*p == '\n') { + sawnl=1; + if (sawcr) { + fwrite(p, 1, 1, fout); + } + else { + fwrite("\r\n", 1, 2, fout); + } + continue; + } + /* output \r - if we get here we are sure it will + be followed by \n */ + if (*p == '\r') { + sawcr = 1; + sawnl = 0; + fwrite(p, 1, 1, fout); + continue; + } + /* dot at start of line */ + if (*p == '.' && sawnl) { + /* followed by \r\n means end of message */ + if ( *(p+1) == '\r' && *(p+2)== '\n') { + goto dot; + } + /* not followed by \r\n just skip */ + sawnl = 0; + continue; + } + sawnl = 0; + fwrite(p, 1, 1, fout); + } } /* wow, serious error---got a premature EOF. */ return IMAP_IOERROR; dot: + fflush(fout); + if (ferror(fout) || fsync(fileno(fout))) { + return IMAP_IOERROR; + } return r; } diff -u -r cyrus-imapd-2.2.12/lib/imapoptions cyrus-imapd-2.2.12-patch/lib/imapoptions --- cyrus-imapd-2.2.12/lib/imapoptions 2004-07-21 21:07:45.000000000 +0200 +++ cyrus-imapd-2.2.12-patch/lib/imapoptions 2005-05-08 10:42:33.000000000 +0200 @@ -674,6 +674,11 @@ proper soultion to non-ASCII characters in headers is offered by RFC 2047 and its predecessors.) */ +{ "rejectnul", 1, SWITCH } +/* If enabled, lmtpd rejects messages with nul characters in the + message body, as previously. Otherwise, nul characters are + stripped. */ + { "rfc2046_strict", 0, SWITCH } /* If enabled, imapd will be strict (per RFC 2046) when matching MIME boundary strings. This means that boundaries containing other