]> Pileus Git - ~andy/fetchmail/blob - fetchmail.c
Fix a search loop.
[~andy/fetchmail] / fetchmail.c
1 /*
2  * fetchmail.c -- main driver module for fetchmail
3  *
4  * For license terms, see the file COPYING in this directory.
5  */
6
7 #include <config.h>
8
9 #include <stdio.h>
10 #include <ctype.h>
11 #if defined(STDC_HEADERS)
12 #include <stdlib.h>
13 #endif
14 #if defined(HAVE_UNISTD_H)
15 #include <unistd.h>
16 #endif
17 #if defined(HAVE_ALLOCA_H)
18 #include <alloca.h>
19 #endif
20 #include <string.h>
21 #include <signal.h>
22 #include <pwd.h>
23 #include <errno.h>
24 #include <sys/time.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <sys/wait.h>
28
29 #ifdef HAVE_GETHOSTBYNAME
30 #include <netdb.h>
31 #endif /* HAVE_GETHOSTBYNAME */
32
33 #include "fetchmail.h"
34 #include "smtp.h"
35 #include "getopt.h"
36
37 #define DROPDEAD        6       /* maximum bad socket opens */
38
39 /* prototypes for internal functions */
40 static int load_params(int, char **, int);
41 static void dump_params (struct query *);
42 static int query_host(struct query *);
43 static char *visbuf(const char *);
44
45 /* controls the detail level of status/progress messages written to stderr */
46 int outlevel;           /* see the O_.* constants above */
47 int yydebug;            /* enable parse debugging */
48
49 /* daemon mode control */
50 int poll_interval;      /* poll interval in seconds */
51 int nodetach;           /* if TRUE, don't detach daemon process */
52 char *logfile;          /* log file for daemon mode */
53 int quitmode;           /* if --quit was set */
54 int check_only;         /* if --probe was set */
55 int cmd_batchlimit;     /* if --batchlimit was set */
56 char *cmd_logfile;      /* if --logfile was set */
57
58 /* miscellaneous global controls */
59 char *rcfile;           /* path name of rc file */
60 char *idfile;           /* UID list file */
61 int versioninfo;        /* emit only version info */
62 char *user;             /* the name of the invoking user */
63
64 static char *lockfile;          /* name of lockfile */
65 static int querystatus;         /* status of query */
66 static int lastsig;             /* last signal received */
67
68 static void termhook();         /* forward declaration of exit hook */
69
70 RETSIGTYPE donothing(sig) int sig; {signal(sig, donothing); lastsig = sig;}
71
72 static void unlockit(void)
73 /* must-do actions for exit (but we can't count on being able to do malloc) */
74 {
75     unlink(lockfile);
76 }
77
78 int main (int argc, char **argv)
79 {
80     int st, bkgd = FALSE;
81     int parsestatus, implicitmode;
82     char *home, *tmpdir, tmpbuf[BUFSIZ]; 
83     struct passwd *pw;
84     struct query *ctl;
85     FILE        *lockfp;
86     pid_t pid;
87
88     if ((user = getenv("USER")) == (char *)NULL)
89         user = getenv("LOGNAME");
90
91     if ((user == (char *)NULL) || (home = getenv("HOME")) == (char *)NULL)
92     {
93         if ((pw = getpwuid(getuid())) != NULL)
94         {
95             user = pw->pw_name;
96             home = pw->pw_dir;
97         }
98         else
99         {
100             fprintf(stderr,"fetchmail: can't find your name and home directory!\n");
101             exit(PS_UNDEFINED);
102         }
103     }
104
105     /*
106      * Backward-compatibility hack.  If we're called by the name of the
107      * ancestral popclient, look for .poprc.  This will actually work 
108      * for popclient files that don't use the removed keywords.
109      */
110     if (strcmp("popclient", argv[0]) == 0)
111         tmpdir = ".poprc";
112     else
113         tmpdir = ".fetchmailrc";
114
115     rcfile = (char *) xmalloc(strlen(home)+strlen(tmpdir)+2);
116     strcpy(rcfile, home);
117     strcat(rcfile, "/");
118     strcat(rcfile, tmpdir);
119
120 #define IDFILE_NAME     ".fetchids"
121     idfile = (char *) xmalloc(strlen(home)+strlen(IDFILE_NAME)+2);
122     strcpy(idfile, home);
123     strcat(idfile, "/");
124     strcat(idfile, IDFILE_NAME);
125   
126     outlevel = O_NORMAL;
127
128     if ((parsestatus = parsecmdline(argc,argv,&cmd_opts)) < 0)
129         exit(PS_SYNTAX);
130
131     if (versioninfo)
132         printf("This is fetchmail release %s pl %s\n", RELEASE_ID, PATCHLEVEL);
133
134     /* avoid parsing the config file if all we're doing is killing a daemon */ 
135     if (!quitmode)
136         implicitmode = load_params(argc, argv, optind);
137
138     /* set up to do lock protocol */
139     if ((tmpdir = getenv("TMPDIR")) == (char *)NULL)
140         tmpdir = "/tmp";
141     strcpy(tmpbuf, tmpdir);
142     strcat(tmpbuf, "/fetchmail-");
143     strcat(tmpbuf, user);
144
145     /* perhaps we just want to check options? */
146     if (versioninfo) {
147         printf("Taking options from command line");
148         if (access(rcfile, 0))
149             printf("\n");
150         else
151             printf(" and %s\n", rcfile);
152         if (outlevel == O_VERBOSE)
153             printf("Lockfile at %s\n", tmpbuf);
154         if (batchlimit)
155             printf("SMTP message batch limit is %d.\n", batchlimit);
156         else
157             printf("No SMTP message batch limit.\n");
158         for (ctl = querylist; ctl; ctl = ctl->next) {
159             if (ctl->active && !(implicitmode && ctl->skip))
160                 dump_params(ctl);
161         }
162         if (querylist == NULL)
163             (void) fprintf(stderr,
164                 "No mailservers set up -- perhaps %s is missing?\n",
165                           rcfile);
166         exit(0);
167     }
168     else if (!quitmode && querylist == NULL) {
169         (void) fputs("fetchmail: no mailservers have been specified.\n", stderr);
170         exit(PS_SYNTAX);
171     }
172
173     /* check for another fetchmail running concurrently */
174     pid = -1;
175     if ((lockfile = (char *) malloc(strlen(tmpbuf) + 1)) == NULL)
176     {
177         fprintf(stderr,"fetchmail: cannot allocate memory for lock name.\n");
178         exit(PS_EXCLUDE);
179     }
180     else
181         (void) strcpy(lockfile, tmpbuf);
182     if ((lockfp = fopen(lockfile, "r")) != NULL )
183     {
184         bkgd = (fscanf(lockfp,"%d %d", &pid, &st) == 2);
185
186         if (kill(pid, 0) == -1) {
187             fprintf(stderr,"fetchmail: removing stale lockfile\n");
188             pid = -1;
189             bkgd = FALSE;
190             remove(lockfile);
191         }
192         fclose(lockfp);
193     }
194
195     /* perhaps user asked us to kill the other fetchmail */
196     if (quitmode)
197     {
198         if (pid == -1) 
199         {
200             fprintf(stderr,"fetchmail: no other fetchmail is running\n");
201             exit(PS_EXCLUDE);
202         }
203         else if (kill(pid, SIGTERM) < 0)
204         {
205             fprintf(stderr,"fetchmail: error killing %s fetchmail at %d.\n",
206                     bkgd ? "background" : "foreground", pid);
207             exit(PS_EXCLUDE);
208         }
209         else
210         {
211             fprintf(stderr,"fetchmail: %s fetchmail at %d killed.\n",
212                     bkgd ? "background" : "foreground", pid);
213             remove(lockfile);
214             exit(0);
215         }
216     }
217
218     /* another fetchmail is running -- wake it up or die */
219     if (pid != -1)
220     {
221         if (check_only)
222             return(PS_EXCLUDE);
223         else if (!implicitmode)
224         {
225             fprintf(stderr,
226                  "fetchmail: can't poll specified hosts with another fetchmail running at %d.\n",
227                  pid);
228                 return(PS_EXCLUDE);
229         }
230         else if (!bkgd)
231         {
232             fprintf(stderr,
233                  "fetchmail: another foreground fetchmail is running at %d.\n",
234                  pid);
235                 return(PS_EXCLUDE);
236         }
237         else if (kill(pid, SIGHUP) == 0)
238         {
239             fprintf(stderr,
240                     "fetchmail: background fetchmail at %d awakened.\n",
241                     pid);
242             return(0);
243         }
244         else
245         {
246             /*
247              * Should never happen -- possible only if a background fetchmail
248              * croaks after the first kill probe above but before the SIGHUP
249              * transmission.
250              */
251             fprintf(stderr,
252                     "fetchmail: elder sibling at %d died mysteriously.\n",
253                     pid);
254             return(PS_UNDEFINED);
255         }
256     }
257
258     /* pick up interactively any passwords we need but don't have */ 
259     for (ctl = querylist; ctl; ctl = ctl->next)
260         if (ctl->active && !(implicitmode && ctl->skip) && !ctl->password[0])
261         {
262             if (ctl->authenticate == A_KERBEROS)
263               /* Server won't care what the password is, but there
264                  must be some non-null string here.  */
265               (void) strncpy(ctl->password, 
266                              ctl->remotename, PASSWORDLEN-1);
267             else
268               {
269                 (void) sprintf(tmpbuf, "Enter password for %s@%s: ",
270                                ctl->remotename, ctl->servernames->id);
271                 (void) strncpy(ctl->password,
272                                (char *)getpassword(tmpbuf),PASSWORDLEN-1);
273               }
274         }
275
276     /*
277      * Maybe time to go to demon mode...
278      */
279     if (poll_interval && !nodetach)
280         daemonize(logfile, termhook);
281
282     /* beyond here we don't want more than one fetchmail running per user */
283     umask(0077);
284     signal(SIGABRT, termhook);
285     signal(SIGINT, termhook);
286     signal(SIGTERM, termhook);
287     signal(SIGALRM, termhook);
288     signal(SIGPIPE, termhook);
289     signal(SIGQUIT, termhook);
290
291     /*
292      * With this simple hack, we make it possible for a foreground 
293      * fetchmail to wake up one in daemon mode.  What we want is the
294      * side effect of interrupting any sleep that may be going on,
295      * forcing fetchmail to re-poll its hosts.
296      */
297     signal(SIGHUP, donothing);
298
299     /* here's the exclusion lock */
300     if ( (lockfp = fopen(lockfile,"w")) != NULL ) {
301         fprintf(lockfp,"%d",getpid());
302         if (poll_interval)
303             fprintf(lockfp," %d", poll_interval);
304         fclose(lockfp);
305         atexit(unlockit);
306     }
307
308     /*
309      * Query all hosts. If there's only one, the error return will
310      * reflect the status of that transaction.
311      */
312     do {
313 #ifdef HAVE_RES_SEARCH
314         sethostent(TRUE);       /* use TCP/IP for mailserver queries */
315 #endif /* HAVE_RES_SEARCH */
316
317         batchcount = 0;
318         for (ctl = querylist; ctl; ctl = ctl->next)
319         {
320             if (ctl->active && !(implicitmode && ctl->skip))
321             {
322 #ifdef HAVE_GETHOSTBYNAME
323                 /*
324                  * This functions partly as an optimization and partly
325                  * as a probe to make sure our nameserver is still up.
326                  * The multidrop case (especially) needs it.
327                  */
328                 if (ctl->authenticate == A_KERBEROS || MULTIDROP(ctl))
329                 {
330                     struct hostent      *namerec;
331
332                     /* compute the canonical name of the host */
333                     errno = 0;
334                     namerec = gethostbyname(ctl->servernames->id);
335                     if (namerec == (struct hostent *)NULL)
336                     {
337                         fprintf(stderr,
338                                 "fetchmail: skipping %s poll, ",
339                                 ctl->servernames->id);
340                         if (errno)
341                         {
342                             perror("general error");
343                             if (errno == ENETUNREACH)
344                                 break;  /* go to sleep */
345                         }
346                         else
347                             herror("DNS error");
348                         continue;
349                     }
350                     else
351                     {
352                         free(ctl->canonical_name);
353                         ctl->canonical_name = xstrdup((char *)namerec->h_name);
354                     }
355                 }
356 #endif /* HAVE_GETHOSTBYNAME */
357
358                 querystatus = query_host(ctl);
359                 if (!check_only)
360                     update_str_lists(ctl);
361             }
362         }
363
364 #ifdef HAVE_RES_SEARCH
365         endhostent();           /* release TCP/IP connection to nameserver */
366 #endif /* HAVE_RES_SEARCH */
367
368         /*
369          * Close all SMTP delivery sockets.  For optimum performance
370          * we'd like to hold them open til end of run, but (1) this
371          * loses if our poll interval is longer than the MTA's inactivity
372          * timeout, and (2) some MTAs (like smail) don't deliver after
373          * each message, but rather queue up mail and wait to actually
374          * deliver it until the input socket is closed. 
375          */
376         for (ctl = querylist; ctl; ctl = ctl->next)
377             if (ctl->smtp_sockfp)
378             {
379                 SMTP_quit(ctl->smtp_sockfp);
380                 fclose(ctl->smtp_sockfp);
381                 ctl->smtp_sockfp = (FILE *)NULL;
382             }
383
384         if (poll_interval)
385         {
386             if (outlevel == O_VERBOSE)
387             {
388                 time_t  now;
389
390                 time(&now);
391                 fprintf(stderr, "fetchmail: sleeping at %s", ctime(&now));
392             }
393
394             /*
395              * We can't use sleep(3) here because we need an alarm(3)
396              * equivalent in order to implement server nonresponse timeout.
397              * We'll just assume setitimer(2) is available since fetchmail
398              * has to have a BSDoid socket layer to work at all.
399              */
400             {
401                 struct itimerval ntimeout;
402
403                 ntimeout.it_interval.tv_sec = ntimeout.it_interval.tv_usec = 0;
404                 ntimeout.it_value.tv_sec  = poll_interval;
405                 ntimeout.it_value.tv_usec = 0;
406
407                 setitimer(ITIMER_REAL,&ntimeout,NULL);
408                 signal(SIGALRM, donothing);
409                 pause();
410                 if (lastsig == SIGHUP)
411                     (void) fputs("fetchmail: awakened by SIGHUP\n", stderr);
412             }
413
414             if (outlevel == O_VERBOSE)
415             {
416                 time_t  now;
417
418                 time(&now);
419                 fprintf(stderr, "fetchmail: awakened at %s", ctime(&now));
420             }
421         }
422     } while
423         (poll_interval);
424
425     if (outlevel == O_VERBOSE)
426         fprintf(stderr,"fetchmail: normal termination, status %d\n",querystatus);
427
428     termhook(0);
429     exit(querystatus);
430 }
431
432 static int load_params(int argc, char **argv, int optind)
433 {
434     int implicitmode, st;
435     struct passwd *pw;
436     struct query def_opts, *ctl, *mp;
437
438     memset(&def_opts, '\0', sizeof(struct query));
439
440     def_opts.protocol = P_AUTO;
441     def_opts.timeout = CLIENT_TIMEOUT;
442     strcpy(def_opts.remotename, user);
443     strcpy(def_opts.smtphost, "localhost");
444
445     /* this builds the host list */
446     if (prc_parse_file(rcfile) != 0)
447         exit(PS_SYNTAX);
448
449     if ((implicitmode = (optind >= argc)))
450     {
451         for (ctl = querylist; ctl; ctl = ctl->next)
452             ctl->active = TRUE;
453     }
454     else
455         for (; optind < argc; optind++) 
456         {
457             /*
458              * If hostname corresponds to a host known from the rc file,
459              * simply declare it active.  Otherwise synthesize a host
460              * record from command line and defaults
461              */
462             for (ctl = querylist; ctl; ctl = ctl->next)
463                 if (str_in_list(&ctl->servernames, argv[optind]))
464                     goto foundit;
465
466             ctl = hostalloc(&cmd_opts);
467             save_str(&ctl->servernames, -1, argv[optind]);
468
469         foundit:
470             ctl->active = TRUE;
471         }
472
473     /* if there's a defaults record, merge it and lose it */ 
474     if (querylist && strcmp(querylist->servernames->id, "defaults") == 0)
475     {
476         for (ctl = querylist; ctl; ctl = ctl->next)
477             optmerge(ctl, querylist);
478         querylist = querylist->next;
479     }
480
481     /* don't allow a defaults record after the first */
482     for (ctl = querylist; ctl; ctl = ctl->next)
483         if (ctl != querylist && strcmp(ctl->servernames->id, "defaults") == 0)
484             exit(PS_SYNTAX);
485
486     /* merge in wired defaults, do sanity checks and prepare internal fields */
487     for (ctl = querylist; ctl; ctl = ctl->next)
488     {
489         if (ctl->active && !(implicitmode && ctl->skip))
490         {
491             /* merge in defaults */
492             optmerge(ctl, &def_opts);
493
494             /* keep lusers from shooting themselves in the foot :-) */
495             if (poll_interval && ctl->limit)
496             {
497                 fprintf(stderr,"fetchmail: you'd never see large messages!\n");
498                 exit(PS_SYNTAX);
499             }
500
501             /* make sure delivery will default to a real local user */
502             if ((pw = getpwnam(user)) == (struct passwd *)NULL)
503             {
504                 fprintf(stderr,
505                         "fetchmail: can't set up default delivery to %s\n", user);
506                 exit(PS_SYNTAX);        /* has to be from bad rc file */
507             }
508             else
509             {
510                 ctl->uid = pw->pw_uid;  /* for local delivery via MDA */
511                 if (!ctl->localnames)   /* for local delivery via SMTP */
512                     save_str(&ctl->localnames, -1, user);
513             }
514
515 #if !defined(HAVE_GETHOSTBYNAME) || !defined(HAVE_RES_SEARCH)
516             /* can't handle multidrop mailboxes unless we can do DNS lookups */
517             if (ctl->localnames && ctl->localnames->next)
518             {
519                 fputs("fetchmail: can't handle multidrop mailboxes without DNS\n",
520                         stderr);
521                 exit(PS_SYNTAX);
522             }
523 #endif /* !HAVE_GETHOSTBYNAME || !HAVE_RES_SEARCH */
524
525             /*
526              * Assign SMTP leaders.  We want to allow all query blocks
527              * sharing the same SMTP host to use the same SMTP connection.
528              * To accomplish this, we initialize each query block's leader
529              * field to point to the first block in the list with a matching 
530              * SMTP host.
531              *
532              * In the typical case, there will be only one SMTP host (the
533              * client machine) and thus just one SMTP leader (and one listener
534              * process) through the entire poll cycle.
535              */
536             if (!ctl->mda[0])
537             {
538                 ctl->smtp_sockfp = (FILE *)NULL;
539                 for (mp = querylist; mp && mp != ctl; mp = mp->next)
540                     if (strcmp(mp->smtphost, ctl->smtphost) == 0)
541                     {
542                         ctl->lead_smtp = mp->lead_smtp;
543                         goto no_new_leader;
544                     }
545                 ctl->lead_smtp = ctl;
546             no_new_leader:;
547             }
548
549             /* similarly, compute server leaders for queries */
550             for (mp = querylist; mp && mp != ctl; mp = mp->next)
551                 if (strcmp(mp->servernames->id, ctl->servernames->id) == 0)
552                 {
553                     ctl->lead_server = mp->lead_server;
554                     goto no_new_server;
555                 }
556             ctl->lead_server = ctl;
557         no_new_server:;
558
559             /* sanity checks */
560             if (ctl->port < 0)
561             {
562                 (void) fprintf(stderr,
563                                "%s configuration invalid, port number cannot be negative",
564                                ctl->servernames->id);
565                 exit(PS_SYNTAX);
566             }
567
568             /* expand MDA commands */
569             if (!check_only && ctl->mda[0])
570             {
571                 char *argp;
572
573                 /* punch nulls into the delimiting whitespace in the args */
574                 for (argp = ctl->mda, ctl->mda_argcount = 1; *argp != '\0'; ctl->mda_argcount++)
575                 {
576                     ctl->mda_argv[ctl->mda_argcount] = argp;
577                     while (!(*argp == '\0' || isspace(*argp)))
578                         argp++;
579                     if (*argp != '\0')
580                         *(argp++) = '\0';  
581                 }
582
583                 ctl->mda_argv[ctl->mda_argcount] = (char *)NULL;
584
585                 ctl->mda_argv[0] = ctl->mda_argv[1];
586                 if ((argp = strrchr(ctl->mda_argv[1], '/')) != (char *)NULL)
587                     ctl->mda_argv[1] = argp + 1 ;
588             }
589         }
590     }
591
592     /* initialize UID handling */
593     if ((st = prc_filecheck(idfile)) != 0)
594         exit(st);
595     else
596         initialize_saved_lists(querylist, idfile);
597
598     /* if cmd_batchlimit was explicitly set, use it to override batchlimit */
599     if (cmd_batchlimit > -1)
600         batchlimit = cmd_batchlimit;
601
602     /* if cmd_logfile was explicitly set, use it to override logfile */
603     if (cmd_logfile)
604         logfile = cmd_logfile;
605
606     return(implicitmode);
607 }
608
609 void termhook(int sig)
610 /* to be executed on normal or signal-induced termination */
611 {
612     struct query        *ctl;
613
614     if (sig != 0)
615         fprintf(stderr, "terminated with signal %d\n", sig);
616
617     /* terminate all SMTP connections cleanly */
618     for (ctl = querylist; ctl; ctl = ctl->next)
619         if (ctl->lead_smtp == ctl && ctl->smtp_sockfp != (FILE *)NULL)
620             SMTP_quit(ctl->smtp_sockfp);
621
622     if (!check_only)
623         write_saved_lists(querylist, idfile);
624
625     exit(querystatus);
626 }
627
628 static char *showproto(int proto)
629 /* protocol index to protocol name mapping */
630 {
631     switch (proto)
632     {
633     case P_AUTO: return("auto"); break;
634     case P_POP2: return("POP2"); break;
635     case P_POP3: return("POP3"); break;
636     case P_IMAP: return("IMAP"); break;
637     case P_APOP: return("APOP"); break;
638     default: return("unknown?!?"); break;
639     }
640 }
641
642 /*
643  * Sequence of protocols to try when autoprobing, most capable to least.
644  */
645 static const int autoprobe[] = {P_IMAP, P_POP3, P_POP2};
646
647 static int query_host(struct query *ctl)
648 /* perform fetch transaction with single host */
649 {
650     int i, st;
651
652     if (outlevel == O_VERBOSE)
653     {
654         time_t now;
655
656         time(&now);
657         fprintf(stderr, "Querying %s (protocol %s) at %s",
658             ctl->servernames->id, showproto(ctl->protocol), ctime(&now));
659     }
660     switch (ctl->protocol) {
661     case P_AUTO:
662         for (i = 0; i < sizeof(autoprobe)/sizeof(autoprobe[0]); i++)
663         {
664             ctl->protocol = autoprobe[i];
665             if ((st = query_host(ctl)) == PS_SUCCESS || st == PS_NOMAIL || st == PS_AUTHFAIL)
666                 break;
667         }
668         ctl->protocol = P_AUTO;
669         return(st);
670         break;
671     case P_POP2:
672         return(doPOP2(ctl));
673         break;
674     case P_POP3:
675     case P_APOP:
676         return(doPOP3(ctl));
677         break;
678     case P_IMAP:
679         return(doIMAP(ctl));
680         break;
681     default:
682         fprintf(stderr,"fetchmail: unsupported protocol selected.\n");
683         return(PS_PROTOCOL);
684     }
685 }
686
687 void dump_params (struct query *ctl)
688 /* display query parameters in English */
689 {
690     printf("Options for retrieving from %s@%s:\n",
691            ctl->remotename, visbuf(ctl->servernames->id));
692 #ifdef HAVE_GETHOSTBYNAME
693     if (ctl->canonical_name)
694         printf("  Canonical DNS name of server is %s.\n", ctl->canonical_name);
695 #endif /* HAVE_GETHOSTBYNAME */
696     if (ctl->servernames->next)
697     {
698         struct idlist *idp;
699
700         printf("  Predeclared mailserver aliases:");
701         for (idp = ctl->servernames->next; idp; idp = idp->next)
702             printf(" %s", idp->id);
703         putchar('\n');
704     }
705     if (ctl->skip || outlevel == O_VERBOSE)
706         printf("  This host will%s be queried when no host is specified.\n",
707                ctl->skip ? " not" : "");
708     if (ctl->password[0] == '\0')
709         printf("  Password will be prompted for.\n");
710     else if (outlevel == O_VERBOSE)
711         if (ctl->protocol == P_APOP)
712             printf("  APOP secret = '%s'.\n", visbuf(ctl->password));
713         else
714             printf("  Password = '%s'.\n", visbuf(ctl->password));
715     if (ctl->protocol == P_POP3 
716                 && ctl->port == KPOP_PORT
717                 && ctl->authenticate == A_KERBEROS)
718         printf("  Protocol is KPOP");
719     else
720         printf("  Protocol is %s", showproto(ctl->protocol));
721     if (ctl->port)
722         printf(" (using port %d)", ctl->port);
723     else if (outlevel == O_VERBOSE)
724         printf(" (using default port)");
725     putchar('.');
726     putchar('\n');
727     if (ctl->authenticate == A_KERBEROS)
728             printf("  Kerberos authentication enabled.\n");
729     printf("  Server nonresponse timeout is %d seconds", ctl->timeout);
730     if (ctl->timeout ==  CLIENT_TIMEOUT)
731         printf(" (default).\n");
732     else
733         printf(".\n");
734
735     printf("  %s messages will be retrieved (--all %s).\n",
736            ctl->fetchall ? "All" : "Only new",
737            ctl->fetchall ? "on" : "off");
738     printf("  Fetched messages will%s be kept on the server (--keep %s).\n",
739            ctl->keep ? "" : " not",
740            ctl->keep ? "on" : "off");
741     printf("  Old messages will%s be flushed before message retrieval (--flush %s).\n",
742            ctl->flush ? "" : " not",
743            ctl->flush ? "on" : "off");
744     printf("  Rewrite of server-local addresses is %sabled (--norewrite %s).\n",
745            ctl->norewrite ? "dis" : "en",
746            ctl->norewrite ? "on" : "off");
747     if (ctl->limit)
748         printf("  Message size limit is %d bytes\n", ctl->limit);
749     else if (outlevel == O_VERBOSE)
750         printf("  No message size limit\n");
751     if (ctl->mda[0])
752     {
753         char **cp;
754
755         printf("  Messages will be delivered with %s, args:",
756                visbuf(ctl->mda_argv[0]));
757         for (cp = ctl->mda_argv+1; *cp; cp++)
758             printf(" %s", visbuf(*cp));
759         putchar('\n');
760     }
761     else
762         printf("  Messages will be SMTP-forwarded to '%s'.\n",
763                visbuf(ctl->smtphost));
764     if (!ctl->localnames)
765         printf("  No localnames declared for this host.\n");
766     else
767     {
768         struct idlist *idp;
769         int count = 0;
770
771         for (idp = ctl->localnames; idp; idp = idp->next)
772             ++count;
773
774         printf("  %d local name(s) recognized%s.\n",
775                count,
776                (count == 1 && !strcmp(ctl->localnames->id, user)) ? " (by default)" : "");
777         if (outlevel == O_VERBOSE)
778         {
779             for (idp = ctl->localnames; idp; idp = idp->next)
780                 if (idp->val.id2)
781                     fprintf(stderr, "\t%s -> %s\n", idp->id, idp->val.id2);
782                 else
783                     fprintf(stderr, "\t%s\n", idp->id);
784             if (ctl->wildcard)
785                 fputs("*\n", stderr);
786         }
787     }
788
789     if (ctl->protocol > P_POP2)
790         if (!ctl->oldsaved)
791             printf("  No UIDs saved from this host.\n");
792         else
793         {
794             struct idlist *idp;
795             int count = 0;
796
797             for (idp = ctl->oldsaved; idp; idp = idp->next)
798                 ++count;
799
800             printf("  %d UIDs saved.\n", count);
801             if (outlevel == O_VERBOSE)
802                 for (idp = ctl->oldsaved; idp; idp = idp->next)
803                     fprintf(stderr, "\t%s\n", idp->id);
804         }
805 }
806
807 int openmailpipe (char **argv)
808 /* open a one-way pipe to a mail delivery agent */
809 {
810     int pipefd [2];
811     int childpid;
812
813     if (outlevel == O_VERBOSE)
814     {
815         char **cp;
816
817         printf("fetchmail: about to deliver via MDA %s, args:",
818                visbuf(argv[0]));
819         for (cp = argv+1; *cp; cp++)
820             printf(" %s", visbuf(*cp));
821         putchar('\n');
822     }
823
824     if (pipe(pipefd) < 0) {
825         perror("fetchmail: openmailpipe: pipe");
826         return(-1);
827     }
828     if ((childpid = fork()) < 0) {
829         perror("fetchmail: openmailpipe: fork");
830         return(-1);
831     }
832     else if (childpid == 0) {
833
834         /* in child process space */
835         close(pipefd[1]);  /* close the 'write' end of the pipe */
836         close(0);          /* get rid of inherited stdin */
837         if (dup(pipefd[0]) != 0) {
838             fputs("fetchmail: openmailpipe: dup() failed\n",stderr);
839             exit(1);
840         }
841
842         execv(argv[0], argv + 1);
843
844         /* if we got here, an error occurred */
845         perror("fetchmail: openmailpipe: exec");
846         _exit(PS_SYNTAX);
847
848     }
849
850     /* in the parent process space */
851     close(pipefd[0]);  /* close the 'read' end of the pipe */
852     return(pipefd[1]);
853 }
854
855 int closemailpipe (fd)
856 /* close the pipe to the mail delivery agent */
857 int fd;
858 {
859     int err, status;
860     int childpid;
861
862     if ((err = close(fd)) != 0)
863         perror("fetchmail: closemailpipe: close failed");
864
865     childpid = wait(&status);
866
867 #if defined(WIFEXITED) && defined(WEXITSTATUS)
868     /*
869      * Try to pass up an error if the MDA returned nonzero status,
870      * on the assumption that this means it was reporting failure.
871      */
872     if (WIFEXITED(status) == 0 || WEXITSTATUS(status) != 0)
873     {
874         perror("fetchmail: MDA exited abnormally or returned nonzero status");
875         err = -1;
876     }
877 #endif
878
879     return(err);
880 }
881
882 /* helper functions for string interpretation and display */
883
884 #define CTRL(x) ((x) & 0x1f)
885
886 void escapes(cp, tp)
887 /* process standard C-style escape sequences in a string */
888 const char      *cp;    /* source string with escapes */
889 char            *tp;    /* target buffer for digested string */
890 {
891     while (*cp)
892     {
893         int     cval = 0;
894
895         if (*cp == '\\' && strchr("0123456789xX", cp[1]))
896         {
897             char *dp, *hex = "00112233445566778899aAbBcCdDeEfF";
898             int dcount = 0;
899
900             if (*++cp == 'x' || *cp == 'X')
901                 for (++cp; (dp = strchr(hex, *cp)) && (dcount++ < 2); cp++)
902                     cval = (cval * 16) + (dp - hex) / 2;
903             else if (*cp == '0')
904                 while (strchr("01234567",*cp) != (char*)NULL && (dcount++ < 3))
905                     cval = (cval * 8) + (*cp++ - '0');
906             else
907                 while ((strchr("0123456789",*cp)!=(char*)NULL)&&(dcount++ < 3))
908                     cval = (cval * 10) + (*cp++ - '0');
909         }
910         else if (*cp == '\\')           /* C-style character escapes */
911         {
912             switch (*++cp)
913             {
914             case '\\': cval = '\\'; break;
915             case 'n': cval = '\n'; break;
916             case 't': cval = '\t'; break;
917             case 'b': cval = '\b'; break;
918             case 'r': cval = '\r'; break;
919             default: cval = *cp;
920             }
921             cp++;
922         }
923         else if (*cp == '^')            /* expand control-character syntax */
924         {
925             cval = CTRL(*++cp);
926             cp++;
927         }
928         else
929             cval = *cp++;
930         *tp++ = cval;
931     }
932     *tp = '\0';
933 }
934
935 static char *visbuf(const char *buf)
936 /* visibilize a given string */
937 {
938     static char vbuf[BUFSIZ];
939     char *tp = vbuf;
940
941     while (*buf)
942     {
943         if (isprint(*buf) || *buf == ' ')
944             *tp++ = *buf++;
945         else if (*buf == '\n')
946         {
947             *tp++ = '\\'; *tp++ = 'n';
948             buf++;
949         }
950         else if (*buf == '\r')
951         {
952             *tp++ = '\\'; *tp++ = 'r';
953             buf++;
954         }
955         else if (*buf == '\b')
956         {
957             *tp++ = '\\'; *tp++ = 'b';
958             buf++;
959         }
960         else if (*buf < ' ')
961         {
962             *tp++ = '\\'; *tp++ = '^'; *tp++ = '@' + *buf;
963             buf++;
964         }
965         else
966         {
967             (void) sprintf(tp, "\\0x%02x", *buf++);
968             tp += strlen(tp);
969         }
970     }
971     *tp++ = '\0';
972     return(vbuf);
973 }
974
975 /* fetchmail.c ends here */