#include <sys/time.h>
#include <signal.h>
+#ifndef HAVE_STRFTIME /* For ctime prototype */
+#include <sys/types.h>
+#include <time.h>
+#endif
+
#ifdef HAVE_GETHOSTBYNAME
#include <netdb.h>
#include "mx.h"
#define SIGCHLD SIGCLD
#endif
+#if INET6
+#define SMTP_PORT "smtp" /* standard SMTP service port */
+#else /* INET6 */
#define SMTP_PORT 25 /* standard SMTP service port */
+#endif /* INET6 */
#ifndef strstr /* glibc-2.1 declares this as a macro */
extern char *strstr(); /* needed on sysV68 R3V7.1. */
static int mytimeout; /* value of nonreponse timeout */
static int msglen; /* actual message length */
+/* use these to track what was happening when the nonresponse timer fired */
+#define GENERAL_WAIT 0
+#define SERVER_WAIT 1
+#define FORWARDING_WAIT 2
+static phase;
+
static void set_timeout(int timeleft)
/* reset the nonresponse-timeout */
{
* name doesn't match any is it time to call the bind library.
* If this happens odds are good we're looking at an MX name.
*/
- if (strcmp(lead_server->truename, name) == 0)
+ if (strcasecmp(lead_server->truename, name) == 0)
return(TRUE);
else if (str_in_list(&lead_server->akalist, name))
return(TRUE);
*/
else if ((he = gethostbyname(name)) != (struct hostent *)NULL)
{
- if (strcmp(ctl->server.truename, he->h_name) == 0)
+ if (strcasecmp(ctl->server.truename, he->h_name) == 0)
goto match;
else
return(FALSE);
else
{
for (mxp = mxrecords; mxp->name; mxp++)
- if (strcmp(ctl->server.truename, mxp->name) == 0)
+ if (strcasecmp(ctl->server.truename, mxp->name) == 0)
goto match;
return(FALSE);
match:;
{
char *cp;
- if ((cp = nxtaddr(hdr)) != (char *)NULL)
- do {
- char *atsign;
-
- if ((atsign = strchr(cp, '@')))
- {
- struct idlist *idp;
+ for (cp = nxtaddr(hdr);
+ cp != NULL;
+ cp = nxtaddr(NULL))
+ {
+ char *atsign;
- /*
- * Does a trailing segment of the hostname match something
- * on the localdomains list? If so, save the whole name
- * and keep going.
- */
- for (idp = ctl->server.localdomains; idp; idp = idp->next)
- {
- char *rhs;
+ if ((atsign = strchr(cp, '@'))) {
+ struct idlist *idp;
- rhs = atsign + (strlen(atsign) - strlen(idp->id));
- if ((rhs[-1] == '.' || rhs[-1] == '@')
- && strcasecmp(rhs, idp->id) == 0)
- {
- if (outlevel == O_VERBOSE)
- error(0, 0, "passed through %s matching %s",
- cp, idp->id);
- save_str(xmit_names, XMIT_ACCEPT, cp);
- accept_count++;
- continue;
- }
- }
+ /*
+ * Does a trailing segment of the hostname match something
+ * on the localdomains list? If so, save the whole name
+ * and keep going.
+ */
+ for (idp = ctl->server.localdomains; idp; idp = idp->next) {
+ char *rhs;
- /*
- * Check to see if the right-hand part is an alias
- * or MX equivalent of the mailserver. If it's
- * not, skip this name. If it is, we'll keep
- * going and try to find a mapping to a client name.
- */
- if (!is_host_alias(atsign+1, ctl))
+ rhs = atsign + (strlen(atsign) - strlen(idp->id));
+ if (rhs > atsign &&
+ (rhs[-1] == '.' || rhs[-1] == '@') &&
+ strcasecmp(rhs, idp->id) == 0)
{
- save_str(xmit_names, XMIT_REJECT, cp);
- reject_count++;
- continue;
+ if (outlevel == O_VERBOSE)
+ error(0, 0, "passed through %s matching %s",
+ cp, idp->id);
+ save_str(xmit_names, XMIT_ACCEPT, cp);
+ accept_count++;
+ break;
}
- atsign[0] = '\0';
}
+ /* if we matched a local domain, idp != NULL */
+ if (idp) continue;
- map_name(cp, ctl, xmit_names);
- } while
- ((cp = nxtaddr((char *)NULL)) != (char *)NULL);
+ /*
+ * Check to see if the right-hand part is an alias
+ * or MX equivalent of the mailserver. If it's
+ * not, skip this name. If it is, we'll keep
+ * going and try to find a mapping to a client name.
+ */
+ if (!is_host_alias(atsign+1, ctl))
+ {
+ save_str(xmit_names, XMIT_REJECT, cp);
+ reject_count++;
+ continue;
+ }
+ atsign[0] = '\0';
+ }
+
+ map_name(cp, ctl, xmit_names);
+ }
}
}
static char *parse_received(struct query *ctl, char *bufp)
-/* try to extract real addressee from the Received line */
+/* try to extract real address from the Received line */
+/* If a valid Received: line is found, we return the full address in
+ * a buffer wich can be parsed from nxtaddr(). This is to ansure that
+ * the local domain part of the address can be passed along in
+ * find_server_names() if it contains one.
+ * Note: We should return a dummy header containing the address
+ * which makes nxtaddr() behave correctly.
+ */
{
char *ok = (char *)NULL;
static char rbuf[HOSTLEN + USERNAMELEN + 4];
* recipient name after a following "for". Otherwise
* punt.
*/
- if (!is_host_alias(rbuf, ctl))
- ok = (char *)NULL;
- else if ((ok = strstr(sp, "for ")) && isspace(ok[-1]))
+ if (is_host_alias(rbuf, ctl) &&
+ (ok = strstr(sp, "for ")) &&
+ isspace(ok[-1]))
{
tp = rbuf;
sp = ok + 4;
+ *tp++ = ':'; /* Here is the hack. This is to be friend */
+ *tp++ = ' '; /* with nxtaddr()... */
if (*sp == '<')
sp++;
while (*sp == '@') /* skip routes */
- while (*sp++ != ':')
+ while (*sp && *sp++ != ':')
continue;
- while (*sp && *sp != '>' && *sp != '@' && *sp != ';')
+ while (*sp && *sp != '>' && *sp != ';')
if (!isspace(*sp))
*tp++ = *sp++;
else
ok = (char *)NULL;
break;
}
+ *tp++ = '\n';
*tp = '\0';
- }
+ if (strlen(rbuf) <= 3) /* apparently nothing has been found */
+ ok = NULL;
+ } else
+ ok = (char *)NULL;
}
if (!ok)
return(NULL);
else
{
- if (outlevel == O_VERBOSE)
- error(0, 0, "found Received address `%s'", rbuf);
+ if (outlevel == O_VERBOSE) {
+ char *lf = rbuf + strlen(rbuf)-1;
+ *lf = '\0';
+ error(0, 0, "found Received address `%s'", rbuf+2);
+ *lf = '\n';
+ }
return(rbuf);
}
}
static int stuffline(struct query *ctl, char *buf)
/* ship a line to the given control block's output sink (SMTP server or MDA) */
{
- int n;
+ int n, oldphase;
char *last;
/* The line may contain NUL characters. Find the last char to use
}
}
+ oldphase = phase;
+ phase = FORWARDING_WAIT;
+
/*
* SMTP byte-stuffing. We only do this if the protocol does *not*
* use .<CR><LF> as EOM. If it does, the server will already have
else if (ctl->smtp_socket != -1)
n = SockWrite(ctl->smtp_socket, buf, last - buf);
+ phase = oldphase;
+
return(n);
}
{
int offset;
struct addrblk *next;
- } *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, *destaddr, *rcv;
- int n, linelen, oldlen, ch, remaining, skipcount;
- char *cp;
+ };
+ struct addrblk *to_addrchain = NULL;
+ struct addrblk **to_chainptr = &to_addrchain;
+ struct addrblk *resent_to_addrchain = NULL;
+ struct addrblk **resent_to_chainptr = &resent_to_addrchain;
+
+ char buf[MSGBUFSIZE+1];
+ char return_path[HOSTLEN + USERNAMELEN + 4];
+ int from_offs, reply_to_offs, resent_from_offs;
+ int app_from_offs, sender_offs, resent_sender_offs;
+ int ctt_offs, env_offs;
+ char *headers, *received_for, *destaddr, *rcv, *cp;
+ int n, linelen, oldlen, ch, remaining, skipcount;
struct idlist *idp, *xmit_names;
flag good_addresses, bad_addresses, has_nuls;
flag no_local_matches = FALSE;
int olderrs;
- next_address = sizeticker = 0;
+ sizeticker = 0;
has_nuls = FALSE;
return_path[0] = '\0';
olderrs = ctl->errcount;
/* read message headers */
headers = received_for = NULL;
- from_offs = ctt_offs = env_offs = -1;
+ from_offs = reply_to_offs = resent_from_offs = app_from_offs =
+ sender_offs = resent_sender_offs = ctt_offs = env_offs = -1;
oldlen = 0;
msglen = 0;
skipcount = 0;
linelen = 0;
line[0] = '\0';
do {
- if ((n = SockRead(sock, buf, sizeof(buf)-1)) == -1)
+ if ((n = SockRead(sock, buf, sizeof(buf)-1)) == -1) {
+ free(line);
+ free(headers);
return(PS_SOCKET);
+ }
linelen += n;
msglen += n;
*/
if (protocol->port != 109)
#endif /* POP2_ENABLE */
- if (num == 1 && !strncasecmp(line, "X-IMAP:", 7))
+ if (num == 1 && !strncasecmp(line, "X-IMAP:", 7)) {
+ free(line);
+ free(headers);
return(PS_RETAINED);
+ }
/*
* This code prevents fetchmail from becoming an accessory after
* unconditionally. Nonempty ones get chucked if the user
* turns on the dropstatus flag.
*/
- if (!strncasecmp(line, "Status:", 7))
+ if (!strncasecmp(line, "Status:", 7)
+ || !strncasecmp(line, "X-Mozilla-Status:", 7))
{
char *cp;
}
}
+ if (ctl->rewrite)
+ line = reply_hack(line, ctl->server.truename);
+
/*
* OK, this is messy. If we're forwarding by SMTP, it's the
* SMTP-receiver's job (according to RFC821, page 22, section
* 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))
+ if (!strncasecmp("Return-Path:", line, 12))
{
strcpy(return_path, nxtaddr(line));
- free(line);
- continue;
+ if (!ctl->mda) {
+ free(line);
+ continue;
+ }
}
- if (ctl->rewrite)
- line = reply_hack(line, ctl->server.truename);
-
if (!headers)
{
oldlen = strlen(line);
newlen = oldlen + strlen(line);
headers = (char *) realloc(headers, newlen + 1);
- if (headers == NULL)
+ if (headers == NULL) {
+ free(line);
return(PS_IOERR);
+ }
strcpy(headers + oldlen, line);
free(line);
line = headers + oldlen;
oldlen = newlen;
}
- if (from_offs == -1 && !strncasecmp("From:", line, 5))
- from_offs = (line - headers);
- else if (from_offs == -1 && !strncasecmp("Resent-From:", line, 12))
- from_offs = (line - headers);
- else if (from_offs == -1 && !strncasecmp("Apparently-From:", line, 16))
+ if (!strncasecmp("From:", line, 5))
from_offs = (line - headers);
+ else if (!strncasecmp("Reply-To:", line, 9))
+ reply_to_offs = (line - headers);
+ else if (!strncasecmp("Resent-From:", line, 12))
+ resent_from_offs = (line - headers);
+ else if (!strncasecmp("Apparently-From:", line, 16))
+ app_from_offs = (line - headers);
+ else if (!strncasecmp("Sender:", line, 7))
+ sender_offs = (line - headers);
+ else if (!strncasecmp("Resent_Sender:", line, 14))
+ resent_sender_offs = (line - headers);
+
else if (!strncasecmp("Content-Transfer-Encoding:", line, 26))
ctt_offs = (line - headers);
else if (!strncasecmp("Message-Id:", buf, 11 ))
else if (!strncasecmp("To:", line, 3)
|| !strncasecmp("Cc:", line, 3)
- || !strncasecmp("Bcc:", line, 4))
+ || !strncasecmp("Bcc:", line, 4)
+ || !strncasecmp("Apparently-To:", line, 14))
+ {
+ *to_chainptr = xmalloc(sizeof(struct addrblk));
+ (*to_chainptr)->offset = (line - headers);
+ to_chainptr = &(*to_chainptr)->next;
+ *to_chainptr = NULL;
+ }
+
+ else if (!strncasecmp("Resent-To:", line, 10)
+ || !strncasecmp("Resent-Cc:", line, 10)
+ || !strncasecmp("Resent-Bcc:", line, 11))
{
- *chainptr = xmalloc(sizeof(struct addrblk));
- (*chainptr)->offset = (line - headers);
- chainptr = &(*chainptr)->next;
- *chainptr = NULL;
+ *resent_to_chainptr = xmalloc(sizeof(struct addrblk));
+ (*resent_to_chainptr)->offset = (line - headers);
+ resent_to_chainptr = &(*resent_to_chainptr)->next;
+ *resent_to_chainptr = NULL;
}
else if (ctl->server.envelope != STRING_DISABLED)
{
if (ctl->server.envelope
- && strcasecmp(ctl->server.envelope, "received"))
+ && strcasecmp(ctl->server.envelope, "Received"))
{
if (env_offs == -1 && !strncasecmp(ctl->server.envelope,
line,
* In fact we have to, as this will tell us where to forward to.
*/
+ /*
+ * 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, fall down to the next available
+ * envelope address wich is the most probable real sender
+ * respectively. *** The order is important! ***
+ * This is especially useful when receiving mailing list
+ * messages in multidrop mode. if a local address doesn't
+ * exist, the bounce message won't be returned blindly to the
+ * author or to the list itself but rather to the list manager
+ * (ex: specified by "Sender:") wich is less anoying. This is
+ * true for most mailing list packages.
+ */
+ if( !return_path[0] ){
+ char *ap = NULL;
+ if (resent_sender_offs >= 0 && (ap = nxtaddr(headers + resent_sender_offs)));
+ else if (sender_offs >= 0 && (ap = nxtaddr(headers + sender_offs)));
+ else if (resent_from_offs >= 0 && (ap = nxtaddr(headers + resent_from_offs)));
+ else if (from_offs >= 0 && (ap = nxtaddr(headers + from_offs)));
+ else if (reply_to_offs >= 0 && (ap = nxtaddr(headers + reply_to_offs)));
+ else if (app_from_offs >= 0 && (ap = nxtaddr(headers + app_from_offs)));
+ if (ap) strcpy( return_path, ap );
+ }
+
/* cons up a list of local recipients */
xmit_names = (struct idlist *)NULL;
bad_addresses = good_addresses = accept_count = reject_count = 0;
* We have the Received for addressee.
* It has to be a mailserver address, or we
* wouldn't have got here.
+ * We use find_server_names() to let local
+ * hostnames go through.
*/
- map_name(received_for, ctl, &xmit_names);
- else
- {
+ find_server_names(received_for, ctl, &xmit_names);
+ else {
/*
* We haven't extracted the envelope address.
- * So check all the header addresses.
+ * So check all the "Resent-To" header addresses if
+ * they exist. If and only if they don't, consider
+ * the "To" adresses.
*/
- while (addrchain)
- {
- register struct addrblk *nextptr;
-
- find_server_names(headers+addrchain->offset, ctl, &xmit_names);
- nextptr = addrchain->next;
- free(addrchain);
- addrchain = nextptr;
+ register struct addrblk *nextptr;
+ if (resent_to_addrchain) {
+ /* delete the "To" chain and substitute it
+ * with the "Resent-To" list
+ */
+ while (to_addrchain) {
+ nextptr = to_addrchain->next;
+ free(to_addrchain);
+ to_addrchain = nextptr;
+ }
+ to_addrchain = resent_to_addrchain;
+ resent_to_addrchain = NULL;
+ }
+ /* now look for remaining adresses */
+ while (to_addrchain) {
+ find_server_names(headers+to_addrchain->offset, ctl, &xmit_names);
+ nextptr = to_addrchain->next;
+ free(to_addrchain);
+ to_addrchain = nextptr;
}
}
if (!accept_count)
if (outlevel == O_VERBOSE)
error(0,0, "forwarding and deletion suppressed due to DNS errors");
free(headers);
+ free_str_list(&xmit_names);
return(PS_TRANSIENT);
}
else if (ctl->mda) /* we have a declared MDA */
/* substitute From address for %F */
if ((cp = strstr(before, "%F")))
{
- char *from = nxtaddr(headers + from_offs);
+ char *from = return_path;
char *sp;
/* \177 had better be out-of-band for MDA commands */
if (!sinkfp)
{
error(0, 0, "MDA open failed");
+ free(headers);
+ free_str_list(&xmit_names);
return(PS_IOERR);
}
{
error(0, errno, "SMTP connect to %s failed",
ctl->smtphost ? ctl->smtphost : "localhost");
+ free(headers);
free_str_list(&xmit_names);
return(PS_SMTP);
}
sprintf(options + strlen(options), " SIZE=%ld", reallen);
/*
- * 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
+ * Try to get the SMTP listener to take the Return-Path
+ * 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.
*
* didn't pass canonicalized From/Return-Path lines, *and* the
* local SMTP listener insists on them.
*/
- ap = (char *)NULL;
- if (return_path[0])
- ap = return_path;
- else if (from_offs == -1 || !(ap = nxtaddr(headers + from_offs)))
- ap = user;
+ ap = (return_path[0]) ? return_path : user;
if (SMTP_from(ctl->smtp_socket, ap, options) != SM_OK)
{
int smtperr = atoi(smtp_response);
* don't prevent it from being deleted.
*/
free(headers);
+ free_str_list(&xmit_names);
return(PS_REFUSED);
case 452: /* insufficient system storage */
*/
SMTP_rset(ctl->smtp_socket); /* required by RFC1870 */
free(headers);
+ free_str_list(&xmit_names);
return(PS_TRANSIENT);
case 552: /* message exceeds fixed maximum message size */
*/
SMTP_rset(ctl->smtp_socket); /* required by RFC1870 */
free(headers);
+ free_str_list(&xmit_names);
return(PS_REFUSED);
default: /* retry with invoking user's address */
{
error(0, -1, "SMTP error: %s", smtp_response);
free(headers);
+ free_str_list(&xmit_names);
return(PS_SMTP); /* should never happen */
}
}
{
error(0, 0, "can't even send to calling user!");
free(headers);
+ free_str_list(&xmit_names);
return(PS_SMTP);
}
}
pclose(sinkfp);
signal(SIGCHLD, sigchld);
}
+ free(headers);
+ free_str_list(&xmit_names);
return(PS_IOERR);
}
else if (outlevel == O_VERBOSE)
stuffline(ctl, errmsg);
}
- free_str_list(&xmit_names);
-
/* issue the delimiter line */
cp = buf;
*cp++ = '\r';
*cp++ = '\0';
stuffline(ctl, buf);
+ free(headers);
+ free_str_list(&xmit_names);
return(PS_SUCCESS);
}
if ((js = setjmp(restart)) == 1)
{
- error(0, 0,
- "timeout after %d seconds waiting for %s.",
- ctl->server.timeout, ctl->server.pollname);
+ if (phase == SERVER_WAIT)
+ error(0, 0,
+ "timeout after %d seconds waiting for server %s.",
+ ctl->server.timeout, ctl->server.pollname);
+ else if (phase == FORWARDING_WAIT)
+ error(0, 0,
+ "timeout after %d seconds waiting for %s.",
+ ctl->server.timeout,
+ ctl->mda ? "MDA" : "SMTP");
+ else
+ error(0, 0, "timeout after %d seconds.", ctl->server.timeout);
+
if (ctl->smtp_socket != -1)
close(ctl->smtp_socket);
if (sock != -1)
close(sock);
+ if (sinkfp)
+ pclose(sinkfp);
ok = PS_ERROR;
}
else
{
char buf [POPBUFSIZE+1], *realhost;
int *msgsizes, len, num, count, new, deletions = 0;
+#if INET6
+ int fetches, dispatches;
+#else /* INET6 */
int port, fetches, dispatches;
+#endif /* INET6 */
struct idlist *idp;
/* execute pre-initialization command, if any */
}
/* open a socket to the mail server */
+#if !INET6
port = ctl->server.port ? ctl->server.port : protocol->port;
+#endif /* !INET6 */
realhost = ctl->server.via ? ctl->server.via : ctl->server.pollname;
+#if INET6
+ if ((sock = SockOpen(realhost,
+ ctl->server.service ? ctl->server.service : protocol->service,
+ ctl->server.netsec)) == -1)
+#else /* INET6 */
if ((sock = SockOpen(realhost, port)) == -1)
+#endif /* INET6 */
{
+#if !INET6
#ifndef EHOSTUNREACH
#define EHOSTUNREACH (-1)
#endif
{
error_build("fetchmail: %s connection to %s failed: ",
protocol->name, ctl->server.pollname);
+#ifdef HAVE_RES_SEARCH
if (h_errno == HOST_NOT_FOUND)
error_complete(0, 0, "host is unknown");
else if (h_errno == NO_ADDRESS)
else if (h_errno)
error_complete(0, 0, "unknown DNS error %d", h_errno);
else
+#endif /* HAVE_RES_SEARCH */
error_complete(0, errno, "local error");
}
+#endif /* INET6 */
ok = PS_SOCKET;
goto closeUp;
}
* now.
*/
+ /* tell the UID code we've seen this */
+ if (ctl->newsaved)
+ for (idp = ctl->newsaved; idp; idp = idp->next)
+ if (idp->val.num == num)
+ MARK_SEEN(idp->val.num);
+
/* maybe we delete this message now? */
if (retained)
{
char *buf; /* buffer to receive input */
int size; /* length of buffer */
{
+ int oldphase = phase; /* we don't have to be re-entrant */
+
+ phase = SERVER_WAIT;
if (SockRead(sock, buf, size) == -1)
+ {
+ phase = oldphase;
return(PS_SOCKET);
+ }
else
{
if (buf[strlen(buf)-1] == '\n')
buf[strlen(buf)-1] = '\r';
if (outlevel == O_VERBOSE)
error(0, 0, "%s< %s", protocol->name, buf);
+ phase = oldphase;
return(PS_SUCCESS);
}
}
int ok;
char buf [POPBUFSIZE+1];
va_list ap;
+ int oldphase = phase; /* we don't have to be re-entrant */
+
+ phase = SERVER_WAIT;
if (protocol->tagged)
(void) sprintf(buf, "%s ", GENSYM);
ok = (protocol->parse_response)(sock, buf);
set_timeout(mytimeout);
+ phase = oldphase;
return(ok);
}