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