#include <sys/time.h>
#include <signal.h>
+#ifdef HAVE_NET_SOCKET_H
+#include <net/socket.h>
+#endif
+
#ifdef HAVE_RES_SEARCH
#include <netdb.h>
#include "mx.h"
int pass; /* how many times have we re-polled? */
int stage; /* where are we? */
int phase; /* where are we, for error-logging purposes? */
+int mytimeout; /* value of nonreponse timeout */
+int suppress_tags; /* emit tags? */
static const struct method *protocol;
static jmp_buf restart;
#define GENSYM (sprintf(tag, "A%04d", ++tagnum % TAGMOD), tag)
static char shroud[PASSWORDLEN]; /* string to shroud in debug output */
-static int mytimeout; /* value of nonreponse timeout */
static int timeoutcount; /* count consecutive timeouts */
static int msglen; /* actual message length */
void set_timeout(int timeleft)
/* reset the nonresponse-timeout */
{
-#ifndef __EMX__
+#if !defined(__EMX__) && !defined(__BEOS__)
struct itimerval ntimeout;
if (timeleft == 0)
int from_offs, reply_to_offs, resent_from_offs;
int app_from_offs, sender_offs, resent_sender_offs;
int env_offs;
- char *received_for, *rcv, *cp;
+ char *received_for, *rcv, *cp, *delivered_to;
int n, linelen, oldlen, ch, remaining, skipcount;
struct idlist *idp;
flag no_local_matches = FALSE;
/* read message headers */
msgblk.reallen = reallen;
- msgblk.headers = received_for = NULL;
+
+ /*
+ * We used to free the header block unconditionally at the end of
+ * readheaders, but it turns out that if close_sink() hits an error
+ * condition the code for sending bouncemail will actually look
+ * at the freed storage and coredump...
+ */
+ if (msgblk.headers)
+ free(msgblk.headers);
+
+ msgblk.headers = received_for = delivered_to = NULL;
from_offs = reply_to_offs = resent_from_offs = app_from_offs =
sender_offs = resent_sender_offs = env_offs = -1;
oldlen = 0;
set_timeout(0);
free(line);
free(msgblk.headers);
+ msgblk.headers = NULL;
return(PS_SOCKET);
}
set_timeout(0);
goto process_headers;
}
+ /*
+ * At least one brain-dead website (netmind.com) is known to
+ * send out robotmail that's missing the RFC822 delimiter blank
+ * line before the body! Without this check fetchmail segfaults.
+ * With it, we treat such messages as though they had the missing
+ * blank line.
+ */
+ if (!isspace(line[0]) && !strchr(line, ':'))
+ {
+ headers_ok = TRUE;
+ free(line);
+ has_nuls = (linelen != strlen(line));
+ goto process_headers;
+ }
+
/* check for RFC822 continuations */
set_timeout(mytimeout);
ch = SockPeek(sock);
sizeticker += linelen;
while (sizeticker >= SIZETICKER)
{
- if (!run.use_syslog && isatty(1))
+ if ((!run.use_syslog && !isafile(1)) || run.showdots)
{
fputc('.', stdout);
fflush(stdout);
* addressed to multiple people on the client machine, there
* will be one copy left in the box for each recipient. Thus,
* if the mail is addressed to N people, each recipient will
- * get N copies.
+ * get N copies. This is bad when N > 1.
*
* Foil this by suppressing all but one copy of a message with
- * a given Message-ID. Note: This implementation only catches
- * runs of successive identical messages, but that should be
- * good enough.
+ * a given Message-ID. The accept_count test ensures that
+ * multiple pieces of email with the same Message-ID, each
+ * with a *single* addressee (the N == 1 case), won't be
+ * suppressed.
+ *
+ * Note: This implementation only catches runs of successive
+ * messages with the same ID, but that should be good
+ * enough. A more general implementation would have to store
+ * ever-growing lists of seen message-IDs; in a long-running
+ * daemon this would turn into a memory leak even if the
+ * implementation were perfect.
*
- * The accept_count test ensures that multiple pieces of identical
- * email, each with a *single* addressee, won't be suppressed.
+ * Don't mess with this code casually. It would be way too easy
+ * to break it in a way that blackholed mail. Better to pass
+ * the occasional duplicate than to do that...
*/
- if (MULTIDROP(ctl) && accept_count > 1 && !strncasecmp(line, "Message-ID:", 11))
+ if (MULTIDROP(ctl) && !strncasecmp(line, "Message-ID:", 11))
{
if (ctl->lastid && !strcasecmp(ctl->lastid, line))
- return(PS_REFUSED);
+ {
+ if (accept_count > 1)
+ return(PS_REFUSED);
+ }
else
{
if (ctl->lastid)
if (num == 1 && !strncasecmp(line, "X-IMAP:", 7)) {
free(line);
free(msgblk.headers);
+ msgblk.headers = NULL;
return(PS_RETAINED);
}
continue;
}
+ /*
+ * We remove all Delivered-To: headers.
+ *
+ * This is to avoid false mail loops messages when delivering
+ * local messages to and from a Postfix/qmail mailserver.
+ */
+ if (ctl->dropdelivered && !strncasecmp(line, "Delivered-To:", 13))
+ {
+ if (delivered_to)
+ free(line);
+ else
+ delivered_to = line;
+ 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
}
else
{
+ char *newhdrs;
int newlen;
newlen = oldlen + strlen(line);
- msgblk.headers = (char *) realloc(msgblk.headers, newlen + 1);
- if (msgblk.headers == NULL) {
+ newhdrs = (char *) realloc(msgblk.headers, newlen + 1);
+ if (newhdrs == NULL) {
free(line);
return(PS_IOERR);
}
+ msgblk.headers = newhdrs;
strcpy(msgblk.headers + oldlen, line);
free(line);
line = msgblk.headers + oldlen;
resent_sender_offs = (line - msgblk.headers);
#ifdef __UNUSED__
- else if (!strncasecmp("Message-Id:", buf, 11))
+ else if (!strncasecmp("Message-Id:", line, 11))
{
if (ctl->server.uidl)
{
char id[IDLEN+1];
- buf[IDLEN+12] = 0; /* prevent stack overflow */
- sscanf(buf+12, "%s", id);
+ line[IDLEN+12] = 0; /* prevent stack overflow */
+ sscanf(line+12, "%s", id);
if (!str_find( &ctl->newsaved, num))
{
struct idlist *new = save_str(&ctl->newsaved,id,UID_SEEN);
#endif /* SDPS_ENABLE */
if (env_offs > -1) /* We have the actual envelope addressee */
find_server_names(msgblk.headers + env_offs, ctl, &msgblk.recipients);
+ else if (delivered_to && ctl->server.envelope != STRING_DISABLED &&
+ ctl->server.envelope && !strcasecmp(ctl->server.envelope, "Delivered-To"))
+ {
+ find_server_names(delivered_to, ctl, &msgblk.recipients);
+ free(delivered_to);
+ }
else if (received_for)
/*
* We have the Received for addressee.
report(stdout,
_("forwarding and deletion suppressed due to DNS errors\n"));
free(msgblk.headers);
+ msgblk.headers = NULL;
free_str_list(&msgblk.recipients);
return(PS_TRANSIENT);
}
&good_addresses, &bad_addresses)) != PS_SUCCESS)
{
free(msgblk.headers);
+ msgblk.headers = NULL;
free_str_list(&msgblk.recipients);
return(n);
}
if (!run.invisible && n != -1)
{
/* utter any per-message Received information we need here */
- sprintf(buf, "Received: from %s\r\n", ctl->server.truename);
+ if (ctl->server.trueaddr) {
+ sprintf(buf, "Received: from %s [%u.%u.%u.%u]\r\n",
+ ctl->server.truename,
+ (unsigned char)ctl->server.trueaddr[0],
+ (unsigned char)ctl->server.trueaddr[1],
+ (unsigned char)ctl->server.trueaddr[2],
+ (unsigned char)ctl->server.trueaddr[3]);
+ } else {
+ sprintf(buf, "Received: from %s\r\n", ctl->server.truename);
+ }
n = stuffline(ctl, buf);
if (n != -1)
{
report(stdout, _("writing RFC822 msgblk.headers\n"));
release_sink(ctl);
free(msgblk.headers);
+ msgblk.headers = NULL;
free_str_list(&msgblk.recipients);
return(PS_IOERR);
}
- else if ((run.poll_interval == 0 || nodetach) && outlevel >= O_VERBOSE && isatty(2))
+ else if ((run.poll_interval == 0 || nodetach) && outlevel >= O_VERBOSE && !isafile(2))
fputs("#", stderr);
/* write error notifications */
*cp++ = '\0';
stuffline(ctl, buf);
- free(msgblk.headers);
+/* free(msgblk.headers); */
free_str_list(&msgblk.recipients);
return(headers_ok ? PS_SUCCESS : PS_TRUNCATED);
}
sizeticker += linelen;
while (sizeticker >= SIZETICKER)
{
- if ((run.poll_interval == 0 || nodetach) && outlevel > O_SILENT && isatty(1))
+ if (outlevel > O_SILENT && (((run.poll_interval == 0 || nodetach) && !isafile(1)) || run.showdots))
{
fputc('.', stdout);
fflush(stdout);
release_sink(ctl);
return(PS_IOERR);
}
- else if (outlevel >= O_VERBOSE && isatty(1))
+ else if (outlevel >= O_VERBOSE && !isafile(1))
{
fputc('*', stdout);
fflush(stdout);
#ifdef KERBEROS_V4
int
-kerberos_auth (socket, canonical)
+kerberos_auth (socket, canonical, principal)
/* authenticate to the server host using Kerberos V4 */
int socket; /* socket to server host */
-#if defined(__FreeBSD__) || defined(__OpenBSD__)
char *canonical; /* server name */
-#else
-const char *canonical; /* server name */
-#endif
+char *principal;
{
char * host_primary;
KTEXT ticket;
CREDENTIALS cred;
Key_schedule schedule;
int rem;
+ char * prin_copy = (char *) NULL;
+ char * prin = (char *) NULL;
+ char * inst = (char *) NULL;
+ char * realm = (char *) NULL;
+
+ if (principal != (char *)NULL && *principal)
+ {
+ char *cp;
+ prin = prin_copy = xstrdup(principal);
+ for (cp = prin_copy; *cp && *cp != '.'; ++cp)
+ ;
+ if (*cp)
+ {
+ *cp++ = '\0';
+ inst = cp;
+ while (*cp && *cp != '@')
+ ++cp;
+ if (*cp)
+ {
+ *cp++ = '\0';
+ realm = cp;
+ }
+ }
+ }
xalloca(ticket, KTEXT, sizeof (KTEXT_ST));
- rem = (krb_sendauth (0L, socket, ticket, "pop",
- canonical,
- ((char *) (krb_realmofhost (canonical))),
+ rem = (krb_sendauth (0L, socket, ticket,
+ prin ? prin : "pop",
+ inst ? inst : canonical,
+ realm ? realm : ((char *) (krb_realmofhost (canonical))),
((unsigned long) 0),
(&msg_data),
(&cred),
((struct sockaddr_in *) 0),
((struct sockaddr_in *) 0),
"KPOPV0.1"));
+ if (prin_copy)
+ {
+ free(prin_copy);
+ }
if (rem != KSUCCESS)
{
report(stderr, _("kerberos error %s\n"), (krb_get_err_text (rem)));
int msg_to_send = FALSE;
struct idlist *head=NULL, *current=NULL;
int max_warning_poll_count;
-#define OVERHD "Subject: Fetchmail oversized-messages warning.\r\n\r\nThe following oversized messages remain on the mail server %s:"
head = ctl->skipped;
if (!head)
*/
if (open_warning_by_mail(ctl, (struct msgblk *)NULL))
return;
- stuff_warning(ctl, OVERHD, ctl->server.pollname);
+ stuff_warning(ctl,
+ _("Subject: Fetchmail oversized-messages warning.\r\n"
+ "\r\n"
+ "The following oversized messages remain on the mail server %s:"),
+ ctl->server.pollname);
if (run.poll_interval == 0)
max_warning_poll_count = 0;
}
close_warning_by_mail(ctl, (struct msgblk *)NULL);
-#undef OVERHD
}
static int do_session(ctl, proto, maxfetch)
if (js == THROW_SIGPIPE)
{
+ signal(SIGPIPE, SIG_IGN);
report(stdout,
_("SIGPIPE thrown from an MDA or a stream socket error\n"));
ok = PS_SOCKET;
/*
* If we've exceeded our threshold for consecutive timeouts,
* try to notify the user, then mark the connection wedged.
+ * Don't do this if the connection can idle, though; idle
+ * timeouts just mean the frequency of mail is low.
*/
if (timeoutcount > MAX_TIMEOUTS
&& !open_warning_by_mail(ctl, (struct msgblk *)NULL))
(void)sleep(1);
#if INET6_ENABLE
if ((mailserver_socket = SockOpen(realhost,
- ctl->server.service ? ctl->server.service : protocol->service,
+ ctl->server.service ? ctl->server.service : ( ctl->use_ssl ? protocol->sslservice : protocol->service ),
ctl->server.netsec, ctl->server.plugin)) == -1)
#else /* INET6_ENABLE */
if ((mailserver_socket = SockOpen(realhost, port, NULL, ctl->server.plugin)) == -1)
{
if (h_errno == HOST_NOT_FOUND)
strcpy(errbuf, _("host is unknown."));
+#ifndef __BEOS__
else if (h_errno == NO_ADDRESS)
strcpy(errbuf, _("name is valid but has no IP address."));
+#endif
else if (h_errno == NO_RECOVERY)
strcpy(errbuf, _("unrecoverable name server error."));
else if (h_errno == TRY_AGAIN)
/* warn the system administrator */
if (open_warning_by_mail(ctl, (struct msgblk *)NULL) == 0)
{
-#define OPENFAIL "Subject: Fetchmail unreachable-server warning.\r\n\r\nFetchmail could not reach the mail server %s:"
- stuff_warning(ctl, OPENFAIL, ctl->server.pollname);
+ stuff_warning(ctl,
+ _("Subject: Fetchmail unreachable-server warning.\r\n"
+ "\r\n"
+ "Fetchmail could not reach the mail server %s:")
+ ctl->server.pollname);
stuff_warning(ctl, errbuf, ctl->server.pollname);
close_warning_by_mail(ctl, (struct msgblk *)NULL);
-#undef OPENFAIL
}
#endif
}
verification. We may want to make this configurable */
if (ctl->use_ssl && SSLOpen(mailserver_socket,ctl->sslkey,ctl->sslcert,realhost) == -1)
{
- report(stderr, "SSL connection failed.");
+ report(stderr, _("SSL connection failed.\n"));
goto closeUp;
}
#endif
if (ctl->server.preauthenticate == A_KERBEROS_V4)
{
set_timeout(mytimeout);
- ok = kerberos_auth(mailserver_socket, ctl->server.truename);
+ ok = kerberos_auth(mailserver_socket, ctl->server.truename,
+ ctl->server.principal);
set_timeout(0);
if (ok != 0)
goto cleanUp;
* calling user a heads-up about the authentication
* failure once it looks like this isn't a fluke
* due to the server being temporarily inaccessible.
+ * When we get third failure, we notify the user. After
+ * that, once we get authorization we let the user know
+ * service is restored.
*/
if (run.poll_interval
- && ctl->authfailcount++ > MAX_AUTHFAILS
+ && ++ctl->authfailcount == 3
&& !open_warning_by_mail(ctl, (struct msgblk *)NULL))
{
stuff_warning(ctl,
_("Fetchmail could not get mail from %s@%s.\r\n"),
ctl->remotename,
ctl->server.truename);
- stuff_warning(ctl,
- _("The attempt to get authorization failed.\r\n" \
- "This probably means your password is invalid, but POP3 servers have\r\n" \
- "other failure modes that fetchmail cannot distinguish from this\r\n" \
- "because they don't send useful error messages on login failure.\r\n"));
+ stuff_warning(ctl, _("\
+The attempt to get authorization failed.\r\n\
+This probably means your password is invalid, but some servers have\r\n\
+other failure modes that fetchmail cannot distinguish from this\r\n\
+because they don't send useful error messages on login failure.\r\n\
+\r\n\
+The fetchmail daemon will continue running and attempt to connect\r\n\
+at each cycle. No future notifications will be sent until service\r\n\
+is restored."));
close_warning_by_mail(ctl, (struct msgblk *)NULL);
- ctl->wedged = TRUE;
}
}
else
goto cleanUp;
}
+ else
+ {
+ if (ctl->authfailcount >= 3)
+ {
+ report(stderr,
+ _("Authorization OK on %s@%s\n"),
+ ctl->remotename,
+ ctl->server.truename);
+ if (!open_warning_by_mail(ctl, (struct msgblk *)NULL))
+ {
+ stuff_warning(ctl,
+ _("Subject: fetchmail authentication OK\r\n"));
+ stuff_warning(ctl,
+ _("Fetchmail was able to log into %s@%s.\r\n"),
+ ctl->remotename,
+ ctl->server.truename);
+ stuff_warning(ctl,
+ _("Service has been restored.\r\n"));
+ close_warning_by_mail(ctl, (struct msgblk *)NULL);
+
+ }
+ ctl->authfailcount = 0;
+ }
+ }
}
ctl->errcount = fetches = 0;
dispatches = 0;
++pass;
+ /* reset timeout, in case we did an IDLE */
+ mytimeout = ctl->server.timeout;
+
if (outlevel >= O_DEBUG)
{
if (idp->id)
if (new == -1 || ctl->fetchall)
new = count;
fetches = new; /* set error status ccorrectly */
- goto no_error;
+ /*
+ * There used to be a `got noerror' here, but this
+ * prevneted checking of multiple folders. This
+ * comment is a reminder in case I introduced some
+ * subtle bug by removing it...
+ */
}
else if (count > 0)
{
{
if (outlevel > O_SILENT)
{
- report_build(stdout, _("skipping message %d"), num);
+ report_build(stdout,
+ _("skipping message %d (%d octets)"),
+ num, msgsizes[num-1]);
if (toolarge && !check_only)
{
char size[32];
*/
if (protocol->fetch_body && !suppress_readbody)
{
- if (outlevel >= O_VERBOSE && isatty(1))
+ if (outlevel >= O_VERBOSE && !isafile(1))
{
fputc('\n', stdout);
fflush(stdout);
/* tell server we got it OK and resynchronize */
if (protocol->trail)
{
- if (outlevel >= O_VERBOSE && isatty(1))
+ if (outlevel >= O_VERBOSE && !isafile(1))
{
fputc('\n', stdout);
fflush(stdout);
struct idlist *sdp;
for (sdp = ctl->newsaved; sdp; sdp = sdp->next)
- if (sdp->val.status.num == num)
+ if ((sdp->val.status.num == num)
+ && (!toolarge || oldmsg))
sdp->val.status.mark = UID_SEEN;
}
}
} while
/*
- * Only re-poll if we had some actual forwards, allowed
- * deletions and had no errors.
+ * Only re-poll if we either had some actual forwards and
+ * either 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:
+ /* no_error: */
/* ordinary termination with no errors -- officially log out */
ok = (protocol->logout_cmd)(mailserver_socket, ctl);
/*
case PS_SOCKET:
msg = _("socket");
break;
- case PS_AUTHFAIL:
- msg = _("authorization");
- break;
case PS_SYNTAX:
msg = _("missing or bad RFC822 header");
break;
report(stderr, _("undefined error\n"));
break;
}
- /* no report on PS_MAXFETCH or PS_UNDEFINED */
- if (ok==PS_SOCKET || ok==PS_AUTHFAIL || ok==PS_SYNTAX
+ /* no report on PS_MAXFETCH or PS_UNDEFINED or PS_AUTHFAIL */
+ if (ok==PS_SOCKET || ok==PS_SYNTAX
|| ok==PS_IOERR || ok==PS_ERROR || ok==PS_PROTOCOL
|| ok==PS_LOCKBUSY || ok==PS_SMTP || ok==PS_DNS)
report(stderr, _("%s error while fetching from %s\n"), msg, ctl->server.pollname);
char buf [MSGBUFSIZE+1];
va_list ap;
- if (protocol->tagged)
+ if (protocol->tagged && !suppress_tags)
(void) sprintf(buf, "%s ", GENSYM);
else
buf[0] = '\0';
phase = SERVER_WAIT;
- if (protocol->tagged)
+ if (protocol->tagged && !suppress_tags)
(void) sprintf(buf, "%s ", GENSYM);
else
buf[0] = '\0';