]> Pileus Git - ~andy/fetchmail/blob - driver.c
gcc -Wall cleanup.
[~andy/fetchmail] / driver.c
1 /*
2  * driver.c -- generic driver for mail fetch method protocols
3  *
4  * Copyright 1996 by Eric S. Raymond
5  * All rights reserved.
6  * For license terms, see the file COPYING in this directory.
7  */
8
9 #include  <config.h>
10 #include  <stdio.h>
11 #include  <setjmp.h>
12 #include  <ctype.h>
13 #if defined(STDC_HEADERS)
14 #include  <stdlib.h>
15 #include  <string.h>
16 #endif
17 #if defined(HAVE_UNISTD_H)
18 #include <unistd.h>
19 #endif
20 #if defined(HAVE_STDARG_H)
21 #include  <stdarg.h>
22 #else
23 #include  <varargs.h>
24 #endif
25 #include  <sys/time.h>
26 #include  <signal.h>
27
28 #ifdef HAVE_GETHOSTBYNAME
29 #include <netdb.h>
30 #include "mx.h"
31 #endif /* HAVE_GETHOSTBYNAME */
32
33 #ifdef KERBEROS_V4
34 #include <krb.h>
35 #include <des.h>
36 #include <netinet/in.h>         /* must be included before "socket.h".*/
37 #include <netdb.h>
38 #endif /* KERBEROS_V4 */
39 #include  "socket.h"
40 #include  "fetchmail.h"
41 #include  "smtp.h"
42
43 #define SMTP_PORT       25      /* standard SMTP service port */
44
45 static const struct method *protocol;
46 static jmp_buf  restart;
47
48 char tag[TAGLEN];
49 static int tagnum;
50 #define GENSYM  (sprintf(tag, "a%04d", ++tagnum), tag)
51
52 static char *shroud;
53
54 static int strcrlf(dst, src, count)
55 /* replace LFs with CR-LF; return length of string with replacements */
56 char *dst;      /* new string with CR-LFs */
57 char *src;      /* original string with LFs */
58 int count;      /* length of src */
59 {
60   int len = count;
61
62   while (count--)
63   {
64       if (*src == '\n')
65       {
66           *dst++ = '\r';
67           len++;
68       }
69       *dst++ = *src++;
70   }
71   *dst = '\0';
72   return len;
73 }
74
75 static void alarm_handler (int signal)
76 /* handle server-timeout signal */
77 {
78     longjmp(restart, 1);
79 }
80
81 static void reply_hack(buf, host)
82 /* hack message headers so replies will work properly */
83 char *buf;              /* header to be hacked */
84 const char *host;       /* server hostname */
85 {
86     const char *from;
87     int state = 0, tokencount = 0;
88     char mycopy[POPBUFSIZE+1];
89
90     if (strncmp("From: ", buf, 6)
91         && strncmp("To: ", buf, 4)
92         && strncmp("Reply-", buf, 6)
93         && strncmp("Cc: ", buf, 4)
94         && strncmp("Bcc: ", buf, 5)) {
95         return;
96     }
97
98     strcpy(mycopy, buf);
99     for (from = mycopy; *from; from++)
100     {
101         switch (state)
102         {
103         case 0:   /* before header colon */
104             if (*from == ':')
105                 state = 1;
106             break;
107
108         case 1:   /* we've seen the colon, we're looking for addresses */
109             if (*from == '"')
110                 state = 3;
111             else if (*from == '(')
112                 state = 4;    
113             else if (*from == '<' || isalnum(*from))
114                 state = 5;
115             else if (isspace(*from))
116                 state = 2;
117             else if (*from == ',')
118                 tokencount = 0;
119             break;
120
121         case 2:     /* found a token boundary -- reset without copying */
122             if (*from != ' ' && *from != '\t')
123             {
124                 tokencount++;
125                 state = 1;
126                 --from;
127                 continue;
128             }
129
130         case 3:   /* we're in a quoted human name, copy and ignore */
131             if (*from == '"')
132                 state = 1;
133             break;
134
135         case 4:   /* we're in a parenthesized human name, copy and ignore */
136             if (*from == ')')
137                 state = 1;
138             break;
139
140         case 5:   /* the real work gets done here */
141             /*
142              * We're in something that might be an address part,
143              * either a bare unquoted/unparenthesized text or text
144              * enclosed in <> as per RFC822.
145              */
146             /* if the address part contains an @, don't mess with it */
147             if (*from == '@')
148                 state = 6;
149
150             /* If the address token is not properly terminated, ignore it. */
151             else if (*from == ' ' || *from == '\t')
152             {
153                 const char *cp;
154
155                 /*
156                  * The only lookahead case.  If we're looking at space or tab,
157                  * we might be looking at a local name immediately followed
158                  * by a human name.
159                  */
160                 for (cp = from; isspace(*cp); cp++)
161                     continue;
162                 if (*cp == '(')
163                 {
164                     strcpy(buf, "@");
165                     strcat(buf, host);
166                     buf += strlen(buf);
167                     state = 1;
168                 }
169             }
170
171             /*
172              * On proper termination with no @, insert hostname.
173              * Case '>' catches <>-enclosed mail IDs.  Case ',' catches
174              * comma-separated bare IDs.
175              */
176             else if (strchr(">,", *from))
177             {
178                 strcpy(buf, "@");
179                 strcat(buf, host);
180                 buf += strlen(buf);
181                 state = 1;
182             }
183
184             /* a single local name alone on the line */
185             else if (*from == '\n' && tokencount == 0)
186             {
187                 strcpy(buf, "@");
188                 strcat(buf, host);
189                 buf += strlen(buf);
190                 state = 2;
191             }
192
193             /* everything else, including alphanumerics, just passes through */
194             break;
195
196         case 6:   /* we're in a remote mail ID, no need to append hostname */
197             if (*from == '>' || *from == ',' || isspace(*from))
198                 state = 1;
199             break;
200         }
201
202         /* all characters from the old buffer get copied to the new one */
203         *buf++ = *from;
204     }
205     *buf++ = '\0';
206 }
207
208 static char *nxtaddr(hdr)
209 /* parse addresses in succession out of a specified RFC822 header */
210 char *hdr;      /* header line to be parsed, NUL to continue in previous hdr */
211 {
212     static char *hp, *tp, address[POPBUFSIZE+1];
213     static      state;
214
215     /*
216      * Note 1: RFC822 escaping with \ is *not* handled.  Note 2: it is
217      * important that this routine not stop on \r, since we use \r as
218      * a marker for RFC822 continuations below.
219      */
220
221     if (hdr)
222     {
223         hp = hdr;
224         state = 0;
225     }
226
227     for (; *hp; hp++)
228     {
229         switch (state)
230         {
231         case 0:   /* before header colon */
232             if (*hp == '\n')
233                 return(NULL);
234             else if (*hp == ':')
235             {
236                 state = 1;
237                 tp = address;
238             }
239             break;
240
241         case 1:   /* we've seen the colon, now grab the address */
242             if (*hp == '\n')    /* end of address list */
243             {
244                 *tp++ = '\0';
245                 state = 6;
246                 return(address);
247             }
248             else if (*hp == ',')  /* end of address */
249             {
250                 *tp++ = '\0';
251                 return(address);
252             }
253             else if (*hp == '"') /* quoted string */
254             {
255                 state = 2;
256                 *tp++ = *hp;
257             }
258             else if (*hp == '(') /* address comment -- ignore */
259                 state = 3;    
260             else if (*hp == '<') /* begin <address> */
261             {
262                 state = 4;
263                 tp = address;
264             }
265             else if (isspace(*hp)) /* ignore space */
266                 state = 1;
267             else   /* just take it */
268             {
269                 state = 1;
270                 *tp++ = *hp;
271             }
272             break;
273
274         case 2:   /* we're in a quoted string, copy verbatim */
275             if (*hp == '\n')
276                 return(NULL);
277             if (*hp != '"')
278                 *tp++ = *hp;
279             else if (*hp == '"')
280             {
281                 *tp++ = *hp;
282                 state = 1;
283             }
284             break;
285
286         case 3:   /* we're in a parenthesized comment, ignore */
287             if (*hp == '\n')
288                 return(NULL);
289             else if (*hp == ')')
290                 state = 1;
291             break;
292
293         case 4:   /* possible <>-enclosed address */
294             if (*hp == '>') /* end of address */
295             {
296                 *tp++ = '\0';
297                 state = 1;
298                 return(address);
299             }
300             else if (*hp == '<')  /* nested <> */
301                 tp = address;
302             else if (*hp == '"') /* quoted address */
303             {
304                 *tp++ = *hp;
305                 state = 5;
306             }
307             else  /* just copy address */
308                 *tp++ = *hp;
309             break;
310
311         case 5:   /* we're in a quoted address, copy verbatim */
312             if (*hp == '\n')  /* mismatched quotes */
313                 return(NULL);
314             if (*hp != '"')  /* just copy it if it isn't a quote */
315                 *tp++ = *hp;
316             else if (*hp == '"')  /* end of quoted string */
317             {
318                 *tp++ = *hp;
319                 state = 4;
320             }
321             break;
322
323         case 6:
324             return(NULL);
325             break;
326         }
327     }
328
329     return(NULL);
330 }
331
332 #ifdef HAVE_GETHOSTBYNAME
333 #define MX_RETRIES      3
334
335 static int is_host_alias(name, ctl)
336 /* determine whether name is a DNS alias of the hostname */
337 const char *name;
338 struct query    *ctl;
339 {
340     struct hostent      *he;
341     int                 i;
342
343     /*
344      * The first two checks are optimizations that will catch a good
345      * many cases.  First, check against the hostname the user specified.
346      * Odds are good this will either be the mailserver's FQDN or a
347      * suffix of it with the mailserver's domain's default host name
348      * omitted.  Next, check against the mailserver's FQDN, in case
349      * it's not the same as the declared hostname.
350      *
351      * Either of these on a mail address is definitive.  Only if the
352      * name doesn't match either is it time to call the bind library.
353      * If this happens odds are good we're looking at an MX name.
354      */
355     if (strcmp(name, ctl->servername) == 0)
356         return(TRUE);
357     else if (strcmp(name, ctl->canonical_name) == 0)
358         return(TRUE);
359
360     /*
361      * We treat DNS lookup failure as a negative on the theory that
362      * the mailserver's DNS server is `nearby' and should be able
363      * to respond quickly and reliably.  Ergo if we get failure,
364      * the name isn't a mailserver alias.
365      */
366     else if ((he = gethostbyname(name)) && strcmp(ctl->canonical_name, he->h_name) == 0)
367         return(TRUE);
368
369     /*
370      * Search for a name match on MX records pointing to the server
371      * site.  These may live far away, so allow a couple of retries.
372      */
373     for (i = 0; i < MX_RETRIES; i++)
374     {
375         struct mxentry *mxrecords, *mxp;
376
377         mxrecords = getmxrecords(name);
378
379         if (mxrecords == (struct mxentry *)NULL)
380             if (h_errno == TRY_AGAIN)
381             {
382                 sleep(1);
383                 continue;
384             }
385             else
386                 break;
387
388         for (mxp = mxrecords; mxp->name; mxp++)
389             if (strcmp(name, mxp->name) == 0)
390                 return(TRUE);
391     }
392
393     return(FALSE);
394 }
395
396 void find_server_names(hdr, ctl, xmit_names)
397 /* parse names out of a RFC822 header into an ID list */
398 const char *hdr;                /* RFC822 header in question */
399 struct query *ctl;      /* list of permissible aliases */
400 struct idlist **xmit_names;     /* list of recipient names parsed out */
401 {
402     if (hdr == (char *)NULL)
403         return;
404     else
405     {
406         char    *cp, *lname;
407
408         if ((cp = nxtaddr(hdr)) != (char *)NULL)
409             do {
410                 char    *atsign = strchr(cp, '@');
411
412                 if (atsign)
413                     if (ctl->norewrite)
414                         continue;
415                     else
416                     {
417                         if (!is_host_alias(atsign+1, ctl))
418                             continue;
419                         atsign[0] = '\0';
420                     }
421                 lname = idpair_find(&ctl->localnames, cp);
422                 if (lname != (char *)NULL)
423                 {
424                     if (outlevel == O_VERBOSE)
425                         fprintf(stderr,
426                                 "fetchmail: mapped %s to local %s\n",
427                                 cp, lname);
428                     save_uid(xmit_names, -1, lname);
429                 }
430             } while
431                 ((cp = nxtaddr((char *)NULL)) != (char *)NULL);
432     }
433 }
434 #endif /* HAVE_GETHOSTBYNAME */
435
436 static int gen_readmsg (socket, mboxfd, len, delimited, ctl)
437 /* read message content and ship to SMTP or MDA */
438 int socket;     /* to which the server is connected */
439 int mboxfd;     /* descriptor to which retrieved message will be written */
440 long len;       /* length of message */
441 int delimited;  /* does the protocol use a message delimiter? */
442 struct query *ctl;      /* query control record */
443
444     char buf [MSGBUFSIZE+1]; 
445     char *bufp, *headers, *unixfrom, *fromhdr, *tohdr, *cchdr, *bcchdr;
446     int n, oldlen;
447     int inheaders,lines,sizeticker;
448
449     /* read the message content from the server */
450     inheaders = 1;
451     headers = unixfrom = fromhdr = tohdr = cchdr = bcchdr = NULL;
452     lines = 0;
453     sizeticker = 0;
454     while (delimited || len > 0)
455     {
456         if ((n = SockGets(socket,buf,sizeof(buf))) < 0)
457             return(PS_SOCKET);
458
459         /* write the message size dots */
460         if (n > 0)
461         {
462             sizeticker += n;
463             while (sizeticker >= SIZETICKER)
464             {
465                 if (outlevel > O_SILENT)
466                     fputc('.',stderr);
467                 sizeticker -= SIZETICKER;
468             }
469         }
470         len -= n;
471         bufp = buf;
472         if (buf[0] == '\0' || buf[0] == '\r' || buf[0] == '\n')
473             inheaders = 0;
474         if (delimited && *bufp == '.') {
475             bufp++;
476             if (*bufp == 0)
477                 break;  /* end of message */
478         }
479         strcat(bufp, "\n");
480      
481         if (inheaders)
482         {
483             if (!ctl->norewrite)
484                 reply_hack(bufp, ctl->servername);
485
486             if (!lines)
487             {
488                 oldlen = strlen(bufp);
489                 headers = malloc(oldlen + 1);
490                 if (headers == NULL)
491                     return(PS_IOERR);
492                 (void) strcpy(headers, bufp);
493                 bufp = headers;
494             }
495             else
496             {
497                 int     newlen;
498
499                 /*
500                  * We deal with RFC822 continuation lines here.
501                  * Replace previous '\n' with '\r' so nxtaddr 
502                  * and reply_hack will be able to see past it.
503                  * (We know this safe because SocketGets stripped
504                  * out all carriage returns in the read loop above
505                  * and we haven't reintroduced any since then.)
506                  * We'll undo this before writing the header.
507                  */
508                 if (isspace(bufp[0]))
509                     headers[oldlen-1] = '\r';
510
511                 newlen = oldlen + strlen(bufp);
512                 headers = realloc(headers, newlen + 1);
513                 if (headers == NULL)
514                     return(PS_IOERR);
515                 strcpy(headers + oldlen, bufp);
516                 bufp = headers + oldlen;
517                 oldlen = newlen;
518             }
519
520             if (!strncmp(bufp,"From ",5))
521                 unixfrom = bufp;
522             else if (!strncasecmp("From:", bufp, 5))
523                 fromhdr = bufp;
524             else if (!strncasecmp("To:", bufp, 3))
525                 tohdr = bufp;
526             else if (!strncasecmp("Cc:", bufp, 3))
527                 cchdr = bufp;
528             else if (!strncasecmp("Bcc:", bufp, 4))
529                 bcchdr = bufp;
530
531             goto skipwrite;
532         }
533         else if (headers)       /* OK, we're at end of headers now */
534         {
535             char                *cp;
536             struct idlist       *idp, *xmit_names;
537
538             /* cons up a list of local recipients */
539             xmit_names = (struct idlist *)NULL;
540 #ifdef HAVE_GETHOSTBYNAME
541             /* is this a multidrop box? */
542             if (ctl->localnames != (struct idlist *)NULL
543                 && ctl->localnames->next != (struct idlist *)NULL)
544             {
545                 /* compute the local address list */
546                 find_server_names(tohdr,  ctl, &xmit_names);
547                 find_server_names(cchdr,  ctl, &xmit_names);
548                 find_server_names(bcchdr, ctl, &xmit_names);
549
550                 /* if nothing supplied localnames, default appropriately */
551                 if (!xmit_names)
552                     save_uid(&xmit_names, -1, dfltuser);
553             }
554             else        /* it's a single-drop box, use first localname */
555 #endif /* HAVE_GETHOSTBYNAME */
556             {
557                 if (ctl->localnames)
558                     save_uid(&xmit_names, -1, ctl->localnames->id);
559                 else
560                     save_uid(&xmit_names, -1, dfltuser);
561             }
562
563             /* time to address the message */
564             if (ctl->mda[0])    /* we have a declared MDA */
565             {
566                 int     i, nlocals = 0;
567                 char    **sargv, **sp;
568
569                 /*
570                  * We go through this in order to be able to handle very
571                  * long lists of users.
572                  */
573                 for (idp = xmit_names; idp; idp = idp->next)
574                     nlocals++;
575                 sp = sargv = (char **)alloca(ctl->mda_argcount+nlocals+2);
576                 for (i = 0; i <= ctl->mda_argcount; i++)
577                     *sp++ = ctl->mda_argv[i];
578                 for (idp = xmit_names; idp; idp = idp->next)
579                     *sp++ = idp->id;
580                 *sp =  (char *)NULL;
581
582 #ifdef HAVE_SETEUID
583                 /*
584                  * Arrange to run with user's permissions if we're root.
585                  * This will initialize the ownership of any files the
586                  * MDA creates properly.  (The seteuid call is available
587                  * under all BSDs and Linux)
588                  */
589                 seteuid(ctl->uid);
590 #endif /* HAVE_SETEUID */
591
592                 mboxfd = openmailpipe(sargv);
593
594 #ifdef HAVE_SETEUID
595                 /* this will fail quietly if we didn't start as root */
596                 seteuid(0);
597 #endif /* HAVE_SETEUID */
598
599                 if (mboxfd < 0)
600                     return(PS_IOERR);
601             }
602             else
603             {
604                 if (SMTP_from(mboxfd, nxtaddr(fromhdr)) != SM_OK)
605                     return(PS_SMTP);
606
607                 for (idp = xmit_names; idp; idp = idp->next)
608                     if (SMTP_rcpt(mboxfd, idp->id) != SM_OK)
609                         return(PS_SMTP);
610
611                 SMTP_data(mboxfd);
612                 if (outlevel == O_VERBOSE)
613                     fputs("SMTP> ", stderr);
614             }
615             free_uid_list(&xmit_names);
616
617             /* change continuation markers back to regular newlines */
618             for (cp = headers; cp < headers +  oldlen; cp++)
619                 if (*cp == '\r')
620                     *cp = '\n';
621
622             /* replace all LFs with CR-LF before sending to the SMTP server */
623             if (!ctl->mda[0])
624             {
625                 char *newheaders = malloc(1 + oldlen * 2);
626
627                 if (newheaders == NULL)
628                     return(PS_IOERR);
629                 oldlen = strcrlf(newheaders, headers, oldlen);
630                 free(headers);
631                 headers = newheaders;
632             }
633             if (write(mboxfd,headers,oldlen) < 0)
634             {
635                 free(headers);
636                 headers = NULL;
637                 perror("gen_readmsg: writing RFC822 headers");
638                 return(PS_IOERR);
639             }
640             else if (outlevel == O_VERBOSE)
641                 fputs("#", stderr);
642             free(headers);
643             headers = NULL;
644         }
645
646         /* SMTP byte-stuffing */
647         if (*bufp == '.' && ctl->mda[0] == 0)
648             write(mboxfd, ".", 1);
649
650         /* write this line to the file after replacing all LFs with CR-LF */
651         if (!ctl->mda[0])
652         {
653             char *newbufp = malloc(1 + strlen(bufp) * 2);
654
655             if (newbufp == NULL)
656                 return(PS_IOERR);
657             strcrlf(newbufp, bufp, strlen(bufp));
658             bufp = newbufp;
659         }
660         n = write(mboxfd,bufp,strlen(bufp));
661         if (!ctl->mda[0])
662             free(bufp);
663         if (n < 0)
664         {
665             perror("gen_readmsg: writing message text");
666             return(PS_IOERR);
667         }
668         else if (outlevel == O_VERBOSE)
669             fputc('*', stderr);
670
671     skipwrite:;
672         lines++;
673     }
674
675     if (ctl->mda[0])
676     {
677         /* close the delivery pipe, we'll reopen before next message */
678         if (closemailpipe(mboxfd))
679             return(PS_IOERR);
680     }
681     else
682     {
683         /* write message terminator */
684         if (SMTP_eom(mboxfd) != SM_OK)
685             return(PS_SMTP);
686     }
687
688     return(0);
689 }
690
691 #ifdef KERBEROS_V4
692 int
693 kerberos_auth (socket, canonical) 
694 /* authenticate to the server host using Kerberos V4 */
695 int socket;             /* socket to server host */
696 char *canonical;        /* server name */
697 {
698     char * host_primary;
699     KTEXT ticket;
700     MSG_DAT msg_data;
701     CREDENTIALS cred;
702     Key_schedule schedule;
703     int rem;
704   
705     ticket = ((KTEXT) (malloc (sizeof (KTEXT_ST))));
706     rem = (krb_sendauth (0L, socket, ticket, "pop",
707                          canonical,
708                          ((char *) (krb_realmofhost (canonical))),
709                          ((unsigned long) 0),
710                          (&msg_data),
711                          (&cred),
712                          (schedule),
713                          ((struct sockaddr_in *) 0),
714                          ((struct sockaddr_in *) 0),
715                          "KPOPV0.1"));
716     free (ticket);
717     if (rem != KSUCCESS)
718     {
719         fprintf (stderr, "fetchmail: kerberos error %s\n", (krb_get_err_text (rem)));
720         return (PS_ERROR);
721     }
722     return (0);
723 }
724 #endif /* KERBEROS_V4 */
725
726 int do_protocol(ctl, proto)
727 /* retrieve messages from server using given protocol method table */
728 struct query *ctl;              /* parsed options with merged-in defaults */
729 const struct method *proto;     /* protocol method table */
730 {
731     int ok, mboxfd = -1;
732     void (*sigsave)();
733
734 #ifndef KERBEROS_V4
735     if (ctl->authenticate == A_KERBEROS)
736     {
737         fputs("fetchmail: Kerberos support not linked.\n", stderr);
738         return(PS_ERROR);
739     }
740 #endif /* KERBEROS_V4 */
741
742     /* lacking methods, there are some options that may fail */
743     if (!proto->is_old)
744     {
745         /* check for unsupported options */
746         if (ctl->flush) {
747             fprintf(stderr,
748                     "Option --flush is not supported with %s\n",
749                     proto->name);
750             return(PS_SYNTAX);
751         }
752         else if (ctl->fetchall) {
753             fprintf(stderr,
754                     "Option --all is not supported with %s\n",
755                     proto->name);
756             return(PS_SYNTAX);
757         }
758     }
759     if (!proto->getsizes && ctl->limit)
760     {
761         fprintf(stderr,
762                 "Option --limit is not supported with %s\n",
763                 proto->name);
764         return(PS_SYNTAX);
765     }
766
767     protocol = proto;
768     tagnum = 0;
769     tag[0] = '\0';      /* nuke any tag hanging out from previous query */
770     ok = 0;
771
772     if (setjmp(restart) == 1)
773         fprintf(stderr,
774                 "fetchmail: timeout after %d seconds waiting for %s.\n",
775                 ctl->timeout, ctl->servername);
776     else
777     {
778         char buf [POPBUFSIZE+1];
779         int *msgsizes, socket, len, num, count, new, deletions = 0;
780
781         /* set up the server-nonresponse timeout */
782         sigsave = signal(SIGALRM, alarm_handler);
783         alarm(ctl->timeout);
784
785         /* open a socket to the mail server */
786         if ((socket = Socket(ctl->servername,
787                              ctl->port ? ctl->port : protocol->port))<0)
788         {
789             perror("fetchmail, connecting to host");
790             ok = PS_SOCKET;
791             goto closeUp;
792         }
793
794 #ifdef KERBEROS_V4
795         if (ctl->authenticate == A_KERBEROS)
796         {
797             ok = (kerberos_auth (socket, ctl->canonical_name));
798             if (ok != 0)
799                 goto cleanUp;
800         }
801 #endif /* KERBEROS_V4 */
802
803         /* accept greeting message from mail server */
804         ok = (protocol->parse_response)(socket, buf);
805         if (ok != 0)
806             goto cleanUp;
807
808         /* try to get authorized to fetch mail */
809         shroud = ctl->password;
810         ok = (protocol->getauth)(socket, ctl, buf);
811         shroud = (char *)NULL;
812         if (ok == PS_ERROR)
813             ok = PS_AUTHFAIL;
814         if (ok != 0)
815             goto cleanUp;
816
817         /* compute number of messages and number of new messages waiting */
818         if ((protocol->getrange)(socket, ctl, &count, &new) != 0)
819             goto cleanUp;
820
821         /* show user how many messages we downloaded */
822         if (outlevel > O_SILENT)
823             if (count == 0)
824                 fprintf(stderr, "No mail from %s@%s\n", 
825                         ctl->remotename,
826                         ctl->servername);
827             else
828             {
829                 fprintf(stderr, "%d message%s", count, count > 1 ? "s" : ""); 
830                 if (new != -1 && (count - new) > 0)
831                     fprintf(stderr, " (%d seen)", count-new);
832                 fprintf(stderr,
833                         " from %s@%s.\n",
834                         ctl->remotename,
835                         ctl->servername);
836             }
837
838         /* we may need to get sizes in order to check message limits */
839         msgsizes = (int *)NULL;
840         if (!ctl->fetchall && proto->getsizes && ctl->limit)
841         {
842             msgsizes = (int *)alloca(sizeof(int) * count);
843
844             if ((ok = (proto->getsizes)(socket, count, msgsizes)) != 0)
845                 return(PS_ERROR);
846         }
847
848         if (check_only)
849         {
850             if (new == -1 || ctl->fetchall)
851                 new = count;
852             ok = ((new > 0) ? PS_SUCCESS : PS_NOMAIL);
853             goto closeUp;
854         }
855         else if (count > 0)
856         {
857             if (ctl->mda[0] == '\0')
858                 if ((mboxfd = Socket(ctl->smtphost, SMTP_PORT)) < 0
859                     || SMTP_ok(mboxfd, NULL) != SM_OK
860                     || SMTP_helo(mboxfd, ctl->servername) != SM_OK)
861                 {
862                     ok = PS_SMTP;
863                     close(mboxfd);
864                     mboxfd = -1;
865                     goto cleanUp;
866                 }
867     
868             /* read, forward, and delete messages */
869             for (num = 1; num <= count; num++)
870             {
871                 int     toolarge = msgsizes && msgsizes[num-1]>ctl->limit;
872                 int     fetch_it = ctl->fetchall ||
873                     (!(protocol->is_old && (protocol->is_old)(socket,ctl,num)) && !toolarge);
874
875                 /* we may want to reject this message if it's old */
876                 if (!fetch_it)
877                 {
878                     if (outlevel > O_SILENT)
879                     {
880                         fprintf(stderr, "skipping message %d", num);
881                         if (toolarge)
882                             fprintf(stderr, " (oversized, %d bytes)", msgsizes[num-1]);
883                     }
884                 }
885                 else
886                 {
887                     /* request a message */
888                     (protocol->fetch)(socket, num, &len);
889
890                     if (outlevel > O_SILENT)
891                     {
892                         fprintf(stderr, "reading message %d", num);
893                         if (len > 0)
894                             fprintf(stderr, " (%d bytes)", len);
895                         if (outlevel == O_VERBOSE)
896                             fputc('\n', stderr);
897                         else
898                             fputc(' ', stderr);
899                     }
900
901                     /*
902                      * If we're forwarding via SMTP, mboxfd is initialized
903                      * at this point (it was set at start of retrieval). 
904                      * If we're using an MDA it's not set -- gen_readmsg()
905                      * may have to parse message headers to know what
906                      * delivery addresses should be passed to the MDA
907                      */
908
909                     /* read the message and ship it to the output sink */
910                     ok = gen_readmsg(socket, mboxfd,
911                                      len, 
912                                      protocol->delimited,
913                                      ctl);
914
915                     /* tell the server we got it OK and resynchronize */
916                     if (protocol->trail)
917                         (protocol->trail)(socket, ctl, num);
918                     if (ok != 0)
919                         goto cleanUp;
920                 }
921
922                 /*
923                  * At this point in flow of control, either we've bombed
924                  * on a protocol error or had delivery refused by the SMTP
925                  * server (unlikely -- I've never seen it) or we've seen
926                  * `accepted for delivery' and the message is shipped.
927                  * It's safe to mark the message seen and delete it on the
928                  * server now.
929                  */
930
931                 /* maybe we delete this message now? */
932                 if (protocol->delete
933                     && (fetch_it ? !ctl->keep : ctl->flush))
934                 {
935                     deletions++;
936                     if (outlevel > O_SILENT) 
937                         fprintf(stderr, " flushed\n");
938                     ok = (protocol->delete)(socket, ctl, num);
939                     if (ok != 0)
940                         goto cleanUp;
941                 }
942                 else if (outlevel > O_SILENT) 
943                 {
944                     /* nuke it from the unseen-messages list */
945                     delete_uid(&ctl->newsaved, num);
946                     fprintf(stderr, " not flushed\n");
947                 }
948             }
949
950             /* remove all messages flagged for deletion */
951             if (protocol->expunge_cmd && deletions > 0)
952             {
953                 ok = gen_transact(socket, protocol->expunge_cmd);
954                 if (ok != 0)
955                     goto cleanUp;
956             }
957
958             ok = gen_transact(socket, protocol->exit_cmd);
959             if (ok == 0)
960                 ok = PS_SUCCESS;
961             close(socket);
962             goto closeUp;
963         }
964         else {
965             ok = gen_transact(socket, protocol->exit_cmd);
966             if (ok == 0)
967                 ok = PS_NOMAIL;
968             close(socket);
969             goto closeUp;
970         }
971
972     cleanUp:
973         if (ok != 0 && ok != PS_SOCKET)
974         {
975             gen_transact(socket, protocol->exit_cmd);
976             close(socket);
977         }
978     }
979
980     alarm(0);
981     signal(SIGALRM, sigsave);
982
983 closeUp:
984     if (mboxfd != -1)
985     {
986         if (!ctl->mda[0])
987             SMTP_quit(mboxfd);
988         close(mboxfd);
989     }
990
991     return(ok);
992 }
993
994 #if defined(HAVE_STDARG_H)
995 void gen_send(int socket, char *fmt, ... )
996 /* assemble command in printf(3) style and send to the server */
997 {
998 #else
999 void gen_send(socket, fmt, va_alist)
1000 /* assemble command in printf(3) style and send to the server */
1001 int socket;             /* socket to which server is connected */
1002 const char *fmt;        /* printf-style format */
1003 va_dcl {
1004 #endif
1005
1006     char buf [POPBUFSIZE+1];
1007     va_list ap;
1008
1009     if (protocol->tagged)
1010         (void) sprintf(buf, "%s ", GENSYM);
1011     else
1012         buf[0] = '\0';
1013
1014 #if defined(HAVE_STDARG_H)
1015     va_start(ap, fmt) ;
1016 #else
1017     va_start(ap);
1018 #endif
1019     vsprintf(buf + strlen(buf), fmt, ap);
1020     va_end(ap);
1021
1022     SockPuts(socket, buf);
1023
1024     if (outlevel == O_VERBOSE)
1025     {
1026         char *cp;
1027
1028         if (shroud && (cp = strstr(buf, shroud)))
1029             memset(cp, '*', strlen(shroud));
1030         fprintf(stderr,"> %s\n", buf);
1031     }
1032 }
1033
1034 #if defined(HAVE_STDARG_H)
1035 int gen_transact(int socket, char *fmt, ... )
1036 /* assemble command in printf(3) style, send to server, accept a response */
1037 {
1038 #else
1039 int gen_transact(socket, fmt, va_alist)
1040 /* assemble command in printf(3) style, send to server, accept a response */
1041 int socket;             /* socket to which server is connected */
1042 const char *fmt;        /* printf-style format */
1043 va_dcl {
1044 #endif
1045
1046   int ok;
1047   char buf [POPBUFSIZE+1];
1048   va_list ap;
1049
1050   if (protocol->tagged)
1051       (void) sprintf(buf, "%s ", GENSYM);
1052   else
1053       buf[0] = '\0';
1054
1055 #if defined(HAVE_STDARG_H)
1056   va_start(ap, fmt) ;
1057 #else
1058   va_start(ap);
1059 #endif
1060   vsprintf(buf + strlen(buf), fmt, ap);
1061   va_end(ap);
1062
1063   SockPuts(socket, buf);
1064   if (outlevel == O_VERBOSE)
1065   {
1066       char *cp;
1067
1068       if (shroud && (cp = strstr(buf, shroud)))
1069           memset(cp, '*', strlen(shroud));
1070       fprintf(stderr,"> %s\n", buf);
1071   }
1072
1073   /* we presume this does its own response echoing */
1074   ok = (protocol->parse_response)(socket, buf);
1075
1076   return(ok);
1077 }
1078
1079 /* driver.c ends here */