]> Pileus Git - ~andy/fetchmail/blobdiff - fetchmail.c
Initial revision
[~andy/fetchmail] / fetchmail.c
index 0dfc7e41611c2faec2c66e8be772fab06cde2120..b75105136fe5ad5f311b0ca513f402ce7c46f2d4 100644 (file)
@@ -44,6 +44,7 @@
 
 #include "getopt.h"
 #include "fetchmail.h"
+#include "socket.h"
 #include "tunable.h"
 #include "smtp.h"
 #include "netrc.h"
@@ -83,6 +84,7 @@ static char *lockfile;                /* name of lockfile */
 static int lock_acquired;      /* have we acquired a lock */
 static int querystatus;                /* status of query */
 static int successes;          /* count number of successful polls */
+static int activecount;                /* count number of active entries */
 static struct runctl cmd_run;  /* global options set from command line */
 static time_t parsetime;       /* time of last parse */
 
@@ -100,7 +102,7 @@ static void unlockit(void)
        unlink(lockfile);
 }
 
-#ifdef __FreeBSD__
+#if defined(__FreeBSD__) && defined(__FreeBSD_USE_KVM)
 /* drop SGID kmem privileage until we need it */
 static void dropprivs(void)
 {
@@ -121,6 +123,24 @@ static void dropprivs(void)
 }
 #endif
 
+#if defined(HAVE_SETLOCALE) && defined(ENABLE_NLS) && defined(HAVE_STRFTIME)
+#include <time.h>
+#include <locale.h>
+static char *timestamp (void)
+{
+    time_t      now;
+    static char buf[60];
+
+    time (&now);
+    setlocale (LC_TIME, "");
+    strftime (buf, sizeof (buf), "%c", localtime(&now));
+    setlocale (LC_TIME, "C");
+    return (buf);
+}
+#else
+#define timestamp rfc822timestamp
+#endif
+
 int main(int argc, char **argv)
 {
     int st, bkgd = FALSE;
@@ -132,7 +152,7 @@ int main(int argc, char **argv)
     pid_t pid;
     int lastsig = 0;
 
-#ifdef __FreeBSD__
+#if defined(__FreeBSD__) && defined(__FreeBSD_USE_KVM)
     dropprivs();
 #endif
 
@@ -151,7 +171,7 @@ int main(int argc, char **argv)
     {
        int i;
 
-       report(stdout, "fetchmail: invoked with");
+       report(stdout, _("fetchmail: invoked with"));
        for (i = 0; i < argc; i++)
            report(stdout, " %s", argv[i]);
        report(stdout, "\n");
@@ -210,6 +230,9 @@ int main(int argc, char **argv)
 #ifndef ETRN_ENABLE
        printf("-ETRN");
 #endif /* ETRN_ENABLE */
+#ifndef ODMR_ENABLE
+       printf("-ODMR");
+#endif /* ODMR_ENABLE */
 #ifdef SSL_ENABLE
        printf("+SSL");
 #endif
@@ -300,13 +323,7 @@ int main(int argc, char **argv)
     {
        if (ctl->active && !(implicitmode && ctl->server.skip)&&!ctl->password)
        {
-           if (ctl->server.preauthenticate == A_KERBEROS_V4 ||
-                       ctl->server.preauthenticate == A_KERBEROS_V5 ||
-                       ctl->server.preauthenticate == A_SSH ||
-#ifdef GSSAPI
-                       ctl->server.protocol == P_IMAP_GSS ||
-#endif /* GSSAPI */
-                       ctl->server.protocol == P_IMAP_K4)
+           if (ctl->server.preauthenticate > A_PASSWORD)
                /* Server won't care what the password is, but there
                   must be some non-null string here.  */
                ctl->password = ctl->remotename;
@@ -476,16 +493,13 @@ int main(int argc, char **argv)
     for (ctl = querylist; ctl; ctl = ctl->next)
     {
        if (ctl->active && !(implicitmode && ctl->server.skip)
-               && ctl->server.protocol != P_ETRN 
-               && ctl->server.protocol != P_IMAP_K4
-#ifdef GSSAPI
-               && ctl->server.protocol != P_IMAP_GSS
-#endif /* GSSAPI */
+               && ctl->server.preauthenticate <= A_PASSWORD
                && !ctl->password)
+       {
            if (!isatty(0))
            {
                fprintf(stderr,
-                       _("fetchmail: can't find a password for %s@s.\n"),
+                       _("fetchmail: can't find a password for %s@%s.\n"),
                        ctl->remotename, ctl->server.pollname);
                return(PS_AUTHFAIL);
            }
@@ -500,6 +514,7 @@ int main(int argc, char **argv)
                               ctl->remotename, ctl->server.pollname);
                ctl->password = xstrdup((char *)fm_getpassword(tmpbuf));
            }
+       }
     }
 
     /*
@@ -516,7 +531,9 @@ int main(int argc, char **argv)
      */
     if (run.poll_interval)
     {
-       if (!nodetach)
+       if (nodetach)
+           deal_with_sigchld();
+       else
            daemonize(run.logfile, terminate_run);
        report(stdout, _("starting fetchmail %s daemon \n"), VERSION);
 
@@ -552,17 +569,25 @@ int main(int argc, char **argv)
 #ifndef O_SYNC
 #define O_SYNC 0       /* use it if we have it */
 #endif
-    if ((st = open(lockfile, O_WRONLY|O_CREAT|O_EXCL|O_SYNC, 0666)) != -1)
+    if (!lock_acquired)
     {
-       sprintf(tmpbuf,"%d", getpid());
-       write(st, tmpbuf, strlen(tmpbuf));
-       if (run.poll_interval)
-       {
-           sprintf(tmpbuf," %d", run.poll_interval);
-           write(st, tmpbuf, strlen(tmpbuf));
-       }
-       close(st);      /* should be safe, fd was opened with O_SYNC */
-       lock_acquired = TRUE;
+      if ((st = open(lockfile, O_WRONLY|O_CREAT|O_EXCL|O_SYNC, 0666)) != -1)
+      {
+         sprintf(tmpbuf,"%d", getpid());
+         write(st, tmpbuf, strlen(tmpbuf));
+         if (run.poll_interval)
+         {
+             sprintf(tmpbuf," %d", run.poll_interval);
+             write(st, tmpbuf, strlen(tmpbuf));
+         }
+         close(st);    /* should be safe, fd was opened with O_SYNC */
+         lock_acquired = TRUE;
+      }
+      else
+      {
+         fprintf(stderr,       _("fetchmail: lock creation failed.\n"));
+         return(PS_EXCLUDE);
+      }
     }
 
     /*
@@ -589,7 +614,30 @@ int main(int argc, char **argv)
        else if (rcstat.st_mtime > parsetime)
        {
            report(stdout, _("restarting fetchmail (%s changed)\n"), rcfile);
-           execvp("fetchmail", argv);
+           /*
+            * Matthias Andree: Isn't this prone to introduction of
+            * "false" programs by interfering with PATH? Those
+            * path-searching execs might not be the best ideas for
+            * this reason.
+            *
+            * Rob Funk: But is there any way for someone to modify
+            * the PATH variable of a running fetchmail?  I don't know
+            * of a way.
+            *
+            * Dave's change makes fetchmail restart itself in exactly
+            * the way it was started from the shell (or shell script)
+            * in the first place.  If you're concerned about PATH
+            * contamination, call fetchmail initially with a full
+            * path, and use Dave's patch.
+            *
+            * Not using a -p variant of exec means that the restart
+            * will break if both (a) the user depended on PATH to
+            * call fetchmail in the first place, and (b) the system
+            * doesn't save the whole path in argv[0] if the whole
+            * path wasn't used in the initial call.  (If I recall
+            * correctly, Linux saves it but many other Unices don't.)
+            */
+           execvp(argv[0], argv);
            report(stderr, _("attempt to re-exec fetchmail failed\n"));
        }
 
@@ -600,107 +648,113 @@ int main(int argc, char **argv)
         * reliability and shave off some per-UDP-packet costs.
         * Unfortunately it interacted badly with diald, which effectively 
         * filters out DNS queries over TCP/IP for reasons having to do
-        * with some obscure kernel problem involving bootstrapping of
+        * with some obscure Linux kernel problem involving bootstrapping of
         * dynamically-addressed links.  I don't understand this mess
         * and don't want to, so it's "See ya!" to this hack.
         */
        sethostent(TRUE);       /* use TCP/IP for mailserver queries */
 #endif /* HAVE_RES_SEARCH */
 
+       activecount = 0;
        batchcount = 0;
        for (ctl = querylist; ctl; ctl = ctl->next)
-       {
-           if (ctl->active && !(implicitmode && ctl->server.skip))
+           if (ctl->active)
            {
-               if (ctl->wedged)
+               activecount++;
+               if (!(implicitmode && ctl->server.skip))
                {
-                   report(stderr, 
-                         _("poll of %s skipped (failed authentication or too many timeouts)\n"),
-                         ctl->server.pollname);
-                   continue;
-               }
-
-               /* check skip interval first so that it counts all polls */
-               if (run.poll_interval && ctl->server.interval) 
-               {
-                   if (ctl->server.poll_count++ % ctl->server.interval) 
+                   if (ctl->wedged)
                    {
-                       if (outlevel >= O_VERBOSE)
-                           report(stdout,
-                                   _("interval not reached, not querying %s\n"),
-                                   ctl->server.pollname);
+                       report(stderr, 
+                              _("poll of %s skipped (failed authentication or too many timeouts)\n"),
+                              ctl->server.pollname);
                        continue;
                    }
-               }
+
+                   /* check skip interval first so that it counts all polls */
+                   if (run.poll_interval && ctl->server.interval) 
+                   {
+                       if (ctl->server.poll_count++ % ctl->server.interval) 
+                       {
+                           if (outlevel >= O_VERBOSE)
+                               report(stdout,
+                                      _("interval not reached, not querying %s\n"),
+                                      ctl->server.pollname);
+                           continue;
+                       }
+                   }
 
 #if (defined(linux) && !INET6_ENABLE) || defined(__FreeBSD__)
-               /*
-                * Don't do monitoring if we were woken by a signal.
-                * Note that interface_approve() does its own error logging.
-                */
-               if (!interface_approve(&ctl->server, !lastsig))
-                   continue;
+                   /*
+                    * Don't do monitoring if we were woken by a signal.
+                    * Note that interface_approve() does its own error logging.
+                    */
+                   if (!interface_approve(&ctl->server, !lastsig))
+                       continue;
 #endif /* (defined(linux) && !INET6_ENABLE) || defined(__FreeBSD__) */
 
-               querystatus = query_host(ctl);
+                   querystatus = query_host(ctl);
 
 #ifdef POP3_ENABLE
-               if (!check_only)
-                   uid_end_query(ctl);
+                   /* leave the UIDL state alone if there have been any errors */
+                   if (!check_only &&
+                               ((querystatus==PS_SUCCESS) || (querystatus==PS_NOMAIL) || (querystatus==PS_MAXFETCH)))
+                       uid_swap_lists(ctl);
 #endif  /* POP3_ENABLE */
 
-               if (querystatus == PS_SUCCESS)
-                   successes++;
-               else if (!check_only && 
-                        ((querystatus!=PS_NOMAIL) || (outlevel==O_DEBUG)))
-               switch(querystatus)
-               {
-               case PS_SUCCESS:
-                   report(stdout, "Query status=0 (SUCCESS)\n"); break ;
-               case PS_NOMAIL: 
-                   report(stdout, "Query status=1 (NOMAIL)\n"); break ;
-               case PS_SOCKET:
-                   report(stdout, "Query status=2 (SOCKET)\n"); break ;
-               case PS_AUTHFAIL:
-                   report(stdout, "Query status=3 (AUTHFAIL)\n"); break ;
-               case PS_PROTOCOL:
-                   report(stdout, "Query status=4 (PROTOCOL)\n"); break ;
-               case PS_SYNTAX:
-                   report(stdout, "Query status=5 (SYNTAX)\n"); break ;
-               case PS_IOERR:
-                   report(stdout, "Query status=6 (IOERR)\n"); break ;
-               case PS_ERROR:
-                   report(stdout, "Query status=7 (ERROR)\n"); break ;
-               case PS_EXCLUDE:
-                   report(stdout, "Query status=8 (EXCLUDE)\n"); break ;
-               case PS_LOCKBUSY:
-                   report(stdout, "Query status=9 (LOCKBUSY)\n"); break ;
-               case PS_SMTP:
-                   report(stdout, "Query status=10 (SMTP)\n"); break ;
-               case PS_DNS:
-                   report(stdout, "Query status=11 (DNS)\n"); break ;
-               case PS_BSMTP:
-                   report(stdout, "Query status=12 (BSMTP)\n"); break ;
-               case PS_MAXFETCH:
-                   report(stdout, "Query status=13 (MAXFETCH)\n"); break ;
-               default:
-                   report(stdout, _("Query status=%d\n"), querystatus); break;
-               }
+                   if (querystatus == PS_SUCCESS)
+                       successes++;
+                   else if (!check_only && 
+                            ((querystatus!=PS_NOMAIL) || (outlevel==O_DEBUG)))
+                       switch(querystatus)
+                       {
+                       case PS_SUCCESS:
+                           report(stdout,_("Query status=0 (SUCCESS)\n"));break;
+                       case PS_NOMAIL: 
+                           report(stdout,_("Query status=1 (NOMAIL)\n")); break;
+                       case PS_SOCKET:
+                           report(stdout,_("Query status=2 (SOCKET)\n")); break;
+                       case PS_AUTHFAIL:
+                           report(stdout,_("Query status=3 (AUTHFAIL)\n"));break;
+                       case PS_PROTOCOL:
+                           report(stdout,_("Query status=4 (PROTOCOL)\n"));break;
+                       case PS_SYNTAX:
+                           report(stdout,_("Query status=5 (SYNTAX)\n")); break;
+                       case PS_IOERR:
+                           report(stdout,_("Query status=6 (IOERR)\n"));  break;
+                       case PS_ERROR:
+                           report(stdout,_("Query status=7 (ERROR)\n"));  break;
+                       case PS_EXCLUDE:
+                           report(stdout,_("Query status=8 (EXCLUDE)\n")); break;
+                       case PS_LOCKBUSY:
+                           report(stdout,_("Query status=9 (LOCKBUSY)\n"));break;
+                       case PS_SMTP:
+                           report(stdout,_("Query status=10 (SMTP)\n")); break;
+                       case PS_DNS:
+                           report(stdout,_("Query status=11 (DNS)\n")); break;
+                       case PS_BSMTP:
+                           report(stdout,_("Query status=12 (BSMTP)\n")); break;
+                       case PS_MAXFETCH:
+                           report(stdout,_("Query status=13 (MAXFETCH)\n"));break;
+                       default:
+                           report(stdout,_("Query status=%d\n"),querystatus);
+                           break;
+                       }
 
 #if (defined(linux) && !INET6_ENABLE) || defined (__FreeBSD__)
-               if (ctl->server.monitor)
-               {
-                   /*
-                    * Allow some time for the link to quiesce.  One
-                    * second is usually sufficient, three is safe.
-                    * Note:  this delay is important - don't remove!
-                    */
-                   sleep(3);
-                   interface_note_activity(&ctl->server);
-               }
+                   if (ctl->server.monitor)
+                   {
+                       /*
+                        * Allow some time for the link to quiesce.  One
+                        * second is usually sufficient, three is safe.
+                        * Note:  this delay is important - don't remove!
+                        */
+                       sleep(3);
+                       interface_note_activity(&ctl->server);
+                   }
 #endif /* (defined(linux) && !INET6_ENABLE) || defined(__FreeBSD__) */
+               }
            }
-       }
 
 #if defined(HAVE_RES_SEARCH) && defined(USE_TCPIP_FOR_DNS)
        endhostent();           /* release TCP/IP connection to nameserver */
@@ -736,7 +790,7 @@ int main(int argc, char **argv)
 
            if (outlevel >= O_VERBOSE)
                report(stdout, 
-                      _("fetchmail: sleeping at %s\n"), rfc822timestamp());
+                      _("fetchmail: sleeping at %s\n"), timestamp());
 
            /*
             * OK, now pause util it's time for the next poll cycle.
@@ -758,7 +812,7 @@ int main(int argc, char **argv)
            }
 
            if (outlevel >= O_VERBOSE)
-               report(stdout, _("awakened at %s\n"), rfc822timestamp());
+               report(stdout, _("awakened at %s\n"), timestamp());
        }
     } while
        (run.poll_interval);
@@ -768,7 +822,16 @@ int main(int argc, char **argv)
                successes ? PS_SUCCESS : querystatus);
 
     terminate_run(0);
-    exit(successes ? PS_SUCCESS : querystatus);
+
+    if (successes)
+       exit(PS_SUCCESS);
+    else if (querystatus)
+       exit(querystatus);
+    else
+       /* in case we interrupted before a successful fetch */
+       exit(PS_NOMAIL);
+
+    exit(successes ?  : querystatus);
 }
 
 static void list_merge(struct idlist **dstl, struct idlist **srcl, int force)
@@ -814,6 +877,7 @@ static void optmerge(struct query *h2, struct query *h1, int force)
     FLAG_MERGE(server.dns);
     FLAG_MERGE(server.checkalias);
     FLAG_MERGE(server.uidl);
+    FLAG_MERGE(server.principal);
 
 #if defined(linux) || defined(__FreeBSD__)
     FLAG_MERGE(server.interface);
@@ -843,6 +907,7 @@ static void optmerge(struct query *h2, struct query *h1, int force)
     FLAG_MERGE(stripcr);
     FLAG_MERGE(pass8bits);
     FLAG_MERGE(dropstatus);
+    FLAG_MERGE(dropdelivered);
     FLAG_MERGE(mimedecode);
     FLAG_MERGE(idle);
     FLAG_MERGE(limit);
@@ -868,10 +933,12 @@ static int load_params(int argc, char **argv, int optind)
     struct stat rcstat;
 
     run.bouncemail = TRUE;
+    run.spambounce = FALSE;    /* don't bounce back to innocent bystanders */
 
     memset(&def_opts, '\0', sizeof(struct query));
     def_opts.smtp_socket = -1;
     def_opts.smtpaddress = (char *)0;
+    def_opts.smtpname = (char *)0;
 #define ANTISPAM(n)    save_str(&def_opts.antispam, STRING_DUMMY, 0)->val.status.num = (n)
     ANTISPAM(571);     /* sendmail */
     ANTISPAM(550);     /* old exim */
@@ -971,6 +1038,8 @@ static int load_params(int argc, char **argv, int optind)
        run.poll_interval = cmd_run.poll_interval;
     if (cmd_run.invisible)
        run.invisible = cmd_run.invisible;
+    if (cmd_run.showdots)
+       run.showdots = cmd_run.showdots;
     if (cmd_run.use_syslog)
        run.use_syslog = (cmd_run.use_syslog == FLAG_TRUE);
     if (cmd_run.postmaster)
@@ -987,7 +1056,7 @@ static int load_params(int argc, char **argv, int optind)
     {
        ctl->wedged = FALSE;
 
-       if (configdump || (ctl->active && !(implicitmode && ctl->server.skip)))
+       if (configdump || ctl->active )
        {
            /* merge in defaults */
            optmerge(ctl, &def_opts, FALSE);
@@ -1010,6 +1079,7 @@ static int load_params(int argc, char **argv, int optind)
            DEFAULT(ctl->forcecr, FALSE);
            DEFAULT(ctl->pass8bits, FALSE);
            DEFAULT(ctl->dropstatus, FALSE);
+           DEFAULT(ctl->dropdelivered, FALSE);
            DEFAULT(ctl->mimedecode, FALSE);
            DEFAULT(ctl->idle, FALSE);
            DEFAULT(ctl->server.dns, TRUE);
@@ -1025,17 +1095,16 @@ static int load_params(int argc, char **argv, int optind)
             * do this unconditionally, but it made fetchmail excessively
             * vulnerable to misconfigured DNS setups.
             *
-            * If we're using ETRN, the smtp hunt list is the list of
-            * systems we're polling on behalf of; these have to be 
-            * fully-qualified domain names.  The default for this list
-            * should be the FQDN of localhost.
+            * If we're using ETRN or ODMR, the smtp hunt list is the
+            * list of systems we're polling on behalf of; these have
+            * to be fully-qualified domain names.  The default for
+            * this list should be the FQDN of localhost.
             *
             * If we're using Kerberos for authentication, we need 
-            * the FQDN in order to generate capability keys.
-            */
-           if (ctl->server.protocol == P_ETRN
-                        || ctl->server.preauthenticate == A_KERBEROS_V4
-                        || ctl->server.preauthenticate == A_KERBEROS_V5)
+            * the FQDN in order to generate capability keys.  */
+           if (ctl->server.protocol==P_ETRN || ctl->server.protocol==P_ODMR
+               || ctl->server.preauthenticate == A_KERBEROS_V4
+               || ctl->server.preauthenticate == A_KERBEROS_V5)
                if (strcmp(fetchmailhost, "localhost") == 0)
                        fetchmailhost = host_fqdn();
 
@@ -1116,68 +1185,71 @@ static int load_params(int argc, char **argv, int optind)
                /* prevent core dump from ill-formed or duplicate entry */
                if (!leadname)
                {
-                   report(stderr, 
-                          _("Lead server has no name.\n"));
+                   report(stderr, _("Lead server has no name.\n"));
                    exit(PS_SYNTAX);
                }
 
                ctl->server.truename = xstrdup(leadname);
            }
 #ifdef HAVE_GETHOSTBYNAME
-           else if (!configdump && (ctl->server.preauthenticate==A_KERBEROS_V4 ||
-               ctl->server.preauthenticate==A_KERBEROS_V5 ||
-                     (ctl->server.dns && MULTIDROP(ctl))))
+           else if (!configdump)
            {
-               struct hostent  *namerec;
-
-               /* compute the canonical name of the host */
-               errno = 0;
-               namerec = gethostbyname(ctl->server.queryname);
-               if (namerec == (struct hostent *)NULL)
+               if (ctl->server.preauthenticate==A_KERBEROS_V4 ||
+                     ctl->server.preauthenticate==A_KERBEROS_V5 ||
+                   (ctl->server.dns && MULTIDROP(ctl)))
                {
-                   report(stderr,
-                         _("couldn't find canonical DNS name of %s\n"),
-                         ctl->server.pollname);
-                   exit(PS_DNS);
+                   struct hostent      *namerec;
+
+                   /* compute the canonical name of the host */
+                   errno = 0;
+                   namerec = gethostbyname(ctl->server.queryname);
+                   if (namerec == (struct hostent *)NULL)
+                   {
+                       report(stderr,
+                              _("couldn't find canonical DNS name of %s\n"),
+                              ctl->server.pollname);
+                       ctl->server.truename = xstrdup(ctl->server.queryname);
+                       ctl->server.trueaddr = NULL;
+                   }
+                   else
+                       ctl->server.truename=xstrdup((char *)namerec->h_name);
                }
-               else
-                   ctl->server.truename=xstrdup((char *)namerec->h_name);
-           }
 #endif /* HAVE_GETHOSTBYNAME */
-           else {
+               else {
 #ifdef HAVE_GETHOSTBYNAME
-             struct hostent    *namerec;
-
-             /* <fetchmail@mail.julianhaight.com>
-                Get the host's IP, so we can report it like this:
-
-                Received: from hostname [10.0.0.1]
-
-                do we actually need to gethostbyname to find the IP?
-                it seems like it would be faster to do this later, when
-                we are actually resolving the hostname for a connection,
-                but I ain't that smart, so I don't know where to make
-                the change later..
-             */
-             errno = 0;
-             namerec = gethostbyname(ctl->server.queryname);
-             if (namerec == (struct hostent *)NULL)
-               {
-                 report(stderr,
-                        _("couldn't find canonical DNS name of %s\n"),
-                        ctl->server.pollname);
-                 exit(PS_DNS);
-               }
-             else {
-               ctl->server.truename=xstrdup((char *)namerec->h_name);
-               ctl->server.trueaddr=xmalloc(namerec->h_length);
-               memcpy(ctl->server.trueaddr, 
-                      namerec->h_addr_list[0],
-                      namerec->h_length);
-             }
+                   struct hostent      *namerec;
+                   
+                   /* <fetchmail@mail.julianhaight.com>
+                      Get the host's IP, so we can report it like this:
+
+                      Received: from hostname [10.0.0.1]
+
+                      do we actually need to gethostbyname to find the IP?
+                      it seems like it would be faster to do this later, when
+                      we are actually resolving the hostname for a connection,
+                      but I ain't that smart, so I don't know where to make
+                      the change later..
+                   */
+                   errno = 0;
+                   namerec = gethostbyname(ctl->server.queryname);
+                   if (namerec == (struct hostent *)NULL)
+                   {
+                       report(stderr,
+                              _("couldn't find canonical DNS name of %s\n"),
+                              ctl->server.pollname);
+                       exit(PS_DNS);
+                   }
+                   else {
+                       ctl->server.truename=xstrdup((char *)namerec->h_name);
+                       ctl->server.trueaddr=xmalloc(namerec->h_length);
+                       memcpy(ctl->server.trueaddr, 
+                              namerec->h_addr_list[0],
+                              namerec->h_length);
+                   }
 #else
-             ctl->server.truename = xstrdup(ctl->server.queryname);
+                   ctl->server.truename = xstrdup(ctl->server.queryname);
 #endif /* HAVE_GETHOSTBYNAME */
+               }
            }
 
            /* if no folders were specified, set up the null one as default */
@@ -1333,7 +1405,10 @@ static void terminate_run(int sig)
     unlockit();
 #endif
 
-    exit(successes ? PS_SUCCESS : querystatus);
+    if (activecount == 0)
+       exit(PS_NOMAIL);
+    else
+       exit(successes ? PS_SUCCESS : querystatus);
 }
 
 /*
@@ -1367,7 +1442,7 @@ static int query_host(struct query *ctl)
               VERSION,
               ctl->server.pollname,
               showproto(ctl->server.protocol),
-              rfc822timestamp());
+              timestamp());
     }
     switch (ctl->server.protocol) {
     case P_AUTO:
@@ -1398,9 +1473,6 @@ static int query_host(struct query *ctl)
 #endif /* POP3_ENABLE */
        break;
     case P_IMAP:
-    case P_IMAP_K4:
-    case P_IMAP_CRAM_MD5:
-    case P_IMAP_LOGIN:
 #ifdef GSSAPI
     case P_IMAP_GSS:
 #endif /* GSSAPI */
@@ -1422,6 +1494,18 @@ static int query_host(struct query *ctl)
        return(PS_PROTOCOL);
 #endif /* HAVE_GETHOSTBYNAME */
 #endif /* ETRN_ENABLE */
+    case P_ODMR:
+#ifndef ODMR_ENABLE
+       report(stderr, _("ODMR support is not configured.\n"));
+       return(PS_PROTOCOL);
+#else
+#ifdef HAVE_GETHOSTBYNAME
+       return(doODMR(ctl));
+#else
+       report(stderr, _("Cannot support ODMR without gethostbyname(2).\n"));
+       return(PS_PROTOCOL);
+#endif /* HAVE_GETHOSTBYNAME */
+#endif /* ODMR_ENABLE */
     default:
        report(stderr, _("unsupported protocol selected.\n"));
        return(PS_PROTOCOL);
@@ -1446,6 +1530,8 @@ static void dump_params (struct runctl *runp,
 #endif
     if (runp->invisible)
        printf(_("Fetchmail will masquerade and will not generate Received\n"));
+    if (runp->showdots)
+       printf(_("Fetchmail will show progress dots even in logfiles.\n"));
     if (runp->postmaster)
        printf(_("Fetchmail will forward misaddressed multidrop messages to %s.\n"),
               runp->postmaster);
@@ -1463,7 +1549,7 @@ static void dump_params (struct runctl *runp,
        printf(_("Options for retrieving from %s@%s:\n"),
               ctl->remotename, visbuf(ctl->server.pollname));
 
-       if (ctl->server.via && (ctl->server.protocol != P_ETRN))
+       if (ctl->server.via && (ctl->server.protocol < P_ETRN))
            printf(_("  Mail will be retrieved via %s\n"), ctl->server.via);
 
        if (ctl->server.interval)
@@ -1474,30 +1560,22 @@ static void dump_params (struct runctl *runp,
        if (ctl->server.skip || outlevel >= O_VERBOSE)
            printf(_("  This host %s be queried when no host is specified.\n"),
                   ctl->server.skip ? _("will not") : _("will"));
-       /*
-        * Don't poll for password when there is one or when using the ETRN
-        * or IMAP-GSS protocol
-        */
-       /* ETRN, IMAP_GSS, and IMAP_K4 do not need a password, so skip this */
-       if ( (ctl->server.protocol != P_ETRN)
-#ifdef GSSAPI
-                               && (ctl->server.protocol != P_IMAP_GSS)
-#endif /* GSSAPI */
-                                       && (ctl->server.protocol != P_IMAP_K4) ) {
-               if (!ctl->password)
-                       printf(_("  Password will be prompted for.\n"));
-               else if (outlevel >= O_VERBOSE)
-               {
-                       if (ctl->server.protocol == P_APOP)
-                               printf(_("  APOP secret = \"%s\".\n"),
-                                                       visbuf(ctl->password));
-                       else if (ctl->server.protocol == P_RPOP)
-                               printf(_("  RPOP id = \"%s\".\n"),
-                                                       visbuf(ctl->password));
-                       else
-                               printf(_("  Password = \"%s\".\n"),
+       if (ctl->server.preauthenticate <= A_PASSWORD)
+       {
+           if (!ctl->password)
+               printf(_("  Password will be prompted for.\n"));
+           else if (outlevel >= O_VERBOSE)
+           {
+               if (ctl->server.protocol == P_APOP)
+                   printf(_("  APOP secret = \"%s\".\n"),
+                          visbuf(ctl->password));
+               else if (ctl->server.protocol == P_RPOP)
+                   printf(_("  RPOP id = \"%s\".\n"),
+                          visbuf(ctl->password));
+               else
+                   printf(_("  Password = \"%s\".\n"),
                                                        visbuf(ctl->password));
-               }
+           }
        }
 
        if (ctl->server.protocol == P_POP3 
@@ -1523,16 +1601,33 @@ static void dump_params (struct runctl *runp,
 #endif /* INET6_ENABLE */
        else if (outlevel >= O_VERBOSE)
            printf(_(" (using default port)"));
-       if (ctl->server.uidl && (ctl->server.protocol != P_ETRN))
+       if (ctl->server.uidl && (ctl->server.protocol < P_ETRN))
            printf(_(" (forcing UIDL use)"));
        putchar('.');
        putchar('\n');
-       if (ctl->server.preauthenticate == A_KERBEROS_V4)
-           printf(_("  Kerberos V4 preauthentication enabled.\n"));
-       else if (ctl->server.preauthenticate == A_KERBEROS_V5)
-           printf(_("  Kerberos V5 preauthentication enabled.\n"));
-       else if (ctl->server.preauthenticate == A_SSH)
+       switch (ctl->server.preauthenticate)
+       {
+       case A_ANY:
+           printf(_("  All authentication methods will be described.\n"));
+           break;
+       case A_PASSWORD:
+           printf(_("  Password authentication will be forced.\n"));
+           break;
+       case A_GSSAPI:
+           printf(_("  GSSAPI preauthentication will be forced.\n"));
+           break;
+       case A_KERBEROS_V4:
+           printf(_("  Kerberos V4 preauthentication will be forced.\n"));
+           break;
+       case A_KERBEROS_V5:
+           printf(_("  Kerberos V5 preauthentication will be forced.\n"));
+           break;
+       case A_SSH:
            printf(_("  End-to-end encryption assumed.\n"));
+           break;
+       }
+       if (ctl->server.principal != (char *) NULL)
+           printf(_("  Mail service principal is: %s\n"), ctl->server.principal);
 #ifdef SSL_ENABLE
        if (ctl->use_ssl)
            printf("  SSL encrypted sessions enabled.\n");
@@ -1544,7 +1639,7 @@ static void dump_params (struct runctl *runp,
        else
            printf(".\n");
 
-       if (ctl->server.protocol != P_ETRN) {
+       if (ctl->server.protocol < P_ETRN) {
                if (!ctl->mailboxes->id)
                    printf(_("  Default mailbox selected.\n"));
                else
@@ -1586,6 +1681,9 @@ static void dump_params (struct runctl *runp,
                printf(_("  Nonempty Status lines will be %s (dropstatus %s)\n"),
                       ctl->dropstatus ? _("discarded") : _("kept"),
                       ctl->dropstatus ? "on" : "off");
+               printf(_("  Delivered-To lines will be %s (dropdelivered %s)\n"),
+                      ctl->dropdelivered ? _("discarded") : _("kept"),
+                      ctl->dropdelivered ? "on" : "off");
                if (NUM_NONZERO(ctl->limit))
                {
                    if (NUM_NONZERO(ctl->limit))
@@ -1608,7 +1706,7 @@ static void dump_params (struct runctl *runp,
                    printf(_("  SMTP message batch limit is %d.\n"), ctl->batchlimit);
                else if (outlevel >= O_VERBOSE)
                    printf(_("  No SMTP message batch limit (--batchlimit 0).\n"));
-               if (ctl->server.protocol == P_IMAP)
+               if (ctl->server.protocol < P_ETRN)
                {
                    if (NUM_NONZERO(ctl->expunge))
                        printf(_("  Deletion interval between expunges forced to %d (--expunge %d).\n"), ctl->expunge, ctl->expunge);
@@ -1618,7 +1716,7 @@ static void dump_params (struct runctl *runp,
        }
        if (ctl->bsmtp)
            printf(_("  Messages will be appended to %s as BSMTP\n"), visbuf(ctl->bsmtp));
-       else if (ctl->mda && (ctl->server.protocol != P_ETRN))
+       else if (ctl->mda && (ctl->server.protocol < P_ETRN))
            printf(_("  Messages will be delivered with \"%s\".\n"), visbuf(ctl->mda));
        else
        {
@@ -1635,8 +1733,11 @@ static void dump_params (struct runctl *runp,
            if (ctl->smtpaddress)
                printf(_("  Host part of MAIL FROM line will be %s\n"),
                       ctl->smtpaddress);
+           if (ctl->smtpname)
+               printf(_("  Address to be put in RCPT TO lines shipped to SMTP will be %s\n"),
+                      ctl->smtpname);
        }
-       if (ctl->server.protocol != P_ETRN)
+       if (ctl->server.protocol < P_ETRN)
        {
                if (ctl->antispam != (struct idlist *)NULL)
                {
@@ -1660,7 +1761,7 @@ static void dump_params (struct runctl *runp,
                   visbuf(ctl->postconnect));
        else if (outlevel >= O_VERBOSE)
            printf(_("  No post-connection command.\n"));
-       if (ctl->server.protocol != P_ETRN) {
+       if (ctl->server.protocol < P_ETRN) {
                if (!ctl->localnames)
                    printf(_("  No localnames declared for this host.\n"));
                else
@@ -1757,7 +1858,7 @@ static void dump_params (struct runctl *runp,
        else if (outlevel >= O_VERBOSE)
            printf(_("  No plugout command specified.\n"));
 
-       if (ctl->server.protocol > P_POP2 && (ctl->server.protocol != P_ETRN))
+       if (ctl->server.protocol > P_POP2 && (ctl->server.protocol < P_ETRN))
        {
            if (!ctl->oldsaved)
                printf(_("  No UIDs saved from this host.\n"));