]> Pileus Git - ~andy/fetchmail/blobdiff - fetchmail.c
Caseblind comparison of names, more explicitness about Kerberos.
[~andy/fetchmail] / fetchmail.c
index a3a734beda355e43a89ca8d65f9e279df623f822..c311bad08ac90ccf9bb95aa028e4c4a69c2702d6 100644 (file)
 #if defined(HAVE_UNISTD_H)
 #include <unistd.h>
 #endif
+#if defined(HAVE_ALLOCA_H)
+#include <alloca.h>
+#endif
 #include <string.h>
 #include <signal.h>
+#if defined(HAVE_SYSLOG)
+#include <syslog.h>
+#endif
 #include <pwd.h>
+#include <errno.h>
 #include <sys/time.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <netdb.h>
 #endif /* HAVE_GETHOSTBYNAME */
 
+#ifdef SUNOS
+#include <stdlib.h>
+#endif
+
 #include "fetchmail.h"
+#include "tunable.h"
 #include "smtp.h"
 #include "getopt.h"
+#include "netrc.h"
 
 #define DROPDEAD       6       /* maximum bad socket opens */
 
@@ -46,15 +59,19 @@ int yydebug;                /* enable parse debugging */
 int poll_interval;     /* poll interval in seconds */
 int nodetach;          /* if TRUE, don't detach daemon process */
 char *logfile;         /* log file for daemon mode */
+int use_syslog;                /* if --syslog was set */
 int quitmode;          /* if --quit was set */
 int check_only;                /* if --probe was set */
-int cmd_batchlimit;    /* if --batchlimit was set */
+char *cmd_logfile;     /* if --logfile was set */
+int cmd_daemon;        /* if --daemon was set */
 
 /* miscellaneous global controls */
 char *rcfile;          /* path name of rc file */
 char *idfile;          /* UID list file */
 int versioninfo;       /* emit only version info */
 char *user;            /* the name of the invoking user */
+char *fetchmailhost;   /* the name of the host running fetchmail */
+char *program_name;    /* the name to prefix error messages with */
 
 static char *lockfile;         /* name of lockfile */
 static int querystatus;                /* status of query */
@@ -64,7 +81,11 @@ static void termhook();              /* forward declaration of exit hook */
 
 RETSIGTYPE donothing(sig) int sig; {signal(sig, donothing); lastsig = sig;}
 
+#ifdef HAVE_ATEXIT
 static void unlockit(void)
+#else  /* use on_exit(), e.g. on SunOS */
+static void unlockit(int n, void *p)
+#endif
 /* must-do actions for exit (but we can't count on being able to do malloc) */
 {
     unlink(lockfile);
@@ -78,8 +99,15 @@ int main (int argc, char **argv)
     struct passwd *pw;
     struct query *ctl;
     FILE       *lockfp;
+    netrc_entry *netrc_list;
+    char *netrc_file;
     pid_t pid;
 
+    if ((program_name = strrchr(argv[0], '/')) != NULL)
+       ++program_name;
+    else
+       program_name = argv[0];
+
     if ((user = getenv("USER")) == (char *)NULL)
         user = getenv("LOGNAME");
 
@@ -97,6 +125,14 @@ int main (int argc, char **argv)
        }
     }
 
+    /* we'll need this for the SMTP forwarding target and error messages */
+    if (gethostname(tmpbuf, sizeof(tmpbuf)))
+    {
+       fprintf(stderr, "fetchmail: can't determine fetchmail's host!");
+       exit(PS_IOERR);
+    }
+    fetchmailhost = xstrdup(tmpbuf);
+
     /*
      * Backward-compatibility hack.  If we're called by the name of the
      * ancestral popclient, look for .poprc.  This will actually work 
@@ -123,19 +159,23 @@ int main (int argc, char **argv)
     if ((parsestatus = parsecmdline(argc,argv,&cmd_opts)) < 0)
        exit(PS_SYNTAX);
 
+    /* this hint to stdio should help messages come out in the right order */
+    setvbuf(stdout, NULL, _IOLBF, POPBUFSIZE);
+
     if (versioninfo)
-       printf("This is fetchmail release %s pl %s\n", RELEASE_ID, PATCHLEVEL);
+       printf("This is fetchmail release %s\n", RELEASE_ID);
 
     /* avoid parsing the config file if all we're doing is killing a daemon */ 
     if (!quitmode)
        implicitmode = load_params(argc, argv, optind);
 
     /* set up to do lock protocol */
-    if ((tmpdir = getenv("TMPDIR")) == (char *)NULL)
-       tmpdir = "/tmp";
-    strcpy(tmpbuf, tmpdir);
-    strcat(tmpbuf, "/fetchmail-");
-    strcat(tmpbuf, user);
+    if (!getuid())
+       strcpy(tmpbuf, "/var/run/fetchmail.pid");
+    else {
+       strcpy(tmpbuf, home);
+       strcat(tmpbuf, "/.fetchmail");
+    }
 
     /* perhaps we just want to check options? */
     if (versioninfo) {
@@ -146,18 +186,13 @@ int main (int argc, char **argv)
            printf(" and %s\n", rcfile);
        if (outlevel == O_VERBOSE)
            printf("Lockfile at %s\n", tmpbuf);
-       if (batchlimit)
-           printf("SMTP message batch limit is %d.\n", batchlimit);
-       else
-           printf("No SMTP message batch limit.\n");
        for (ctl = querylist; ctl; ctl = ctl->next) {
-           if (ctl->active && !(implicitmode && ctl->skip))
+           if (ctl->active && !(implicitmode && ctl->server.skip))
                dump_params(ctl);
        }
        if (querylist == NULL)
            (void) fprintf(stderr,
-               "No mailservers set up -- perhaps %s is missing?\n",
-                         rcfile);
+               "No mailservers set up -- perhaps %s is missing?\n", rcfile);
        exit(0);
     }
     else if (!quitmode && querylist == NULL) {
@@ -182,7 +217,7 @@ int main (int argc, char **argv)
            fprintf(stderr,"fetchmail: removing stale lockfile\n");
            pid = -1;
            bkgd = FALSE;
-           remove(lockfile);
+           unlink(lockfile);
        }
        fclose(lockfp);
     }
@@ -205,7 +240,7 @@ int main (int argc, char **argv)
        {
            fprintf(stderr,"fetchmail: %s fetchmail at %d killed.\n",
                    bkgd ? "background" : "foreground", pid);
-           remove(lockfile);
+           unlink(lockfile);
            exit(0);
        }
     }
@@ -214,7 +249,11 @@ int main (int argc, char **argv)
     if (pid != -1)
     {
        if (check_only)
+       {
+           fprintf(stderr,
+                "fetchmail: can't check mail while another fetchmail to same host is running.\n");
            return(PS_EXCLUDE);
+        }
        else if (!implicitmode)
        {
            fprintf(stderr,
@@ -229,7 +268,7 @@ int main (int argc, char **argv)
                 pid);
                return(PS_EXCLUDE);
        }
-       else if (kill(pid, SIGHUP) == 0)
+       else if (kill(pid, SIGUSR1) == 0)
        {
            fprintf(stderr,
                    "fetchmail: background fetchmail at %d awakened.\n",
@@ -240,7 +279,7 @@ int main (int argc, char **argv)
        {
            /*
             * Should never happen -- possible only if a background fetchmail
-            * croaks after the first kill probe above but before the SIGHUP
+            * croaks after the first kill probe above but before the SIGUSR1/SIGHUP
             * transmission.
             */
            fprintf(stderr,
@@ -250,29 +289,57 @@ int main (int argc, char **argv)
        }
     }
 
+    /* parse the ~/.netrc file, for future password lookups. */
+    netrc_file = (char *) xmalloc (strlen (home) + 8);
+    strcpy (netrc_file, home);
+    strcat (netrc_file, "/.netrc");
+
+    netrc_list = parse_netrc (netrc_file);
+
     /* pick up interactively any passwords we need but don't have */ 
     for (ctl = querylist; ctl; ctl = ctl->next)
-       if (ctl->active && !(implicitmode && ctl->skip) && !ctl->password[0])
+       if (ctl->active && !(implicitmode && ctl->server.skip) && !ctl->password)
        {
-           if (ctl->authenticate == A_KERBEROS)
-             /* Server won't care what the password is, but there
-                must be some non-null string here.  */
-             (void) strncpy(ctl->password, 
-                            ctl->remotename, PASSWORDLEN-1);
+           if (ctl->server.authenticate == A_KERBEROS_V4)
+               /* Server won't care what the password is, but there
+                  must be some non-null string here.  */
+               ctl->password = ctl->remotename;
            else
-             {
+           {
+               /* Look up the host and account in the .netrc file. */
+               netrc_entry *p = search_netrc(netrc_list,ctl->server.names->id);
+               while (p && strcmp (p->account, ctl->remotename))
+                   p = search_netrc (p->next, ctl->remotename);
+
+               if (p)
+               {
+                   /* We found the entry, so use the password. */
+                   ctl->password = xstrdup(p->password);
+               }
+           }
+
+           if (ctl->server.protocol != P_ETRN && !ctl->password)
+           {
                (void) sprintf(tmpbuf, "Enter password for %s@%s: ",
-                              ctl->remotename, ctl->servername);
-               (void) strncpy(ctl->password,
-                              (char *)getpassword(tmpbuf),PASSWORDLEN-1);
-             }
+                              ctl->remotename, ctl->server.names->id);
+               ctl->password = xstrdup((char *)getpassword(tmpbuf));
+           }
        }
 
     /*
      * Maybe time to go to demon mode...
      */
-    if (poll_interval && !nodetach)
-       daemonize(logfile, termhook);
+#if defined(HAVE_SYSLOG)
+    if (use_syslog)
+       openlog(program_name, LOG_PID, LOG_MAIL);
+#endif
+
+    if (poll_interval)
+    {
+       if (!nodetach)
+           daemonize(logfile, termhook);
+       error( 0, 0, "starting fetchmail %s daemon ", RELEASE_ID);
+    }
 
     /* beyond here we don't want more than one fetchmail running per user */
     umask(0077);
@@ -289,7 +356,11 @@ int main (int argc, char **argv)
      * side effect of interrupting any sleep that may be going on,
      * forcing fetchmail to re-poll its hosts.
      */
-    signal(SIGHUP, donothing);
+    signal(SIGUSR1, donothing);
+
+    /* pacify people who think all system daemons wake up on SIGHUP */
+    if (poll_interval && !getuid())
+       signal(SIGHUP, donothing);
 
     /* here's the exclusion lock */
     if ( (lockfp = fopen(lockfile,"w")) != NULL ) {
@@ -297,7 +368,12 @@ int main (int argc, char **argv)
        if (poll_interval)
            fprintf(lockfp," %d", poll_interval);
        fclose(lockfp);
+
+#ifdef HAVE_ATEXIT
        atexit(unlockit);
+#else
+       on_exit(unlockit, (char *)NULL);
+#endif
     }
 
     /*
@@ -312,38 +388,65 @@ int main (int argc, char **argv)
        batchcount = 0;
        for (ctl = querylist; ctl; ctl = ctl->next)
        {
-           if (ctl->active && !(implicitmode && ctl->skip))
+           if (ctl->active && !(implicitmode && ctl->server.skip))
            {
+#ifdef linux
+               /* interface_approve() does its own error logging */
+               if (!interface_approve(&ctl->server))
+                   continue;
+#endif /* linux */
+
 #ifdef HAVE_GETHOSTBYNAME
                /*
                 * This functions partly as an optimization and partly
                 * as a probe to make sure our nameserver is still up.
                 * The multidrop case (especially) needs it.
                 */
-               if (ctl->authenticate == A_KERBEROS || MULTIDROP(ctl))
+               if (ctl->server.authenticate==A_KERBEROS_V4 || MULTIDROP(ctl))
                {
                    struct hostent      *namerec;
 
                    /* compute the canonical name of the host */
-                   namerec = gethostbyname(ctl->servername);
+                   errno = 0;
+                   namerec = gethostbyname(ctl->server.names->id);
                    if (namerec == (struct hostent *)NULL)
                    {
-                       fprintf(stderr,
-                               "fetchmail: skipping %s poll, nameserver isn't responding\n",
-                               ctl->servername);
+                       error(0, errno,
+                               "skipping %s poll, ",
+                               ctl->server.names->id);
+                       if (errno)
+                       {
+                           if (errno == ENETUNREACH)
+                               break;  /* go to sleep */
+                       }
+#ifdef HAVE_HERROR             /* NEXTSTEP doesn't */
+                       else
+                           herror("DNS error");
+#endif /* HAVE_HERROR */
                        continue;
                    }
                    else
                    {
-                       free(ctl->canonical_name);
-                       ctl->canonical_name = xstrdup((char *)namerec->h_name);
+                       free(ctl->server.canonical_name);
+                       ctl->server.canonical_name = xstrdup((char *)namerec->h_name);
                    }
                }
 #endif /* HAVE_GETHOSTBYNAME */
 
                querystatus = query_host(ctl);
                if (!check_only)
-                   update_uid_lists(ctl);
+                   update_str_lists(ctl);
+#ifdef linux
+               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
            }
        }
 
@@ -367,6 +470,9 @@ int main (int argc, char **argv)
                ctl->smtp_sockfp = (FILE *)NULL;
            }
 
+       /*
+        * OK, we've polled.  Now sleep.
+        */
        if (poll_interval)
        {
            if (outlevel == O_VERBOSE)
@@ -393,8 +499,16 @@ int main (int argc, char **argv)
                setitimer(ITIMER_REAL,&ntimeout,NULL);
                signal(SIGALRM, donothing);
                pause();
-               if (lastsig == SIGHUP)
-                   (void) fputs("fetchmail: awakened by SIGHUP\n", stderr);
+               signal(SIGALRM, SIG_IGN);
+               if (lastsig == SIGUSR1
+                       || ((poll_interval && !getuid()) && lastsig == SIGHUP))
+               {
+#ifdef SYS_SIGLIST_DECLARED
+                   error(0, 0, "awakened by %s", sys_siglist[lastsig]);
+#else
+                   error(0, 0, "awakened by signal %d", lastsig);
+#endif
+               }
            }
 
            if (outlevel == O_VERBOSE)
@@ -423,10 +537,10 @@ static int load_params(int argc, char **argv, int optind)
 
     memset(&def_opts, '\0', sizeof(struct query));
 
-    def_opts.protocol = P_AUTO;
-    def_opts.timeout = CLIENT_TIMEOUT;
-    strcpy(def_opts.remotename, user);
-    strcpy(def_opts.smtphost, "localhost");
+    def_opts.server.protocol = P_AUTO;
+    def_opts.server.timeout = CLIENT_TIMEOUT;
+    def_opts.remotename = user;
+    save_str(&def_opts.smtphunt, -1, fetchmailhost);
 
     /* this builds the host list */
     if (prc_parse_file(rcfile) != 0)
@@ -446,33 +560,33 @@ static int load_params(int argc, char **argv, int optind)
             * record from command line and defaults
             */
            for (ctl = querylist; ctl; ctl = ctl->next)
-               if (strcmp(ctl->servername, argv[optind]) == 0)
+               if (str_in_list(&ctl->server.names, argv[optind]))
                    goto foundit;
 
            ctl = hostalloc(&cmd_opts);
-           strcpy(ctl->servername, argv[optind]);
+           save_str(&ctl->server.names, -1, argv[optind]);
 
        foundit:
            ctl->active = TRUE;
        }
 
     /* if there's a defaults record, merge it and lose it */ 
-    if (querylist && strcmp(querylist->servername, "defaults") == 0)
+    if (querylist && strcmp(querylist->server.names->id, "defaults") == 0)
     {
-       for (ctl = querylist; ctl; ctl = ctl->next)
+       for (ctl = querylist->next; ctl; ctl = ctl->next)
            optmerge(ctl, querylist);
        querylist = querylist->next;
     }
 
     /* don't allow a defaults record after the first */
     for (ctl = querylist; ctl; ctl = ctl->next)
-       if (strcmp(ctl->servername, "defaults") == 0)
+       if (ctl != querylist && strcmp(ctl->server.names->id, "defaults") == 0)
            exit(PS_SYNTAX);
 
     /* merge in wired defaults, do sanity checks and prepare internal fields */
     for (ctl = querylist; ctl; ctl = ctl->next)
     {
-       if (ctl->active && !(implicitmode && ctl->skip))
+       if (ctl->active && !(implicitmode && ctl->server.skip))
        {
            /* merge in defaults */
            optmerge(ctl, &def_opts);
@@ -495,7 +609,7 @@ static int load_params(int argc, char **argv, int optind)
            {
                ctl->uid = pw->pw_uid;  /* for local delivery via MDA */
                if (!ctl->localnames)   /* for local delivery via SMTP */
-                   save_uid(&ctl->localnames, -1, user);
+                   save_str_pair(&ctl->localnames, user, NULL);
            }
 
 #if !defined(HAVE_GETHOSTBYNAME) || !defined(HAVE_RES_SEARCH)
@@ -508,70 +622,51 @@ static int load_params(int argc, char **argv, int optind)
            }
 #endif /* !HAVE_GETHOSTBYNAME || !HAVE_RES_SEARCH */
 
-           /*
-            * Assign SMTP leaders.  We want to allow all query blocks
-            * sharing the same SMTP host to use the same SMTP connection.
-            * To accomplish this, we initialize each query block's leader
-            * field to point to the first block in the list with a matching 
-            * SMTP host.
-            *
-            * In the typical case, there will be only one SMTP host (the
-            * client machine) and thus just one SMTP leader (and one listener
-            * process) through the entire poll cycle.
-            */
-           if (!ctl->mda[0])
-           {
-               ctl->smtp_sockfp = (FILE *)NULL;
-               for (mp = querylist; mp && mp != ctl; mp = mp->next)
-                   if (strcmp(mp->smtphost, ctl->smtphost) == 0)
-                   {
-                       ctl->lead_smtp = mp->lead_smtp;
-                       goto no_new_leader;
-                   }
-               ctl->lead_smtp = ctl;
-           no_new_leader:;
-           }
-
-           /* similarly, compute server leaders for queries */
-           ctl->aka = (struct idlist *)NULL;
+           /* compute server leaders for queries */
            for (mp = querylist; mp && mp != ctl; mp = mp->next)
-               if (strcmp(mp->servername, ctl->servername) == 0)
+               if (strcmp(mp->server.names->id, ctl->server.names->id) == 0)
                {
-                   ctl->lead_server = mp->lead_server;
+                   ctl->server.lead_server = mp->server.lead_server;
                    goto no_new_server;
                }
-           ctl->lead_server = ctl;
+           ctl->server.lead_server = &(ctl->server);
        no_new_server:;
 
+           /* this code enables flags to be turned off */
+#define DEFAULT(flag, dflt)    if (flag == FLAG_TRUE)\
+                                       flag = TRUE;\
+                               else if (flag == FLAG_FALSE)\
+                                       flag = FALSE;\
+                               else\
+                                       flag = (dflt)
+           DEFAULT(ctl->keep, FALSE);
+           DEFAULT(ctl->flush, FALSE);
+           DEFAULT(ctl->fetchall, FALSE);
+           DEFAULT(ctl->rewrite, TRUE);
+           DEFAULT(ctl->stripcr, (ctl->mda != (char *)NULL)); 
+           DEFAULT(ctl->forcecr, FALSE);
+           DEFAULT(ctl->server.dns, TRUE);
+           DEFAULT(ctl->server.uidl, FALSE);
+#undef DEFAULT
+
+           /* plug in the semi-standard way of indicating a mail address */
+           if (ctl->server.envelope == (char *)NULL)
+               ctl->server.envelope = "X-Envelope-To:";
+
            /* sanity checks */
-           if (ctl->port < 0)
+           if (ctl->server.port < 0)
            {
                (void) fprintf(stderr,
                               "%s configuration invalid, port number cannot be negative",
-                              ctl->servername);
+                              ctl->server.names->id);
                exit(PS_SYNTAX);
            }
-
-           /* expand MDA commands */
-           if (!check_only && ctl->mda[0])
+           if (ctl->server.protocol == P_RPOP && ctl->server.port >= 1024)
            {
-               char *argp;
-
-               /* punch nulls into the delimiting whitespace in the args */
-               for (argp = ctl->mda, ctl->mda_argcount = 1; *argp != '\0'; ctl->mda_argcount++)
-               {
-                   ctl->mda_argv[ctl->mda_argcount] = argp;
-                   while (!(*argp == '\0' || isspace(*argp)))
-                       argp++;
-                   if (*argp != '\0')
-                       *(argp++) = '\0';  
-               }
-
-               ctl->mda_argv[ctl->mda_argcount] = (char *)NULL;
-
-               ctl->mda_argv[0] = ctl->mda_argv[1];
-               if ((argp = strrchr(ctl->mda_argv[1], '/')) != (char *)NULL)
-                   ctl->mda_argv[1] = argp + 1 ;
+               (void) fprintf(stderr,
+                              "%s configuration invalid, RPOP requires a privileged port",
+                              ctl->server.names->id);
+               exit(PS_SYNTAX);
            }
        }
     }
@@ -582,9 +677,17 @@ static int load_params(int argc, char **argv, int optind)
     else
        initialize_saved_lists(querylist, idfile);
 
-    /* if cmd_batchlimit was explicitly set, use it to override batchlimit */
-    if (cmd_batchlimit > -1)
-       batchlimit = cmd_batchlimit;
+    /* if cmd_logfile was explicitly set, use it to override logfile */
+    if (cmd_logfile)
+       logfile = cmd_logfile;
+
+    /* likewise for poll_interval */
+    if (cmd_daemon >= 0)
+       poll_interval = cmd_daemon;
+
+    /* check and daemon options are not compatible */
+    if (check_only && poll_interval)
+       poll_interval = 0;
 
     return(implicitmode);
 }
@@ -594,13 +697,23 @@ void termhook(int sig)
 {
     struct query       *ctl;
 
-    if (sig != 0)
-       fprintf(stderr, "terminated with signal %d\n", sig);
+    /*
+     * Sending SMTP QUIT on signal is theoretically nice, but led to a 
+     * subtle bug.  If fetchmail was terminated by signal while it was 
+     * shipping message text, it would hang forever waiting for a
+     * command acknowledge.  In theory we could disable the QUIT
+     * only outside of the message send.  In practice, we don't
+     * care.  All mailservers hang up on a dropped TCP/IP connection
+     * anyway.
+     */
 
-    /* terminate all SMTP connections cleanly */
-    for (ctl = querylist; ctl; ctl = ctl->next)
-       if (ctl->lead_smtp == ctl && ctl->smtp_sockfp != (FILE *)NULL)
-           SMTP_quit(ctl->smtp_sockfp);
+    if (sig != 0)
+        error(0, 0, "terminated with signal %d", sig);
+    else
+       /* terminate all SMTP connections cleanly */
+       for (ctl = querylist; ctl; ctl = ctl->next)
+           if (ctl->smtp_sockfp != (FILE *)NULL)
+               SMTP_quit(ctl->smtp_sockfp);
 
     if (!check_only)
        write_saved_lists(querylist, idfile);
@@ -618,6 +731,8 @@ static char *showproto(int proto)
     case P_POP3: return("POP3"); break;
     case P_IMAP: return("IMAP"); break;
     case P_APOP: return("APOP"); break;
+    case P_RPOP: return("RPOP"); break;
+    case P_ETRN: return("ETRN"); break;
     default: return("unknown?!?"); break;
     }
 }
@@ -637,18 +752,19 @@ static int query_host(struct query *ctl)
        time_t now;
 
        time(&now);
-       fprintf(stderr, "Querying %s (protocol %s) at %s",
-           ctl->servername, showproto(ctl->protocol), ctime(&now));
+       fprintf(stderr, "fetchmail: %s querying %s (protocol %s) at %s",
+           RELEASE_ID,
+           ctl->server.names->id, showproto(ctl->server.protocol), ctime(&now));
     }
-    switch (ctl->protocol) {
+    switch (ctl->server.protocol) {
     case P_AUTO:
        for (i = 0; i < sizeof(autoprobe)/sizeof(autoprobe[0]); i++)
        {
-           ctl->protocol = autoprobe[i];
+           ctl->server.protocol = autoprobe[i];
            if ((st = query_host(ctl)) == PS_SUCCESS || st == PS_NOMAIL || st == PS_AUTHFAIL)
                break;
        }
-       ctl->protocol = P_AUTO;
+       ctl->server.protocol = P_AUTO;
        return(st);
        break;
     case P_POP2:
@@ -656,13 +772,16 @@ static int query_host(struct query *ctl)
        break;
     case P_POP3:
     case P_APOP:
+    case P_RPOP:
        return(doPOP3(ctl));
        break;
     case P_IMAP:
        return(doIMAP(ctl));
        break;
+    case P_ETRN:
+       return(doETRN(ctl));
     default:
-       fprintf(stderr,"fetchmail: unsupported protocol selected.\n");
+       error(0, 0, "unsupported protocol selected.");
        return(PS_PROTOCOL);
     }
 }
@@ -671,40 +790,67 @@ void dump_params (struct query *ctl)
 /* display query parameters in English */
 {
     printf("Options for retrieving from %s@%s:\n",
-          ctl->remotename, visbuf(ctl->servername));
+          ctl->remotename, visbuf(ctl->server.names->id));
+
+    if (logfile)
+       printf("  Logfile is %s\n", logfile);
+    if (poll_interval)
+       printf("  Poll interval is %d seconds\n", poll_interval);
 #ifdef HAVE_GETHOSTBYNAME
-    if (ctl->canonical_name)
-       printf("  Canonical DNS name of server is %s.\n", ctl->canonical_name);
+    if (ctl->server.canonical_name)
+       printf("  Canonical DNS name of server is %s.\n", ctl->server.canonical_name);
 #endif /* HAVE_GETHOSTBYNAME */
-    if (ctl->skip || outlevel == O_VERBOSE)
+    if (ctl->server.names->next)
+    {
+       struct idlist *idp;
+
+       printf("  Predeclared mailserver aliases:");
+       for (idp = ctl->server.names->next; idp; idp = idp->next)
+           printf(" %s", idp->id);
+       putchar('\n');
+    }
+    if (ctl->server.skip || outlevel == O_VERBOSE)
        printf("  This host will%s be queried when no host is specified.\n",
-              ctl->skip ? " not" : "");
-    if (ctl->password[0] == '\0')
+              ctl->server.skip ? " not" : "");
+    if (!ctl->password)
        printf("  Password will be prompted for.\n");
     else if (outlevel == O_VERBOSE)
-       if (ctl->protocol == P_APOP)
+       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->protocol == P_POP3 
-               && ctl->port == KPOP_PORT
-               && ctl->authenticate == A_KERBEROS)
+    if (ctl->server.protocol == P_POP3 
+               && ctl->server.port == KPOP_PORT
+               && ctl->server.authenticate == A_KERBEROS_V4)
        printf("  Protocol is KPOP");
     else
-       printf("  Protocol is %s", showproto(ctl->protocol));
-    if (ctl->port)
-       printf(" (using port %d)", ctl->port);
+       printf("  Protocol is %s", showproto(ctl->server.protocol));
+    if (ctl->server.port)
+       printf(" (using port %d)", ctl->server.port);
     else if (outlevel == O_VERBOSE)
        printf(" (using default port)");
+    if (ctl->server.uidl)
+       printf(" (forcing UIDL use)");
     putchar('.');
     putchar('\n');
-    if (ctl->authenticate == A_KERBEROS)
-           printf("  Kerberos authentication enabled.\n");
-    printf("  Server nonresponse timeout is %d seconds", ctl->timeout);
-    if (ctl->timeout ==  CLIENT_TIMEOUT)
+    if (ctl->server.authenticate == A_KERBEROS_V4)
+           printf("  Kerberos V4 preauthentication enabled.\n");
+    printf("  Server nonresponse timeout is %d seconds", ctl->server.timeout);
+    if (ctl->server.timeout ==  CLIENT_TIMEOUT)
        printf(" (default).\n");
     else
        printf(".\n");
+    if (ctl->server.localdomains)
+    {
+       struct idlist *idp;
+
+       printf("  Local domains:");
+       for (idp = ctl->server.localdomains; idp; idp = idp->next)
+           printf(" %s", idp->id);
+       putchar('\n');
+    }
 
     printf("  %s messages will be retrieved (--all %s).\n",
           ctl->fetchall ? "All" : "Only new",
@@ -716,25 +862,43 @@ void dump_params (struct query *ctl)
           ctl->flush ? "" : " not",
           ctl->flush ? "on" : "off");
     printf("  Rewrite of server-local addresses is %sabled (--norewrite %s).\n",
-          ctl->norewrite ? "dis" : "en",
-          ctl->norewrite ? "on" : "off");
+          ctl->rewrite ? "en" : "dis",
+          ctl->rewrite ? "off" : "on");
+    printf("  Carriage-return stripping is %sabled (--stripcr %s).\n",
+          ctl->stripcr ? "en" : "dis",
+          ctl->stripcr ? "on" : "off");
+    printf("  Carriage-return forcing is %sabled (--forcecr %s).\n",
+          ctl->forcecr ? "en" : "dis",
+          ctl->forcecr ? "on" : "off");
     if (ctl->limit)
-       printf("  Message size limit is %d bytes\n", ctl->limit);
+       printf("  Message size limit is %d bytes (--limit %d).\n", 
+              ctl->limit, ctl->limit);
+    else if (outlevel == O_VERBOSE)
+       printf("  No message size limit (--limit 0).\n");
+    if (ctl->fetchlimit)
+       printf("  Received-message limit is %d (--fetchlimit %d).\n",
+              ctl->fetchlimit, ctl->fetchlimit);
+    else if (outlevel == O_VERBOSE)
+       printf("  No received-message limit (--fetchlimit 0).\n");
+    if (ctl->batchlimit)
+       printf("  SMTP message batch limit is %d.\n", ctl->batchlimit);
     else if (outlevel == O_VERBOSE)
-       printf("  No message size limit\n");
-    if (ctl->mda[0])
+       printf("  No SMTP message batch limit.\n");
+    if (ctl->mda)
+       printf("  Messages will be delivered with '%s.'\n", visbuf(ctl->mda));
+    else
     {
-       char **cp;
+       struct idlist *idp;
 
-       printf("  Messages will be delivered with %s, args:",
-              visbuf(ctl->mda_argv[0]));
-       for (cp = ctl->mda_argv+1; *cp; cp++)
-           printf(" %s", visbuf(*cp));
-       putchar('\n');
+       printf("  Messages will be SMTP-forwarded to:");
+       for (idp = ctl->smtphunt; idp; idp = idp->next)
+           printf(" %s", idp->id);
+       printf("\n");
     }
-    else
-       printf("  Messages will be SMTP-forwarded to '%s'.\n",
-              visbuf(ctl->smtphost));
+    if (ctl->preconnect)
+       printf("  Server connection will be preinitialized with '%s.'\n", visbuf(ctl->preconnect));
+    else if (outlevel == O_VERBOSE)
+       printf("  No preinitialization command.\n");
     if (!ctl->localnames)
        printf("  No localnames declared for this host.\n");
     else
@@ -749,14 +913,38 @@ void dump_params (struct query *ctl)
               count,
               (count == 1 && !strcmp(ctl->localnames->id, user)) ? " (by default)" : "");
        if (outlevel == O_VERBOSE)
+       {
            for (idp = ctl->localnames; idp; idp = idp->next)
                if (idp->val.id2)
-                   fprintf(stderr, "\t%s -> %s\n", idp->id, idp->val.id2);
+                   printf("\t%s -> %s\n", idp->id, idp->val.id2);
                else
-                   fprintf(stderr, "\t%s\n", idp->id);
+                   printf("\t%s\n", idp->id);
+           if (ctl->wildcard)
+               fputs("*\n", stdout);
+       }
+
+       printf("  DNS lookup for multidrop addresses is %sabled.\n",
+              ctl->server.dns ? "en" : "dis",
+              ctl->server.dns ? "off" : "on");
+
+       if (count > 1)
+           if (ctl->server.envelope == STRING_DISABLED)
+               printf("  Envelope-address routing is disabled\n");
+           else
+               printf("  Envelope header is assumed to be: %s\n", ctl->server.envelope);
     }
+#ifdef linux
+    if (ctl->server.interface)
+       printf("  Connection must be through interface %s.\n", ctl->server.interface);
+    else if (outlevel == O_VERBOSE)
+       printf("  No interface requirement specified.\n");
+    if (ctl->server.monitor)
+       printf("  Polling loop will monitor %s.\n", ctl->server.monitor);
+    else if (outlevel == O_VERBOSE)
+       printf("  No monitor interface specified.\n");
+#endif
 
-    if (ctl->protocol > P_POP2)
+    if (ctl->server.protocol > P_POP2)
        if (!ctl->oldsaved)
            printf("  No UIDs saved from this host.\n");
        else
@@ -770,89 +958,12 @@ void dump_params (struct query *ctl)
            printf("  %d UIDs saved.\n", count);
            if (outlevel == O_VERBOSE)
                for (idp = ctl->oldsaved; idp; idp = idp->next)
-                   fprintf(stderr, "\t%s %s\n", ctl->servername, idp->id);
-       }
-}
-
-int openmailpipe (char **argv)
-/* open a one-way pipe to a mail delivery agent */
-{
-    int pipefd [2];
-    int childpid;
-
-    if (outlevel == O_VERBOSE)
-    {
-       char **cp;
-
-       printf("fetchmail: about to deliver via MDA %s, args:",
-              visbuf(argv[0]));
-       for (cp = argv+1; *cp; cp++)
-           printf(" %s", visbuf(*cp));
-       putchar('\n');
-    }
-
-    if (pipe(pipefd) < 0) {
-       perror("fetchmail: openmailpipe: pipe");
-       return(-1);
-    }
-    if ((childpid = fork()) < 0) {
-       perror("fetchmail: openmailpipe: fork");
-       return(-1);
-    }
-    else if (childpid == 0) {
-
-       /* in child process space */
-       close(pipefd[1]);  /* close the 'write' end of the pipe */
-       close(0);          /* get rid of inherited stdin */
-       if (dup(pipefd[0]) != 0) {
-           fputs("fetchmail: openmailpipe: dup() failed\n",stderr);
-           exit(1);
+                   fprintf(stderr, "\t%s\n", idp->id);
        }
-
-       execv(argv[0], argv + 1);
-
-       /* if we got here, an error occurred */
-       perror("fetchmail: openmailpipe: exec");
-       _exit(PS_SYNTAX);
-
-    }
-
-    /* in the parent process space */
-    close(pipefd[0]);  /* close the 'read' end of the pipe */
-    return(pipefd[1]);
-}
-
-int closemailpipe (fd)
-/* close the pipe to the mail delivery agent */
-int fd;
-{
-    int err, status;
-    int childpid;
-
-    if ((err = close(fd)) != 0)
-       perror("fetchmail: closemailpipe: close failed");
-
-    childpid = wait(&status);
-
-#if defined(WIFEXITED) && defined(WEXITSTATUS)
-    /*
-     * Try to pass up an error if the MDA returned nonzero status,
-     * on the assumption that this means it was reporting failure.
-     */
-    if (WIFEXITED(status) == 0 && WEXITSTATUS(status) != 0)
-    {
-       perror("fetchmail: MDA exited abnormally or returned nonzero status");
-       err = -1;
-    }
-#endif
-
-    return(err);
 }
 
 /* helper functions for string interpretation and display */
 
-#define CTRL(x)        ((x) & 0x1f)
-
 void escapes(cp, tp)
 /* process standard C-style escape sequences in a string */
 const char     *cp;    /* source string with escapes */
@@ -890,11 +1001,6 @@ char              *tp;    /* target buffer for digested string */
            }
            cp++;
        }
-       else if (*cp == '^')            /* expand control-character syntax */
-       {
-           cval = CTRL(*++cp);
-           cp++;
-       }
        else
            cval = *cp++;
        *tp++ = cval;