]> Pileus Git - ~andy/fetchmail/blobdiff - driver.c
Merge branch 'legacy_63'
[~andy/fetchmail] / driver.c
index db8d52b8c8b0abb4c2b4c3b835b56d63b12272c1..a6a18754efd550628d292c444ffa767bcb5a5752 100644 (file)
--- a/driver.c
+++ b/driver.c
 #include  <setjmp.h>
 #include  <errno.h>
 #include  <string.h>
-#ifdef HAVE_MEMORY_H
-#include  <memory.h>
-#endif /* HAVE_MEMORY_H */
-#if defined(STDC_HEADERS)
 #include  <stdlib.h>
 #include  <limits.h>
-#endif
-#if defined(HAVE_UNISTD_H)
 #include <unistd.h>
-#endif
-#if defined(HAVE_SYS_ITIMER_H)
-#include <sys/itimer.h>
-#endif
 #include  <signal.h>
-#ifdef HAVE_SYS_WAIT_H
 #include <sys/wait.h>
-#endif
+#include <sys/time.h>
 
-#ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
-#endif
-#ifdef HAVE_NET_SOCKET_H
-#include <net/socket.h>
-#endif
 #include <netdb.h>
 #ifdef HAVE_PKG_hesiod
+#ifdef __cplusplus
+extern "C" {
+#endif
 #include <hesiod.h>
+#ifdef __cplusplus
+}
+#endif
 #endif
 
 #include <langinfo.h>
 
 #include "kerberos.h"
-#ifdef KERBEROS_V4
-#include <netinet/in.h>
-#endif /* KERBEROS_V4 */
 
-#include "i18n.h"
+#include "gettext.h"
 #include "socket.h"
 
 #include "fetchmail.h"
@@ -92,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)
@@ -102,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;
@@ -134,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;
@@ -432,7 +352,8 @@ static int eat_trailer(int sock, struct query *ctl)
 
 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;
@@ -448,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;
        }
 
@@ -458,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 
@@ -505,14 +426,11 @@ static int fetch_messages(int mailserver_socket, struct query *ctl,
         }
        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"),
@@ -609,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)
@@ -650,7 +569,10 @@ static int fetch_messages(int mailserver_socket, struct query *ctl,
            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)
@@ -682,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;
@@ -711,7 +669,10 @@ static int fetch_messages(int mailserver_socket, struct query *ctl,
                              len);
 
                if (err == PS_TRANSIENT)
+               {
                    suppress_delete = suppress_forward = TRUE;
+                   (*transient_errors)++;
+               }
                else if (err)
                    return(err);
 
@@ -804,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? */
@@ -942,7 +898,7 @@ 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 */
@@ -1076,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);
@@ -1119,6 +1055,7 @@ static int do_session(
                    ctl->sslcommonname : realhost, ctl->server.pollname,
                    &ctl->remotename) == -1)
        {
+           set_timeout(0);
            report(stderr, GT_("SSL connection failed.\n"));
            err = PS_SOCKET;
            goto cleanUp;
@@ -1135,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)
@@ -1167,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)
            {
@@ -1186,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
@@ -1312,6 +1243,7 @@ is restored."));
            pass = 0;
            do {
                dispatches = 0;
+               transient_errors = 0;
                ++pass;
 
                /* reset timeout, in case we did an IDLE */
@@ -1441,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)
                    {
@@ -1544,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");
@@ -1605,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)
     {