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