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