#endif
#if defined(HAVE_ALLOCA_H)
#include <alloca.h>
+#else
+#ifdef _AIX
+ #pragma alloca
+#endif
#endif
#if defined(HAVE_SYS_ITIMER_H)
#include <sys/itimer.h>
#include <krb.h>
#define krb_get_err_text(e) (krb_err_txt[e])
#else
-#if defined(__FreeBSD__)
+#if defined(__FreeBSD__) || defined(__linux__)
#define krb_get_err_text(e) (krb_err_txt[e])
#include <krb.h>
#include <des.h>
#define SMTP_PORT 25 /* standard SMTP service port */
+#ifndef strstr /* glibc-2.1 declares this as a macro */
extern char *strstr(); /* needed on sysV68 R3V7.1. */
+#endif /* strstr */
int fetchlimit; /* how often to tear down the server connection */
int batchcount; /* count of messages sent in current batch */
flag peek_capable; /* can we peek for better error recovery? */
+int pass; /* how many times have we re-polled? */
static const struct method *protocol;
static jmp_buf restart;
char tag[TAGLEN];
static int tagnum;
-#define GENSYM (sprintf(tag, "a%04d", ++tagnum), tag)
+#define GENSYM (sprintf(tag, "a%04d", ++tagnum % TAGMOD), tag)
static char *shroud; /* string to shroud in debug output, if non-NULL */
static int mytimeout; /* value of nonreponse timeout */
case NO_RECOVERY: /* non-recoverable name server error */
case TRY_AGAIN: /* temporary error on authoritative server */
default:
- error(0, 0,
+ error(0, -1,
"nameserver failure while looking for `%s' during poll of %s.",
name, ctl->server.pollname);
ctl->errcount++;
struct idlist **xmit_names; /* list of recipient names parsed out */
{
const char *lname;
-
+ int sl;
+ int off = 0;
+
lname = idpair_find(&ctl->localnames, name);
if (!lname && ctl->wildcard)
lname = name;
if (lname != (char *)NULL)
{
+ /*
+ * If the name of the user begins with a
+ * qmail virtual domain prefix, remove
+ * the prefix
+ */
+ if (ctl->server.qvirtual)
+ {
+ sl=strlen(ctl->server.qvirtual);
+ if (!strncasecmp(lname,ctl->server.qvirtual,sl)) off=sl;
+ }
if (outlevel == O_VERBOSE)
- error(0, 0, "mapped %s to local %s", name, lname);
- save_str(xmit_names, XMIT_ACCEPT, lname);
+ error(0, 0, "mapped %s to local %s", name, lname+off);
+ save_str(xmit_names, XMIT_ACCEPT, lname+off);
accept_count++;
}
}
static char *parse_received(struct query *ctl, char *bufp)
/* try to extract real addressee from the Received line */
{
- char *ok;
+ char *ok = (char *)NULL;
static char rbuf[HOSTLEN + USERNAMELEN + 4];
/*
* address in the Received line. Sendmail itself only
* does this when the mail has a single recipient.
*/
- if ((ok = strstr(bufp, "by ")) == (char *)NULL)
- ok = (char *)NULL;
- else
+ if ((ok = strstr(bufp, "by ")) && isspace(ok[-1]))
{
char *sp, *tp;
* recipient name after a following "for". Otherwise
* punt.
*/
- if (is_host_alias(rbuf, ctl))
- ok = strstr(sp, "for ");
- else
+ if (!is_host_alias(rbuf, ctl))
ok = (char *)NULL;
- }
-
- if (ok != 0)
- {
- char *sp, *tp;
-
- tp = rbuf;
- sp = ok + 4;
- if (*sp == '<')
- sp++;
- while (*sp && *sp != '>' && *sp != '@' && *sp != ';')
- if (!isspace(*sp))
- *tp++ = *sp++;
- else
- {
- /* uh oh -- whitespace here can't be right! */
- ok = (char *)NULL;
- break;
- }
- *tp = '\0';
+ else if ((ok = strstr(sp, "for ")) && isspace(ok[-1]))
+ {
+ tp = rbuf;
+ sp = ok + 4;
+ if (*sp == '<')
+ sp++;
+ while (*sp == '@') /* skip routes */
+ while (*sp++ != ':')
+ continue;
+ while (*sp && *sp != '>' && *sp != '@' && *sp != ';')
+ if (!isspace(*sp))
+ *tp++ = *sp++;
+ else
+ {
+ /* uh oh -- whitespace here can't be right! */
+ ok = (char *)NULL;
+ break;
+ }
+ *tp = '\0';
+ }
}
if (!ok)
{
/*
* RFC 1123 requires that the domain name in HELO address is a
- * "valid principal domain name" for the client host. We
- * violate this with malice aforethought in order to make the
- * Received headers and logging look right.
+ * "valid principal domain name" for the client host. If we're
+ * running in invisible mode, violate this with malice
+ * aforethought in order to make the Received headers and
+ * logging look right.
*
* In fact this code relies on the RFC1123 requirement that the
* SMTP listener must accept messages even if verification of the
* What it will affect is the listener's logging.
*/
struct idlist *idp;
+ char *id_me = use_invisible ? ctl->server.truename : fetchmailhost;
+
+ errno = 0;
- /* run down the SMTP hunt list looking for a server that's up */
+ /*
+ * Run down the SMTP hunt list looking for a server that's up.
+ * Use both explicit hunt entries (value TRUE) and implicit
+ * (default) ones (value FALSE).
+ */
for (idp = ctl->smtphunt; idp; idp = idp->next)
{
ctl->smtphost = idp->id; /* remember last host tried. */
continue;
if (SMTP_ok(ctl->smtp_socket) == SM_OK &&
- SMTP_ehlo(ctl->smtp_socket,
- ctl->server.truename,
+ SMTP_ehlo(ctl->smtp_socket, id_me,
&ctl->server.esmtp_options) == SM_OK)
break; /* success */
continue;
if (SMTP_ok(ctl->smtp_socket) == SM_OK &&
- SMTP_helo(ctl->smtp_socket, ctl->server.truename) == SM_OK)
+ SMTP_helo(ctl->smtp_socket, id_me) == SM_OK)
break; /* success */
close(ctl->smtp_socket);
* use .<CR><LF> as EOM. If it does, the server will already have
* decorated any . lines it sends back up.
*/
- if (!protocol->delimited && *buf == '.')
- if (sinkfp && ctl->mda)
- fputs(".", sinkfp);
- else if (ctl->smtp_socket != -1)
- SockWrite(ctl->smtp_socket, buf, 1);
+ if (*buf == '.')
+ if (protocol->delimited) /* server has already byte-stuffed */
+ {
+ if (ctl->mda)
+ ++buf;
+ else
+ /* writing to SMTP, leave the byte-stuffing in place */;
+ }
+ else /* if (!protocol->delimited) -- not byte-stuffed already */
+ {
+ if (!ctl->mda)
+ SockWrite(ctl->smtp_socket, buf, 1); /* byte-stuff it */
+ else
+ /* leave it alone */;
+ }
/* we may need to strip carriage returns */
if (ctl->stripcr)
return(n);
}
-static int readheaders(sock, len, ctl, realname, num)
+static int readheaders(sock, fetchlen, reallen, ctl, num)
/* read message headers and ship to SMTP or MDA */
int sock; /* to which the server is connected */
-long len; /* length of message */
+long fetchlen; /* length of message according to fetch response */
+long reallen; /* length of message according to getsizes */
struct query *ctl; /* query control record */
-char *realname; /* real name of host */
int num; /* index of message */
{
struct addrblk
} *addrchain = NULL, **chainptr = &addrchain;
char buf[MSGBUFSIZE+1], return_path[MSGBUFSIZE+1];
int from_offs, ctt_offs, env_offs, next_address;
- char *headers, *received_for;
- int n, linelen, oldlen, ch, remaining;
+ char *headers, *received_for, *desthost, *rcv;
+ int n, linelen, oldlen, ch, remaining, skipcount;
char *cp;
struct idlist *idp, *xmit_names;
flag good_addresses, bad_addresses, has_nuls;
/* read message headers */
headers = received_for = NULL;
from_offs = ctt_offs = env_offs = -1;
- oldlen = msglen = 0;
- for (remaining = len; remaining > 0 || protocol->delimited; remaining -= linelen)
+ oldlen = 0;
+ msglen = 0;
+ skipcount = 0;
+
+ for (remaining = fetchlen; remaining > 0 || protocol->delimited; remaining -= linelen)
{
char *line;
}
set_timeout(ctl->server.timeout);
- /* leave extra room for reply_hack to play with */
- line = (char *) realloc(line, strlen(line) + strlen(buf) + HOSTLEN + 1);
+
+ line = (char *) realloc(line, strlen(line) + strlen(buf) +1);
+
strcat(line, buf);
if (line[0] == '\r' && line[1] == '\n')
break;
break;
}
- /* maybe this is a special message that holds mailbox annotations? */
- if (protocol->retain_hdr && protocol->retain_hdr(num, line))
- return(PS_RETAINED);
+ /*
+ * The University of Washington IMAP server (the reference
+ * implementation of IMAP4 written by Mark Crispin) relies
+ * on being able to keep base-UID information in a special
+ * message at the head of the mailbox. This message should
+ * neither be deleted nor forwarded.
+ */
+#ifdef POP2_ENABLE
+ /*
+ * We disable this check under POP2 because there's no way to
+ * prevent deletion of the message. So at least we ought to
+ * forward it to the user so he or she will have some clue
+ * that things have gone awry.
+ */
+ if (protocol->port != 109)
+#endif /* POP2_ENABLE */
+ if (num == 1 && !strncasecmp(line, "X-IMAP:", 7))
+ return(PS_RETAINED);
/*
* This code prevents fetchmail from becoming an accessory after
}
if (ctl->rewrite)
- reply_hack(line, realname);
+ line = reply_hack(line, ctl->server.truename);
if (!headers)
{
{
if( ctl->server.uidl )
{
- char id[IDLEN+1];
+ char id[IDLEN+1];
+ /* prevent stack overflows */
+ buf[IDLEN+12] = 0;
sscanf( buf+12, "%s", id);
- if( !str_in_list( &ctl->newsaved, id ) )
+ if( !str_find( &ctl->newsaved, num ) )
save_str(&ctl->newsaved, num, id );
}
}
if (env_offs == -1 && !strncasecmp(ctl->server.envelope,
line,
strlen(ctl->server.envelope)))
+ {
+ if (skipcount++ != ctl->server.envskip)
+ continue;
env_offs = (line - headers);
+ }
}
#ifdef HAVE_RES_SEARCH
else if (!received_for && !strncasecmp("Received:", line, 9))
+ {
+ if (skipcount++ != ctl->server.envskip)
+ continue;
received_for = parse_received(ctl, line);
+ }
#endif /* HAVE_RES_SEARCH */
}
}
*/
if (headers == (char *)NULL)
{
+#ifdef HAVE_SNPRINTF
+ snprintf(buf, sizeof(buf),
+#else
sprintf(buf,
+#endif /* HAVE_SNPRINTF */
"From: <FETCHMAIL-DAEMON@%s>\r\nTo: %s@localhost\r\nSubject: Headerless mail from %s's mailbox on %s\r\n",
- fetchmailhost, user, ctl->remotename, realname);
+ fetchmailhost, user, ctl->remotename, ctl->server.truename);
headers = xstrdup(buf);
}
map_name(received_for, ctl, &xmit_names);
else
{
- int i;
-
/*
* We haven't extracted the envelope address.
* So check all the header addresses.
else if (ctl->mda) /* we have a declared MDA */
{
int length = 0;
- char *names, *cmd;
+ char *names, *before, *after;
- /*
- * We go through this in order to be able to handle very
- * long lists of users and (re)implement %s.
- */
for (idp = xmit_names; idp; idp = idp->next)
if (idp->val.num == XMIT_ACCEPT)
- length += (strlen(idp->id) + 1);
- names = (char *)alloca(length);
- names[0] = '\0';
- for (idp = xmit_names; idp; idp = idp->next)
- if (idp->val.num == XMIT_ACCEPT)
- {
- strcat(names, idp->id);
- strcat(names, " ");
- }
- cmd = (char *)alloca(strlen(ctl->mda) + length);
- sprintf(cmd, ctl->mda, names);
+ good_addresses++;
+
+ desthost = "localhost";
+
+ length = strlen(ctl->mda) + 1;
+ before = xstrdup(ctl->mda);
+
+ /* sub user addresses for %T (or %s for backward compatibility) */
+ cp = (char *)NULL;
+ if (strstr(before, "%s") || (cp = strstr(before, "%T")))
+ {
+ char *sp;
+
+ if (cp && cp[1] == 'T')
+ cp[1] = 's';
+
+ /* \177 had better be out-of-band for MDA commands */
+ for (sp = before; *sp; sp++)
+ if (*sp == '%' && sp[1] != 's' && sp[1] != 'T')
+ *sp = '\177';
+
+ /*
+ * We go through this in order to be able to handle very
+ * long lists of users and (re)implement %s.
+ */
+ for (idp = xmit_names; idp; idp = idp->next)
+ if (idp->val.num == XMIT_ACCEPT)
+ length += (strlen(idp->id) + 1);
+
+ names = (char *)alloca(++length);
+ names[0] = '\0';
+ for (idp = xmit_names; idp; idp = idp->next)
+ if (idp->val.num == XMIT_ACCEPT)
+ {
+ strcat(names, idp->id);
+ strcat(names, " ");
+ }
+ after = (char *)alloca(length);
+#ifdef SNPRINTF
+ snprintf(after, length, before, names);
+#else
+ sprintf(after, before, names);
+#endif /* SNPRINTF */
+ free(before);
+ before = after;
+
+ for (sp = before; *sp; sp++)
+ if (*sp == '\177')
+ *sp = '%';
+ }
+
+ /* substitute From address for %F */
+ if ((cp = strstr(before, "%F")))
+ {
+ char *from = nxtaddr(headers + from_offs);
+ char *sp;
+
+ /* \177 had better be out-of-band for MDA commands */
+ for (sp = before; *sp; sp++)
+ if (*sp == '%' && sp[1] != 'F')
+ *sp = '\177';
+
+ length += strlen(from);
+ after = alloca(length);
+ cp[1] = 's';
+#ifdef SNPRINTF
+ snprintf(after, length, before, from);
+#else
+ sprintf(after, before, from);
+#endif /* SNPRINTF */
+ free(before);
+ before = after;
+
+ for (sp = before; *sp; sp++)
+ if (*sp == '\177')
+ *sp = '%';
+ }
+
if (outlevel == O_VERBOSE)
- error(0, 0, "about to deliver with: %s", cmd);
+ error(0, 0, "about to deliver with: %s", before);
#ifdef HAVE_SETEUID
/*
seteuid(ctl->uid);
#endif /* HAVE_SETEUID */
- sinkfp = popen(cmd, "w");
+ sinkfp = popen(before, "w");
#ifdef HAVE_SETEUID
/* this will fail quietly if we didn't start as root */
if (!sinkfp)
{
- error(0, -1, "MDA open failed");
+ error(0, 0, "MDA open failed");
return(PS_IOERR);
}
}
else
{
- char *ap, *ctt, options[MSGBUFSIZE], addr[128], *desthost;
+ char *ap, *ctt, options[MSGBUFSIZE], addr[128];
/* build a connection to the SMTP listener */
if ((smtp_open(ctl) == -1))
{
- free_str_list(&xmit_names);
error(0, errno, "SMTP connect to %s failed",
ctl->smtphost ? ctl->smtphost : "localhost");
+ free_str_list(&xmit_names);
return(PS_SMTP);
}
options[0] = '\0';
if (ctl->server.esmtp_options & ESMTP_8BITMIME)
if (ctl->pass8bits)
- sprintf(options, " BODY=8BITMIME");
+ strcpy(options, " BODY=8BITMIME");
else if ((ctt_offs >= 0) && (ctt = nxtaddr(headers + ctt_offs)))
{
if (!strcasecmp(ctt,"7BIT"))
- sprintf(options, " BODY=7BIT");
+ strcpy(options, " BODY=7BIT");
else if (!strcasecmp(ctt,"8BIT"))
- sprintf(options, " BODY=8BITMIME");
+ strcpy(options, " BODY=8BITMIME");
}
- if ((ctl->server.esmtp_options & ESMTP_SIZE))
- sprintf(options + strlen(options), " SIZE=%ld", len);
+ if ((ctl->server.esmtp_options & ESMTP_SIZE) && reallen > 0)
+ sprintf(options + strlen(options), " SIZE=%ld", reallen);
/*
* If there is a Return-Path address on the message, this was
SMTP_data(ctl->smtp_socket);
}
- /* we may need to strip carriage returns */
- if (ctl->stripcr)
+ n = 0;
+ /*
+ * Some server/sendmail combinations cause problems when our
+ * synthetic Received line is before the From header. Cope
+ * with this...
+ */
+ if ((rcv = strstr(headers, "Received:")) == (char *)NULL)
+ rcv = headers;
+ if (rcv > headers)
{
- char *sp, *tp;
+ *rcv = '\0';
+ n = stuffline(ctl, headers);
+ *rcv = 'R';
+ }
+ if (!use_invisible && n != -1)
+ {
+ /* utter any per-message Received information we need here */
+ sprintf(buf, "Received: from %s\n", ctl->server.truename);
+ n = stuffline(ctl, buf);
+ if (n != -1)
+ {
+ sprintf(buf, "\tby %s (fetchmail-%s %s run for %s)\n",
+ fetchmailhost,
+ RELEASE_ID,
+ protocol->name,
+ ctl->remotename);
+ n = stuffline(ctl, buf);
+ if (n != -1)
+ {
+ time_t now;
- for (sp = tp = headers; *sp; sp++)
- if (*sp != '\r')
- *tp++ = *sp;
- *tp = '\0';
+ buf[0] = '\t';
+ if (good_addresses == 0)
+ {
+ sprintf(buf+1,
+ "for <%s@%s> (by default); ",
+ user, desthost);
+ }
+ else if (good_addresses == 1)
+ {
+ for (idp = xmit_names; idp; idp = idp->next)
+ if (idp->val.num == XMIT_ACCEPT)
+ break; /* only report first address */
+ sprintf(buf+1, "for <%s@%s> (%s); ",
+ idp->id, desthost,
+ MULTIDROP(ctl) ? "multi-drop" : "single-drop");
+ }
+ else
+ buf[1] = '\0';
+ time(&now);
+ strcat(buf, ctime(&now));
+ n = stuffline(ctl, buf);
+ }
+ }
}
- /* write all the headers */
- n = 0;
- if (ctl->mda && sinkfp)
- n = fwrite(headers, 1, strlen(headers), sinkfp);
- else if (ctl->smtp_socket != -1)
- n = SockWrite(ctl->smtp_socket, headers, strlen(headers));
- free(headers);
+ if (n != -1)
+ n = stuffline(ctl, rcv); /* ship out rest of headers */
- if (n < 0)
+ if (n == -1)
{
error(0, errno, "writing RFC822 headers");
if (ctl->mda)
flag forward; /* TRUE to forward */
{
int linelen;
- char buf[MSGBUFSIZE+1], *cp;
+ char buf[MSGBUFSIZE+1];
/* pass through the text lines */
while (protocol->delimited || len > 0)
}
}
len -= linelen;
- msglen += linelen;
/* check for end of message */
if (protocol->delimited && *buf == '.')
break;
else if (buf[1] == '\n' && buf[2] == '\0')
break;
+ else
+ msglen--; /* subtract the size of the dot escape */
+
+ msglen += linelen;
/* ship out the text line */
if (forward)
if (rem != KSUCCESS)
{
error(0, -1, "kerberos error %s", (krb_get_err_text (rem)));
- return (PS_ERROR);
+ return (PS_AUTHFAIL);
}
return (0);
}
struct query *ctl; /* parsed options with merged-in defaults */
const struct method *proto; /* protocol method table */
{
- int ok, js, pst, sock = -1;
- char *msg, *cp, realname[HOSTLEN];
+ int ok, js, sock = -1;
+ char *msg;
void (*sigsave)();
#ifndef KERBEROS_V4
}
protocol = proto;
+ pass = 0;
tagnum = 0;
tag[0] = '\0'; /* nuke any tag hanging out from previous query */
ok = 0;
- error_init(poll_interval == 0 && !logfile);
/* set up the server-nonresponse timeout */
sigsave = signal(SIGALRM, timeout_handler);
}
else
{
- char buf [POPBUFSIZE+1], *sp, *realhost;
+ char buf [POPBUFSIZE+1], *realhost;
int *msgsizes, len, num, count, new, deletions = 0;
- int port, fetches;
+ int port, fetches, dispatches;
struct idlist *idp;
/* execute pre-initialization command, if any */
goto cleanUp;
set_timeout(ctl->server.timeout);
- /*
- * Try to parse the host's actual name out of the greeting
- * message. We do this so that the progress messages will
- * make sense even if the connection is indirected through
- * ssh. *Do* use this for hacking reply headers, but *don't*
- * use it for error logging, as the names in the log should
- * correlate directly back to rc file entries.
- *
- * This assumes that the first space-delimited token found
- * that contains at least two dots (with the characters on
- * each side of the dot alphanumeric to exclude version
- * numbers) is the hostname. The hostname candidate may not
- * contain @ -- if it does it's probably a mailserver
- * maintainer's name. If no such token is found, fall back on
- * the .fetchmailrc id.
- */
- pst = 0;
- sp = (char *)NULL; /* sacrifice to -Wall */
- for (cp = buf; *cp; cp++)
- {
- switch (pst)
- {
- case 0: /* skip to end of current token */
- if (*cp == ' ')
- pst = 1;
- break;
-
- case 1: /* look for blank-delimited token */
- if (*cp != ' ')
- {
- sp = cp;
- pst = 2;
- }
- break;
-
- case 2: /* look for first dot */
- if (*cp == '@')
- pst = 0;
- else if (*cp == ' ')
- pst = 1;
- else if (*cp == '.' && isalpha(cp[1]) && isalpha(cp[-1]))
- pst = 3;
- break;
-
- case 3: /* look for second dot */
- if (*cp == '@')
- pst = 0;
- else if (*cp == ' ')
- pst = 1;
- else if (*cp == '.' && isalpha(cp[1]) && isalpha(cp[-1]))
- pst = 4;
- break;
-
- case 4: /* look for trailing space */
- if (*cp == '@')
- pst = 0;
- else if (*cp == ' ')
- {
- pst = 5;
- goto done;
- }
- break;
- }
- }
- done:
- if (pst == 5)
- {
- char *tp = realname;
-
- while (sp < cp)
- *tp++ = *sp++;
- *tp = '\0';
- }
- else
- strcpy(realname, ctl->server.pollname);
-
/* try to get authorized to fetch mail */
if (protocol->getauth)
{
if (ok == PS_LOCKBUSY)
error(0, -1, "Lock-busy error on %s@%s",
ctl->remotename,
- realname);
+ ctl->server.truename);
else
{
if (ok == PS_ERROR)
ok = PS_AUTHFAIL;
error(0, -1, "Authorization failure on %s@%s",
ctl->remotename,
- realname);
+ ctl->server.truename);
}
goto cleanUp;
}
/* 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);
+ pass = 0;
+ do {
+ dispatches = 0;
+ ++pass;
- /* 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);
- }
+ if (outlevel >= O_VERBOSE)
+ if (idp->id)
+ error(0, 0, "selecting or re-polling folder %s", idp->id);
+ 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 at %s (folder %s)",
+ ctl->remotename, ctl->server.truename, 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 at %s", ctl->remotename, ctl->server.truename);
+ if (outlevel > O_SILENT)
+ if (count == -1) /* only used for ETRN */
+ error(0, 0, "Polling %s", ctl->server.truename);
+ else if (count != 0)
+ {
+ if (new != -1 && (count - new) > 0)
+ error(0, 0, "%d message%s (%d seen) for %s.",
+ count, count > 1 ? "s" : "", count-new, buf);
+ else
+ error(0, 0, "%d message%s for %s.",
+ count, count > 1 ? "s" : "", buf);
+ }
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;
+ {
+ /* these are pointless in normal daemon mode */
+ if (pass == 1 && (poll_interval == 0 || outlevel == O_VERBOSE))
+ error(0, 0, "No mail for %s", buf);
+ }
- /*
- * 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);
+ /* very important, this is where we leave the do loop */
+ if (count == 0)
+ break;
- /*
- * 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;
}
+ else if (count > 0)
+ {
+ flag force_retrieval;
- /* 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;
-
- /* 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
+ /*
+ * 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);
+
+ /*
+ * 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;
+ int i;
- /* request a message */
- ok = (protocol->fetch_headers)(sock, ctl, num, &len);
+ msgsizes = (int *)alloca(sizeof(int) * count);
+ for (i = 0; i < count; i++)
+ msgsizes[i] = -1;
+
+ 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 ((ok = (protocol->trail)(sock, ctl, num)))
+ flag wholesize = !protocol->fetch_body;
+
+ /* 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 && !msgsizes)
- 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, msgsizes[num-1],
+ ctl, 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)
{
- ok = (protocol->trail)(sock, ctl, num);
- if (ok != 0)
+ if (outlevel == O_VERBOSE)
+ fputc('\n', stderr);
+
+ 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? */
- if (msglen != len)
- error(0, 0, "size of message %d (%d) was not what was expected (%d)", num, msglen, len);
+ /* 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 */
- if (outlevel == O_VERBOSE)
- fputc('\n', stderr);
+ /* 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;
+ /* count # messages forwarded on this pass */
+ if (!suppress_forward)
+ dispatches++;
+
+ /*
+ * 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]);
+ }
+
+ /* end-of-message processing starts here */
- /* close the delivery pipe, we'll reopen before next message */
- rc = pclose(sinkfp);
- signal(SIGCHLD, sigchld);
- if (rc)
+ 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);
+#ifdef POP3_ENABLE
+ delete_str(&ctl->newsaved, num);
+#endif /* POP3_ENABLE */
+ }
+ 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");
- }
- 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);
+ /* perhaps this as many as we're ready to handle */
+ if (ctl->fetchlimit > 0 && ctl->fetchlimit <= fetches)
+ goto no_error;
}
- 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 had some actual forwards, allowed
+ * deletions and had no errors.
+ * Otherwise it is far too easy to get into infinite loops.
+ */
+ (dispatches && protocol->retry && !ctl->keep && !ctl->errcount);
}
no_error:
set_timeout(ctl->server.timeout);
- ok = gen_transact(sock, protocol->exit_cmd);
+ ok = (protocol->logout_cmd)(sock, ctl);
+ /*
+ * Hmmmm...arguably this would be incorrect if we had fetches but
+ * no dispatches (due to oversized messages, etc.)
+ */
if (ok == 0)
ok = (fetches > 0) ? PS_SUCCESS : PS_NOMAIL;
set_timeout(0);
cleanUp:
set_timeout(ctl->server.timeout);
if (ok != 0 && ok != PS_SOCKET)
- gen_transact(sock, protocol->exit_cmd);
+ (protocol->logout_cmd)(sock, ctl);
set_timeout(0);
close(sock);
}
}
#if defined(HAVE_STDARG_H)
-void gen_send(int sock, char *fmt, ... )
+void gen_send(int sock, const char *fmt, ... )
/* assemble command in printf(3) style and send to the server */
#else
void gen_send(sock, fmt, va_alist)