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