]> Pileus Git - ~andy/fetchmail/commitdiff
Patch to allow processing of messages to continue when an error occurs
authorCraig Brown <craig.brown@globalrelay.net>
Fri, 20 Aug 2010 20:35:39 +0000 (13:35 -0700)
committerCraig Brown <craig.brown@globalrelay.net>
Fri, 20 Aug 2010 20:35:39 +0000 (13:35 -0700)
  retrieving a message body.  Messages with errors may be marked as
  read and/or skipped.  Skipping the message allows the session to
  continue.

conf.c
driver.c
fetchmail.c
fetchmail.h
options.c
sink.c

diff --git a/conf.c b/conf.c
index 57581381ae8f5f1d97b52d693493433ef6c3bc29..8b27042e289f22304971dbcadc2e93fbc59e5e86 100644 (file)
--- a/conf.c
+++ b/conf.c
@@ -394,6 +394,18 @@ void dump_config(struct runctl *runp, struct query *querylist)
        stringdump("smtpaddress", ctl->smtpaddress);
        stringdump("smtpname", ctl->smtpname);
 
+        if (ctl->retrieveerrormode == RE_ABORT)
+            stringdump("retrieve-error", "abort");
+        else {
+            indent('\0');
+            fprintf(stdout, "'retrieve-error':'");
+            if (ctl->retrieveerrormode & RE_SKIP_MASK)
+                fputs("skip,", stdout);
+            if (ctl->retrieveerrormode & RE_MARK_SEEN_MASK)
+                fputs("markseen", stdout);
+            fputs("',\n", stdout);
+        }
+
        indent('\0');
        fprintf(stdout, "'antispam':'");
        for (idp = ctl->antispam; idp; idp = idp->next)
index bccaf6631a61e3c42b7c538ba57c758edefd1c39..fae5add991648927cbe7161942539da9f731483a 100644 (file)
--- a/driver.c
+++ b/driver.c
@@ -682,8 +682,44 @@ static int fetch_messages(int mailserver_socket, struct query *ctl,
                if (separatefetchbody)
                {
                    len = -1;
-                   if ((err=(ctl->server.base_protocol->fetch_body)(mailserver_socket,ctl,num,&len)))
+                   if ((err=(ctl->server.base_protocol->fetch_body)(mailserver_socket,ctl,num,&len))) {
+                       if (err == PS_ERROR && ctl->retrieveerrormode) {
+                           /*
+                            * Mark a message with a protocol error as seen.
+                            * This can be used to see which messages we've attempted
+                            * to download, but failed.
+                            */
+                           if (ctl->retrieveerrormode & RE_MARK_SEEN_MASK) {
+                               if ((ctl->server.base_protocol->mark_seen)(mailserver_socket,ctl,num)) {
+                                   return(err);
+                               }
+                           }
+
+                           if (ctl->retrieveerrormode & RE_SKIP_MASK) {
+                               /*
+                                * Do not abort download session.  Continue with the next message.
+                                *
+                                * Prevents a malformed message from blocking all other messages
+                                * behind it in the mailbox from being downloaded.
+                                *
+                                * Reconnect to SMTP to force this incomplete message to be dropped.
+                                * Required because we've already begun the DATA portion of the
+                                * interaction with the SMTP server (commands are ignored/
+                                * considered part of the message data).
+                                */
+                               abort_message_sink(ctl);
+
+                               // Ensure we don't delete the failed message from the server.
+                               suppress_delete = TRUE;
+
+                               // Bookkeeping required before next message can be downloaded.
+                               goto flagthemail;
+                           }
+                       }
+
                        return(err);
+                   }
+
                    /*
                     * Work around a bug in Novell's
                     * broken GroupWise IMAP server;
index e431c0ecf705780362d495c6f1d29536cfd7bb93..3b71075c222e20d3eb6d033e40a06c32dad85165 100644 (file)
@@ -979,6 +979,7 @@ static void optmerge(struct query *h2, struct query *h1, int force)
     FLAG_MERGE(fetchsizelimit);
     FLAG_MERGE(fastuidl);
     FLAG_MERGE(batchlimit);
+    FLAG_MERGE(retrieveerrormode);
 #ifdef SSL_ENABLE
     FLAG_MERGE(use_ssl);
     FLAG_MERGE(sslkey);
index aa58bcddf159c90c3a81bc071fcb667907f51145..de01225a0a403a9470dda755df1539d18cfa60ff 100644 (file)
@@ -154,6 +154,11 @@ char *strstr(const char *, const char *);
 #define                O_DEBUG         3       /* prolix */
 #define                O_MONITOR       O_VERBOSE
 
+/* Message retrieval error mode */
+#define                RE_ABORT        0       /* abort session */
+#define                RE_SKIP_MASK    1       /* skip the message, continue session */
+#define                RE_MARK_SEEN_MASK       2       /* mark the message as seen */
+
 #define                SIZETICKER      1024    /* print 1 dot per this many bytes */
 
 /*
@@ -362,6 +367,8 @@ struct query
     int fastuidlcount;         /* internal count for frequency of binary search */
     int        batchlimit;             /* max # msgs to pass in single SMTP session */
     int        expunge;                /* max # msgs to pass between expunges */
+    flag retrieveerrormode;    /** behaviour when a server error is encountered while reading a message
+                                 (abort|skip|markseen) -default behaviour is abort */
     flag use_ssl;              /* use SSL encrypted session */
     char *sslkey;              /* optional SSL private key file */
     char *sslcert;             /* optional SSL certificate file */
index d0c7c2ab7ec598feafc7bceb105e365c754b7cd4..c20b84011d42a11a17a646892e2a3f62ddf91b4e 100644 (file)
--- a/options.c
+++ b/options.c
@@ -55,7 +55,8 @@ enum {
     LA_IDLE,
     LA_NOSOFTBOUNCE,
     LA_SOFTBOUNCE,
-    LA_BADHEADER
+    LA_BADHEADER,
+    LA_RETRIEVEERROR
 };
 
 /* options still left: CgGhHjJoORTWxXYz */
@@ -109,6 +110,7 @@ static const struct option longoptions[] = {
   {"norewrite",        no_argument,       (int *) 0, 'n' },
   {"limit",    required_argument, (int *) 0, 'l' },
   {"warnings", required_argument, (int *) 0, 'w' },
+  {"retrieve-error",   required_argument, (int *) 0, LA_RETRIEVEERROR },
 
   {"folder",   required_argument, (int *) 0, 'r' },
   {"smtphost", required_argument, (int *) 0, 'S' },
@@ -608,6 +610,25 @@ int parsecmdline (int argc /** argument count */,
            ctl->server.tracepolls = FLAG_TRUE;
            break;
 
+       case LA_RETRIEVEERROR:
+           buf = xstrdup(optarg);
+           cp = strtok(buf, ",");
+           do {
+               if (strcmp(cp, "abort") == 0)
+                   ctl->retrieveerrormode = RE_ABORT;
+               else if (strcmp(cp, "skip") == 0)
+                   ctl->retrieveerrormode |= RE_SKIP_MASK;
+               else if (strcmp(cp, "markseen") == 0)
+                   ctl->retrieveerrormode |= RE_MARK_SEEN_MASK;
+               else {
+                   fprintf(stderr,GT_("Invalid retrieve-error mode `%s' specified.\n"), cp);
+                   errflag++;
+               }
+           } while
+               ((cp = strtok((char *)NULL, ",")));
+           free(buf);
+           break;
+
        case '?':
        default:
            helpflag++;
@@ -678,6 +699,7 @@ int parsecmdline (int argc /** argument count */,
        P(GT_("  -n, --norewrite   don't rewrite header addresses\n"));
        P(GT_("  -l, --limit       don't fetch messages over given size\n"));
        P(GT_("  -w, --warnings    interval between warning mail notification\n"));
+       P(GT_("      --retrieve-error set behaviour when a server error occurs while retrieving a message\n"));
 
        P(GT_("  -S, --smtphost    set SMTP forwarding host\n"));
        P(GT_("      --fetchdomains fetch mail for specified domains\n"));
diff --git a/sink.c b/sink.c
index a3f5dea371f46ec2c5a2cead90f41ff6a00ac53c..9d3d1b57343d25937cea914ec4acffe810dde702 100644 (file)
--- a/sink.c
+++ b/sink.c
@@ -1633,4 +1633,18 @@ void close_warning_by_mail(struct query *ctl, struct msgblk *msg)
     close_sink(ctl, msg, TRUE);
 }
 
+void abort_message_sink(struct query *ctl)
+/*
+ * Forcibly close the SMTP connection and re-open.
+ *
+ * Used to abort message delivery once the DATA command has been issued.
+ * Required because all text after the DATA command is considered to be
+ * part of the message body (it is impossible to issue an SMTP command
+ * to abort message delivery once the DATA command has been issued).
+ */
+{
+  smtp_close(ctl, 0);
+  smtp_setup(ctl);
+}
+
 /* sink.c ends here */