X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=driver.c;h=5d8e2a95fed41b530003dc4ebe312e7c9f88fa75;hb=c5b468066e7dcf28f94a65bb56189afb87ed48a4;hp=934ddacb078b6035e216d2dc25fbe64b40e0c3ef;hpb=10af4f205182e3acd6654d4997e2351e7860849a;p=~andy%2Ffetchmail diff --git a/driver.c b/driver.c index 934ddacb..5d8e2a95 100644 --- a/driver.c +++ b/driver.c @@ -11,6 +11,9 @@ #include #include #include +#ifdef HAVE_MEMORY_H +#include +#endif /* HAVE_MEMORY_H */ #if defined(STDC_HEADERS) #include #endif @@ -36,18 +39,20 @@ #include "mx.h" #endif /* HAVE_GETHOSTBYNAME */ -#ifdef SUNOS -#include -#endif - #ifdef KERBEROS_V4 #if defined (__bsdi__) #include /* order of includes matters */ #include #define krb_get_err_text(e) (krb_err_txt[e]) #else +#if defined(__FreeBSD__) +#define krb_get_err_text(e) (krb_err_txt[e]) #include #include +#else +#include +#include +#endif /* ! defined (__FreeBSD__) */ #endif /* ! defined (__bsdi__) */ #include #include @@ -78,6 +83,9 @@ static int tagnum; static char *shroud; /* string to shroud in debug output, if non-NULL */ static int mytimeout; /* value of nonreponse timeout */ +#ifdef MSGLEN +static int msglen; /* actual message length */ +#endif /* MSGLEN */ static void set_timeout(int timeleft) /* reset the nonresponse-timeout */ @@ -114,33 +122,25 @@ static int is_host_alias(const char *name, struct query *ctl) ctl->server.lead_server ? ctl->server.lead_server : &ctl->server; /* - * The first three checks are optimizations that will catch a good + * The first two checks are optimizations that will catch a good * many cases. * - * (1) check against the poll name the user specified. Odds are - * good this will either be the mailserver's FQDN or a suffix of + * (1) check against the `true name' deduced from the poll label + * and the via option (if present) at the beginning of the poll cycle. + * Odds are good this will either be the mailserver's FQDN or a suffix of * it with the mailserver's domain's default host name omitted. * - * (2) Check the `via' or true host name if present, just in case - * the poll name is a label for one of a couple of different - * configurations and the real server name is here. - * - * (3) Then check the rest of the `also known as' + * (2) Then check the rest of the `also known as' * cache accumulated by previous DNS checks. This cache is primed * by the aka list option. * - * (4) Finally check against the mailserver's FQDN, in case - * it's not the same as the declared hostname. - * * Any of these on a mail address is definitive. Only if the * 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 (str_in_list(&lead_server->names, name)) - return(TRUE); - else if (ctl->server.via && strcmp(name, ctl->server.via) == 0) + if (strcmp(lead_server->truename, name) == 0) return(TRUE); - else if (strcmp(name, ctl->server.canonical_name) == 0) + else if (str_in_list(&lead_server->akalist, name)) return(TRUE); else if (!ctl->server.dns) return(FALSE); @@ -153,7 +153,7 @@ static int is_host_alias(const char *name, struct query *ctl) */ else if ((he = gethostbyname(name)) != (struct hostent *)NULL) { - if (strcmp(ctl->server.canonical_name, he->h_name) == 0) + if (strcmp(ctl->server.truename, he->h_name) == 0) goto match; else return(FALSE); @@ -172,7 +172,7 @@ static int is_host_alias(const char *name, struct query *ctl) putchar('\n'); /* terminate the progress message */ error(0, 0, "nameserver failure while looking for `%s' during poll of %s.", - name, ctl->server.names->id); + name, ctl->server.pollname); ctl->errcount++; break; } @@ -197,7 +197,7 @@ static int is_host_alias(const char *name, struct query *ctl) default: error(0, 0, "nameserver failure while looking for `%s' during poll of %s.", - name, ctl->server.names->id); + name, ctl->server.pollname); ctl->errcount++; break; } @@ -205,14 +205,14 @@ static int is_host_alias(const char *name, struct query *ctl) else { for (mxp = mxrecords; mxp->name; mxp++) - if (strcmp(ctl->server.canonical_name, mxp->name) == 0) + if (strcmp(ctl->server.truename, mxp->name) == 0) goto match; return(FALSE); match:; } /* add this name to relevant server's `also known as' list */ - save_str(&lead_server->names, -1, name); + save_str(&lead_server->akalist, -1, name); return(TRUE); } @@ -300,10 +300,10 @@ struct idlist **xmit_names; /* list of recipient names parsed out */ } } -char *parse_received(struct query *ctl, char *bufp) +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]; /* @@ -314,9 +314,7 @@ char *parse_received(struct query *ctl, char *bufp) * 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; @@ -333,30 +331,25 @@ char *parse_received(struct query *ctl, char *bufp) * 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 && *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) @@ -370,11 +363,9 @@ char *parse_received(struct query *ctl, char *bufp) } #endif /* HAVE_RES_SEARCH */ -int smtp_open(struct query *ctl) +static int smtp_open(struct query *ctl) /* try to open a socket to the appropriate SMTP server for this query */ { - struct idlist *idp; - /* maybe it's time to close the socket in order to force delivery */ if (ctl->batchlimit > 0 && (ctl->smtp_socket != -1) && batchcount++ == ctl->batchlimit) { @@ -383,8 +374,8 @@ int smtp_open(struct query *ctl) batchcount = 0; } - /* run down the SMTP hunt list looking for a server that's up */ - for (idp = ctl->smtphunt; idp; idp = idp->next) + /* if no socket to any SMTP host is already set up, try to open one */ + if (ctl->smtp_socket == -1) { /* * RFC 1123 requires that the domain name in HELO address is a @@ -395,112 +386,126 @@ int smtp_open(struct query *ctl) * 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). + * + * How we compute the true mailhost name to pass to the + * listener doesn't affect behavior on RFC1123- violating + * listener that check for name match; we're going to lose + * on those anyway because we can never give them a name + * that matches the local machine fetchmail is running on. + * What it will affect is the listener's logging. */ + struct idlist *idp; - /* if no socket to this host is already set up, try to open ESMTP */ - if (ctl->smtp_socket == -1) + /* run down the SMTP hunt list looking for a server that's up */ + for (idp = ctl->smtphunt; idp; idp = idp->next) { -#ifndef HAVE_RES_SEARCH - char *fakename; -#endif /* HAVE_RES_SEARCH */ + ctl->smtphost = idp->id; /* remember last host tried. */ if ((ctl->smtp_socket = SockOpen(idp->id,SMTP_PORT)) == -1) continue; -#ifndef HAVE_RES_SEARCH + if (SMTP_ok(ctl->smtp_socket) == SM_OK && + SMTP_ehlo(ctl->smtp_socket, + ctl->server.truename, + &ctl->server.esmtp_options) == SM_OK) + break; /* success */ + /* - * How we compute the fake client name to pass to the - * listener doesn't affect behavior on RFC1123- violating - * listener that check for name match; we're going to lose - * on those anyway because we can never give them a name - * that matches the local machine fetchmail is running on. - * What it will affect is the listener's logging. - * - * If we have the mailserver's canonical FQDN that is clearly - * the right thing to log. If we don't life is more complicated. - * The problem is there are two clashing cases: - * - * (1) The poll name is a label. In that case we want the - * log to show the via or true mailserver name. - * - * (2) The poll name is the true one, the via name is localhost. - * This is going to be typical for ssh-using configurations. - * - * We're going to assume the via name is true unless it's - * localhost. + * 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. */ - if (ctrl->server.via && strcmp(ctrl->server.via, "localhost")) - fakename = ctrl->server.via; - else - fakename = ctrl->server->names.id; -#endif /* HAVE_RES_SEARCH */ - - if (SMTP_ok(ctl->smtp_socket) != SM_OK - || SMTP_ehlo(ctl->smtp_socket, -#ifdef HAVE_RES_SEARCH - ctl->server.canonical_name, -#else - fakename, -#endif /* HAVE_RES_SEARCH */ - &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. - */ - close(ctl->smtp_socket); - ctl->smtp_socket = -1; - } - else - { - ctl->smtphost = idp->id; - break; - } - } + close(ctl->smtp_socket); + ctl->smtp_socket = -1; - /* if opening for ESMTP failed, try SMTP */ - if (ctl->smtp_socket == -1) - { + /* if opening for ESMTP failed, try SMTP */ if ((ctl->smtp_socket = SockOpen(idp->id,SMTP_PORT)) == -1) continue; - else if (SMTP_ok(ctl->smtp_socket) != SM_OK - || SMTP_helo(ctl->smtp_socket, -#ifdef HAVE_RES_SEARCH - ctl->server.canonical_name -#else - fakename -#endif /* HAVE_RES_SEARCH */ - ) != SM_OK) - { - close(ctl->smtp_socket); - ctl->smtp_socket = -1; - } - else - { - ctl->smtphost = idp->id; - break; - } + + if (SMTP_ok(ctl->smtp_socket) == SM_OK && + SMTP_helo(ctl->smtp_socket, ctl->server.truename) == SM_OK) + break; /* success */ + + close(ctl->smtp_socket); + ctl->smtp_socket = -1; } } + if (outlevel >= O_VERBOSE && ctl->smtp_socket != -1) + error(0, 0, "forwarding to SMTP port on %s", ctl->smtphost); + return(ctl->smtp_socket); } -/* these are shared by readheaders and readbody */ +/* these are shared by stuffline, readheaders and readbody */ static FILE *sinkfp; static RETSIGTYPE (*sigchld)(); static int sizeticker; -static int readheaders(sock, len, ctl, realname) +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; + + /* fix message lines that have only \n termination (for qmail) */ + if (ctl->forcecr) + { + char *cp = buf + strlen(buf) - 1; + + if (*cp == '\n' && (cp == buf || cp[-1] != '\r')) + { + *cp++ = '\r'; + *cp++ = '\n'; + *cp++ = '\0'; + } + } + + /* + * SMTP byte-stuffing. We only do this if the protocol does *not* + * use . 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); + + /* we may need to strip carriage returns */ + if (ctl->stripcr) + { + char *sp, *tp; + + for (sp = tp = buf; *sp; sp++) + if (*sp != '\r') + *tp++ = *sp; + *tp = '\0'; + } + + n = 0; + if (ctl->mda) + n = fwrite(buf, 1, strlen(buf), sinkfp); + else if (ctl->smtp_socket != -1) + n = SockWrite(ctl->smtp_socket, buf, strlen(buf)); + + return(n); +} + +static int readheaders(sock, len, ctl, realname, num) /* read message headers and ship to SMTP or MDA */ int sock; /* to which the server is connected */ long len; /* length of message */ struct query *ctl; /* query control record */ char *realname; /* real name of host */ +int num; /* index of message */ { + struct addrblk + { + int offset; + struct addrblk *next; + } *addrchain = NULL, **chainptr = &addrchain; char buf[MSGBUFSIZE+1], return_path[MSGBUFSIZE+1]; - int from_offs, ctt_offs, env_offs, addressoffs[128], next_address; - char *headers, *received_for; + int from_offs, ctt_offs, env_offs, next_address; + char *headers, *received_for, *desthost; int n, linelen, oldlen, ch, remaining; char *cp; struct idlist *idp, *xmit_names; @@ -519,7 +524,10 @@ char *realname; /* real name of host */ headers = received_for = NULL; from_offs = ctt_offs = env_offs = -1; oldlen = 0; - for (remaining = len; remaining > 0; remaining -= linelen) +#ifdef MSGLEN + msglen = 0; +#endif /* MSGLEN */ + for (remaining = len; remaining > 0 || protocol->delimited; remaining -= linelen) { char *line; @@ -530,6 +538,9 @@ char *realname; /* real name of host */ if ((n = SockRead(sock, buf, sizeof(buf)-1)) == -1) return(PS_SOCKET); linelen += n; +#ifdef MSGLEN + msglen += n; +#endif /* MSGLEN */ /* lines may not be properly CRLF terminated; fix this for qmail */ if (ctl->forcecr) @@ -544,8 +555,9 @@ char *realname; /* real name of host */ } 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; @@ -574,6 +586,25 @@ char *realname; /* real name of host */ break; } + /* + * 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 != pop2) +#endif /* POP2_ENABLE */ + if (num == 1 && !strncasecmp(line, "X-IMAP:", 7)) + return(PS_RETAINED); + /* * This code prevents fetchmail from becoming an accessory after * the fact to upstream sendmails with the `E' option on. This @@ -597,6 +628,34 @@ char *realname; /* real name of host */ continue; } + /* + * If we see a Status line, it may have been inserted by an MUA + * on the mail host, or it may have been inserted by the server + * program after the headers in the transaction stream. This + * can actually hose some new-mail notifiers such as xbuffy, + * which assumes any Status line came from a *local* MDA and + * therefore indicates that the message has been seen. + * + * Some buggy POP servers (including at least the 3.3(20) + * version of the one distributed with IMAP) insert empty + * Status lines in the transaction stream; we'll chuck those + * unconditionally. Nonempty ones get chucked if the user + * turns on the dropstatus flag. + */ + if (!strncasecmp(line, "Status:", 7)) + { + char *cp; + + for (cp = line + 7; *cp && isspace(*cp); cp++) + continue; + + if (!*cp || ctl->dropstatus) + { + free(line); + continue; + } + } + /* * OK, this is messy. If we're forwarding by SMTP, it's the * SMTP-receiver's job (according to RFC821, page 22, section @@ -621,7 +680,7 @@ char *realname; /* real name of host */ } if (ctl->rewrite) - reply_hack(line, realname); + line = reply_hack(line, realname); if (!headers) { @@ -651,41 +710,47 @@ char *realname; /* real name of host */ from_offs = (line - headers); else if (from_offs == -1 && !strncasecmp("Apparently-From:", line, 16)) from_offs = (line - headers); - - else if (ctl->server.envelope != STRING_DISABLED && env_offs == -1 - && !strncasecmp(ctl->server.envelope, - line, - strlen(ctl->server.envelope))) - env_offs = (line - headers); - - else if (!strncasecmp("To:", line, 3)) + else if (!strncasecmp("Content-Transfer-Encoding:", line, 26)) + ctt_offs = (line - headers); + else if (!strncasecmp("Message-Id:", buf, 11 )) { - if (next_address >= sizeof(addressoffs)/sizeof(addressoffs[0])) - error(PS_UNDEFINED, errno, "too many destination headers"); - addressoffs[next_address++] = (line - headers); - } + if( ctl->server.uidl ) + { + char id[IDLEN+1]; + sscanf( buf+12, "%s", id); + if( !str_in_list( &ctl->newsaved, id ) ) + save_str(&ctl->newsaved, num, id ); + } + } + + else if (!MULTIDROP(ctl)) + continue; - else if (!strncasecmp("Cc:", line, 3)) + else if (!strncasecmp("To:", line, 3) + || !strncasecmp("Cc:", line, 3) + || !strncasecmp("Bcc:", line, 4)) { - if (next_address >= sizeof(addressoffs)/sizeof(addressoffs[0])) - error(PS_UNDEFINED, errno, "too many destination headers"); - addressoffs[next_address++] = (line - headers); + *chainptr = xmalloc(sizeof(struct addrblk)); + (*chainptr)->offset = (line - headers); + chainptr = &(*chainptr)->next; + *chainptr = NULL; } - else if (!strncasecmp("Bcc:", line, 4)) + else if (ctl->server.envelope != STRING_DISABLED) { - if (next_address >= sizeof(addressoffs)/sizeof(addressoffs[0])) - error(PS_UNDEFINED, errno, "too many destination headers"); - addressoffs[next_address++] = (line - headers); - } - - else if (!strncasecmp("Content-Transfer-Encoding:", line, 26)) - ctt_offs = (line - headers); - + if (ctl->server.envelope + && strcasecmp(ctl->server.envelope, "received")) + { + if (env_offs == -1 && !strncasecmp(ctl->server.envelope, + line, + strlen(ctl->server.envelope))) + env_offs = (line - headers); + } #ifdef HAVE_RES_SEARCH - else if (MULTIDROP(ctl) && !received_for && !strncasecmp("Received:", line, 9)) - received_for = parse_received(ctl, line); + else if (!received_for && !strncasecmp("Received:", line, 9)) + received_for = parse_received(ctl, line); #endif /* HAVE_RES_SEARCH */ + } } /* @@ -734,8 +799,15 @@ char *realname; /* real name of host */ * We haven't extracted the envelope address. * So check all the header addresses. */ - for (i = 0; i < next_address; i++) - find_server_names(headers + addressoffs[i], ctl, &xmit_names); + while (addrchain) + { + register struct addrblk *nextptr; + + find_server_names(headers+addrchain->offset, ctl, &xmit_names); + nextptr = addrchain->next; + free(addrchain); + addrchain = nextptr; + } } if (!accept_count) { @@ -767,13 +839,18 @@ char *realname; /* real name of host */ int length = 0; char *names, *cmd; + desthost = "localhost"; + /* * 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); + good_addresses++; + } names = (char *)alloca(length); names[0] = '\0'; for (idp = xmit_names; idp; idp = idp->next) @@ -814,13 +891,13 @@ char *realname; /* real name of host */ } else { - char *ap, *ctt, options[MSGBUFSIZE]; + 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, -1, "SMTP connect to %s failed", + error(0, errno, "SMTP connect to %s failed", ctl->smtphost ? ctl->smtphost : "localhost"); return(PS_SMTP); } @@ -936,14 +1013,23 @@ char *realname; /* real name of host */ * * RFC 1123 requires that the domain name part of the * RCPT TO address be "canonicalized", that is a FQDN - * or MX but not a CNAME. RFC1123 doesn't say whether - * the FQDN part can be null (as it frequently will be - * here), but it's hard to see how this could cause a - * problem. + * or MX but not a CNAME. Some listeners (like exim) + * enforce this. */ + desthost = ctl->smtphost ? ctl->smtphost : "localhost"; for (idp = xmit_names; idp; idp = idp->next) if (idp->val.num == XMIT_ACCEPT) - if (SMTP_rcpt(ctl->smtp_socket, idp->id) == SM_OK) + { + if (strchr(idp->id, '@')) + strcpy(addr, idp->id); + else +#ifdef HAVE_SNPRINTF + snprintf(addr, sizeof(addr)-1, "%s@%s", idp->id, desthost); +#else + sprintf(addr, "%s@%s", idp->id, desthost); +#endif /* HAVE_SNPRINTF */ + + if (SMTP_rcpt(ctl->smtp_socket, addr) == SM_OK) good_addresses++; else { @@ -952,39 +1038,62 @@ char *realname; /* real name of host */ error(0, 0, "SMTP listener doesn't like recipient address `%s'", idp->id); } - if (!good_addresses && SMTP_rcpt(ctl->smtp_socket, user) != SM_OK) + } + if (!good_addresses) { - error(0, 0, - "can't even send to calling user!"); - free(headers); - return(PS_SMTP); +#ifdef HAVE_SNPRINTF + snprintf(addr, sizeof(addr)-1, "%s@%s", user, desthost); +#else + sprintf(addr, "%s@%s", user, desthost); +#endif /* HAVE_SNPRINTF */ + + if (SMTP_rcpt(ctl->smtp_socket, addr) != SM_OK) + { + error(0, 0, "can't even send to calling user!"); + free(headers); + return(PS_SMTP); + } } /* tell it we're ready to send data */ SMTP_data(ctl->smtp_socket); } - /* we may need to strip carriage returns */ - if (ctl->stripcr) + /* utter any per-message Received information we need here */ { - char *sp, *tp; - - for (sp = tp = headers; *sp; sp++) - if (*sp != '\r') - *tp++ = *sp; - *tp = '\0'; - + time_t now; + + /* write a line describing fetchmail's processing of the message */ + sprintf(buf, + "Received: from %s\r\n\tby %s (fetchmail-%s %s run by %s)\r\n", + ctl->server.truename, + fetchmailhost, + RELEASE_ID, + protocol->name, + ctl->remotename); + + if (!good_addresses) + sprintf(buf + strlen(buf), + "\tfor <%s@%s> (by default); ", + user, desthost); + if (good_addresses == 1) + { + for (idp = xmit_names; idp; idp = idp->next) + if (idp->val.num == XMIT_ACCEPT) + { + sprintf(buf + strlen(buf), "\tfor <%s@%s> (%s); ", + idp->id, desthost, + MULTIDROP(ctl) ? "multi-drop" : "single-drop"); + break; /* only report first address */ + } + } + time(&now); + strcat(buf, ctime(&now)); + strcpy(buf + strlen(buf) - 1, "\r\n"); } - /* 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 < 0) + /* ship out the synthetic Received line and the headers */ + if (stuffline(ctl, buf) < 0 || stuffline(ctl, headers) < 0) { error(0, errno, "writing RFC822 headers"); if (ctl->mda) @@ -1053,64 +1162,47 @@ char *realname; /* real name of host */ } - if (ctl->mda && !ctl->forcecr) - strcat(errmsg, "\n"); - else - strcat(errmsg, "\r\n"); - - /* we may need to strip carriage returns */ - if (ctl->stripcr) - { - char *sp, *tp; - - for (sp = tp = errmsg; *sp; sp++) - if (*sp != '\r') - *tp++ = *sp; - *tp = '\0'; - } + strcat(errmsg, "\n"); /* ship out the error line */ if (sinkfp) - { - if (ctl->mda) - fwrite(errmsg, sizeof(char), strlen(errmsg), sinkfp); - else - SockWrite(ctl->smtp_socket, errmsg, strlen(errmsg)); - } + stuffline(ctl, errmsg); } free_str_list(&xmit_names); /* issue the delimiter line */ - if (sinkfp && ctl->mda) - fputc('\n', sinkfp); - else if (ctl->smtp_socket != -1) - { - if (ctl->stripcr) - SockWrite(ctl->smtp_socket, "\n", 1); - else - SockWrite(ctl->smtp_socket, "\r\n", 2); - } + cp = buf; + *cp++ = '\r'; + *cp++ = '\n'; + *cp++ = '\0'; + stuffline(ctl, buf); return(PS_SUCCESS); } -static int readbody(sock, ctl, forward, len, delimited) +static int readbody(sock, ctl, forward, len) /* read and dispose of a message body presented on sock */ struct query *ctl; /* query control record */ int sock; /* to which the server is connected */ int len; /* length of message */ flag forward; /* TRUE to forward */ -flag delimited; /* does the protocol use a message delimiter? */ { int linelen; char buf[MSGBUFSIZE+1], *cp; /* pass through the text lines */ - while (delimited || len > 0) + while (protocol->delimited || len > 0) { if ((linelen = SockRead(sock, buf, sizeof(buf)-1)) == -1) + { + if (ctl->mda) + { + pclose(sinkfp); + signal(SIGCHLD, sigchld); + } return(PS_SOCKET); + } set_timeout(ctl->server.timeout); /* write the message size dots */ @@ -1125,56 +1217,21 @@ flag delimited; /* does the protocol use a message delimiter? */ } } len -= linelen; - - /* fix messages that have only \n line-termination (for qmail) */ - if (ctl->forcecr) - { - cp = buf + strlen(buf) - 1; - if (*cp == '\n' && (cp == buf || cp[-1] != '\r')) - { - *cp++ = '\r'; - *cp++ = '\n'; - *cp++ = '\0'; - } - } +#ifdef MSGLEN + msglen += linelen; +#endif /* MSGLEN */ /* check for end of message */ - if (delimited && *buf == '.') - if (buf[1] == '\r' && buf[2] == '\n') + if (protocol->delimited && *buf == '.') + if (buf[1] == '\r' && buf[2] == '\n' && buf[3] == '\0') + break; + else if (buf[1] == '\n' && buf[2] == '\0') break; /* ship out the text line */ if (forward) { - int n; - - /* - * SMTP byte-stuffing. We only do this if the protocol does *not* - * use . as EOM. If it does, the server will already have - * decorated any . lines it sends back up. - */ - if (!delimited && *buf == '.') - if (sinkfp && ctl->mda) - fputs(".", sinkfp); - else if (ctl->smtp_socket != -1) - SockWrite(ctl->smtp_socket, buf, 1); - - /* we may need to strip carriage returns */ - if (ctl->stripcr) - { - char *sp, *tp; - - for (sp = tp = buf; *sp; sp++) - if (*sp != '\r') - *tp++ = *sp; - *tp = '\0'; - } - - n = 0; - if (ctl->mda) - n = fwrite(buf, 1, strlen(buf), sinkfp); - else if (ctl->smtp_socket != -1) - n = SockWrite(ctl->smtp_socket, buf, strlen(buf)); + int n = stuffline(ctl, buf); if (n < 0) { @@ -1199,7 +1256,11 @@ int kerberos_auth (socket, canonical) /* authenticate to the server host using Kerberos V4 */ int socket; /* socket to server host */ +#ifdef __FreeBSD__ +char *canonical; /* server name */ +#else const char *canonical; /* server name */ +#endif { char * host_primary; KTEXT ticket; @@ -1285,7 +1346,7 @@ const struct method *proto; /* protocol method table */ { error(0, 0, "timeout after %d seconds waiting for %s.", - ctl->server.timeout, ctl->server.names->id); + ctl->server.timeout, ctl->server.pollname); if (ctl->smtp_socket != -1) close(ctl->smtp_socket); if (sock != -1) @@ -1310,7 +1371,7 @@ const struct method *proto; /* protocol method table */ /* open a socket to the mail server */ port = ctl->server.port ? ctl->server.port : protocol->port; - realhost = ctl->server.via ? ctl->server.via : ctl->server.names->id; + realhost = ctl->server.via ? ctl->server.via : ctl->server.pollname; if ((sock = SockOpen(realhost, port)) == -1) { #ifndef EHOSTUNREACH @@ -1319,7 +1380,7 @@ const struct method *proto; /* protocol method table */ if (outlevel == O_VERBOSE || errno != EHOSTUNREACH) { error_build("fetchmail: %s connection to %s failed: ", - protocol->name, ctl->server.names->id); + protocol->name, ctl->server.pollname); if (h_errno == HOST_NOT_FOUND) error_complete(0, 0, "host is unknown"); else if (h_errno == NO_ADDRESS) @@ -1340,7 +1401,7 @@ const struct method *proto; /* protocol method table */ #ifdef KERBEROS_V4 if (ctl->server.preauthenticate == A_KERBEROS_V4) { - ok = kerberos_auth(sock, ctl->server.canonical_name); + ok = kerberos_auth(sock, ctl->server.truename); if (ok != 0) goto cleanUp; set_timeout(ctl->server.timeout); @@ -1427,7 +1488,7 @@ const struct method *proto; /* protocol method table */ *tp = '\0'; } else - strcpy(realname, ctl->server.names->id); + strcpy(realname, ctl->server.pollname); /* try to get authorized to fetch mail */ if (protocol->getauth) @@ -1479,9 +1540,13 @@ const struct method *proto; /* protocol method table */ (void) sprintf(buf, "%s@%s", ctl->remotename, realname); if (outlevel > O_SILENT) if (count == -1) /* only used for ETRN */ - error(0, 0, "Polling %s", buf); + error(0, 0, "Polling %s", realname); else if (count == 0) - error(0, 0, "No mail at %s", buf); + { + /* these are pointless in normal daemon mode */ + if (poll_interval == 0 || outlevel == O_VERBOSE ) + error(0, 0, "No mail at %s", buf); + } else { if (new != -1 && (count - new) > 0) @@ -1528,12 +1593,12 @@ const struct method *proto; /* protocol method table */ force_retrieval = !peek_capable && (ctl->errcount > 0); /* - * We may need to get sizes in order to check message - * limits. Or it may be forced because the fetch methods - * don't return reliable sizes. + * 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. */ - msgsizes = (int *)NULL; - if (proto->getsizes && (proto->force_getsizes || ctl->limit > 0)) + if (proto->getsizes) { msgsizes = (int *)alloca(sizeof(int) * count); @@ -1552,6 +1617,24 @@ const struct method *proto; /* protocol method table */ && (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 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) + { + 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) @@ -1597,9 +1680,11 @@ const struct method *proto; /* protocol method table */ * Read the message headers and ship them to the * output sink. */ - ok = readheaders(sock, len, ctl, realname); - if (ok == PS_TRANSIENT) - suppress_delete = TRUE; + 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) @@ -1624,7 +1709,7 @@ const struct method *proto; /* protocol method table */ { if ((ok=(protocol->fetch_body)(sock,ctl,num,&len))) goto cleanUp; - if (outlevel > O_SILENT && !msgsizes) + if (outlevel > O_SILENT && !wholesize) error_build(" (%d body bytes) ", len); set_timeout(ctl->server.timeout); } @@ -1636,10 +1721,9 @@ const struct method *proto; /* protocol method table */ ok = readbody(sock, ctl, !suppress_forward, - len, - protocol->delimited); + len); if (ok == PS_TRANSIENT) - suppress_delete = TRUE; + suppress_delete = suppress_forward = TRUE; else if (ok) goto cleanUp; set_timeout(ctl->server.timeout); @@ -1654,6 +1738,12 @@ const struct method *proto; /* protocol method table */ } } +#ifdef MSGLEN + /* check to see if the numbers matched? */ + if (msgsizes && msglen != msgsizes[num-1]) + error(0, 0, "size of message %d (%d) was not what was expected (%d)", num, msglen, msgsizes[num-1]); +#endif /* MSGLEN */ + /* end-of-message processing starts here */ if (outlevel == O_VERBOSE) fputc('\n', stderr); @@ -1695,7 +1785,12 @@ const struct method *proto; /* protocol method table */ */ /* maybe we delete this message now? */ - if (protocol->delete + if (retained) + { + if (outlevel > O_SILENT) + error_complete(0, 0, " retained"); + } + else if (protocol->delete && !suppress_delete && (fetch_it ? !ctl->keep : ctl->flush)) { @@ -1762,6 +1857,9 @@ const struct method *proto; /* protocol method table */ case PS_SMTP: msg = "SMTP transaction"; break; + case PS_DNS: + msg = "DNS lookup"; + break; case PS_UNDEFINED: error(0, 0, "undefined"); break; @@ -1769,9 +1867,20 @@ const struct method *proto; /* protocol method table */ if (ok==PS_SOCKET || ok==PS_AUTHFAIL || ok==PS_SYNTAX || ok==PS_IOERR || ok==PS_ERROR || ok==PS_PROTOCOL || ok==PS_LOCKBUSY || ok==PS_SMTP) - error(0, -1, "%s error while fetching from %s", msg, ctl->server.names->id); + error(0,-1, "%s error while fetching from %s", msg, ctl->server.pollname); closeUp: + /* execute post-initialization command, if any */ + if (ctl->postconnect && (ok = system(ctl->postconnect))) + { + char buf[80]; + + sprintf(buf, "post-connection command failed with status %d", ok); + error(0, 0, buf); + if (ok == PS_SUCCESS) + ok = PS_SYNTAX; + } + signal(SIGALRM, sigsave); return(ok); }