]> Pileus Git - ~andy/fetchmail/blobdiff - driver.c
Note Earl's regression fix for SSL_CTX_clear_options() on older OpenSSL.
[~andy/fetchmail] / driver.c
index d93c01c2ec1cb3c31195890c87dad18449a1bef5..c2917268cedfce70fa9d70f085bad27b7f4f201b 100644 (file)
--- a/driver.c
+++ b/driver.c
 #endif
 #include <netdb.h>
 #ifdef HAVE_PKG_hesiod
+#ifdef __cplusplus
+extern "C" {
+#endif
 #include <hesiod.h>
+#ifdef __cplusplus
+}
+#endif
 #endif
 
 #include <langinfo.h>
@@ -53,6 +59,8 @@
 #include "getaddrinfo.h"
 #include "tunable.h"
 
+#include "sdump.h"
+
 /* throw types for runtime errors */
 #define THROW_TIMEOUT  1               /* server timed out */
 
@@ -246,15 +254,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
@@ -418,9 +428,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;
@@ -484,20 +503,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"),
@@ -580,6 +599,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;
 
@@ -591,6 +613,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)
@@ -612,8 +635,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(' ');
+               }
            }
 
            /* 
@@ -625,10 +651,14 @@ 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)
@@ -637,14 +667,8 @@ static int fetch_messages(int mailserver_socket, struct query *ctl,
            /* 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
@@ -677,9 +701,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 +717,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 +783,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 +815,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 +948,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 +1023,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)
@@ -1054,25 +1083,6 @@ static int do_session(
                    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 +1098,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;
@@ -1141,7 +1155,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 +1176,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 +1305,7 @@ is restored."));
            pass = 0;
            do {
                dispatches = 0;
+               transient_errors = 0;
                ++pass;
 
                /* reset timeout, in case we did an IDLE */
@@ -1415,10 +1435,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 +1460,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 +1530,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.
@@ -1559,7 +1590,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;
     }