aboutsummaryrefslogtreecommitdiffstats
path: root/driver.c
diff options
context:
space:
mode:
Diffstat (limited to 'driver.c')
-rw-r--r--driver.c551
1 files changed, 284 insertions, 267 deletions
diff --git a/driver.c b/driver.c
index f4cba8fb..3ea9b062 100644
--- a/driver.c
+++ b/driver.c
@@ -1535,325 +1535,342 @@ const struct method *proto; /* protocol method table */
/* now iterate over each folder selected */
for (idp = ctl->mailboxes; idp; idp = idp->next)
{
- if (outlevel >= O_VERBOSE)
- if (idp->next)
- error(0, 0, "selecting folder %s");
- else
- error(0, 0, "selecting default folder");
-
- /* compute number of messages and number of new messages waiting */
- ok = (protocol->getrange)(sock, ctl, idp->id, &count, &new);
- if (ok != 0)
- goto cleanUp;
- set_timeout(ctl->server.timeout);
-
- /* show user how many messages we downloaded */
- if (idp->id)
- (void) sprintf(buf, "%s@%s:%s",
- ctl->remotename, realname, idp->id);
- else
- (void) sprintf(buf, "%s@%s", ctl->remotename, realname);
- if (outlevel > O_SILENT)
- if (count == -1) /* only used for ETRN */
- error(0, 0, "Polling %s", realname);
- else if (count == 0)
- {
- /* these are pointless in normal daemon mode */
- if (poll_interval == 0 || outlevel == O_VERBOSE )
- error(0, 0, "No mail at %s", buf);
- }
+ do {
+ if (outlevel >= O_VERBOSE)
+ if (idp->next)
+ error(0, 0, "selecting or re-polling folder %s");
+ else
+ error(0, 0, "selecting or re-polling default folder");
+
+ /* compute # of messages and number of new messages waiting */
+ ok = (protocol->getrange)(sock, ctl, idp->id, &count, &new);
+ if (ok != 0)
+ goto cleanUp;
+ set_timeout(ctl->server.timeout);
+
+ /* show user how many messages we downloaded */
+ if (idp->id)
+ (void) sprintf(buf, "%s@%s:%s",
+ ctl->remotename, realname, idp->id);
else
- {
- if (new != -1 && (count - new) > 0)
- error(0, 0, "%d message%s (%d seen) at %s.",
- count, count > 1 ? "s" : "", count-new, buf);
+ (void) sprintf(buf, "%s@%s", ctl->remotename, realname);
+ if (outlevel > O_SILENT)
+ if (count == -1) /* only used for ETRN */
+ error(0, 0, "Polling %s", realname);
+ else if (count == 0)
+ {
+ /* these are pointless in normal daemon mode */
+ if (poll_interval == 0 || outlevel == O_VERBOSE )
+ error(0, 0, "No mail at %s", buf);
+ break;
+ }
else
- error(0, 0, "%d message%s at %s.",
- count, count > 1 ? "s" : "", buf);
- }
-
- if (check_only)
- {
- if (new == -1 || ctl->fetchall)
- new = count;
- ok = ((new > 0) ? PS_SUCCESS : PS_NOMAIL);
- goto cleanUp;
- }
- else if (count > 0)
- {
- flag force_retrieval;
-
- /*
- * What forces this code is that in POP3 and IMAP2BIS you can't
- * fetch a message without having it marked `seen'. In IMAP4,
- * on the other hand, you can (peek_capable is set to convey
- * this).
- *
- * The result of being unable to peek is that if there's
- * any kind of transient error (DNS lookup failure, or
- * sendmail refusing delivery due to process-table limits)
- * the message will be marked "seen" on the server without
- * having been delivered. This is not a big problem if
- * fetchmail is running in foreground, because the user
- * will see a "skipped" message when it next runs and get
- * clued in.
- *
- * But in daemon mode this leads to the message being silently
- * ignored forever. This is not acceptable.
- *
- * We compensate for this by checking the error count from the
- * previous pass and forcing all messages to be considered new
- * if it's nonzero.
- */
- force_retrieval = !peek_capable && (ctl->errcount > 0);
+ {
+ if (new != -1 && (count - new) > 0)
+ error(0, 0, "%d message%s (%d seen) at %s.",
+ count, count > 1 ? "s" : "", count-new, buf);
+ else
+ error(0, 0, "%d message%s at %s.",
+ count, count > 1 ? "s" : "", buf);
+ }
- /*
- * We need the size of each method before it's loaded in
- * order to pass via the ESMTP SIZE option. If the protocol
- * has a getsizes method, we presume this means it doesn't
- * get reliable sizes from message fetch responses.
- */
- if (proto->getsizes)
+ if (check_only)
{
- msgsizes = (int *)alloca(sizeof(int) * count);
-
- ok = (proto->getsizes)(sock, count, msgsizes);
- if (ok != 0)
- goto cleanUp;
- set_timeout(ctl->server.timeout);
+ if (new == -1 || ctl->fetchall)
+ new = count;
+ ok = ((new > 0) ? PS_SUCCESS : PS_NOMAIL);
+ goto cleanUp;
}
-
- /* read, forward, and delete messages */
- for (num = 1; num <= count; num++)
- {
- flag toolarge = (ctl->limit > 0)
- && msgsizes && (msgsizes[num-1] > ctl->limit);
- flag fetch_it = !toolarge
- && (ctl->fetchall || force_retrieval || !(protocol->is_old && (protocol->is_old)(sock,ctl,num)));
- flag suppress_delete = FALSE;
- flag suppress_forward = FALSE;
- flag retained = FALSE;
+ else if (count > 0)
+ {
+ flag force_retrieval;
/*
- * This check copes with Post Office/NT's annoying habit
- * of randomly prepending bogus LIST items of length -1.
- * Patrick Audley <paudley@pobox.com> tells us:
- * LIST shows a size of -1, RETR and TOP return
- * "-ERR System error - couldn't open message", and DELE
- * succeeds but doesn't actually delete the message.
+ * What forces this code is that in POP3 and
+ * IMAP2BIS you can't fetch a message without
+ * having it marked `seen'. In IMAP4, on the
+ * other hand, you can (peek_capable is set to
+ * convey this).
+ *
+ * The result of being unable to peek is that if there's
+ * any kind of transient error (DNS lookup failure, or
+ * sendmail refusing delivery due to process-table limits)
+ * the message will be marked "seen" on the server without
+ * having been delivered. This is not a big problem if
+ * fetchmail is running in foreground, because the user
+ * will see a "skipped" message when it next runs and get
+ * clued in.
+ *
+ * But in daemon mode this leads to the message
+ * being silently ignored forever. This is not
+ * acceptable.
+ *
+ * We compensate for this by checking the error
+ * count from the previous pass and forcing all
+ * messages to be considered new if it's nonzero.
*/
- if (msgsizes && msgsizes[num-1] == -1)
- {
- if (outlevel >= O_VERBOSE)
- error(0, 0,
- "Skipping message %d, length -1",
- num - 1);
- continue;
- }
-
- /* we may want to reject this message if it's old */
- if (!fetch_it)
- {
- if (outlevel > O_SILENT)
- {
- error_build("skipping message %d", num);
- if (toolarge)
- error_build(" (oversized, %d bytes)", msgsizes[num-1]);
- }
- }
- else
+ force_retrieval = !peek_capable && (ctl->errcount > 0);
+
+ /*
+ * We need the size of each message before it's
+ * loaded in order to pass via the ESMTP SIZE
+ * option. If the protocol has a getsizes method,
+ * we presume this means it doesn't get reliable
+ * sizes from message fetch responses.
+ */
+ if (proto->getsizes)
{
- flag wholesize = !protocol->fetch_body;
+ msgsizes = (int *)alloca(sizeof(int) * count);
- /* request a message */
- ok = (protocol->fetch_headers)(sock, ctl, num, &len);
+ ok = (proto->getsizes)(sock, count, msgsizes);
if (ok != 0)
goto cleanUp;
set_timeout(ctl->server.timeout);
+ }
- /* -1 means we didn't see a size in the response */
- if (len == -1 && msgsizes)
+ /* read, forward, and delete messages */
+ for (num = 1; num <= count; num++)
+ {
+ flag toolarge = (ctl->limit > 0)
+ && msgsizes && (msgsizes[num-1] > ctl->limit);
+ flag fetch_it = !toolarge
+ && (ctl->fetchall || force_retrieval || !(protocol->is_old && (protocol->is_old)(sock,ctl,num)));
+ flag suppress_delete = FALSE;
+ flag suppress_forward = FALSE;
+ flag retained = FALSE;
+
+ /*
+ * This check copes with Post Office/NT's
+ * annoying habit of randomly prepending bogus
+ * LIST items of length -1. Patrick Audley
+ * <paudley@pobox.com> tells us: LIST shows a
+ * size of -1, RETR and TOP return "-ERR
+ * System error - couldn't open message", and
+ * DELE succeeds but doesn't actually delete
+ * the message.
+ */
+ if (msgsizes && msgsizes[num-1] == -1)
{
- len = msgsizes[num - 1];
- wholesize = TRUE;
+ if (outlevel >= O_VERBOSE)
+ error(0, 0,
+ "Skipping message %d, length -1",
+ num - 1);
+ continue;
}
- if (outlevel > O_SILENT)
+ /* we may want to reject this message if it's old */
+ if (!fetch_it)
{
- error_build("reading message %d of %d",num,count);
-
- if (len > 0)
- error_build(" (%d %sbytes)",
- len, wholesize ? "" : "header ");
- if (outlevel == O_VERBOSE)
- error_complete(0, 0, "");
- else
- error_build(" ");
+ if (outlevel > O_SILENT)
+ {
+ error_build("skipping message %d", num);
+ if (toolarge)
+ error_build(" (oversized, %d bytes)",
+ msgsizes[num-1]);
+ }
}
-
- /*
- * Read the message headers and ship them to the
- * output sink.
- */
- ok = readheaders(sock, len, ctl, realname, num);
- if (ok == PS_RETAINED)
- suppress_forward = retained = TRUE;
- else if (ok == PS_TRANSIENT)
- suppress_delete = suppress_forward = TRUE;
- else if (ok == PS_REFUSED)
- suppress_forward = TRUE;
- else if (ok)
- goto cleanUp;
- set_timeout(ctl->server.timeout);
-
- /*
- * If we're using IMAP4 or something else that
- * can fetch headers separately from bodies,
- * it's time to request the body now. This
- * fetch may be skipped if we got an anti-spam
- * or other PS_REFUSED error response during
- * read_headers.
- */
- if (protocol->fetch_body)
+ else
{
- if (outlevel == O_VERBOSE)
- fputc('\n', stderr);
+ flag wholesize = !protocol->fetch_body;
- if ((ok = (protocol->trail)(sock, ctl, num)))
+ /* request a message */
+ ok = (protocol->fetch_headers)(sock,ctl,num, &len);
+ if (ok != 0)
goto cleanUp;
set_timeout(ctl->server.timeout);
- len = 0;
- if (!suppress_forward)
+
+ /* -1 means we didn't see a size in the response */
+ if (len == -1 && msgsizes)
{
- if ((ok=(protocol->fetch_body)(sock,ctl,num,&len)))
- goto cleanUp;
- if (outlevel > O_SILENT && !wholesize)
- error_build(" (%d body bytes) ", len);
- set_timeout(ctl->server.timeout);
+ len = msgsizes[num - 1];
+ wholesize = TRUE;
}
- }
- /* process the body now */
- if (len > 0)
- {
- ok = readbody(sock,
- ctl,
- !suppress_forward,
- len);
- if (ok == PS_TRANSIENT)
+ if (outlevel > O_SILENT)
+ {
+ error_build("reading message %d of %d",
+ num,count);
+
+ if (len > 0)
+ error_build(" (%d %sbytes)",
+ len, wholesize ? "" : "header ");
+ if (outlevel == O_VERBOSE)
+ error_complete(0, 0, "");
+ else
+ error_build(" ");
+ }
+
+ /*
+ * Read the message headers and ship them to the
+ * output sink.
+ */
+ ok = readheaders(sock, len, ctl, realname, num);
+ if (ok == PS_RETAINED)
+ suppress_forward = retained = TRUE;
+ else if (ok == PS_TRANSIENT)
suppress_delete = suppress_forward = TRUE;
+ else if (ok == PS_REFUSED)
+ suppress_forward = TRUE;
else if (ok)
goto cleanUp;
set_timeout(ctl->server.timeout);
- /* tell server we got it OK and resynchronize */
- if (protocol->trail)
+ /*
+ * If we're using IMAP4 or something else that
+ * can fetch headers separately from bodies,
+ * it's time to request the body now. This
+ * fetch may be skipped if we got an anti-spam
+ * or other PS_REFUSED error response during
+ * read_headers.
+ */
+ if (protocol->fetch_body)
{
if (outlevel == O_VERBOSE)
fputc('\n', stderr);
- ok = (protocol->trail)(sock, ctl, num);
- if (ok != 0)
+ if ((ok = (protocol->trail)(sock, ctl, num)))
goto cleanUp;
set_timeout(ctl->server.timeout);
+ len = 0;
+ if (!suppress_forward)
+ {
+ if ((ok=(protocol->fetch_body)(sock,ctl,num,&len)))
+ goto cleanUp;
+ if (outlevel > O_SILENT && !wholesize)
+ error_build(" (%d body bytes) ", len);
+ set_timeout(ctl->server.timeout);
+ }
}
- }
- /*
- * Check to see if the numbers matched?
- *
- * Yes, some servers foo this up horribly.
- * All IMAP servers seem to get it right, and
- * so does Eudora QPOP at least in 2.xx
- * versions.
- *
- * Microsoft Exchange gets it completely
- * wrong, reporting compressed rather than
- * actual sizes (so the actual length of
- * message is longer than the reported size).
- * Another fine example of Microsoft brain death!
- *
- * Some older POP servers, like the old UCB
- * POP server and the pre-QPOP QUALCOMM
- * versions, report a longer size in the LIST
- * response than actually gets shipped up.
- * It's unclear what is going on here, as the
- * QUALCOMM server (at least) seems to be
- * reporting the on-disk size correctly.
- */
- if (msgsizes && msglen != msgsizes[num-1])
- {
- if (outlevel >= O_VERBOSE)
- error(0, 0,
- "message %d was not the expected length (%d != %d)",
- num, msglen, msgsizes[num-1]);
- }
+ /* process the body now */
+ if (len > 0)
+ {
+ ok = readbody(sock,
+ ctl,
+ !suppress_forward,
+ len);
+ if (ok == PS_TRANSIENT)
+ suppress_delete = suppress_forward = TRUE;
+ else if (ok)
+ goto cleanUp;
+ set_timeout(ctl->server.timeout);
- /* end-of-message processing starts here */
+ /* tell server we got it OK and resynchronize */
+ if (protocol->trail)
+ {
+ if (outlevel == O_VERBOSE)
+ fputc('\n', stderr);
+
+ ok = (protocol->trail)(sock, ctl, num);
+ if (ok != 0)
+ goto cleanUp;
+ set_timeout(ctl->server.timeout);
+ }
+ }
- if (ctl->mda)
- {
- int rc;
+ /*
+ * Check to see if the numbers matched?
+ *
+ * Yes, some servers foo this up horribly.
+ * All IMAP servers seem to get it right, and
+ * so does Eudora QPOP at least in 2.xx
+ * versions.
+ *
+ * Microsoft Exchange gets it completely
+ * wrong, reporting compressed rather than
+ * actual sizes (so the actual length of
+ * message is longer than the reported size).
+ * Another fine example of Microsoft brain death!
+ *
+ * Some older POP servers, like the old UCB
+ * POP server and the pre-QPOP QUALCOMM
+ * versions, report a longer size in the LIST
+ * response than actually gets shipped up.
+ * It's unclear what is going on here, as the
+ * QUALCOMM server (at least) seems to be
+ * reporting the on-disk size correctly.
+ */
+ if (msgsizes && msglen != msgsizes[num-1])
+ {
+ if (outlevel >= O_VERBOSE)
+ error(0, 0,
+ "message %d was not the expected length (%d != %d)",
+ num, msglen, msgsizes[num-1]);
+ }
- /* close the delivery pipe, we'll reopen before next message */
- rc = pclose(sinkfp);
- signal(SIGCHLD, sigchld);
- if (rc)
+ /* end-of-message processing starts here */
+
+ if (ctl->mda)
{
- error(0, -1, "MDA exited abnormally or returned nonzero status");
- goto cleanUp;
+ int rc;
+
+ /* close the delivery pipe, we'll reopen before next message */
+ rc = pclose(sinkfp);
+ signal(SIGCHLD, sigchld);
+ if (rc)
+ {
+ error(0, -1, "MDA exited abnormally or returned nonzero status");
+ goto cleanUp;
+ }
}
- }
- else if (!suppress_forward)
- {
- /* write message terminator */
- if (SMTP_eom(ctl->smtp_socket) != SM_OK)
+ else if (!suppress_forward)
{
- error(0, -1, "SMTP listener refused delivery");
- ctl->errcount++;
- suppress_delete = TRUE;
+ /* write message terminator */
+ if (SMTP_eom(ctl->smtp_socket) != SM_OK)
+ {
+ error(0, -1, "SMTP listener refused delivery");
+ ctl->errcount++;
+ suppress_delete = TRUE;
+ }
}
+
+ fetches++;
}
- fetches++;
- }
+ /*
+ * At this point in flow of control, either
+ * we've bombed on a protocol error or had
+ * delivery refused by the SMTP server
+ * (unlikely -- I've never seen it) or we've
+ * seen `accepted for delivery' and the
+ * message is shipped. It's safe to mark the
+ * message seen and delete it on the server
+ * now.
+ */
- /*
- * At this point in flow of control, either we've bombed
- * on a protocol error or had delivery refused by the SMTP
- * server (unlikely -- I've never seen it) or we've seen
- * `accepted for delivery' and the message is shipped.
- * It's safe to mark the message seen and delete it on the
- * server now.
- */
+ /* maybe we delete this message now? */
+ if (retained)
+ {
+ if (outlevel > O_SILENT)
+ error_complete(0, 0, " retained");
+ }
+ else if (protocol->delete
+ && !suppress_delete
+ && (fetch_it ? !ctl->keep : ctl->flush))
+ {
+ deletions++;
+ if (outlevel > O_SILENT)
+ error_complete(0, 0, " flushed");
+ ok = (protocol->delete)(sock, ctl, num);
+ if (ok != 0)
+ goto cleanUp;
+ set_timeout(ctl->server.timeout);
+ delete_str(&ctl->newsaved, num);
+ }
+ else if (outlevel > O_SILENT)
+ error_complete(0, 0, " not flushed");
- /* maybe we delete this message now? */
- if (retained)
- {
- if (outlevel > O_SILENT)
- error_complete(0, 0, " retained");
+ /* perhaps this as many as we're ready to handle */
+ if (ctl->fetchlimit > 0 && ctl->fetchlimit <= fetches)
+ goto no_error;
}
- else if (protocol->delete
- && !suppress_delete
- && (fetch_it ? !ctl->keep : ctl->flush))
- {
- deletions++;
- if (outlevel > O_SILENT)
- error_complete(0, 0, " flushed");
- ok = (protocol->delete)(sock, ctl, num);
- if (ok != 0)
- goto cleanUp;
- set_timeout(ctl->server.timeout);
- delete_str(&ctl->newsaved, num);
- }
- else if (outlevel > O_SILENT)
- error_complete(0, 0, " not flushed");
-
- /* perhaps this as many as we're ready to handle */
- if (ctl->fetchlimit > 0 && ctl->fetchlimit <= fetches)
- goto no_error;
}
- }
+ } while
+ /*
+ * Only re-poll if we allowed deletions and had no errors.
+ * Otherwise it is far too easy to get into infinite loops.
+ */
+ (protocol->retry && !ctl->keep && !ctl->errcount);
}
no_error: