#if defined(HAVE_ALLOCA_H)
#include <alloca.h>
#endif
+#if defined(HAVE_SYS_ITIMER_H)
+#include <sys/itimer.h>
+#endif
#include <sys/time.h>
#include <signal.h>
extern char *strstr(); /* needed on sysV68 R3V7.1. */
-int batchlimit; /* how often to tear down the delivery connection */
int fetchlimit; /* how often to tear down the server connection */
int batchcount; /* count of messages sent in current batch */
int peek_capable; /* can we peek for better error recovery? */
return(TRUE);
else if (strcmp(name, ctl->server.canonical_name) == 0)
return(TRUE);
- else if (ctl->server.no_dns)
+ else if (!ctl->server.dns)
return(FALSE);
/*
"nameserver failure while looking for `%s' during poll of %s.",
name, ctl->server.names->id);
ctl->errcount++;
- longjmp(restart, 2); /* try again next poll cycle */
break;
}
"nameserver failure while looking for `%s' during poll of %s.",
name, ctl->server.names->id);
ctl->errcount++;
- longjmp(restart, 2); /* try again next poll cycle */
break;
}
}
}
char *parse_received(struct query *ctl, char *bufp)
-/* try to extract */
+/* try to extract real addressee from the Received line */
{
char *ok;
static char rbuf[HOSTLEN + USERNAMELEN + 4];
static FILE *smtp_open(struct query *ctl)
/* try to open a socket to the appropriate SMTP server for this query */
{
- struct query *lead;
-
- lead = ctl->lead_smtp; /* go to the SMTP leader for this query */
+ struct idlist *idp;
/* maybe it's time to close the socket in order to force delivery */
- if (batchlimit && lead->smtp_sockfp && batchcount++ == batchlimit)
+ if (ctl->batchlimit && ctl->smtp_sockfp && batchcount++ == ctl->batchlimit)
{
- fclose(lead->smtp_sockfp);
- lead->smtp_sockfp = (FILE *)NULL;
+ fclose(ctl->smtp_sockfp);
+ ctl->smtp_sockfp = (FILE *)NULL;
batchcount = 0;
}
- /*
- * 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.
- *
- * In fact this code relies on the RFC1123 requirement that the
- * SMTP listener must accept messages even if verification of the
- * HELO name fails (RFC1123 section 5.2.5, paragraph 2).
- */
-
- /* if no socket to this host is already set up, try to open ESMTP */
- if (lead->smtp_sockfp == (FILE *)NULL)
+ /* run down the SMTP hunt list looking for a server that's up */
+ for (idp = ctl->smtphunt; idp; idp = idp->next)
{
- if ((lead->smtp_sockfp = SockOpen(lead->smtphost, SMTP_PORT)) == (FILE *)NULL)
- return((FILE *)NULL);
- else if (SMTP_ok(lead->smtp_sockfp) != SM_OK
- || SMTP_ehlo(lead->smtp_sockfp,
- ctl->server.names->id,
- &lead->server.esmtp_options) != SM_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.
+ *
+ * In fact this code relies on the RFC1123 requirement that the
+ * SMTP listener must accept messages even if verification of the
+ * HELO name fails (RFC1123 section 5.2.5, paragraph 2).
+ */
+
+ /* if no socket to this host is already set up, try to open ESMTP */
+ if (ctl->smtp_sockfp == (FILE *)NULL)
{
- /*
- * RFC 1869 warns that some listeners hang up on a failed EHLO,
- * so it's safest not to assume the socket will still be good.
- */
- fclose(lead->smtp_sockfp);
- lead->smtp_sockfp = (FILE *)NULL;
+ if ((ctl->smtp_sockfp = SockOpen(idp->id,SMTP_PORT))==(FILE *)NULL)
+ return((FILE *)NULL);
+ else if (SMTP_ok(ctl->smtp_sockfp) != SM_OK
+ || SMTP_ehlo(ctl->smtp_sockfp,
+ ctl->server.names->id,
+ &ctl->server.esmtp_options) != SM_OK)
+ {
+ /*
+ * RFC 1869 warns that some listeners hang up on a failed EHLO,
+ * so it's safest not to assume the socket will still be good.
+ */
+ fclose(ctl->smtp_sockfp);
+ ctl->smtp_sockfp = (FILE *)NULL;
+ }
+ else
+ {
+ ctl->smtphost = idp->id;
+ break;
+ }
}
- }
- /* if opening for ESMTP failed, try SMTP */
- if (lead->smtp_sockfp == (FILE *)NULL)
- {
- if ((lead->smtp_sockfp = SockOpen(lead->smtphost, SMTP_PORT)) == (FILE *)NULL)
- return((FILE *)NULL);
- else if (SMTP_ok(lead->smtp_sockfp) != SM_OK
- || SMTP_helo(lead->smtp_sockfp, ctl->server.names->id) != SM_OK)
+ /* if opening for ESMTP failed, try SMTP */
+ if (ctl->smtp_sockfp == (FILE *)NULL)
{
- fclose(lead->smtp_sockfp);
- lead->smtp_sockfp = (FILE *)NULL;
+ if ((ctl->smtp_sockfp = SockOpen(idp->id,SMTP_PORT))==(FILE *)NULL)
+ return((FILE *)NULL);
+ else if (SMTP_ok(ctl->smtp_sockfp) != SM_OK
+ || SMTP_helo(ctl->smtp_sockfp, ctl->server.names->id) != SM_OK)
+ {
+ fclose(ctl->smtp_sockfp);
+ ctl->smtp_sockfp = (FILE *)NULL;
+ }
+ else
+ {
+ ctl->smtphost = idp->id;
+ break;
+ }
}
}
- return(lead->smtp_sockfp);
+ return(ctl->smtp_sockfp);
}
static int gen_readmsg(sockfp, len, delimited, ctl, realname)
{
char buf [MSGBUFSIZE+1];
int from_offs, to_offs, cc_offs, bcc_offs, ctt_offs, env_offs;
- char *headers, *received_for;
- int n, oldlen, ch, sizeticker, delete_ok;
+ char *headers, *received_for, *return_path;
+ int n, oldlen, ch, sizeticker, delete_ok, remaining;
FILE *sinkfp;
RETSIGTYPE (*sigchld)();
#ifdef HAVE_GETHOSTBYNAME
#ifdef HAVE_RES_SEARCH
int no_local_matches = FALSE;
#endif /* HAVE_RES_SEARCH */
+ int olderrs;
sizeticker = 0;
delete_ok = TRUE;
+ remaining = len;
+ olderrs = ctl->errcount;
/* read message headers */
- headers = received_for = NULL;
+ headers = received_for = return_path = NULL;
from_offs = to_offs = cc_offs = bcc_offs = ctt_offs = env_offs = -1;
oldlen = 0;
for (;;)
((ch = SockPeek(sockfp)) == ' ' || ch == '\t');
/* write the message size dots */
- if ((outlevel>O_SILENT && outlevel<O_VERBOSE) && (n=strlen(line)) > 0)
+ n = strlen(line);
+ if ((outlevel > O_SILENT && outlevel < O_VERBOSE) && n > 0)
{
sizeticker += n;
while (sizeticker >= SIZETICKER)
sizeticker -= SIZETICKER;
}
}
- len -= n;
+ remaining -= n;
/* check for end of headers; don't save terminating line */
if (line[0] == '\r' && line[1] == '\n')
break;
}
- if (!ctl->no_rewrite)
+
+ /*
+ * OK, this is messy. If we're forwarding by SMTP, it's the
+ * SMTP-receiver's job (according to RFC821, page 22, section
+ * 4.1.1) to generate a Return-Path line on final delivery.
+ * The trouble is, we've already got one because the
+ * mailserver's SMTP thought *it* was responsible for final
+ * delivery.
+ *
+ * Stash away the contents of Return-Path for use in generating
+ * MAIL FROM later on, then prevent the header from being saved
+ * with the others. In effect, we strip it off here.
+ *
+ * If the SMTP server conforms to the standards, and fetchmail gets the
+ * envelope sender from the Return-Path, the new Return-Path should be
+ * exactly the same as the original one.
+ */
+ if (!ctl->mda && !strncasecmp("Return-Path:", line, 12))
+ {
+ return_path = xstrdup(nxtaddr(line));
+ continue;
+ }
+
+ if (ctl->rewrite)
reply_hack(line, realname);
if (!headers)
else if (!strncasecmp("To:", line, 3))
to_offs = (line - headers);
- else if (env_offs == -1 && !strncasecmp(ctl->server.envelope,
+ else if (ctl->server.envelope != STRING_DISABLED && env_offs == -1
+ && !strncasecmp(ctl->server.envelope,
line,
strlen(ctl->server.envelope)))
env_offs = (line - headers);
ctt_offs = (line - headers);
#ifdef HAVE_RES_SEARCH
- else if (MULTIDROP(ctl) && !received_for && !strncasecmp("Received:", line, 9))
+ else if (ctl->server.envelope != STRING_DISABLED && MULTIDROP(ctl) && !received_for && !strncasecmp("Received:", line, 9))
received_for = parse_received(ctl, line);
#endif /* HAVE_RES_SEARCH */
}
+ /*
+ * Hack time. If the first line of the message was blank, with no headers
+ * (this happens occasionally due to bad gatewaying software) cons up
+ * a set of fake headers.
+ *
+ * If you modify the fake header template below, be sure you don't
+ * make either From or To address @-less, otherwise the reply_hack
+ * logic will do bad things.
+ */
+ if (headers == (char *)NULL)
+ {
+ sprintf(buf,
+ "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);
+ headers = xstrdup(buf);
+ }
+
/*
* We can now process message headers before reading the text.
* In fact we have to, as this will tell us where to forward to.
#endif /* HAVE_RES_SEARCH */
save_str(&xmit_names, -1, ctl->localnames->id);
- /* time to address the message */
- if (ctl->mda) /* we have a declared MDA */
+
+ /*
+ * Time to either address the message or decide we can't deliver it yet.
+ */
+ if (ctl->errcount > olderrs) /* there were DNS errors above */
+ {
+ delete_ok = FALSE;
+ sinkfp = (FILE *)NULL;
+ if (outlevel == O_VERBOSE)
+ error(0,0, "forwarding and deletion suppressed due to DNS errors");
+ }
+ else if (ctl->mda) /* we have a declared MDA */
{
int length = 0;
char *names, *cmd;
if (!ctl->mda && ((sinkfp = smtp_open(ctl)) == NULL))
{
free_str_list(&xmit_names);
- error(0, 0, "SMTP connect failed");
+ error(0, 0, "SMTP connect to %s failed", ctl->smtphost);
+ if (return_path)
+ free(return_path);
return(PS_SMTP);
}
sprintf(options + strlen(options), " SIZE=%d", len);
/*
- * Try to get the SMTP listener to take the header
- * From address as MAIL FROM (this makes the logging
- * nicer). If it won't, fall back on the calling-user
- * ID. This won't affect replies, which use the header
- * From address anyway.
+ * If there is a Return-Path address on the message, this was
+ * almost certainly the MAIL FROM address given the originating
+ * sendmail. This is the best thing to use for logging the
+ * message origin (it sets up the right behavior for bounces and
+ * mailing lists). Otherwise, take the From address.
+ *
+ * Try to get the SMTP listener to take the Return-Path or
+ * From address as MAIL FROM . If it won't, fall back on the
+ * calling-user ID. This won't affect replies, which use the
+ * header From address anyway.
*
* RFC 1123 requires that the domain name part of the
* MAIL FROM address be "canonicalized", that is a
* is if rewrite is on). RFC 1123 is silent on whether
* a nonexistent hostname part is considered canonical.
*
- * This is a potential problem if the MTAs further
- * upstream didn't pass canonicalized From lines, *and*
- * the local SMTP listener insists on them.
+ * This is a potential problem if the MTAs further upstream
+ * didn't pass canonicalized From/Return-Path lines, *and* the
+ * local SMTP listener insists on them.
*/
- if (from_offs == -1 || !(ap = nxtaddr(headers + from_offs)))
+ ap = (char *)NULL;
+ if (return_path)
+ ap = return_path;
+ else if (from_offs == -1 || !(ap = nxtaddr(headers + from_offs)))
ap = user;
if (SMTP_from(sinkfp, ap, options) != SM_OK)
{
error(0, 0, "SMTP error: %s", smtp_response);
/*
- * There'a one problem with this flow of control;
+ * There's one problem with this flow of control;
* there's no way to avoid reading the whole message
* off the server, even if the MAIL FROM response
* tells us that it's just to be discarded. We could
if (SMTP_from(sinkfp, user, options) != SM_OK)
{
error(0,0,"SMTP error: %s", smtp_response);
+ if (return_path)
+ free(return_path);
return(PS_SMTP); /* should never happen */
}
}
{
error(0, 0,
"can't even send to calling user!");
+ if (return_path)
+ free(return_path);
return(PS_SMTP);
}
SMTP_data(sinkfp);
skiptext:;
+ if (return_path)
+ free(return_path);
}
/* we may need to strip carriage returns */
*/
/* pass through the text lines */
- while (delimited || len > 0)
+ while (delimited || remaining > 0)
{
if (!SockGets(buf, sizeof(buf)-1, sockfp))
return(PS_SOCKET);
sizeticker -= SIZETICKER;
}
}
- len -= n;
+ remaining -= n;
/* check for end of message */
if (delimited && *buf == '.')
if (SMTP_eom(sinkfp) != SM_OK)
{
error(0, 0, "SMTP listener refused delivery");
+ ctl->errcount++;
return(PS_TRANSIENT);
}
}
ctl->server.timeout, ctl->server.names->id);
ok = PS_ERROR;
}
- else if (js == 2)
- {
- /* error message printed at point of longjmp */
- ok = PS_ERROR;
- }
else
{
char buf [POPBUFSIZE+1];
#ifndef EHOSTUNREACH
#define EHOSTUNREACH (-1)
#endif
- if (errno != EHOSTUNREACH)
+ if (outlevel == O_VERBOSE || errno != EHOSTUNREACH)
error(0, errno, "connecting to host");
ok = PS_SOCKET;
goto closeUp;
}
#ifdef KERBEROS_V4
- if (ctl->authenticate == A_KERBEROS)
+ if (ctl->server.authenticate == A_KERBEROS)
{
ok = kerberos_auth(fileno(sockfp), ctl->server.canonical_name);
if (ok != 0)
}
else if (count > 0)
{
+ int force_retrieval, fetches;
+
/*
* What forces this code is that in POP3 and IMAP2BIS you can't
* fetch a message without having it marked `seen'. In IMAP4,
* previous pass and forcing all messages to be considered new
* if it's nonzero.
*/
- int force_retrieval = !peek_capable && (ctl->errcount > 0);
+ force_retrieval = !peek_capable && (ctl->errcount > 0);
- ctl->errcount = 0;
+ ctl->errcount = fetches = 0;
/* read, forward, and delete messages */
for (num = 1; num <= count; num++)
goto cleanUp;
vtalarm(ctl->server.timeout);
}
+
+ fetches++;
}
/*
error_complete(0, 0, " not flushed");
/* perhaps this as many as we're ready to handle */
- if (ctl->fetchlimit && ctl->fetchlimit <= num)
+ if (ctl->fetchlimit && ctl->fetchlimit <= fetches)
break;
}
{
char *cp;
- if (shroud && (cp = strstr(buf, shroud)))
+ if (shroud && shroud[0] && (cp = strstr(buf, shroud)))
{
char *sp;
{
char *cp;
- if (shroud && (cp = strstr(buf, shroud)))
+ if (shroud && shroud[0] && (cp = strstr(buf, shroud)))
{
char *sp;
*cp++ = '*';
while (*sp)
*cp++ = *sp++;
- *sp = '\0';
+ *cp = '\0';
}
buf[strlen(buf)-1] = '\0';
error(0, 0, "%s> %s", protocol->name, buf);