]> Pileus Git - ~andy/fetchmail/blobdiff - driver.c
Change name of string-saver functions.
[~andy/fetchmail] / driver.c
index 15a202f87d62451a1a481c2c00542de49c7c7cf7..70192ac59b468e3d0e24332948ff793551ae7386 100644 (file)
--- a/driver.c
+++ b/driver.c
@@ -43,6 +43,7 @@
 #define        SMTP_PORT       25      /* standard SMTP service port */
 
 int batchlimit;                /* how often to tear down the delivery connection */
+int batchcount;                /* count of messages sent in current batch */
 
 static const struct method *protocol;
 static jmp_buf restart;
@@ -75,13 +76,12 @@ int count;  /* length of src */
   return len;
 }
 
-static void vtalarm(timeleft)
+static void vtalarm(int timeleft)
 /* reset the nonresponse-timeout */
-int    timeleft;
 {
     struct itimerval ntimeout;
 
-    ntimeout.it_interval.tv_sec = ntimeout.it_interval.tv_sec = 0;
+    ntimeout.it_interval.tv_sec = ntimeout.it_interval.tv_usec = 0;
     ntimeout.it_value.tv_sec  = timeleft;
     ntimeout.it_value.tv_usec = 0;
     setitimer(ITIMER_VIRTUAL, &ntimeout, (struct itimerval *)NULL);
@@ -93,74 +93,132 @@ static void vtalarm_handler (int signal)
     longjmp(restart, 1);
 }
 
-#ifdef HAVE_GETHOSTBYNAME
+#ifdef HAVE_RES_SEARCH
 #define MX_RETRIES     3
 
-static int is_host_alias(name, ctl)
+static int is_host_alias(const char *name, struct query *ctl)
 /* determine whether name is a DNS alias of the hostname */
-const char *name;
-struct query   *ctl;
 {
     struct hostent     *he;
-    int                        i;
+    struct mxentry     *mxp, *mxrecords;
 
     /*
      * The first two checks are optimizations that will catch a good
-     * many cases.  First, check against the hostname the user specified.
-     * Odds are good this will either be the mailserver's FQDN or a
-     * suffix of it with the mailserver's domain's default host name
-     * omitted.  Next, check against the mailserver's FQDN, in case
+     * many cases.  (1) check against the hostname the user
+     * specified.  Odds are good this will either be the mailserver's
+     * FQDN or a suffix of it with the mailserver's domain's default
+     * host name omitted.  Then check the rest of the `also known as'
+     * cache accumulated by previous DNS checks.  This cache is primed
+     * by the aka list option.
+     *
+     * (2) check against the mailserver's FQDN, in case
      * it's not the same as the declared hostname.
      *
      * Either of these on a mail address is definitive.  Only if the
      * name doesn't match either is it time to call the bind library.
      * If this happens odds are good we're looking at an MX name.
      */
-    if (strcmp(name, ctl->servername) == 0)
+    if (str_in_list(&ctl->lead_server->servernames, name))
        return(TRUE);
     else if (strcmp(name, ctl->canonical_name) == 0)
        return(TRUE);
 
     /*
-     * We treat DNS lookup failure as a negative on the theory that
-     * the mailserver's DNS server is `nearby' and should be able
-     * to respond quickly and reliably.  Ergo if we get failure,
-     * the name isn't a mailserver alias.
+     * We know DNS service was up at the beginning of this poll cycle.
+     * If it's down, our nameserver has crashed.  We don't want to try
+     * delivering the current message or anything else from this
+     * mailbox until it's back up.
      */
-    else if ((he = gethostbyname(name)) && strcmp(ctl->canonical_name, he->h_name) == 0)
-       return(TRUE);
+    else if ((he = gethostbyname(name)) != (struct hostent *)NULL)
+    {
+       if (strcmp(ctl->canonical_name, he->h_name) == 0)
+           goto match;
+       else
+           return(FALSE);
+    }
+    else
+       switch (h_errno)
+       {
+       case HOST_NOT_FOUND:    /* specified host is unknown */
+       case NO_ADDRESS:        /* valid, but does not have an IP address */
+           break;
+
+       case NO_RECOVERY:       /* non-recoverable name server error */
+       case TRY_AGAIN:         /* temporary error on authoritative server */
+       default:
+           if (outlevel != O_SILENT)
+               putchar('\n');  /* terminate the progress message */
+           fprintf(stderr,
+               "fetchmail: nameserver failure while looking for `%s' during poll of %s.\n",
+               name, ctl->servernames->id);
+           ctl->errcount++;
+           longjmp(restart, 2);        /* try again next poll cycle */
+           break;
+       }
 
     /*
-     * Search for a name match on MX records pointing to the server
-     * site.  These may live far away, so allow a couple of retries.
+     * We're only here if DNS was OK but the gethostbyname() failed
+     * with a HOST_NOT_FOUND or NO_ADDRESS error.
+     * Search for a name match on MX records pointing to the server.
      */
-    for (i = 0; i < MX_RETRIES; i++)
-    {
-       struct mxentry *mxrecords, *mxp;
+    h_errno = 0;
+    if ((mxrecords = getmxrecords(name)) == (struct mxentry *)NULL)
+       switch (h_errno)
+       {
+       case HOST_NOT_FOUND:    /* specified host is unknown */
+           return(FALSE);
+
+       case NO_ADDRESS:        /* valid, but does not have an IP address */
+           for (mxp = mxrecords; mxp->name; mxp++)
+               if (strcmp(name, mxp->name) == 0)
+                   goto match;
+           break;
+
+       case NO_RECOVERY:       /* non-recoverable name server error */
+       case TRY_AGAIN:         /* temporary error on authoritative server */
+       default:
+           fprintf(stderr,
+               "fetchmail: nameserver failure while looking for `%s' during poll of %s.\n",
+               name, ctl->servernames->id);
+           ctl->errcount++;
+           longjmp(restart, 2);        /* try again next poll cycle */
+           break;
+       }
 
-       mxrecords = getmxrecords(name);
+    return(FALSE);
 
-       if (mxrecords == (struct mxentry *)NULL)
-           if (h_errno == TRY_AGAIN)
-           {
-               sleep(1);
-               continue;
-           }
-           else
-               break;
+match:
+    /* add this name to relevant server's `also known as' list */
+    save_str(&ctl->lead_server->servernames, -1, name);
+    return(TRUE);
+}
 
-       for (mxp = mxrecords; mxp->name; mxp++)
-           if (strcmp(name, mxp->name) == 0)
-               return(TRUE);
-    }
+static void map_name(name, ctl, xmit_names)
+/* add given name to xmit_names if it matches declared localnames */
+const char *name;              /* name to map */
+struct query *ctl;             /* list of permissible aliases */
+struct idlist **xmit_names;    /* list of recipient names parsed out */
+{
+    const char *lname;
 
-    return(FALSE);
+    lname = idpair_find(&ctl->localnames, name);
+    if (!lname && ctl->wildcard)
+       lname = name;
+
+    if (lname != (char *)NULL)
+    {
+       if (outlevel == O_VERBOSE)
+           fprintf(stderr,
+                   "fetchmail: mapped %s to local %s\n",
+                   name, lname);
+       save_str(xmit_names, -1, lname);
+    }
 }
 
 void find_server_names(hdr, ctl, xmit_names)
 /* parse names out of a RFC822 header into an ID list */
 const char *hdr;               /* RFC822 header in question */
-struct query *ctl;     /* list of permissible aliases */
+struct query *ctl;             /* list of permissible aliases */
 struct idlist **xmit_names;    /* list of recipient names parsed out */
 {
     if (hdr == (char *)NULL)
@@ -186,28 +244,17 @@ struct idlist **xmit_names;       /* list of recipient names parsed out */
                    atsign[0] = '\0';
                }
 
-               lname = idpair_find(&ctl->localnames, cp);
-               if (lname != (char *)NULL)
-               {
-                   if (outlevel == O_VERBOSE)
-                       fprintf(stderr,
-                               "fetchmail: mapped %s to local %s\n",
-                               cp, lname);
-                   save_uid(xmit_names, -1, lname);
-               }
+               map_name(cp, ctl, xmit_names);
            } while
                ((cp = nxtaddr((char *)NULL)) != (char *)NULL);
     }
 }
-#endif /* HAVE_GETHOSTBYNAME */
+#endif /* HAVE_RES_SEARCH */
 
-static FILE *smtp_open(ctl)
+static FILE *smtp_open(struct query *ctl)
 /* try to open a socket to the appropriate SMTP server for this query */ 
-struct query *ctl;
 {
-    static int batchcount;
-
-    ctl = ctl->leader; /* go to the SMTP leader for this query */
+    ctl = ctl->lead_smtp; /* go to the SMTP leader for this query */
 
     /* maybe it's time to close the socket in order to force delivery */
     if (batchlimit && ctl->smtp_sockfp && batchcount++ == batchlimit)
@@ -222,8 +269,8 @@ struct query *ctl;
     {
        if ((ctl->smtp_sockfp = Socket(ctl->smtphost, SMTP_PORT)) == (FILE *)NULL)
            return((FILE *)NULL);
-       else if (SMTP_ok(ctl->smtp_sockfp, NULL) != SM_OK
-                || SMTP_helo(ctl->smtp_sockfp, ctl->servername) != SM_OK)
+       else if (SMTP_ok(ctl->smtp_sockfp) != SM_OK
+                || SMTP_helo(ctl->smtp_sockfp, ctl->servernames->id) != SM_OK)
        {
            fclose(ctl->smtp_sockfp);
            ctl->smtp_sockfp = (FILE *)NULL;
@@ -235,20 +282,23 @@ struct query *ctl;
 
 static int gen_readmsg (sockfp, len, delimited, ctl)
 /* read message content and ship to SMTP or MDA */
-FILE *sockfp;  /* to which the server is connected */
-long len;      /* length of message */
-int delimited; /* does the protocol use a message delimiter? */
+FILE *sockfp;          /* to which the server is connected */
+long len;              /* length of message */
+int delimited;         /* does the protocol use a message delimiter? */
 struct query *ctl;     /* query control record */
 {
     char buf [MSGBUFSIZE+1]; 
-    char *bufp, *headers, *fromhdr, *tohdr, *cchdr, *bcchdr;
+    char *bufp, *headers, *fromhdr,*tohdr,*cchdr,*bcchdr,*received_for,*envto;
     int n, oldlen, mboxfd;
     int inheaders,lines,sizeticker;
     FILE *sinkfp;
+#ifdef HAVE_GETHOSTBYNAME
+    char rbuf[HOSTLEN + USERNAMELEN + 4]; 
+#endif /* HAVE_GETHOSTBYNAME */
 
     /* read the message content from the server */
     inheaders = 1;
-    headers = fromhdr = tohdr = cchdr = bcchdr = NULL;
+    headers = fromhdr = tohdr = cchdr = bcchdr = received_for = envto = NULL;
     lines = 0;
     sizeticker = 0;
     oldlen = 0;
@@ -283,7 +333,7 @@ struct query *ctl;  /* query control record */
        if (inheaders)
         {
            if (!ctl->norewrite)
-               reply_hack(bufp, ctl->servername);
+               reply_hack(bufp, ctl->servernames->id);
 
            if (!lines)
            {
@@ -325,12 +375,63 @@ struct query *ctl;        /* query control record */
                fromhdr = bufp;
            else if (!strncasecmp("To:", bufp, 3))
                tohdr = bufp;
-           else if (!strncasecmp("To:", bufp, 3))
-               tohdr = bufp;
+           else if (!strncasecmp("Apparently-To:", bufp, 14))
+               envto = bufp;
+           else if (!strncasecmp("X-Envelope-To:", bufp, 14))
+               envto = bufp;
            else if (!strncasecmp("Cc:", bufp, 3))
                cchdr = bufp;
            else if (!strncasecmp("Bcc:", bufp, 4))
                bcchdr = bufp;
+#ifdef HAVE_GETHOSTBYNAME
+           else if (MULTIDROP(ctl) && !strncasecmp("Received:", bufp, 9))
+           {
+               char *ok;
+
+               /*
+                * Try to extract the real envelope addressee.  We look here
+                * specifically for the mailserver's Received line.
+                * Note: this will only work for sendmail, or an MTA that
+                * shares sendmail's convention of embedding the envelope
+                * address in the Received line.
+                */
+               strcpy(rbuf, "by ");
+               strcat(rbuf, ctl->canonical_name);
+               if ((ok = strstr(bufp, rbuf)))
+                   ok = strstr(ok, "for <");
+               else
+                   ok = (char *)NULL;
+               if (ok)
+               {
+                   char        *sp, *tp;
+
+                   tp = rbuf;
+                   sp = ok + 4;
+                   if (*sp == '<')
+                       sp++;
+                   while (*sp && *sp != '>' && *sp != '@' && *sp != ';')
+                       if (!isspace(*sp))
+                           *tp++ = *sp++;
+                       else
+                       {
+                           /* uh oh -- whitespace here can't be right! */
+                           ok = (char *)NULL;
+                           break;
+                       }
+                   *tp = '\0';
+               }
+
+               if (ok)
+               {
+                   received_for = alloca(strlen(rbuf)+1);
+                   strcpy(received_for, rbuf);
+                   if (outlevel == O_VERBOSE)
+                       fprintf(stderr, 
+                               "fetchmail: found Received address `%s'\n",
+                               received_for);
+               }
+           }
+#endif /* HAVE_GETHOSTBYNAME */
 
            goto skipwrite;
        }
@@ -338,31 +439,50 @@ struct query *ctl;        /* query control record */
        {
            char                *cp;
            struct idlist       *idp, *xmit_names;
+           int                 good_addresses, bad_addresses;
+#ifdef HAVE_RES_SEARCH
+           int                 no_local_matches = FALSE;
+#endif /* HAVE_RES_SEARCH */
 
            /* cons up a list of local recipients */
            xmit_names = (struct idlist *)NULL;
-#ifdef HAVE_GETHOSTBYNAME
+           bad_addresses = good_addresses = 0;
+#ifdef HAVE_RES_SEARCH
            /* is this a multidrop box? */
            if (MULTIDROP(ctl))
            {
-               /* compute the local address list */
-               find_server_names(tohdr,  ctl, &xmit_names);
-               find_server_names(cchdr,  ctl, &xmit_names);
-               find_server_names(bcchdr, ctl, &xmit_names);
+               if (envto)          /* We have the actual envelope addressee */
+                   find_server_names(envto, ctl, &xmit_names);
+               else if (received_for)
+                   /*
+                    * We have the Received for addressee.  
+                    * It has to be a mailserver address, or we
+                    * wouldn't have got here.
+                    */
+                   map_name(received_for, ctl, &xmit_names);
+               else
+               {
+                   /*
+                    * We haven't extracted the envelope address.
+                    * So check all the header addresses.
+                    */
+                   find_server_names(tohdr,  ctl, &xmit_names);
+                   find_server_names(cchdr,  ctl, &xmit_names);
+                   find_server_names(bcchdr, ctl, &xmit_names);
+               }
+               if (!xmit_names)
+               {
+                   no_local_matches = TRUE;
+                   save_str(&xmit_names, -1, user);
+                   if (outlevel == O_VERBOSE)
+                       fprintf(stderr, 
+                               "fetchmail: no local matches, forwarding to %s\n",
+                               user);
+               }
            }
            else        /* it's a single-drop box, use first localname */
-#endif /* HAVE_GETHOSTBYNAME */
-           {
-               if (ctl->localnames)
-                   save_uid(&xmit_names, -1, ctl->localnames->id);
-           }
-
-           /* if nothing supplied localnames, default appropriately */
-           if (!xmit_names)
-               if (getuid() == 0)
-                   save_uid(&xmit_names, -1, ctl->remotename);
-               else
-                   save_uid(&xmit_names, -1, user);
+#endif /* HAVE_RES_SEARCH */
+               save_str(&xmit_names, -1, ctl->localnames->id);
 
            /* time to address the message */
            if (ctl->mda[0])    /* we have a declared MDA */
@@ -372,8 +492,7 @@ struct query *ctl;  /* query control record */
 
                /*
                 * We go through this in order to be able to handle very
-                * long lists of users and (re
-)implement %s.
+                * long lists of users and (re)implement %s.
                 */
                for (idp = xmit_names; idp; idp = idp->next)
                    nlocals++;
@@ -413,38 +532,49 @@ struct query *ctl;        /* query control record */
            {
                char    *ap;
 
-               if (ctl->mda[0] == '\0' && ((sinkfp = smtp_open(ctl)) < 0))
+               /* build a connection to the SMTP listener */
+               if (ctl->mda[0] == '\0' && ((sinkfp = smtp_open(ctl)) == NULL))
                {
-                   free_uid_list(&xmit_names);
+                   free_str_list(&xmit_names);
                    fprintf(stderr, "fetchmail: SMTP connect failed\n");
                    return(PS_SMTP);
                }
 
-               if (!fromhdr)
-               {
-                   fprintf(stderr, "fetchmail: I see no From header\n");
-                   return(PS_SMTP);
-               }
-
-               if (SMTP_from(sinkfp, ap = nxtaddr(fromhdr)) != SM_OK)
-               {
-                   fprintf(stderr, "fetchmail: SMTP listener doesn't like the From address `%s'\n", ap);
-                   return(PS_SMTP);
-               }
+               /*
+                * Try to get the SMTP listener to take the header
+                * From address as MAIL FROM (this makes the logging
+                * nicer).  If it won't, fall back on the calling-user
+                * ID.  This won't affect replies, which use the header
+                * From address anyway.
+                */
+               if (!fromhdr || !(ap = nxtaddr(fromhdr)) 
+                                       || SMTP_from(sinkfp, ap) != SM_OK)
+                   if (SMTP_from(sinkfp, user) != SM_OK)
+                       return(PS_SMTP);        /* should never happen */
 
+               /* now list the recipient addressees */
                for (idp = xmit_names; idp; idp = idp->next)
-                   if (SMTP_rcpt(sinkfp, idp->id) != SM_OK)
+                   if (SMTP_rcpt(sinkfp, idp->id) == SM_OK)
+                       good_addresses++;
+                   else
                    {
+                       bad_addresses++;
+                       idp->val.num = 0;
                        fprintf(stderr, 
-                               "fetchmail: SMTP listener doesn't like the To address `%s'\n", idp->id);
-                       return(PS_SMTP);
+                               "fetchmail: SMTP listener doesn't like recipient address `%s'\n", idp->id);
                    }
+               if (!good_addresses && SMTP_rcpt(sinkfp, user) != SM_OK)
+               {
+                   fprintf(stderr, 
+                           "fetchmail: can't even send to calling user!\n");
+                   return(PS_SMTP);
+               }
 
+               /* tell it we're ready to send data */
                SMTP_data(sinkfp);
                if (outlevel == O_VERBOSE)
                    fputs("SMTP> ", stderr);
            }
-           free_uid_list(&xmit_names);
 
            /* change continuation markers back to regular newlines */
            for (cp = headers; cp < headers + oldlen; cp++)
@@ -478,6 +608,56 @@ struct query *ctl; /* query control record */
                fputs("#", stderr);
            free(headers);
            headers = NULL;
+
+           /* write error notifications */
+#ifdef HAVE_RES_SEARCH
+           if (no_local_matches || bad_addresses)
+#else
+           if (bad_addresses)
+#endif /* HAVE_RES_SEARCH */
+           {
+               int     errlen = 0;
+               char    errhd[USERNAMELEN + POPBUFSIZE], *errmsg;
+
+               errmsg = errhd;
+               (void) strcpy(errhd, "X-Fetchmail-Warning: ");
+#ifdef HAVE_RES_SEARCH
+               if (no_local_matches)
+               {
+                   strcat(errhd, "no recipient addresses matched declared local names");
+                   if (bad_addresses)
+                       strcat(errhd, "; ");
+               }
+#endif /* HAVE_RES_SEARCH */
+
+               if (bad_addresses)
+               {
+                   strcat(errhd, "SMTP listener rejected local recipient addresses: ");
+                   errlen = strlen(errhd);
+                   for (idp = xmit_names; idp; idp = idp->next)
+                       if (!idp->val.num)
+                           errlen += strlen(idp->id) + 2;
+
+                   errmsg = alloca(errlen+3);
+                   (void) strcpy(errmsg, errhd);
+                   for (idp = xmit_names; idp; idp = idp->next)
+                       if (!idp->val.num)
+                       {
+                           strcat(errmsg, idp->id);
+                           if (idp->next)
+                               strcat(errmsg, ", ");
+                       }
+               }
+
+               strcat(errmsg, "\n");
+
+               if (ctl->mda[0])
+                   write(mboxfd, errmsg, strlen(errmsg));
+               else
+                   SockWrite(errmsg, strlen(errmsg), sinkfp);
+           }
+
+           free_str_list(&xmit_names);
        }
 
        /* SMTP byte-stuffing */
@@ -537,7 +717,7 @@ int
 kerberos_auth (socket, canonical) 
 /* authenticate to the server host using Kerberos V4 */
 int socket;            /* socket to server host */
-char *canonical;       /* server name */
+const char *canonical; /* server name */
 {
     char * host_primary;
     KTEXT ticket;
@@ -572,7 +752,7 @@ int do_protocol(ctl, proto)
 struct query *ctl;             /* parsed options with merged-in defaults */
 const struct method *proto;    /* protocol method table */
 {
-    int ok;
+    int ok, js;
     void (*sigsave)();
 
 #ifndef KERBEROS_V4
@@ -617,11 +797,16 @@ const struct method *proto;       /* protocol method table */
     sigsave = signal(SIGVTALRM, vtalarm_handler);
     vtalarm(mytimeout = ctl->timeout);
 
-    if (setjmp(restart) == 1)
+    if ((js = setjmp(restart)) == 1)
     {
        fprintf(stderr,
                "fetchmail: timeout after %d seconds waiting for %s.\n",
-               ctl->timeout, ctl->servername);
+               ctl->timeout, ctl->servernames->id);
+       ok = PS_ERROR;
+    }
+    else if (js == 2)
+    {
+       /* error message printed at point of longjmp */
        ok = PS_ERROR;
     }
     else
@@ -631,7 +816,7 @@ const struct method *proto; /* protocol method table */
        FILE *sockfp;
 
        /* open a socket to the mail server */
-       if ((sockfp = Socket(ctl->servername,
+       if ((sockfp = Socket(ctl->servernames->id,
                             ctl->port ? ctl->port : protocol->port)) == NULL)
        {
            perror("fetchmail, connecting to host");
@@ -675,7 +860,7 @@ const struct method *proto; /* protocol method table */
            if (count == 0)
                fprintf(stderr, "No mail from %s@%s\n", 
                        ctl->remotename,
-                       ctl->servername);
+                       ctl->servernames->id);
            else
            {
                fprintf(stderr, "%d message%s", count, count > 1 ? "s" : ""); 
@@ -684,7 +869,7 @@ const struct method *proto; /* protocol method table */
                fprintf(stderr,
                        " from %s@%s.\n",
                        ctl->remotename,
-                       ctl->servername);
+                       ctl->servernames->id);
            }
 
        /* we may need to get sizes in order to check message limits */
@@ -697,6 +882,7 @@ const struct method *proto; /* protocol method table */
                return(PS_ERROR);
        }
 
+
        if (check_only)
        {
            if (new == -1 || ctl->fetchall)
@@ -706,12 +892,35 @@ const struct method *proto;       /* protocol method table */
        }
        else if (count > 0)
        {    
+           /*
+            * What forces this code is that in POP3 you can't fetch a
+            * message without having it marked `seen'.
+            *
+            * The result is that if there's any kind of transient error
+            * (DNS lookup failure, or sendmail refusing delivery due to
+            * process-table limits) the message will be marked "seen" on
+            * the server without having been delivered.  This is not a
+            * big problem if fetchmail is running in foreground, because
+            * the user will see a "skipped" message when it next runs and
+            * get clued in.
+            *
+            * But in daemon mode this leads to the message being silently
+            * ignored forever.  This is not acceptable.
+            *
+            * We compensate for this by checking the error count from the 
+            * previous pass and forcing all messages to be considered new
+            * if it's nonzero.
+            */
+           int force_retrieval = (ctl->errcount > 0);
+
+           ctl->errcount = 0;
+
            /* read, forward, and delete messages */
            for (num = 1; num <= count; num++)
            {
-               int     toolarge = msgsizes && msgsizes[num-1]>ctl->limit;
+               int     toolarge = msgsizes && (msgsizes[num-1] > ctl->limit);
                int     fetch_it = ctl->fetchall ||
-                   (!(protocol->is_old && (protocol->is_old)(sockfp,ctl,num)) && !toolarge);
+                   (!toolarge && (force_retrieval || !(protocol->is_old && (protocol->is_old)(sockfp,ctl,num))));
 
                /* we may want to reject this message if it's old */
                if (!fetch_it)
@@ -774,13 +983,10 @@ const struct method *proto;       /* protocol method table */
                    vtalarm(ctl->timeout);
                    if (ok != 0)
                        goto cleanUp;
+                   delete_str(&ctl->newsaved, num);
                }
                else if (outlevel > O_SILENT) 
-               {
-                   /* nuke it from the unseen-messages list */
-                   delete_uid(&ctl->newsaved, num);
                    fprintf(stderr, " not flushed\n");
-               }
            }
 
            /* remove all messages flagged for deletion */
@@ -842,7 +1048,7 @@ const struct method *proto;        /* protocol method table */
     }
     if (ok==PS_SOCKET || ok==PS_AUTHFAIL || ok==PS_SYNTAX || ok==PS_IOERR
                || ok==PS_ERROR || ok==PS_PROTOCOL || ok==PS_SMTP)
-       fprintf(stderr, " error while talking to %s\n", ctl->servername);
+       fprintf(stderr, " error while talking to %s\n", ctl->servernames->id);
 
 closeUp:
     signal(SIGVTALRM, sigsave);