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