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