]> Pileus Git - ~andy/fetchmail/blobdiff - driver.c
STEP 9: Eliminate the static buffer in the socket library.
[~andy/fetchmail] / driver.c
index b4a4048f679ac564a04639af5ac7c81c65e66365..dee11f45b0b673e019b2286e4578ac5f779ef941 100644 (file)
--- a/driver.c
+++ b/driver.c
@@ -8,10 +8,15 @@
 
 #include  <config.h>
 #include  <stdio.h>
+#include  <setjmp.h>
+#include  <ctype.h>
 #if defined(STDC_HEADERS)
 #include  <stdlib.h>
 #include  <string.h>
 #endif
+#if defined(HAVE_UNISTD_H)
+#include <unistd.h>
+#endif
 #if defined(HAVE_STDARG_H)
 #include  <stdarg.h>
 #else
 
 #define        SMTP_PORT       25      /* standard SMTP service port */
 
-static struct method *protocol;
-
-static int alarmed;    /* a flag to indicate that SIGALRM happened */
-static int mytimeout;  /* server-nonresponse timeout for current query */
-static char *srvname;  /* current server name for timeout message */
+static const struct method *protocol;
+static jmp_buf restart;
 
 char tag[TAGLEN];
 static int tagnum;
 #define GENSYM (sprintf(tag, "a%04d", ++tagnum), tag)
 
-static char *shroud;
+static char *shroud;   /* string to shroud in debug output, if  non-NULL */
+static int mytimeout;  /* value of nonreponse timeout */
+
+static int strcrlf(dst, src, count)
+/* replace LFs with CR-LF; return length of string with replacements */
+char *dst;     /* new string with CR-LFs */
+char *src;     /* original string with LFs */
+int count;     /* length of src */
+{
+  int len = count;
+
+  while (count--)
+  {
+      if (*src == '\n')
+      {
+         *dst++ = '\r';
+         len++;
+      }
+      *dst++ = *src++;
+  }
+  *dst = '\0';
+  return len;
+}
+
+static void vtalarm(timeleft)
+/* reset the nonresponse-timeout */
+int    timeleft;
+{
+    struct itimerval ntimeout;
+
+    ntimeout.it_interval.tv_sec = ntimeout.it_interval.tv_sec = 0;
+    ntimeout.it_value.tv_sec  = timeleft;
+    ntimeout.it_value.tv_usec = 0;
+    setitimer(ITIMER_VIRTUAL, &ntimeout, (struct itimerval *)NULL);
+}
+
+static void vtalarm_handler (int signal)
+/* handle server-timeout SIGVTALARM signal */
+{
+    longjmp(restart, 1);
+}
 
 static void reply_hack(buf, host)
 /* hack message headers so replies will work properly */
@@ -55,7 +97,7 @@ char *buf;            /* header to be hacked */
 const char *host;      /* server hostname */
 {
     const char *from;
-    int state = 0, tokencount = 0;
+    int parendepth, state = 0, tokencount = 0;
     char mycopy[POPBUFSIZE+1];
 
     if (strncmp("From: ", buf, 6)
@@ -80,13 +122,14 @@ const char *host;  /* server hostname */
            if (*from == '"')
                state = 3;
            else if (*from == '(')
+           {
+               parendepth = 1;
                state = 4;    
+           }
            else if (*from == '<' || isalnum(*from))
                state = 5;
            else if (isspace(*from))
                state = 2;
-           else if (*from == ',')
-               tokencount = 0;
            break;
 
        case 2:     /* found a token boundary -- reset without copying */
@@ -104,7 +147,11 @@ const char *host;  /* server hostname */
            break;
 
        case 4:   /* we're in a parenthesized human name, copy and ignore */
-           if (*from == ')')
+           if (*from == '(')
+               ++parendepth;
+           else if (*from == ')')
+               --parendepth;
+           if (parendepth == 0)
                state = 1;
            break;
 
@@ -149,11 +196,12 @@ const char *host; /* server hostname */
                strcpy(buf, "@");
                strcat(buf, host);
                buf += strlen(buf);
+               tokencount = 0;
                state = 1;
            }
 
            /* a single local name alone on the line */
-           else if (*from == '\n' && tokencount == 0)
+           else if (*from == '\n' && tokencount == 1)
            {
                strcpy(buf, "@");
                strcat(buf, host);
@@ -181,7 +229,8 @@ static char *nxtaddr(hdr)
 char *hdr;     /* header line to be parsed, NUL to continue in previous hdr */
 {
     static char        *hp, *tp, address[POPBUFSIZE+1];
-    static     state;
+    static int state;
+    int parendepth;
 
     /*
      * Note 1: RFC822 escaping with \ is *not* handled.  Note 2: it is
@@ -227,7 +276,10 @@ char *hdr; /* header line to be parsed, NUL to continue in previous hdr */
                *tp++ = *hp;
            }
            else if (*hp == '(') /* address comment -- ignore */
+           {
+               parendepth = 1;
                state = 3;    
+           }
            else if (*hp == '<') /* begin <address> */
            {
                state = 4;
@@ -257,7 +309,11 @@ char *hdr; /* header line to be parsed, NUL to continue in previous hdr */
        case 3:   /* we're in a parenthesized comment, ignore */
            if (*hp == '\n')
                return(NULL);
+           else if (*hp == '(')
+               ++parendepth;
            else if (*hp == ')')
+               --parendepth;
+           if (parendepth == 0)
                state = 1;
            break;
 
@@ -303,13 +359,13 @@ char *hdr;        /* header line to be parsed, NUL to continue in previous hdr */
 #ifdef HAVE_GETHOSTBYNAME
 #define MX_RETRIES     3
 
-static int is_host_alias(name, queryctl)
+static int is_host_alias(name, ctl)
 /* determine whether name is a DNS alias of the hostname */
 const char *name;
-struct hostrec *queryctl;
+struct query   *ctl;
 {
     struct hostent     *he;
-    int                        i, n;
+    int                        i;
 
     /*
      * The first two checks are optimizations that will catch a good
@@ -323,9 +379,9 @@ struct hostrec      *queryctl;
      * 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, queryctl->servername) == 0)
+    if (strcmp(name, ctl->servername) == 0)
        return(TRUE);
-    else if (strcmp(name, queryctl->canonical_name) == 0)
+    else if (strcmp(name, ctl->canonical_name) == 0)
        return(TRUE);
 
     /*
@@ -334,7 +390,7 @@ struct hostrec      *queryctl;
      * to respond quickly and reliably.  Ergo if we get failure,
      * the name isn't a mailserver alias.
      */
-    else if ((he = gethostbyname(name)) && strcmp(queryctl->canonical_name, he->h_name) == 0)
+    else if ((he = gethostbyname(name)) && strcmp(ctl->canonical_name, he->h_name) == 0)
        return(TRUE);
 
     /*
@@ -343,12 +399,11 @@ struct hostrec    *queryctl;
      */
     for (i = 0; i < MX_RETRIES; i++)
     {
-       struct mxentry mxresp[32];
-       int j;
+       struct mxentry *mxrecords, *mxp;
 
-       n = getmxrecords(name, sizeof(mxresp)/sizeof(struct mxentry), mxresp);
+       mxrecords = getmxrecords(name);
 
-       if (n == -1)
+       if (mxrecords == (struct mxentry *)NULL)
            if (h_errno == TRY_AGAIN)
            {
                sleep(1);
@@ -357,18 +412,18 @@ struct hostrec    *queryctl;
            else
                break;
 
-       for (j = 0; j < n; j++)
-           if (strcmp(name, mxresp[i].name) == 0)
+       for (mxp = mxrecords; mxp->name; mxp++)
+           if (strcmp(name, mxp->name) == 0)
                return(TRUE);
     }
 
     return(FALSE);
 }
 
-void find_server_names(hdr, queryctl, xmit_names)
+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 hostrec *queryctl;      /* 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)
@@ -379,18 +434,22 @@ struct idlist **xmit_names;       /* list of recipient names parsed out */
 
        if ((cp = nxtaddr(hdr)) != (char *)NULL)
            do {
-               char    *atsign = strchr(cp, '@');
+               char    *atsign;
 
-               if (atsign)
-                   if (queryctl->norewrite)
+               if ((atsign = strchr(cp, '@')))
+               {
+                   /*
+                    * Address has an @. Check to see if the right-hand part
+                    * is an alias or MX equivalent of the mailserver.  If it's
+                    * not, skip this name.  If it is, we'll keep going and try
+                    * to find a mapping to a client name.
+                    */
+                   if (!is_host_alias(atsign+1, ctl))
                        continue;
-                   else
-                   {
-                       if (!is_host_alias(atsign+1, queryctl))
-                           continue;
-                       atsign[0] = '\0';
-                   }
-               lname = idpair_find(&queryctl->localnames, cp);
+                   atsign[0] = '\0';
+               }
+
+               lname = idpair_find(&ctl->localnames, cp);
                if (lname != (char *)NULL)
                {
                    if (outlevel == O_VERBOSE)
@@ -405,32 +464,52 @@ struct idlist **xmit_names;       /* list of recipient names parsed out */
 }
 #endif /* HAVE_GETHOSTBYNAME */
 
-static int gen_readmsg (socket, mboxfd, len, delimited, queryctl)
+static FILE *smtp_open(ctl)
+/* try to open a socket to the appropriate SMTP server for this query */ 
+struct query *ctl;
+{
+    ctl = ctl->leader; /* go to the SMTP leader for this query */
+
+    /* if no socket to this host is already set up, try to open one */
+    if (ctl->smtp_sockfp == (FILE *)NULL)
+    {
+       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)
+       {
+           fclose(ctl->smtp_sockfp);
+           ctl->smtp_sockfp = (FILE *)NULL;
+       }
+    }
+
+    return(ctl->smtp_sockfp);
+}
+
+static int gen_readmsg (sockfp, len, delimited, ctl)
 /* read message content and ship to SMTP or MDA */
-int socket;    /* to which the server is connected */
-int mboxfd;    /* descriptor to which retrieved message will be written */
+FILE *sockfp;  /* to which the server is connected */
 long len;      /* length of message */
 int delimited; /* does the protocol use a message delimiter? */
-struct hostrec *queryctl;      /* query control record */
-{ 
+struct query *ctl;     /* query control record */
+{
     char buf [MSGBUFSIZE+1]; 
-    char fromBuf[MSGBUFSIZE+1];
-    char *bufp, *headers, *unixfrom, *fromhdr, *tohdr, *cchdr, *bcchdr;
-    int n, oldlen;
-    int inheaders;
-    int lines,sizeticker;
-    /* This keeps the retrieved message count for display purposes */
-    static int msgnum = 0;  
+    char *bufp, *headers, *fromhdr, *tohdr, *cchdr, *bcchdr;
+    int n, oldlen, mboxfd;
+    int inheaders,lines,sizeticker;
+    FILE *sinkfp;
 
     /* read the message content from the server */
     inheaders = 1;
-    headers = unixfrom = fromhdr = tohdr = cchdr = bcchdr = NULL;
+    headers = fromhdr = tohdr = cchdr = bcchdr = NULL;
     lines = 0;
     sizeticker = 0;
+    oldlen = 0;
     while (delimited || len > 0)
     {
-       if ((n = SockGets(socket,buf,sizeof(buf))) < 0)
+       if ((n = SockGets(buf,sizeof(buf),sockfp)) < 0)
            return(PS_SOCKET);
+       vtalarm(ctl->timeout);
 
        /* write the message size dots */
        if (n > 0)
@@ -438,7 +517,7 @@ struct hostrec *queryctl;   /* query control record */
            sizeticker += n;
            while (sizeticker >= SIZETICKER)
            {
-               if (outlevel > O_SILENT && outlevel < O_VERBOSE)
+               if (outlevel > O_SILENT)
                    fputc('.',stderr);
                sizeticker -= SIZETICKER;
            }
@@ -456,8 +535,8 @@ struct hostrec *queryctl;   /* query control record */
      
        if (inheaders)
         {
-           if (!queryctl->norewrite)
-               reply_hack(bufp, queryctl->servername);
+           if (!ctl->norewrite)
+               reply_hack(bufp, ctl->servername);
 
            if (!lines)
            {
@@ -476,7 +555,7 @@ struct hostrec *queryctl;   /* query control record */
                 * We deal with RFC822 continuation lines here.
                 * Replace previous '\n' with '\r' so nxtaddr 
                 * and reply_hack will be able to see past it.
-                * (We know this safe because SocketGets stripped
+                * (We know this is safe because SocketGets stripped
                 * out all carriage returns in the read loop above
                 * and we haven't reintroduced any since then.)
                 * We'll undo this before writing the header.
@@ -493,9 +572,7 @@ struct hostrec *queryctl;   /* query control record */
                oldlen = newlen;
            }
 
-           if (!strncmp(bufp,"From ",5))
-               unixfrom = bufp;
-           else if (!strncasecmp("From:", bufp, 5))
+           if (!strncasecmp("From:", bufp, 5))
                fromhdr = bufp;
            else if (!strncasecmp("To:", bufp, 3))
                tohdr = bufp;
@@ -506,61 +583,114 @@ struct hostrec *queryctl;        /* query control record */
 
            goto skipwrite;
        }
-       else if (headers)
+       else if (headers)       /* OK, we're at end of headers now */
        {
            char                *cp;
+           struct idlist       *idp, *xmit_names;
 
-           if (!queryctl->mda[0])
+           /* cons up a list of local recipients */
+           xmit_names = (struct idlist *)NULL;
+#ifdef HAVE_GETHOSTBYNAME
+           /* is this a multidrop box? */
+           if (MULTIDROP(ctl))
            {
-               if (SMTP_from(mboxfd, nxtaddr(fromhdr)) != SM_OK)
-                   return(PS_SMTP);
+               /* 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);
+           }
+           else        /* it's a single-drop box, use first localname */
+#endif /* HAVE_GETHOSTBYNAME */
+           {
+               if (ctl->localnames)
+                   save_uid(&xmit_names, -1, ctl->localnames->id);
+           }
 
-#ifdef HAVE_GETHOSTBYNAME
-               /* is this a multidrop box? */
-               if (queryctl->localnames != (struct idlist *)NULL
-                   && queryctl->localnames->next != (struct idlist *)NULL)
-               {
-                   struct idlist       *idp, *xmit_names;
+           /* 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);
+
+           /* time to address the message */
+           if (ctl->mda[0])    /* we have a declared MDA */
+           {
+               int     i, nlocals = 0;
+               char    **sargv, **sp;
+
+               /*
+                * We go through this in order to be able to handle very
+                * long lists of users.
+                */
+               for (idp = xmit_names; idp; idp = idp->next)
+                   nlocals++;
+               sp = sargv = (char **)alloca(sizeof(char **) * ctl->mda_argcount+nlocals+2);
+               for (i = 0; i < ctl->mda_argcount; i++)
+                   *sp++ = ctl->mda_argv[i];
+               for (idp = xmit_names; idp; idp = idp->next)
+                   *sp++ = idp->id;
+               *sp =  (char *)NULL;
 
-                   /* compute the local address list */
-                   xmit_names = (struct idlist *)NULL;
-                   find_server_names(tohdr,  queryctl, &xmit_names);
-                   find_server_names(cchdr,  queryctl, &xmit_names);
-                   find_server_names(bcchdr, queryctl, &xmit_names);
+#ifdef HAVE_SETEUID
+               /*
+                * Arrange to run with user's permissions if we're root.
+                * This will initialize the ownership of any files the
+                * MDA creates properly.  (The seteuid call is available
+                * under all BSDs and Linux)
+                */
+               seteuid(ctl->uid);
+#endif /* HAVE_SETEUID */
 
-                   /* if nothing supplied localnames, default appropriately */
-                   if (!xmit_names)
-                       save_uid(&xmit_names, -1, dfltuser);
+               mboxfd = openmailpipe(sargv);
 
-                   for (idp = xmit_names; idp; idp = idp->next)
-                       if (SMTP_rcpt(mboxfd, idp->id) != SM_OK)
-                           return(PS_SMTP);
+#ifdef HAVE_SETEUID
+               /* this will fail quietly if we didn't start as root */
+               seteuid(0);
+#endif /* HAVE_SETEUID */
+
+               if (mboxfd < 0)
+               {
+                   fprintf(stderr, "fetchmail: MDA open failed\n");
+                   return(PS_IOERR);
+               }
+           }
+           else
+           {
+               if (ctl->mda[0] == '\0' && ((sinkfp = smtp_open(ctl)) < 0))
+               {
                    free_uid_list(&xmit_names);
+                   fprintf(stderr, "fetchmail: SMTP connect failed\n");
+                   return(PS_SMTP);
                }
-               else    /* it's a single-drop box, use first localname */
-#endif /* HAVE_GETHOSTBYNAME */
+
+               if (SMTP_from(sinkfp, nxtaddr(fromhdr)) != SM_OK)
                {
-                   if (queryctl->localnames)
-                       cp = queryctl->localnames->id;
-                   else
-                       cp = dfltuser;
+                   fprintf(stderr, "fetchmail: SMTP listener is confused\n");
+                   return(PS_SMTP);
+               }
 
-                   if (SMTP_rcpt(mboxfd, cp) != SM_OK)
+               for (idp = xmit_names; idp; idp = idp->next)
+                   if (SMTP_rcpt(sinkfp, idp->id) != SM_OK)
+                   {
+                       fprintf(stderr, "fetchmail: SMTP listener is upset\n");
                        return(PS_SMTP);
-               }
+                   }
 
-               SMTP_data(mboxfd);
+               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++)
+           for (cp = headers; cp < headers + oldlen; cp++)
                if (*cp == '\r')
                    *cp = '\n';
+           headers[oldlen++] = '\0';
 
            /* replace all LFs with CR-LF before sending to the SMTP server */
-           if (!queryctl->mda[0])
+           if (!ctl->mda[0])
            {
                char *newheaders = malloc(1 + oldlen * 2);
 
@@ -570,11 +700,18 @@ struct hostrec *queryctl; /* query control record */
                free(headers);
                headers = newheaders;
            }
-           if (write(mboxfd,headers,oldlen) < 0)
+
+           /* write all the headers */
+           if (ctl->mda[0])
+               n = write(mboxfd,headers,oldlen);
+           else
+               n = SockWrite(headers, oldlen, sinkfp);
+
+           if (n < 0)
            {
                free(headers);
                headers = NULL;
-               perror("gen_readmsg: writing RFC822 headers");
+               perror("fetchmail: writing RFC822 headers");
                return(PS_IOERR);
            }
            else if (outlevel == O_VERBOSE)
@@ -584,11 +721,11 @@ struct hostrec *queryctl; /* query control record */
        }
 
        /* SMTP byte-stuffing */
-       if (*bufp == '.' && queryctl->mda[0] == 0)
-           write(mboxfd, ".", 1);
+       if (*bufp == '.' && ctl->mda[0] == 0)
+           SockWrite(".", 1, sinkfp);
 
-       /* write this line to the file after replacing all LFs with CR-LF */
-       if (!queryctl->mda[0])
+       /* replace all LFs with CR-LF  in the line */
+       if (!ctl->mda[0])
        {
            char *newbufp = malloc(1 + strlen(bufp) * 2);
 
@@ -597,12 +734,18 @@ struct hostrec *queryctl; /* query control record */
            strcrlf(newbufp, bufp, strlen(bufp));
            bufp = newbufp;
        }
-       n = write(mboxfd,bufp,strlen(bufp));
-       if (!queryctl->mda[0])
+
+       /* ship out the text line */
+       if (ctl->mda[0])
+           n = write(mboxfd,bufp,strlen(bufp));
+       else
+           n = SockWrite(bufp, strlen(bufp), sinkfp);
+
+       if (!ctl->mda[0])
            free(bufp);
        if (n < 0)
        {
-           perror("gen_readmsg: writing message text");
+           perror("fetchmail: writing message text");
            return(PS_IOERR);
        }
        else if (outlevel == O_VERBOSE)
@@ -612,21 +755,31 @@ struct hostrec *queryctl; /* query control record */
        lines++;
     }
 
-    if (alarmed)
-       return (0);
-    /* write message terminator */
-    if (!queryctl->mda[0])
-       if (SMTP_eom(mboxfd) != SM_OK)
+    if (ctl->mda[0])
+    {
+       /* close the delivery pipe, we'll reopen before next message */
+       if (closemailpipe(mboxfd))
+           return(PS_IOERR);
+    }
+    else
+    {
+       /* write message terminator */
+       if (SMTP_eom(sinkfp) != SM_OK)
+       {
+           fputs("fetchmail: SMTP listener refused delivery\n", stderr);
            return(PS_SMTP);
+       }
+    }
+
     return(0);
 }
 
 #ifdef KERBEROS_V4
 int
-kerberos_auth (socket, servername
+kerberos_auth (socket, canonical
 /* authenticate to the server host using Kerberos V4 */
 int socket;            /* socket to server host */
-char *servername;      /* server name */
+char *canonical;       /* server name */
 {
     char * host_primary;
     KTEXT ticket;
@@ -635,23 +788,10 @@ char *servername; /* server name */
     Key_schedule schedule;
     int rem;
   
-    /* Get the primary name of the host.  */
-    {
-       struct hostent * hp = (gethostbyname (servername));
-       if (hp == 0)
-       {
-           fprintf (stderr, "fetchmail: server %s unknown: n", servername);
-           return (PS_ERROR);
-       }
-       host_primary = ((char *) (malloc ((strlen (hp -> h_name)) + 1)));
-       strcpy (host_primary, (hp -> h_name));
-    }
-  
     ticket = ((KTEXT) (malloc (sizeof (KTEXT_ST))));
-    rem
-       = (krb_sendauth (0L, socket, ticket, "pop",
-                        host_primary,
-                        ((char *) (krb_realmofhost (host_primary))),
+    rem = (krb_sendauth (0L, socket, ticket, "pop",
+                        canonical,
+                        ((char *) (krb_realmofhost (canonical))),
                         ((unsigned long) 0),
                         (&msg_data),
                         (&cred),
@@ -660,7 +800,6 @@ char *servername;   /* server name */
                         ((struct sockaddr_in *) 0),
                         "KPOPV0.1"));
     free (ticket);
-    free (host_primary);
     if (rem != KSUCCESS)
     {
        fprintf (stderr, "fetchmail: kerberos error %s\n", (krb_get_err_text (rem)));
@@ -670,25 +809,16 @@ char *servername; /* server name */
 }
 #endif /* KERBEROS_V4 */
 
-int do_protocol(queryctl, proto)
+int do_protocol(ctl, proto)
 /* retrieve messages from server using given protocol method table */
-struct hostrec *queryctl;      /* parsed options with merged-in defaults */
-struct method *proto;          /* protocol method table */
+struct query *ctl;             /* parsed options with merged-in defaults */
+const struct method *proto;    /* protocol method table */
 {
-    int ok, len;
-    int mboxfd = -1;
-    char buf [POPBUFSIZE+1], host[HOSTLEN+1];
-    int socket;
+    int ok;
     void (*sigsave)();
-    int num, count, new, deletions = 0;
-
-    srvname = queryctl->servername;
-    alarmed = 0;
-    sigsave = signal(SIGALRM, alarm_handler);
-    alarm (mytimeout = queryctl->timeout);
 
 #ifndef KERBEROS_V4
-    if (queryctl->authenticate == A_KERBEROS)
+    if (ctl->authenticate == A_KERBEROS)
     {
        fputs("fetchmail: Kerberos support not linked.\n", stderr);
        return(PS_ERROR);
@@ -699,245 +829,243 @@ struct method *proto;           /* protocol method table */
     if (!proto->is_old)
     {
        /* check for unsupported options */
-       if (queryctl->flush) {
+       if (ctl->flush) {
            fprintf(stderr,
                    "Option --flush is not supported with %s\n",
                    proto->name);
-            alarm(0);
-            signal(SIGALRM, sigsave);
            return(PS_SYNTAX);
        }
-       else if (queryctl->fetchall) {
+       else if (ctl->fetchall) {
            fprintf(stderr,
                    "Option --all is not supported with %s\n",
                    proto->name);
-            alarm(0);
-            signal(SIGALRM, sigsave);
            return(PS_SYNTAX);
        }
     }
+    if (!proto->getsizes && ctl->limit)
+    {
+       fprintf(stderr,
+               "Option --limit is not supported with %s\n",
+               proto->name);
+       return(PS_SYNTAX);
+    }
 
+    protocol = proto;
     tagnum = 0;
     tag[0] = '\0';     /* nuke any tag hanging out from previous query */
-    protocol = proto;
+    ok = 0;
 
-    /* open a socket to the mail server */
-    if ((socket = Socket(queryctl->servername,
-                        queryctl->port ? queryctl->port : protocol->port))<0 
-         || alarmed)
+    /* set up the server-nonresponse timeout */
+    sigsave = signal(SIGVTALRM, vtalarm_handler);
+    vtalarm(mytimeout = ctl->timeout);
+
+    if (setjmp(restart) == 1)
+       fprintf(stderr,
+               "fetchmail: timeout after %d seconds waiting for %s.\n",
+               ctl->timeout, ctl->servername);
+    else
     {
-       perror("fetchmail, connecting to host");
-       ok = PS_SOCKET;
-       goto closeUp;
-    }
+       char buf [POPBUFSIZE+1];
+       int *msgsizes, len, num, count, new, deletions = 0;
+       FILE *sockfp;
+
+       /* open a socket to the mail server */
+       if ((sockfp = Socket(ctl->servername,
+                            ctl->port ? ctl->port : protocol->port))<0)
+       {
+           perror("fetchmail, connecting to host");
+           ok = PS_SOCKET;
+           goto closeUp;
+       }
 
 #ifdef KERBEROS_V4
-    if (queryctl->authenticate == A_KERBEROS)
-    {
-       ok = (kerberos_auth (socket, queryctl->servername));
+       if (ctl->authenticate == A_KERBEROS)
+       {
+           ok = (kerberos_auth (fileno(sockfp), ctl->canonical_name));
+           vtalarm(ctl->timeout);
+           if (ok != 0)
+               goto cleanUp;
+       }
+#endif /* KERBEROS_V4 */
+
+       /* accept greeting message from mail server */
+       ok = (protocol->parse_response)(sockfp, buf);
+       vtalarm(ctl->timeout);
        if (ok != 0)
            goto cleanUp;
-    }
-#endif /* KERBEROS_V4 */
 
-    /* accept greeting message from mail server */
-    ok = (protocol->parse_response)(socket, buf);
-    if (alarmed || ok != 0)
-       goto cleanUp;
-
-    /* try to get authorized to fetch mail */
-    shroud = queryctl->password;
-    ok = (protocol->getauth)(socket, queryctl, buf);
-    shroud = (char *)NULL;
-    if (alarmed || ok == PS_ERROR)
-       ok = PS_AUTHFAIL;
-    if (alarmed || ok != 0)
-       goto cleanUp;
-
-    /* compute number of messages and number of new messages waiting */
-    if ((protocol->getrange)(socket, queryctl, &count, &new) != 0 || alarmed)
-       goto cleanUp;
-
-    /* show user how many messages we downloaded */
-    if (outlevel > O_SILENT && outlevel < O_VERBOSE)
-       if (count == 0)
-           fprintf(stderr, "No mail from %s@%s\n", 
-                   queryctl->remotename,
-                   queryctl->servername);
-       else
-       {
-           fprintf(stderr, "%d message%s", count, count > 1 ? "s" : ""); 
-           if (new != -1 && (count - new) > 0)
-               fprintf(stderr, " (%d seen)", count-new);
-           fprintf(stderr,
-                   " from %s@%s.\n",
-                   queryctl->remotename,
-                   queryctl->servername);
-       }
+       /* try to get authorized to fetch mail */
+       shroud = ctl->password;
+       ok = (protocol->getauth)(sockfp, ctl, buf);
+       vtalarm(ctl->timeout);
+       shroud = (char *)NULL;
+       if (ok == PS_ERROR)
+           ok = PS_AUTHFAIL;
+       if (ok != 0)
+           goto cleanUp;
 
-    if (check_only)
-    {
-       if (new == -1 || queryctl->fetchall)
-           new = count;
-       ok = ((new > 0) ? PS_SUCCESS : PS_NOMAIL);
-       goto closeUp;
-    }
-    else if (count > 0)
-    {
-       if (queryctl->mda[0] == '\0')
-           if ((mboxfd = Socket(queryctl->smtphost, SMTP_PORT)) < 0
-               || SMTP_ok(mboxfd, NULL) != SM_OK
-               || SMTP_helo(mboxfd, queryctl->servername) != SM_OK 
-                || alarmed)
+       /* compute number of messages and number of new messages waiting */
+       if ((protocol->getrange)(sockfp, ctl, &count, &new) != 0)
+           goto cleanUp;
+       vtalarm(ctl->timeout);
+
+       /* show user how many messages we downloaded */
+       if (outlevel > O_SILENT)
+           if (count == 0)
+               fprintf(stderr, "No mail from %s@%s\n", 
+                       ctl->remotename,
+                       ctl->servername);
+           else
            {
-               ok = PS_SMTP;
-               close(mboxfd);
-               mboxfd = -1;
-               goto cleanUp;
+               fprintf(stderr, "%d message%s", count, count > 1 ? "s" : ""); 
+               if (new != -1 && (count - new) > 0)
+                   fprintf(stderr, " (%d seen)", count-new);
+               fprintf(stderr,
+                       " from %s@%s.\n",
+                       ctl->remotename,
+                       ctl->servername);
            }
-    
-       /* read, forward, and delete messages */
-       for (num = 1; num <= count; num++)
+
+       /* we may need to get sizes in order to check message limits */
+       msgsizes = (int *)NULL;
+       if (!ctl->fetchall && proto->getsizes && ctl->limit)
        {
-           int fetch_it = queryctl->fetchall ||
-               !(protocol->is_old && (protocol->is_old)(socket,queryctl,num));
+           msgsizes = (int *)alloca(sizeof(int) * count);
 
-           /* we may want to reject this message if it's old */
-           if (!fetch_it)
-               fprintf(stderr, "skipping message %d ", num);
-           else
+           if ((ok = (proto->getsizes)(sockfp, count, msgsizes)) != 0)
+               return(PS_ERROR);
+       }
+
+       if (check_only)
+       {
+           if (new == -1 || ctl->fetchall)
+               new = count;
+           ok = ((new > 0) ? PS_SUCCESS : PS_NOMAIL);
+           goto closeUp;
+       }
+       else if (count > 0)
+       {    
+           /* read, forward, and delete messages */
+           for (num = 1; num <= count; num++)
            {
-               /* request a message */
-               (protocol->fetch)(socket, num, &len);
+               int     toolarge = msgsizes && msgsizes[num-1]>ctl->limit;
+               int     fetch_it = ctl->fetchall ||
+                   (!(protocol->is_old && (protocol->is_old)(sockfp,ctl,num)) && !toolarge);
 
-               if (outlevel > O_SILENT)
+               /* we may want to reject this message if it's old */
+               if (!fetch_it)
                {
-                   fprintf(stderr, "reading message %d", num);
-                   if (len > 0)
-                       fprintf(stderr, " (%d bytes)", len);
-                   if (outlevel == O_VERBOSE)
-                       fputc('\n', stderr);
-                   else
-                       fputc(' ', stderr);
+                   if (outlevel > O_SILENT)
+                   {
+                       fprintf(stderr, "skipping message %d", num);
+                       if (toolarge)
+                           fprintf(stderr, " (oversized, %d bytes)", msgsizes[num-1]);
+                   }
                }
-
-               /* open the delivery pipe now if we're using an MDA */
-               if (queryctl->mda[0])
+               else
                {
-#ifdef HAVE_SETEUID
-                   /*
-                    * Arrange to run with user's permissions if we're root.
-                    * This will initialize the ownership of any files the
-                    * MDA creates properly.  (The seteuid call is available
-                    * under all BSDs and Linux)
-                    */
-                   seteuid(queryctl->uid);
-#endif /* HAVE_SETEUID */
-                   mboxfd = openmailpipe(queryctl);
-#ifdef HAVE_SETEUID
-                   /* this will fail quietly if we didn't start as root */
-                   seteuid(0);
-#endif /* HAVE_SETEUID */
+                   /* request a message */
+                   (protocol->fetch)(sockfp, num, &len);
+                   vtalarm(ctl->timeout);
+
+                   if (outlevel > O_SILENT)
+                   {
+                       fprintf(stderr, "reading message %d", num);
+                       if (len > 0)
+                           fprintf(stderr, " (%d bytes)", len);
+                       if (outlevel == O_VERBOSE)
+                           fputc('\n', stderr);
+                       else
+                           fputc(' ', stderr);
+                   }
 
-                   if (mboxfd < 0)
+                   /* read the message and ship it to the output sink */
+                   ok = gen_readmsg(sockfp,
+                                    len, 
+                                    protocol->delimited,
+                                    ctl);
+                   vtalarm(ctl->timeout);
+                   if (ok != 0)
                        goto cleanUp;
+
+                   /* tell the server we got it OK and resynchronize */
+                   if (protocol->trail)
+                       (protocol->trail)(sockfp, ctl, num);
                }
 
-               /* read the message and ship it to the output sink */
-               ok = gen_readmsg(socket, mboxfd,
-                                len, 
-                                protocol->delimited,
-                                queryctl);
+               /*
+                * At this point in flow of control, either we've bombed
+                * on a protocol error or had delivery refused by the SMTP
+                * server (unlikely -- I've never seen it) or we've seen
+                * `accepted for delivery' and the message is shipped.
+                * It's safe to mark the message seen and delete it on the
+                * server now.
+                */
 
-               /* close the delivery pipe, we'll reopen before next message */
-               if (queryctl->mda[0])
-                   if ((ok = closemailpipe(mboxfd)) != 0 || alarmed)
+               /* maybe we delete this message now? */
+               if (protocol->delete
+                   && (fetch_it ? !ctl->keep : ctl->flush))
+               {
+                   deletions++;
+                   if (outlevel > O_SILENT) 
+                       fprintf(stderr, " flushed\n");
+                   ok = (protocol->delete)(sockfp, ctl, num);
+                   vtalarm(ctl->timeout);
+                   if (ok != 0)
                        goto cleanUp;
-
-               /* tell the server we got it OK and resynchronize */
-               if (protocol->trail)
-                   (protocol->trail)(socket, queryctl, num);
-               if (alarmed || ok != 0)
-                   goto cleanUp;
+               }
+               else if (outlevel > O_SILENT) 
+               {
+                   /* nuke it from the unseen-messages list */
+                   delete_uid(&ctl->newsaved, num);
+                   fprintf(stderr, " not flushed\n");
+               }
            }
 
-           /*
-            * At this point in flow of control, either we've bombed
-            * on a protocol error or had delivery refused by the SMTP
-            * server (unlikely -- I've never seen it) or we've seen
-            * `accepted for delivery' and the message is shipped.
-            * It's safe to mark the message seen and delete it on the
-            * server now.
-            */
-
-           /* maybe we delete this message now? */
-           if (protocol->delete
-               && (fetch_it ? !queryctl->keep : queryctl->flush))
+           /* remove all messages flagged for deletion */
+           if (protocol->expunge_cmd && deletions > 0)
            {
-               deletions++;
-               if (outlevel > O_SILENT && outlevel < O_VERBOSE) 
-                   fprintf(stderr, " flushed\n", num);
-               ok = (protocol->delete)(socket, queryctl, num);
-               if (alarmed || ok != 0)
+               ok = gen_transact(sockfp, protocol->expunge_cmd);
+               if (ok != 0)
                    goto cleanUp;
            }
-           else if (outlevel > O_SILENT && outlevel < O_VERBOSE) 
-           {
-               /* nuke it from the unseen-messages list */
-               delete_uid(&queryctl->newsaved, num);
-               fprintf(stderr, " not flushed\n", num);
-           }
+
+           ok = gen_transact(sockfp, protocol->exit_cmd);
+           if (ok == 0)
+               ok = PS_SUCCESS;
+           fclose(sockfp);
+           goto closeUp;
+       }
+       else {
+           ok = gen_transact(sockfp, protocol->exit_cmd);
+           if (ok == 0)
+               ok = PS_NOMAIL;
+           fclose(sockfp);
+           goto closeUp;
        }
 
-       /* remove all messages flagged for deletion */
-        if (protocol->expunge_cmd && deletions > 0)
+    cleanUp:
+       if (ok != 0 && ok != PS_SOCKET)
        {
-           ok = gen_transact(socket, protocol->expunge_cmd);
-           if (alarmed || ok != 0)
-               goto cleanUp;
-        }
-
-       ok = gen_transact(socket, protocol->exit_cmd);
-       if (alarmed || ok == 0)
-           ok = PS_SUCCESS;
-       close(socket);
-       goto closeUp;
-    }
-    else {
-       ok = gen_transact(socket, protocol->exit_cmd);
-       if (ok == 0)
-           ok = PS_NOMAIL;
-       close(socket);
-       goto closeUp;
+           gen_transact(sockfp, protocol->exit_cmd);
+           fclose(sockfp);
+       }
     }
 
-cleanUp:
-    if (ok != 0 && ok != PS_SOCKET)
-    {
-       gen_transact(socket, protocol->exit_cmd);
-       close(socket);
-    }
+    signal(SIGVTALRM, sigsave);
 
 closeUp:
-    if (mboxfd != -1)
-    {
-        if (!queryctl->mda[0])
-           SMTP_quit(mboxfd);
-       close(mboxfd);
-    }
-    alarm(0);
-    signal(SIGALRM, sigsave);
     return(ok);
 }
 
 #if defined(HAVE_STDARG_H)
-void gen_send(int socket, char *fmt, ... )
+void gen_send(FILE *sockfp, char *fmt, ... )
 /* assemble command in printf(3) style and send to the server */
 {
 #else
-void gen_send(socket, fmt, va_alist)
+void gen_send(sockfp, fmt, va_alist)
 /* assemble command in printf(3) style and send to the server */
-int socket;            /* socket to which server is connected */
+FILE *sockfp;          /* socket to which server is connected */
 const char *fmt;       /* printf-style format */
 va_dcl {
 #endif
@@ -958,7 +1086,8 @@ va_dcl {
     vsprintf(buf + strlen(buf), fmt, ap);
     va_end(ap);
 
-    SockPuts(socket, buf);
+    strcat(buf, "\r\n");
+    SockWrite(buf, strlen(buf), sockfp);
 
     if (outlevel == O_VERBOSE)
     {
@@ -966,18 +1095,18 @@ va_dcl {
 
        if (shroud && (cp = strstr(buf, shroud)))
            memset(cp, '*', strlen(shroud));
-       fprintf(stderr,"> %s\n", buf);
+       fprintf(stderr,"> %s", buf);
     }
 }
 
 #if defined(HAVE_STDARG_H)
-int gen_transact(int socket, char *fmt, ... )
+int gen_transact(FILE *sockfp, char *fmt, ... )
 /* assemble command in printf(3) style, send to server, accept a response */
 {
 #else
-int gen_transact(socket, fmt, va_alist)
+int gen_transact(sockfp, fmt, va_alist)
 /* assemble command in printf(3) style, send to server, accept a response */
-int socket;            /* socket to which server is connected */
+FILE *sockfp;          /* socket to which server is connected */
 const char *fmt;       /* printf-style format */
 va_dcl {
 #endif
@@ -999,51 +1128,22 @@ va_dcl {
   vsprintf(buf + strlen(buf), fmt, ap);
   va_end(ap);
 
-  SockPuts(socket, buf);
+  strcat(buf, "\r\n");
+  SockWrite(buf, strlen(buf), sockfp);
   if (outlevel == O_VERBOSE)
   {
       char *cp;
 
       if (shroud && (cp = strstr(buf, shroud)))
          memset(cp, '*', strlen(shroud));
-      fprintf(stderr,"> %s\n", buf);
+      fprintf(stderr,"> %s", buf);
   }
 
   /* we presume this does its own response echoing */
-  ok = (protocol->parse_response)(socket, buf);
+  ok = (protocol->parse_response)(sockfp, buf);
+  vtalarm(mytimeout);
 
   return(ok);
 }
 
-int strcrlf(dst, src, count)
-/* replace LFs with CR-LF; return length of string with replacements */
-char *dst;     /* new string with CR-LFs */
-char *src;     /* original string with LFs */
-int count;     /* length of src */
-{
-  int len = count;
-
-  while (count--)
-  {
-      if (*src == '\n')
-      {
-         *dst++ = '\r';
-         len++;
-      }
-      *dst++ = *src++;
-  }
-  *dst = '\0';
-  return len;
-}
-
-void 
-alarm_handler (int signal)
-/* handle server-timeout signal */
-{
-    alarmed = 1;
-    fprintf(stderr,
-           "fetchmail: timeout after %d seconds waiting for %s.\n",
-           mytimeout, srvname);
-}
-
 /* driver.c ends here */