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