1 /* Copyright 1993-95 by Carl Harris, Jr. Copyright 1996 by Eric S. Raymond
3 * For license terms, see the file COPYING in this directory.
6 /***********************************************************************
9 programmer: Carl Harris, ceharris@mal.com
10 Extensively hacked and improved by esr.
11 description: main driver module for fetchmail
13 ***********************************************************************/
20 #if defined(STDC_HEADERS)
25 #if defined(HAVE_UNISTD_H)
33 #include <sys/types.h>
39 #include "fetchmail.h"
42 #define RELEASE_TAG "3.1"
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);
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 */
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 */
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 */
68 /* args for the MDA, parsed out in the usual fashion by parseMDAargs() */
71 /*********************************************************************
73 description: main driver routine
75 argc argument count as passed by runtime startup code.
76 argv argument strings as passed by runtime startup code.
78 return value: an exit status code for the shell -- see the
79 PS_.* constants defined above.
80 calls: parsecmdline, setdefaults, openuserfolder, doPOP2.
82 *********************************************************************/
84 static void termhook();
85 static char *lockfile;
87 static struct hostrec *hostp, *hostlist = (struct hostrec *)NULL;
93 int mboxfd, st, sargc;
94 struct hostrec cmd_opts, def_opts;
96 char *servername, *user, *tmpdir, tmpbuf[256], *sargv[64];
100 if (setdefaults(&def_opts) != 0)
103 if (argc > sizeof(sargv))
105 for (sargc = 0; sargc < argc; sargc++)
106 sargv[sargc] = argv[sargc];
108 if ((parsestatus = parsecmdline(sargc,sargv,&cmd_opts)) < 0)
114 if (prc_parse_file(rcfile) != 0)
118 append_server_names(&sargc, sargv, sizeof(sargv));
120 /* build in-core data list on all hosts */
121 while ((servername = getnextserver(sargc, sargv, &parsestatus)) != (char *)0)
123 if (strcmp(servername, "defaults") == 0)
126 hostp = (struct hostrec *)xmalloc(sizeof(struct hostrec));
128 prc_mergeoptions(servername, &cmd_opts, &def_opts, hostp);
129 strcpy(hostp->servername, servername);
131 hostp->lastid[0] = '\0';
133 hostp->next = hostlist;
137 /* set up to do lock protocol */
138 if ((tmpdir = getenv("TMPDIR")) == (char *)NULL)
140 strcpy(tmpbuf, tmpdir);
141 strcat(tmpbuf, "/fetchmail-");
142 gethostname(tmpbuf + strlen(tmpbuf), HOSTLEN+1);
143 if ((user = getenv("USER")) != (char *)NULL)
146 strcat(tmpbuf, user);
149 /* perhaps we just want to check options? */
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);
155 if (outlevel == O_VERBOSE)
156 printf(" Lockfile at %s\n", tmpbuf);
158 if (hostlist == NULL)
159 (void) fprintf(stderr,
160 "No mailservers set up -- perhaps %s is missing?\n",
164 else if (hostlist == NULL) {
165 (void) fputs("fetchmail: no mailservers have been specified.\n", stderr);
169 if ((lockfile = (char *) malloc(strlen(tmpbuf) + 1)) == NULL)
171 fprintf(stderr,"fetchmail: cannot allocate memory for lock name.\n");
175 (void) strcpy(lockfile, tmpbuf);
177 /* perhaps user asked us to remove a lock */
182 if ( (fp = fopen(lockfile, "r")) == NULL ) {
183 fprintf(stderr,"fetchmail: no other fetchmail is running\n");
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);
192 fprintf(stderr,"fetchmail: fetchmail at %d is dead.\n", pid);
200 /* beyond here we don't want more than one fetchmail running per user */
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);
209 /* let's get stored message IDs from previous transactions */
210 if ((st = prc_filecheck(idfile)) != 0) {
212 } else if ((tmpfp = fopen(idfile, "r")) != (FILE *)NULL) {
213 char buf[POPBUFSIZE+1], host[HOSTLEN+1], id[IDLEN+1];
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);
227 * Maybe time to go to demon mode...
230 daemonize(logfile, termhook);
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());
246 * Query all hosts. If there's only one, the error return will
247 * reflect the status of that transaction.
250 for (hostp = hostlist; hostp; hostp = hostp->next) {
251 popstatus = query_host(hostp);
254 sleep(poll_interval);
258 if (outlevel == O_VERBOSE)
259 fprintf(stderr, "normal termination, status %d\n", popstatus);
265 void termhook(int sig)
271 fprintf(stderr, "terminated with signal %d\n", sig);
273 for (hostp = hostlist; hostp; hostp = hostp->next) {
274 if (hostp->lastid[0])
278 /* write updated last-seen IDs */
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);
293 /*********************************************************************
295 description: protocol index to name mapping
298 return value: string name of protocol
301 *********************************************************************/
303 char *showproto(proto)
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;
319 * Sequence of protocols to try when autoprobing
321 static const int autoprobe[] = {P_POP3, P_IMAP, P_POP2};
323 int query_host(queryctl)
324 /* perform fetch transaction with single host */
325 struct hostrec *queryctl;
329 if (outlevel == O_VERBOSE)
334 fprintf(stderr, "Querying %s (protocol %s) at %s",
335 queryctl->servername, showproto(queryctl->protocol), ctime(&now));
337 switch (queryctl->protocol) {
339 for (i = 0; i < sizeof(autoprobe)/sizeof(autoprobe[0]); i++)
341 queryctl->protocol = autoprobe[i];
342 if ((st = query_host(queryctl)) == PS_SUCCESS || st == PS_NOMAIL || st == PS_AUTHFAIL)
345 queryctl->protocol = P_AUTO;
349 return(doPOP2(queryctl));
353 return(doPOP3(queryctl));
356 return(doIMAP(queryctl));
359 fprintf(stderr,"fetchmail: unsupported protocol selected.\n");
364 /*********************************************************************
365 function: showversioninfo
366 description: display program release and compiler info
371 *********************************************************************/
373 int showversioninfo()
375 printf("This is fetchmail release %s\n",RELEASE_TAG);
378 /*********************************************************************
379 function: dump_params
380 description: display program options in English
382 queryctl merged options
386 globals: linelimit, outlimit.
387 *********************************************************************/
389 int dump_params (queryctl)
390 struct hostrec *queryctl;
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));
401 printf(" (using port %d)", queryctl->port);
402 else if (outlevel == O_VERBOSE)
403 printf(" (using default port)");
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");
420 switch(queryctl->output)
423 printf(" Messages will be SMTP-forwarded to '%s'\n", queryctl->smtphost);
426 printf(" Messages will be appended to '%s'\n", queryctl->userfolder);
429 printf(" Messages will be delivered with");
430 for (cp = queryctl->mda; *cp; cp += strlen(cp) + 1) {
436 printf(" Messages will be dumped to standard output\n");
438 printf(" Message destination unknown?!?\n");
440 if (outlevel == O_VERBOSE)
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)
448 printf(" (MDA would have been");
449 for (cp = queryctl->mda; *cp; cp += strlen(cp) + 1) {
457 printf(" No limit on retrieved message length.\n");
459 printf(" Text retrieved per message will be at most %d bytes.\n",
461 if (queryctl->lastid[0])
462 printf(" ID of last message retrieved %s\n", queryctl->lastid);
465 /*********************************************************************
466 function: openuserfolder
467 description: open the file to which the retrieved messages will
468 be appended. Write-lock the folder if possible.
471 queryctl fully-determined options (i.e. parsed, defaults invoked,
474 return value: file descriptor for the open file, else -1.
477 *********************************************************************/
479 int openuserfolder (queryctl)
480 struct hostrec *queryctl;
484 if (queryctl->output == TO_STDOUT)
486 else /* queryctl->output == TO_FOLDER */
487 if ((fd = open(queryctl->userfolder,O_CREAT|O_WRONLY|O_APPEND,0600)) >= 0) {
489 if (flock(fd, LOCK_EX) == -1)
494 #endif /* HAVE_FLOCK */
498 perror("fetchmail: openuserfolder: open()");
506 /*********************************************************************
507 function: openmailpipe
508 description: open a one-way pipe to the mail delivery agent.
510 queryctl fully-determined options (i.e. parsed, defaults invoked,
513 return value: open file descriptor for the pipe or -1.
515 globals: reads mda_argv.
516 *********************************************************************/
518 int openmailpipe (queryctl)
519 struct hostrec *queryctl;
523 char binmailargs [80];
525 if (pipe(pipefd) < 0) {
526 perror("fetchmail: openmailpipe: pipe");
529 if ((childpid = fork()) < 0) {
530 perror("fetchmail: openmailpipe: fork");
533 else if (childpid == 0) {
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);
543 execv(queryctl->mda, mda_argv+1);
545 /* if we got here, an error occurred */
546 perror("fetchmail: openmailpipe: exec");
551 /* in the parent process space */
552 close(pipefd[0]); /* close the 'read' end of the pipe */
558 /*********************************************************************
559 function: closeuserfolder
560 description: close the user-specified mail folder.
562 fd mail folder descriptor.
564 return value: zero if success else -1.
567 *********************************************************************/
569 int closeuserfolder(fd)
574 if (fd != 1) { /* not stdout */
581 perror("fetchmail: closeuserfolder: close");
588 /*********************************************************************
589 function: closemailpipe
590 description: close pipe to the mail delivery agent.
592 queryctl fully-determined options record
595 return value: 0 if success, else -1.
598 *********************************************************************/
600 int closemailpipe (fd)
606 if (outlevel == O_VERBOSE)
607 fprintf(stderr, "about to close pipe %d\n", fd);
610 #if defined(STDC_HEADERS)
611 childpid = wait(NULL);
613 childpid = wait((int *) 0);
616 perror("fetchmail: closemailpipe: close");
618 if (outlevel == O_VERBOSE)
619 fprintf(stderr, "closed pipe %d\n", fd);
626 /*********************************************************************
627 function: parseMDAargs
628 description: parse the argument string given in agent option into
629 a regular *argv[] array.
631 queryctl fully-determined options record pointer.
635 globals: writes mda_argv.
636 *********************************************************************/
638 int parseMDAargs (queryctl)
639 struct hostrec *queryctl;
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;
648 argp = queryctl->mda;
649 while (*argp != '\0' && isspace(*argp)) /* skip null first arg */
652 /* now punch nulls into the delimiting whitespace in the args */
657 mda_argv[argi] = argp; /* store pointer to this argument */
659 /* find end of this argument */
660 while (!(*argp == '\0' || isspace(*argp)))
663 /* punch in a null terminator */
667 mda_argv[argi] = (char *) 0;