X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=driver.c;h=a6a18754efd550628d292c444ffa767bcb5a5752;hb=f16d8d23439b5569f0c2e1af22494708b507f277;hp=064ed1fec282388d525c608d02b64847e578358d;hpb=f530db9e41b95eff04098686753db4938e30dbe0;p=~andy%2Ffetchmail diff --git a/driver.c b/driver.c index 064ed1fe..a6a18754 100644 --- a/driver.c +++ b/driver.c @@ -10,49 +10,38 @@ #include #include #include -#ifdef HAVE_MEMORY_H -#include -#endif /* HAVE_MEMORY_H */ -#if defined(STDC_HEADERS) #include #include -#endif -#if defined(HAVE_UNISTD_H) #include -#endif -#if defined(HAVE_SYS_ITIMER_H) -#include -#endif #include -#ifdef HAVE_SYS_WAIT_H #include -#endif +#include -#ifdef HAVE_SYS_SOCKET_H #include -#endif -#ifdef HAVE_NET_SOCKET_H -#include -#endif #include #ifdef HAVE_PKG_hesiod +#ifdef __cplusplus +extern "C" { +#endif #include +#ifdef __cplusplus +} +#endif #endif #include #include "kerberos.h" -#ifdef KERBEROS_V4 -#include -#endif /* KERBEROS_V4 */ -#include "i18n.h" +#include "gettext.h" #include "socket.h" #include "fetchmail.h" #include "getaddrinfo.h" #include "tunable.h" +#include "sdump.h" + /* throw types for runtime errors */ #define THROW_TIMEOUT 1 /* server timed out */ @@ -90,7 +79,6 @@ void resetidletimeout(void) void set_timeout(int timeleft) /* reset the nonresponse-timeout */ { -#if !defined(__EMX__) && !defined(__BEOS__) struct itimerval ntimeout; if (timeleft == 0) @@ -100,10 +88,9 @@ void set_timeout(int timeleft) ntimeout.it_value.tv_sec = timeleft; ntimeout.it_value.tv_usec = 0; setitimer(ITIMER_REAL, &ntimeout, (struct itimerval *)NULL); -#endif } -static RETSIGTYPE timeout_handler (int signal) +static void timeout_handler (int signal) /* handle SIGALRM signal indicating a server timeout */ { (void)signal; @@ -132,74 +119,9 @@ static int cleanupSockClose (int fd) return (scerror); } -#ifdef KERBEROS_V4 -static int kerberos_auth(socket, canonical, principal) -/* authenticate to the server host using Kerberos V4 */ -int socket; /* socket to server host */ -char *canonical; /* server name */ -char *principal; -{ - KTEXT ticket; - MSG_DAT msg_data; - 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; - } - } - } - - ticket = xmalloc(sizeof (KTEXT_ST)); - rem = (krb_sendauth (0L, socket, ticket, - prin ? prin : "pop", - inst ? inst : canonical, - realm ? realm : ((char *) (krb_realmofhost (canonical))), - ((unsigned long) 0), - (&msg_data), - (&cred), - (schedule), - ((struct sockaddr_in *) 0), - ((struct sockaddr_in *) 0), - "KPOPV0.1")); - free(ticket); - if (prin_copy) - { - free(prin_copy); - } - if (rem != KSUCCESS) - { - report(stderr, GT_("kerberos error %s\n"), (krb_get_err_text (rem))); - return (PS_AUTHFAIL); - } - return (0); -} -#endif /* KERBEROS_V4 */ - #ifdef KERBEROS_V5 -static int kerberos5_auth(socket, canonical) -/* authenticate to the server host using Kerberos V5 */ -int socket; /* socket to server host */ -const char *canonical; /* server name */ +/** authenticate to the server host using Kerberos V5 */ +static int kerberos5_auth(int socket /** socket to server host */, const char *canonical /** server name */) { krb5_error_code retval; krb5_context context; @@ -246,15 +168,17 @@ const char *canonical; /* server name */ if (retval) { #ifdef HEIMDAL if (err_ret && err_ret->e_text) { - report(stderr, GT_("krb5_sendauth: %s [server says '%*s'] \n"), - error_message(retval), - err_ret->e_text); + char *t = err_ret->e_text; + char *tt = sdump(t, strlen(t)); + report(stderr, GT_("krb5_sendauth: %s [server says '%s']\n"), + error_message(retval), tt); + free(tt); #else if (err_ret && err_ret->text.length) { - report(stderr, GT_("krb5_sendauth: %s [server says '%*s'] \n"), - error_message(retval), - err_ret->text.length, - err_ret->text.data); + char *tt = sdump(err_ret->text.data, err_ret->text.length); + report(stderr, GT_("krb5_sendauth: %s [server says '%s']\n"), + error_message(retval), tt); + free(tt); #endif krb5_free_error(context, err_ret); } else @@ -359,11 +283,13 @@ static void send_size_warnings(struct query *ctl) size = atoi(current->id); if (ctl->limitflush) stuff_warning(NULL, ctl, - GT_(" %d msg %d octets long deleted by fetchmail."), + ngettext(" %d message %d octets long deleted by fetchmail.", + " %d messages %d octets long deleted by fetchmail.", nbr), nbr, size); else stuff_warning(NULL, ctl, - GT_(" %d msg %d octets long skipped by fetchmail."), + ngettext(" %d message %d octets long skipped by fetchmail.", + " %d messages %d octets long skipped by fetchmail.", nbr), nbr, size); } current->val.status.num++; @@ -416,9 +342,18 @@ static void mark_oversized(struct query *ctl, int size) } } +static int eat_trailer(int sock, struct query *ctl) +{ + /* we only need this LF if we're printing ticker dots + * AND we are dumping protocol traces. */ + if (outlevel >= O_VERBOSE && want_progress()) fputc('\n', stdout); + return (ctl->server.base_protocol->trail)(sock, ctl, tag); +} + static int fetch_messages(int mailserver_socket, struct query *ctl, int count, int **msgsizes, int maxfetch, - int *fetches, int *dispatches, int *deletions) + int *fetches, int *dispatches, int *deletions, + int *transient_errors) /* fetch messages in lockstep mode */ { flag force_retrieval; @@ -434,7 +369,7 @@ static int fetch_messages(int mailserver_socket, struct query *ctl, * could be "auto". */ switch (ctl->server.protocol) { - case P_POP3: case P_APOP: case P_RPOP: + case P_POP3: fetchsizelimit = 1; } @@ -444,7 +379,7 @@ static int fetch_messages(int mailserver_socket, struct query *ctl, } /* - * What forces this code is that in POP2 and + * What forces this code is that in * IMAP2bis you can't fetch a message without * having it marked `seen'. In POP3 and IMAP4, on the * other hand, you can (peek_capable is set by @@ -482,20 +417,20 @@ static int fetch_messages(int mailserver_socket, struct query *ctl, /* check if the message is old * Note: the size of the message may not be known here */ - if (ctl->fetchall || force_retrieval) - ; - else if (ctl->server.base_protocol->is_old && (ctl->server.base_protocol->is_old)(mailserver_socket,ctl,num)) - msgcode = MSGLEN_OLD; + if (ctl->fetchall || force_retrieval) { + /* empty */ + } else { + if (ctl->server.base_protocol->is_old && (ctl->server.base_protocol->is_old)(mailserver_socket,ctl,num)) { + msgcode = MSGLEN_OLD; + } + } if (msgcode == MSGLEN_OLD) { - /* To avoid flooding the syslog when using --keep, - * report "Skipped message" only when: - * 1) --verbose is on, or - * 2) fetchmail does not use syslog - */ - if ( (outlevel >= O_VERBOSE) || - (outlevel > O_SILENT && !run.use_syslog) - ) + /* + * To avoid flooding the logs when using --keep, report + * skipping for old messages only when --flush is on. + */ + if (outlevel > O_SILENT && ctl->flush) { report_build(stdout, GT_("skipping message %s@%s:%d"), @@ -578,6 +513,9 @@ static int fetch_messages(int mailserver_socket, struct query *ctl, } else { + /* XXX FIXME: make this one variable, wholesize and + separatefetchbody query the same variable just with + inverted logic */ flag wholesize = !ctl->server.base_protocol->fetch_body; flag separatefetchbody = (ctl->server.base_protocol->fetch_body) ? TRUE : FALSE; @@ -589,6 +527,7 @@ static int fetch_messages(int mailserver_socket, struct query *ctl, GT_("couldn't fetch headers, message %s@%s:%d (%d octets)\n"), ctl->remotename, ctl->server.truename, num, msgsize); + (*transient_errors)++; continue; } else if (err != 0) @@ -610,8 +549,11 @@ static int fetch_messages(int mailserver_socket, struct query *ctl, if (len > 0) report_build(stdout, wholesize ? GT_(" (%d octets)") : GT_(" (%d header octets)"), len); - if (outlevel >= O_VERBOSE) - report_complete(stdout, "\n"); + if (want_progress()) { + /* flush and add a blank to append ticker dots */ + report_flush(stdout); + putchar(' '); + } } /* @@ -623,28 +565,24 @@ static int fetch_messages(int mailserver_socket, struct query *ctl, /* pass the suppress_readbody flag only if the underlying * protocol does not fetch the body separately */ separatefetchbody ? 0 : &suppress_readbody); + if (err == PS_RETAINED) suppress_forward = suppress_delete = retained = TRUE; else if (err == PS_TRANSIENT) + { suppress_delete = suppress_forward = TRUE; + (*transient_errors)++; + } else if (err == PS_REFUSED) suppress_forward = TRUE; - else if (err == PS_TRUNCATED) - suppress_readbody = TRUE; else if (err) return(err); /* tell server we got it OK and resynchronize */ if (separatefetchbody && ctl->server.base_protocol->trail) { - if (outlevel >= O_VERBOSE && !is_a_file(1) && !run.use_syslog) - { - fputc('\n', stdout); - fflush(stdout); - } - - if ((err = (ctl->server.base_protocol->trail)(mailserver_socket, ctl, tag))) - return(err); + err = eat_trailer(mailserver_socket, ctl); + if (err) return(err); } /* do not read the body which is not being forwarded only if @@ -666,8 +604,44 @@ static int fetch_messages(int mailserver_socket, struct query *ctl, if (separatefetchbody) { len = -1; - if ((err=(ctl->server.base_protocol->fetch_body)(mailserver_socket,ctl,num,&len))) + if ((err=(ctl->server.base_protocol->fetch_body)(mailserver_socket,ctl,num,&len))) { + if (err == PS_ERROR && ctl->server.retrieveerror) { + /* + * Mark a message with a protocol error as seen. + * This can be used to see which messages we've attempted + * to download, but failed. + */ + if (ctl->server.retrieveerror == RE_MARKSEEN) { + if ((ctl->server.base_protocol->mark_seen)(mailserver_socket,ctl,num)) { + return(err); + } + } + + if (ctl->server.retrieveerror != RE_ABORT) { + /* + * Do not abort download session. Continue with the next message. + * + * Prevents a malformed message from blocking all other messages + * behind it in the mailbox from being downloaded. + * + * Reconnect to SMTP to force this incomplete message to be dropped. + * Required because we've already begun the DATA portion of the + * interaction with the SMTP server (commands are ignored/ + * considered part of the message data). + */ + abort_message_sink(ctl); + + // Ensure we don't delete the failed message from the server. + suppress_delete = TRUE; + + // Bookkeeping required before next message can be downloaded. + goto flagthemail; + } + } + return(err); + } + /* * Work around a bug in Novell's * broken GroupWise IMAP server; @@ -677,9 +651,15 @@ static int fetch_messages(int mailserver_socket, struct query *ctl, */ if (len == -1) len = msgsize - msgblk.msglen; - if (outlevel > O_SILENT && !wholesize) - report_build(stdout, - GT_(" (%d body octets)"), len); + if (!wholesize) { + if (outlevel > O_SILENT) + report_build(stdout, + GT_(" (%d body octets)"), len); + if (want_progress()) { + report_flush(stdout); + putchar(' '); + } + } } /* process the body now */ @@ -687,23 +667,19 @@ static int fetch_messages(int mailserver_socket, struct query *ctl, ctl, !suppress_forward, len); + if (err == PS_TRANSIENT) + { suppress_delete = suppress_forward = TRUE; + (*transient_errors)++; + } else if (err) return(err); /* tell server we got it OK and resynchronize */ - if (ctl->server.base_protocol->trail) - { - if (outlevel >= O_VERBOSE && !is_a_file(1) && !run.use_syslog) - { - fputc('\n', stdout); - fflush(stdout); - } - - err = (ctl->server.base_protocol->trail)(mailserver_socket, ctl, tag); - if (err != 0) - return(err); + if (ctl->server.base_protocol->trail) { + err = eat_trailer(mailserver_socket, ctl); + if (err) return(err); } } @@ -757,16 +733,17 @@ static int fetch_messages(int mailserver_socket, struct query *ctl, flagthemail: /* - * 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 + * 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. */ + /* in softbounce mode, suppress deletion and marking as seen */ + if (suppress_forward) + suppress_delete = suppress_delete || run.softbounce; + /* maybe we delete this message now? */ if (retained) { @@ -788,16 +765,11 @@ flagthemail: } else { - if ( (outlevel >= O_VERBOSE) || - /* To avoid flooding the syslog when using --keep, - * report "Skipped message" only when: - * 1) --verbose is on, or - * 2) fetchmail does not use syslog, or - * 3) the message was skipped for some other - * reason than just being old. - */ - (outlevel > O_SILENT && (!run.use_syslog || msgcode != MSGLEN_OLD)) - ) + /* + * To avoid flooding the logs when using --keep, report + * skipping of new messages only. + */ + if (outlevel > O_SILENT && msgcode != MSGLEN_OLD) report_complete(stdout, GT_(" not flushed\n")); /* maybe we mark this message as seen now? */ @@ -926,14 +898,18 @@ static int do_session( /* sigsetjmp returned zero -> normal operation */ char buf[MSGBUFSIZE+1], *realhost; int count, newm, bytes; - int fetches, dispatches, oldphase; + int fetches, dispatches, transient_errors, oldphase; struct idlist *idp; /* execute pre-initialization command, if any */ if (ctl->preconnect && (err = system(ctl->preconnect))) { - report(stderr, - GT_("pre-connection command failed with status %d\n"), err); + if (WIFSIGNALED(err)) + report(stderr, + GT_("pre-connection command terminated with signal %d\n"), WTERMSIG(err)); + else + report(stderr, + GT_("pre-connection command failed with status %d\n"), WEXITSTATUS(err)); err = PS_SYNTAX; goto closeUp; } @@ -997,6 +973,9 @@ static int do_session( hints.ai_socktype = SOCK_STREAM; hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_CANONNAME; +#ifdef AI_ADDRCONFIG + hints.ai_flags |= AI_ADDRCONFIG; +#endif error = fm_getaddrinfo(ctl->server.queryname, NULL, &hints, &res); if (error) @@ -1053,26 +1032,6 @@ static int do_session( ctl->server.base_protocol->name, ctl->server.pollname); strlcpy(errbuf, strerror(err_no), sizeof(errbuf)); report_complete(stderr, ": %s\n", errbuf); - -#ifdef __UNUSED__ - /* - * Don't use this. It was an attempt to address Debian bug - * #47143 (Notify user by mail when pop server nonexistent). - * Trouble is, that doesn't work; you trip over the case - * where your SLIP or PPP link is down... - */ - /* warn the system administrator */ - if (open_warning_by_mail(ctl) == 0) - { - stuff_warning(iana_charset, ctl, - GT_("Subject: Fetchmail unreachable-server warning.")); - stuff_warning(NULL, ctl, ""); - stuff_warning(NULL, ctl, GT_("Fetchmail could not reach the mail server %s:"), - ctl->server.pollname); - stuff_warning(NULL, ctl, errbuf, ctl->server.pollname); - close_warning_by_mail(ctl, (struct msgblk *)NULL); - } -#endif } err = PS_SOCKET; set_timeout(0); @@ -1088,11 +1047,15 @@ static int do_session( set_timeout(mytimeout); /* perform initial SSL handshake on open connection */ - /* Note: We pass the realhost name over for certificate - verification. We may want to make this configurable */ - if (ctl->use_ssl && SSLOpen(mailserver_socket,ctl->sslcert,ctl->sslkey,ctl->sslproto,ctl->sslcertck, - ctl->sslcertpath,ctl->sslfingerprint,realhost,ctl->server.pollname,&ctl->remotename) == -1) + if (ctl->use_ssl && + SSLOpen(mailserver_socket, ctl->sslcert, ctl->sslkey, + ctl->sslproto, ctl->sslcertck, + ctl->sslcertfile, ctl->sslcertpath, + ctl->sslfingerprint, ctl->sslcommonname ? + ctl->sslcommonname : realhost, ctl->server.pollname, + &ctl->remotename) == -1) { + set_timeout(0); report(stderr, GT_("SSL connection failed.\n")); err = PS_SOCKET; goto cleanUp; @@ -1109,17 +1072,6 @@ static int do_session( */ set_timeout(0); phase = oldphase; -#ifdef KERBEROS_V4 - if (ctl->server.authenticate == A_KERBEROS_V4 && (strcasecmp(proto->name,"IMAP") != 0)) - { - set_timeout(mytimeout); - err = kerberos_auth(mailserver_socket, ctl->server.truename, - ctl->server.principal); - set_timeout(0); - if (err != 0) - goto cleanUp; - } -#endif /* KERBEROS_V4 */ #ifdef KERBEROS_V5 if (ctl->server.authenticate == A_KERBEROS_V5) @@ -1141,7 +1093,9 @@ static int do_session( stage = STAGE_GETAUTH; if (ctl->server.base_protocol->getauth) { + set_timeout(mytimeout); err = (ctl->server.base_protocol->getauth)(mailserver_socket, ctl, buf); + set_timeout(0); if (err != 0) { @@ -1160,6 +1114,9 @@ static int do_session( ctl->server.truename, (ctl->wehaveauthed ? GT_(" (previously authorized)") : "") ); + if (ctl->server.authenticate == A_ANY && !ctl->wehaveauthed) { + report(stderr, GT_("For help, see http://www.fetchmail.info/fetchmail-FAQ.html#R15\n")); + } /* * If we're running in background, try to mail the @@ -1286,6 +1243,7 @@ is restored.")); pass = 0; do { dispatches = 0; + transient_errors = 0; ++pass; /* reset timeout, in case we did an IDLE */ @@ -1415,10 +1373,20 @@ is restored.")); err = fetch_messages(mailserver_socket, ctl, count, &msgsizes, maxfetch, - &fetches, &dispatches, &deletions); + &fetches, &dispatches, &deletions, + &transient_errors); if (err != PS_SUCCESS && err != PS_MAXFETCH) goto cleanUp; + if (transient_errors > MAX_TRANSIENT_ERRORS) + { + if (outlevel > O_SILENT) + { + report(stderr, GT_("Too many mails skipped (%d > %d) due to transient errors for %s\n"), + transient_errors, MAX_TRANSIENT_ERRORS, buf); + } + } + if (!check_only && ctl->skipped && run.poll_interval > 0 && !nodetach) { @@ -1430,9 +1398,11 @@ is restored.")); /* end-of-mailbox processing before we repoll or switch to another one */ if (ctl->server.base_protocol->end_mailbox_poll) { - err = (ctl->server.base_protocol->end_mailbox_poll)(mailserver_socket, ctl); - if (err) + tmperr = (ctl->server.base_protocol->end_mailbox_poll)(mailserver_socket, ctl); + if (tmperr) { + err = tmperr; goto cleanUp; + } } /* Return now if we have reached the fetchlimit */ if (maxfetch && maxfetch <= fetches) @@ -1498,7 +1468,6 @@ is restored.")); smtp_close(ctl, 0); if (mailserver_socket != -1) { cleanupSockClose(mailserver_socket); - mailserver_socket = -1; } /* If there was a connect timeout, the socket should be closed. * mailserver_socket_temp contains the socket to close. @@ -1517,7 +1486,7 @@ is restored.")); msg = GT_("socket"); break; case PS_SYNTAX: - msg = GT_("missing or bad RFC822 header"); + msg = GT_("missing or bad RFC822 header or command line option"); break; case PS_IOERR: msg = GT_("MDA"); @@ -1559,7 +1528,10 @@ closeUp: /* execute wrapup command, if any */ if (ctl->postconnect && (tmperr = system(ctl->postconnect))) { - report(stderr, GT_("post-connection command failed with status %d\n"), tmperr); + if (WIFSIGNALED(tmperr)) + report(stderr, GT_("post-connection command terminated with signal %d\n"), WTERMSIG(tmperr)); + else + report(stderr, GT_("post-connection command failed with status %d\n"), WEXITSTATUS(tmperr)); if (err == PS_SUCCESS) err = PS_SYNTAX; } @@ -1575,14 +1547,6 @@ int do_protocol(struct query *ctl /** parsed options with merged-in defaults */, { int err; -#ifndef KERBEROS_V4 - if (ctl->server.authenticate == A_KERBEROS_V4) - { - report(stderr, GT_("Kerberos V4 support not linked.\n")); - return(PS_ERROR); - } -#endif /* KERBEROS_V4 */ - #ifndef KERBEROS_V5 if (ctl->server.authenticate == A_KERBEROS_V5) {