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)
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;
FLAG_MERGE(fetchsizelimit);
FLAG_MERGE(fastuidl);
FLAG_MERGE(batchlimit);
+ FLAG_MERGE(retrieveerrormode);
#ifdef SSL_ENABLE
FLAG_MERGE(use_ssl);
FLAG_MERGE(sslkey);
#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 */
/*
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 */
LA_IDLE,
LA_NOSOFTBOUNCE,
LA_SOFTBOUNCE,
- LA_BADHEADER
+ LA_BADHEADER,
+ LA_RETRIEVEERROR
};
/* options still left: CgGhHjJoORTWxXYz */
{"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' },
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++;
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"));
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 */