]> Pileus Git - ~andy/fetchmail/blob - fetchmail.c
f78d149afe252fe9ff9caeceb38dc00b53b1e2f3
[~andy/fetchmail] / fetchmail.c
1 /* Copyright 1993-95 by Carl Harris, Jr. Copyright 1996 by Eric S. Raymond
2  * All rights reserved.
3  * For license terms, see the file COPYING in this directory.
4  */
5
6 /***********************************************************************
7   module:       fetchmail.c
8   project:      fetchmail
9   programmer:   Carl Harris, ceharris@mal.com
10                 Extensively hacked and improved by esr.
11   description:  main driver module for fetchmail
12
13  ***********************************************************************/
14
15
16 #include <config.h>
17
18 #include <stdio.h>
19
20 #if defined(STDC_HEADERS)
21 #include <stdlib.h>
22 #include <string.h>
23 #endif
24
25 #if defined(HAVE_UNISTD_H)
26 #include <unistd.h>
27 #endif
28
29 #include <signal.h>
30 #include <pwd.h>
31 #include <errno.h>
32
33 #include <sys/types.h>
34 #include <sys/file.h>
35 #include <sys/wait.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38
39 #include "fetchmail.h"
40
41 /* release info */
42 #define         RELEASE_TAG     "3.1"
43
44 #ifdef HAVE_PROTOTYPES
45 /* prototypes for internal functions */
46 int showoptions (struct hostrec *queryctl);
47 int parseMDAargs (struct hostrec *queryctl);
48 int showversioninfo (void);
49 int dump_options (struct hostrec *queryctl);
50 int query_host(struct hostrec *queryctl);
51 #endif
52
53 /* controls the detail level of status/progress messages written to stderr */
54 int outlevel;           /* see the O_.* constants above */
55 int yydebug;            /* enable parse debugging */
56
57 /* daemon mode control */
58 int poll_interval;      /* poll interval in seconds */
59 char *logfile;          /* log file for daemon mode */
60 int quitmode;           /* if --quit was set */
61
62 /* miscellaneous global controls */
63 char *rcfile;           /* path name of rc file */
64 char *idfile;           /* path name of id file */
65 int linelimit;          /* limit # lines retrieved per site */
66 int versioninfo;        /* emit only version info */
67
68 /* args for the MDA, parsed out in the usual fashion by parseMDAargs() */
69 char *mda_argv [32];
70
71 /*********************************************************************
72   function:      main
73   description:   main driver routine 
74   arguments:     
75     argc         argument count as passed by runtime startup code.
76     argv         argument strings as passed by runtime startup code.
77
78   return value:  an exit status code for the shell -- see the 
79                  PS_.* constants defined above.
80   calls:         parsecmdline, setdefaults, openuserfolder, doPOP2.
81   globals:       none.
82  *********************************************************************/
83
84 static void termhook();
85 static char *lockfile;
86 static int popstatus;
87 static struct hostrec *hostp, *hostlist = (struct hostrec *)NULL;
88
89 main (argc,argv)
90 int argc;
91 char **argv;
92
93     int mboxfd, st, sargc;
94     struct hostrec cmd_opts, def_opts;
95     int parsestatus;
96     char *servername, *user, *tmpdir, tmpbuf[256], *sargv[64]; 
97     FILE        *tmpfp;
98     pid_t pid;
99
100     if (setdefaults(&def_opts) != 0)
101         exit(PS_UNDEFINED);
102
103     if (argc > sizeof(sargv))
104         exit(PS_SYNTAX);
105     for (sargc = 0; sargc < argc; sargc++)
106         sargv[sargc] = argv[sargc];
107
108     if ((parsestatus = parsecmdline(sargc,sargv,&cmd_opts)) < 0)
109         exit(PS_SYNTAX);
110
111     if (versioninfo)
112         showversioninfo();
113
114     if (prc_parse_file(rcfile) != 0)
115         exit(PS_SYNTAX);
116
117     if (optind >= argc)
118         append_server_names(&sargc, sargv, sizeof(sargv));
119
120     /* build in-core data list on all hosts */
121     while ((servername = getnextserver(sargc, sargv, &parsestatus)) != (char *)0)
122     {
123         if (strcmp(servername, "defaults") == 0)
124             continue;
125
126         hostp = (struct hostrec *)xmalloc(sizeof(struct hostrec));
127
128         prc_mergeoptions(servername, &cmd_opts, &def_opts, hostp);
129         strcpy(hostp->servername, servername);
130         parseMDAargs(hostp);
131         hostp->lastid[0] = '\0';
132
133         hostp->next = hostlist;
134         hostlist = hostp;
135     }
136
137     /* set up to do lock protocol */
138     if ((tmpdir = getenv("TMPDIR")) == (char *)NULL)
139         tmpdir = "/tmp";
140     strcpy(tmpbuf, tmpdir);
141     strcat(tmpbuf, "/fetchmail-");
142     gethostname(tmpbuf + strlen(tmpbuf), HOSTLEN+1);
143     if ((user = getenv("USER")) != (char *)NULL)
144     {
145         strcat(tmpbuf, "-");
146         strcat(tmpbuf, user);
147     }
148
149     /* perhaps we just want to check options? */
150     if (versioninfo) {
151         printf("Taking options from command line and %s\n", rcfile);
152         for (hostp = hostlist; hostp; hostp = hostp->next) {
153             printf("Options for host %s:\n", hostp->servername);
154             dump_params(hostp);
155             if (outlevel == O_VERBOSE)
156                 printf("  Lockfile at %s\n", tmpbuf);
157         }
158         if (hostlist == NULL)
159             (void) fprintf(stderr,
160                 "No mailservers set up -- perhaps %s is missing?\n",
161                           rcfile);
162         exit(0);
163     }
164     else if (hostlist == NULL) {
165         (void) fputs("fetchmail: no mailservers have been specified.\n", stderr);
166         exit(PS_SYNTAX);
167     }
168
169     if ((lockfile = (char *) malloc(strlen(tmpbuf) + 1)) == NULL)
170     {
171         fprintf(stderr,"fetchmail: cannot allocate memory for lock name.\n");
172         exit(PS_EXCLUDE);
173     }
174     else
175         (void) strcpy(lockfile, tmpbuf);
176
177     /* perhaps user asked us to remove a lock */
178     if (quitmode)
179     {
180         FILE* fp;
181
182         if ( (fp = fopen(lockfile, "r")) == NULL ) {
183             fprintf(stderr,"fetchmail: no other fetchmail is running\n");
184             return(PS_EXCLUDE);
185         }
186   
187         fscanf(fp,"%d",&pid);
188         fprintf(stderr,"fetchmail: killing fetchmail at PID %d\n",pid);
189         if ( kill(pid,SIGTERM) < 0 )
190             fprintf(stderr,"fetchmail: error killing the process %d.\n",pid);
191         else
192             fprintf(stderr,"fetchmail: fetchmail at %d is dead.\n", pid);
193   
194         fclose(fp);
195         remove(lockfile);
196         exit(0);
197     }
198
199
200     /* beyond here we don't want more than one fetchmail running per user */
201     umask(0077);
202     if ( (tmpfp = fopen(lockfile, "r")) != NULL ) {
203         fscanf(tmpfp,"%d",&pid);
204         fprintf(stderr,"Another session appears to be running at pid %d.\nIf you are sure that this is incorrect, remove %s file.\n",pid,lockfile);
205         fclose(tmpfp);
206         return(PS_EXCLUDE);
207     }
208
209     /* let's get stored message IDs from previous transactions */
210     if ((st = prc_filecheck(idfile)) != 0) {
211         return (st);
212     } else if ((tmpfp = fopen(idfile, "r")) != (FILE *)NULL) {
213         char buf[POPBUFSIZE+1], host[HOSTLEN+1], id[IDLEN+1];
214
215         while (fgets(buf, POPBUFSIZE, tmpfp) != (char *)NULL) {
216             if ((st = sscanf(buf, "%s %s\n", host, id)) == 2) {
217                 for (hostp = hostlist; hostp; hostp = hostp->next) {
218                     if (strcmp(host, hostp->servername) == 0)
219                         strcpy(hostp->lastid, id);
220                 }
221             }
222         }
223         fclose(tmpfp);
224     }
225
226     /*
227      * Maybe time to go to demon mode...
228      */
229     if (poll_interval)
230         daemonize(logfile, termhook);
231
232     /* if not locked, assert a lock */
233     signal(SIGABRT, termhook);
234     signal(SIGINT, termhook);
235     signal(SIGTERM, termhook);
236     signal(SIGALRM, termhook);
237     signal(SIGHUP, termhook);
238     signal(SIGPIPE, termhook);
239     signal(SIGQUIT, termhook);
240     if ( (tmpfp = fopen(lockfile,"w")) != NULL ) {
241         fprintf(tmpfp,"%d",getpid());
242         fclose(tmpfp);
243     }
244
245     /*
246      * Query all hosts. If there's only one, the error return will
247      * reflect the status of that transaction.
248      */
249     do {
250         for (hostp = hostlist; hostp; hostp = hostp->next) {
251             popstatus = query_host(hostp);
252         }
253
254         sleep(poll_interval);
255     } while
256         (poll_interval);
257
258     if (outlevel == O_VERBOSE)
259         fprintf(stderr, "normal termination, status %d\n", popstatus);
260
261     termhook(0);
262     exit(popstatus);
263 }
264
265 void termhook(int sig)
266 {
267     FILE *tmpfp;
268     int idcount = 0;
269
270     if (sig != 0)
271         fprintf(stderr, "terminated with signal %d\n", sig);
272
273     for (hostp = hostlist; hostp; hostp = hostp->next) {
274         if (hostp->lastid[0])
275             idcount++;
276     }
277
278     /* write updated last-seen IDs */
279     if (!idcount)
280         unlink(idfile);
281     else if ((tmpfp = fopen(idfile, "w")) != (FILE *)NULL) {
282         for (hostp = hostlist; hostp; hostp = hostp->next) {
283             if (hostp->lastid[0])
284                 fprintf(tmpfp, "%s %s\n", hostp->servername, hostp->lastid);
285         }
286         fclose(tmpfp);
287     }
288
289     unlink(lockfile);
290     exit(popstatus);
291 }
292
293 /*********************************************************************
294   function:      showproto
295   description:   protocol index to name mapping
296   arguments:
297     proto        protocol index
298   return value:  string name of protocol
299   calls:         none.
300   globals:       none.
301  *********************************************************************/
302
303 char *showproto(proto)
304 int proto;
305 {
306     switch (proto)
307     {
308     case P_AUTO: return("auto"); break;
309     case P_POP2: return("POP2"); break;
310     case P_POP3: return("POP3"); break;
311     case P_IMAP: return("IMAP"); break;
312     case P_APOP: return("APOP"); break;
313     case P_RPOP: return("RPOP"); break;
314     default: return("unknown?!?"); break;
315     }
316 }
317
318 /*
319  * Sequence of protocols to try when autoprobing
320  */
321 static const int autoprobe[] = {P_POP3, P_IMAP, P_POP2};
322
323 int query_host(queryctl)
324 /* perform fetch transaction with single host */
325 struct hostrec *queryctl;
326 {
327     int i, st;
328
329     if (outlevel == O_VERBOSE)
330     {
331         time_t now;
332
333         time(&now);
334         fprintf(stderr, "Querying %s (protocol %s) at %s",
335             queryctl->servername, showproto(queryctl->protocol), ctime(&now));
336     }
337     switch (queryctl->protocol) {
338     case P_AUTO:
339         for (i = 0; i < sizeof(autoprobe)/sizeof(autoprobe[0]); i++)
340         {
341             queryctl->protocol = autoprobe[i];
342             if ((st = query_host(queryctl)) == PS_SUCCESS || st == PS_NOMAIL || st == PS_AUTHFAIL)
343                 break;
344         }
345         queryctl->protocol = P_AUTO;
346         return(st);
347         break;
348     case P_POP2:
349         return(doPOP2(queryctl));
350         break;
351     case P_POP3:
352     case P_APOP:
353         return(doPOP3(queryctl));
354         break;
355     case P_IMAP:
356         return(doIMAP(queryctl));
357         break;
358     default:
359         fprintf(stderr,"fetchmail: unsupported protocol selected.\n");
360         return(PS_PROTOCOL);
361     }
362 }
363  
364 /*********************************************************************
365   function:      showversioninfo
366   description:   display program release and compiler info
367   arguments:     none.
368   return value:  none.
369   calls:         none.
370   globals:       none.
371  *********************************************************************/
372
373 int showversioninfo()
374 {
375     printf("This is fetchmail release %s\n",RELEASE_TAG);
376 }
377
378 /*********************************************************************
379   function:      dump_params
380   description:   display program options in English
381   arguments:
382     queryctl      merged options
383
384   return value:  none.
385   calls:         none.
386   globals:       linelimit, outlimit.
387 *********************************************************************/
388
389 int dump_params (queryctl)
390 struct hostrec *queryctl;
391 {
392     char *cp;
393
394     printf("  Username = '%s'\n", queryctl->remotename);
395     if (queryctl->password && outlevel == O_VERBOSE)
396         printf("  Password = '%s'\n", queryctl->password);
397     if (queryctl->rpopid)
398         printf("  RPOP id = '%s'\n", queryctl->rpopid);
399     printf("  Protocol is %s", showproto(queryctl->protocol));
400     if (queryctl->port)
401         printf(" (using port %d)", queryctl->port);
402     else if (outlevel == O_VERBOSE)
403         printf(" (using default port)");
404     putchar('\n');
405
406     printf("  Fetched messages will%s be kept on the server (--keep %s).\n",
407            queryctl->keep ? "" : " not",
408            queryctl->keep ? "on" : "off");
409     printf("  %s messages will be retrieved (--all %s).\n",
410            queryctl->fetchall ? "All" : "Only new",
411            queryctl->fetchall ? "on" : "off");
412     printf("  Old messages will%s be flushed before message retrieval (--flush %s).\n",
413            queryctl->flush ? "" : " not",
414            queryctl->flush ? "on" : "off");
415     printf("  Rewrite of host-local addresses is %sabled (--norewrite %s)\n",
416            queryctl->norewrite ? "dis" : "en",
417            queryctl->norewrite ? "on" : "off");
418
419
420     switch(queryctl->output)
421     {
422     case TO_SMTP:
423         printf("  Messages will be SMTP-forwarded to '%s'\n", queryctl->smtphost);
424         break;
425     case TO_FOLDER:
426         printf("  Messages will be appended to '%s'\n", queryctl->userfolder);
427         break;
428     case TO_MDA:
429         printf("  Messages will be delivered with");
430         for (cp = queryctl->mda; *cp; cp += strlen(cp) + 1) {
431             printf(" %s", cp);
432         }
433         putchar('\n');
434         break;
435     case TO_STDOUT:
436         printf("  Messages will be dumped to standard output\n");
437     default:
438         printf("  Message destination unknown?!?\n");
439     }
440     if (outlevel == O_VERBOSE)
441     {
442         if (queryctl->smtphost[0] != '\0' && queryctl->output != TO_SMTP)
443             printf("  (SMTP host would have been '%s')\n", queryctl->smtphost);
444         if (queryctl->output != TO_FOLDER)
445             printf("  (Mail folder would have been '%s')\n", queryctl->userfolder);
446         if (queryctl->output != TO_MDA)
447         {
448             printf("  (MDA would have been");
449             for (cp = queryctl->mda; *cp; cp += strlen(cp) + 1) {
450                 printf(" %s", cp);
451             }
452             printf(")\n");
453         }
454     }
455
456     if (linelimit == 0)
457         printf("  No limit on retrieved message length.\n");
458     else
459         printf("  Text retrieved per message will be at most %d bytes.\n",
460                linelimit);
461     if (queryctl->lastid[0])
462         printf("  ID of last message retrieved %s\n", queryctl->lastid);
463 }
464
465 /*********************************************************************
466   function:      openuserfolder
467   description:   open the file to which the retrieved messages will
468                  be appended.  Write-lock the folder if possible.
469
470   arguments:     
471     queryctl     fully-determined options (i.e. parsed, defaults invoked,
472                  etc).
473
474   return value:  file descriptor for the open file, else -1.
475   calls:         none.
476   globals:       none.
477  *********************************************************************/
478
479 int openuserfolder (queryctl)
480 struct hostrec *queryctl;
481 {
482     int fd;
483
484     if (queryctl->output == TO_STDOUT)
485         return(1);
486     else    /* queryctl->output == TO_FOLDER */
487         if ((fd = open(queryctl->userfolder,O_CREAT|O_WRONLY|O_APPEND,0600)) >= 0) {
488 #ifdef HAVE_FLOCK
489             if (flock(fd, LOCK_EX) == -1)
490             {
491                 close(fd);
492                 fd = -1;
493             }
494 #endif /* HAVE_FLOCK */
495             return(fd);
496         }
497         else {
498             perror("fetchmail: openuserfolder: open()");
499             return(-1);
500         }
501   
502 }
503
504
505
506 /*********************************************************************
507   function:      openmailpipe
508   description:   open a one-way pipe to the mail delivery agent.
509   arguments:     
510     queryctl     fully-determined options (i.e. parsed, defaults invoked,
511                  etc).
512
513   return value:  open file descriptor for the pipe or -1.
514   calls:         none.
515   globals:       reads mda_argv.
516  *********************************************************************/
517
518 int openmailpipe (queryctl)
519 struct hostrec *queryctl;
520 {
521     int pipefd [2];
522     int childpid;
523     char binmailargs [80];
524
525     if (pipe(pipefd) < 0) {
526         perror("fetchmail: openmailpipe: pipe");
527         return(-1);
528     }
529     if ((childpid = fork()) < 0) {
530         perror("fetchmail: openmailpipe: fork");
531         return(-1);
532     }
533     else if (childpid == 0) {
534
535         /* in child process space */
536         close(pipefd[1]);  /* close the 'write' end of the pipe */
537         close(0);          /* get rid of inherited stdin */
538         if (dup(pipefd[0]) != 0) {
539             fputs("fetchmail: openmailpipe: dup() failed\n",stderr);
540             exit(1);
541         }
542
543         execv(queryctl->mda, mda_argv+1);
544
545         /* if we got here, an error occurred */
546         perror("fetchmail: openmailpipe: exec");
547         return(-1);
548
549     }
550
551     /* in the parent process space */
552     close(pipefd[0]);  /* close the 'read' end of the pipe */
553     return(pipefd[1]);
554 }
555
556
557
558 /*********************************************************************
559   function:      closeuserfolder
560   description:   close the user-specified mail folder.
561   arguments:     
562     fd           mail folder descriptor.
563
564   return value:  zero if success else -1.
565   calls:         none.
566   globals:       none.
567  *********************************************************************/
568
569 int closeuserfolder(fd)
570 int fd;
571 {
572     int err;
573
574     if (fd != 1) {   /* not stdout */
575         err = close(fd);
576     }   
577     else
578         err = 0;
579   
580     if (err)
581         perror("fetchmail: closeuserfolder: close");
582
583     return(err);
584 }
585
586
587
588 /*********************************************************************
589   function:      closemailpipe
590   description:   close pipe to the mail delivery agent.
591   arguments:     
592     queryctl     fully-determined options record
593     fd           pipe descriptor.
594
595   return value:  0 if success, else -1.
596   calls:         none.
597   globals:       none.
598  *********************************************************************/
599
600 int closemailpipe (fd)
601 int fd;
602 {
603     int err;
604     int childpid;
605
606     if (outlevel == O_VERBOSE)
607         fprintf(stderr, "about to close pipe %d\n", fd);
608
609     err = close(fd);
610 #if defined(STDC_HEADERS)
611     childpid = wait(NULL);
612 #else
613     childpid = wait((int *) 0);
614 #endif
615     if (err)
616         perror("fetchmail: closemailpipe: close");
617
618     if (outlevel == O_VERBOSE)
619         fprintf(stderr, "closed pipe %d\n", fd);
620   
621     return(err);
622 }
623
624
625
626 /*********************************************************************
627   function:      parseMDAargs
628   description:   parse the argument string given in agent option into
629                  a regular *argv[] array.
630   arguments:
631     queryctl     fully-determined options record pointer.
632
633   return value:  none.
634   calls:         none.
635   globals:       writes mda_argv.
636  *********************************************************************/
637
638 int parseMDAargs (queryctl)
639 struct hostrec *queryctl;
640 {
641     int argi;
642     char *argp;
643
644     /* first put the last segment of the MDA pathname in argv[0] */
645     argp = strrchr(queryctl->mda, '/');
646     mda_argv[0] = argp ? (argp + 1) : queryctl->mda;
647   
648     argp = queryctl->mda;
649     while (*argp != '\0' && isspace(*argp))     /* skip null first arg */
650         argp++;                                 
651
652     /* now punch nulls into the delimiting whitespace in the args */
653     for (argi = 1;  
654          *argp != '\0';
655          argi++) {
656
657         mda_argv[argi] = argp;     /* store pointer to this argument */
658
659         /* find end of this argument */
660         while (!(*argp == '\0' || isspace(*argp)))
661             argp++;
662
663         /* punch in a null terminator */
664         if (*argp != '\0')
665             *(argp++) = '\0';  
666     }
667     mda_argv[argi] = (char *) 0;
668
669 }
670
671