X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=fetchmail.c;h=f7bcfd940e1abae19fbc45e9a262db6a3ea65f9e;hb=91644ff0b2e6eb3b26c5544a4a769ca4aaf08a14;hp=fc3deff460ae3238946632ced43484688ae67827;hpb=9eceafcf76a3af412e9b347212030e1a79f866f7;p=~andy%2Ffetchmail diff --git a/fetchmail.c b/fetchmail.c index fc3deff4..f7bcfd94 100644 --- a/fetchmail.c +++ b/fetchmail.c @@ -43,6 +43,12 @@ #include "smtp.h" #include "netrc.h" #include "i18n.h" +#include "lock.h" + +/* need these (and sys/types.h) for res_init() */ +#include +#include +#include #ifndef ENETUNREACH #define ENETUNREACH 128 /* Interactive doesn't know this */ @@ -60,19 +66,17 @@ int outlevel; /* see the O_.* constants above */ struct runctl run; /* global controls for this run */ flag nodetach; /* if TRUE, don't detach daemon process */ flag quitmode; /* if --quit was set */ +int quitind; /* optind after position of last --quit option */ flag check_only; /* if --probe was set */ flag versioninfo; /* emit only version info */ char *user; /* the name of the invoking user */ char *home; /* invoking user's home directory */ char *fmhome; /* fetchmail's home directory */ -char *program_name; /* the name to prefix error messages with */ +const char *program_name; /* the name to prefix error messages with */ flag configdump; /* dump control blocks for configurator */ -char *fetchmailhost; /* either `localhost' or the host's FQDN */ +const char *fetchmailhost; /* either `localhost' or the host's FQDN */ -#ifdef NET_SECURITY -void *request = NULL; -int requestlen = 0; -#endif /* NET_SECURITY */ +static int quitonly; /* if we should quit after killing the running daemon */ static int querystatus; /* status of query */ static int successes; /* count number of successful polls */ @@ -129,6 +133,22 @@ static RETSIGTYPE donothing(int sig) lastsig = sig; } +static void printcopyright(FILE *fp) { + fprintf(fp, GT_("Copyright (C) 2002, 2003 Eric S. Raymond\n" + "Copyright (C) 2004 Matthias Andree, Eric S. Raymond,\n" + " Robert M. Funk, Graham Wilson\n" + "Copyright (C) 2005 - 2006, 2010 - 2012 Sunil Shetye\n" + "Copyright (C) 2005 - 2012 Matthias Andree\n" + )); + fprintf(fp, GT_("Fetchmail comes with ABSOLUTELY NO WARRANTY. This is free software, and you\n" + "are welcome to redistribute it under certain conditions. For details,\n" + "please see the file COPYING in the source or documentation directory.\n")); +#ifdef SSL_ENABLE + /* Do not translate this */ + fprintf(fp, "This product includes software developed by the OpenSSL Project\nfor use in the OpenSSL Toolkit. (http://www.openssl.org/)\n"); +#endif +} + const char *iana_charset; int main(int argc, char **argv) @@ -189,7 +209,7 @@ int main(int argc, char **argv) * call near the beginning of the polling loop for details). We want * to be sure the lock gets nuked on any error exit, basically. */ - lock_dispose(); + fm_lock_dispose(); #ifdef HAVE_GETCWD /* save the current directory */ @@ -199,13 +219,20 @@ int main(int argc, char **argv) } #endif - if ((parsecmdline(argc,argv, &cmd_run, &cmd_opts)) < 0) - exit(PS_SYNTAX); + { + int i; + + i = parsecmdline(argc, argv, &cmd_run, &cmd_opts); + if (i < 0) + exit(PS_SYNTAX); + + if (quitmode && quitind == argc) + quitonly = 1; + } if (versioninfo) { - printf(GT_("This is fetchmail release %s"), VERSION); - printf( + const char *features = #ifdef POP2_ENABLE "+POP2" #endif /* POP2_ENABLE */ @@ -216,7 +243,7 @@ int main(int argc, char **argv) "-IMAP" #endif /* IMAP_ENABLE */ #ifdef GSSAPI - "+IMAP-GSS" + "+GSS" #endif /* GSSAPI */ #ifdef RPA_ENABLE "+RPA" @@ -239,22 +266,30 @@ int main(int argc, char **argv) #ifdef OPIE_ENABLE "+OPIE" #endif /* OPIE_ENABLE */ -#ifdef INET6_ENABLE - "+INET6" -#endif /* INET6_ENABLE */ #ifdef HAVE_PKG_hesiod "+HESIOD" #endif -#ifdef NET_SECURITY - "+NETSEC" -#endif /* NET_SECURITY */ #ifdef HAVE_SOCKS "+SOCKS" #endif /* HAVE_SOCKS */ #ifdef ENABLE_NLS "+NLS" #endif /* ENABLE_NLS */ - "\n"); +#ifdef KERBEROS_V4 + "+KRB4" +#endif /* KERBEROS_V4 */ +#ifdef KERBEROS_V5 + "+KRB5" +#endif /* KERBEROS_V5 */ +#ifndef HAVE_RES_SEARCH + "-DNS" +#endif + ".\n"; + printf(GT_("This is fetchmail release %s"), VERSION); + fputs(features, stdout); + puts(""); + printcopyright(stdout); + puts(""); fputs("Fallback MDA: ", stdout); #ifdef FALLBACK_MDA fputs(FALLBACK_MDA, stdout); @@ -265,13 +300,42 @@ int main(int argc, char **argv) fflush(stdout); /* this is an attempt to help remote debugging */ - system("uname -a"); + if (system("uname -a")) { /* NOOP to quench GCC complaint */ } } - /* avoid parsing the config file if all we're doing is killing a daemon */ - if (!(quitmode && argc == 2)) + /* avoid parsing the config file if all we're doing is killing a daemon */ + if (!quitonly) implicitmode = load_params(argc, argv, optind); + if (run.logfile) { + /* nodetach -> turn off logfile option */ + if (nodetach) { + if (outlevel >= O_NORMAL) { fprintf(stderr, GT_("The nodetach option is in effect, ignoring logfile option.\n")); } + xfree(run.logfile); + } + +#if 0 + /* not in daemon mode -> turn off logfile option */ + if (0 == run.poll_interval) { + if (outlevel >= O_NORMAL) { fprintf(stderr, GT_("Not running in daemon mode, ignoring logfile option.\n")); } + xfree(run.logfile); + } +#endif + + /* log file not writable -> turn off logfile option */ + if (run.logfile && 0 != access(run.logfile, F_OK)) { + if (outlevel >= O_NORMAL) { fprintf(stderr, GT_("Logfile \"%s\" does not exist, ignoring logfile option.\n"), run.logfile); } + xfree(run.logfile); + } + + /* log file not writable -> turn off logfile option */ + if (run.logfile && 0 != access(run.logfile, W_OK)) { + fprintf(stderr, GT_("Logfile \"%s\" is not writable, aborting.\n"), run.logfile); + xfree(run.logfile); + exit(PS_UNDEFINED); + } + } + #if defined(HAVE_SYSLOG) /* logging should be set up early in case we were restarted from exec */ if (run.use_syslog) @@ -282,14 +346,33 @@ int main(int argc, char **argv) /* Assume BSD4.2 openlog with two arguments */ openlog(program_name, LOG_PID); #endif - report_init(-1); + /* precedence: logfile (if effective) overrides syslog. */ + if (run.logfile) { + syslog(LOG_ERR, GT_("syslog and logfile options are both set, ignoring syslog, and logging to %s"), run.logfile); + run.use_syslog = 0; + report_init((run.poll_interval == 0 || nodetach) && !run.logfile); /* when changing this, change copy below, too */ + } else { + report_init(-1); + } } else #endif - report_init((run.poll_interval == 0 || nodetach) && !run.logfile); + report_init((run.poll_interval == 0 || nodetach) && !run.logfile); /* when changing this, change copy above, too */ + +#ifdef POP3_ENABLE + /* initialize UID handling */ + { + int st; + + if (!versioninfo && (st = prc_filecheck(run.idfile, !versioninfo)) != 0) + exit(st); + else + initialize_saved_lists(querylist, run.idfile); + } +#endif /* POP3_ENABLE */ /* construct the lockfile */ - lock_setup(); + fm_lock_setup(&run); #ifdef HAVE_SETRLIMIT /* @@ -345,6 +428,9 @@ int main(int argc, char **argv) } } + free_netrc(netrc_list); + netrc_list = 0; + /* perhaps we just want to check options? */ if (versioninfo) { @@ -371,12 +457,12 @@ int main(int argc, char **argv) } /* check for another fetchmail running concurrently */ - pid = lock_state(); + pid = fm_lock_state(); bkgd = (pid < 0); pid = bkgd ? -pid : pid; /* if no mail servers listed and nothing in background, we're done */ - if (!(quitmode && argc == 2) && pid == 0 && querylist == NULL) { + if (!quitonly && pid == 0 && querylist == NULL) { (void)fputs(GT_("fetchmail: no mailservers have been specified.\n"),stderr); exit(PS_SYNTAX); } @@ -384,36 +470,40 @@ int main(int argc, char **argv) /* perhaps user asked us to kill the other fetchmail */ if (quitmode) { - if (pid == 0) - { - fprintf(stderr,GT_("fetchmail: no other fetchmail is running\n")); - if (argc == 2) - exit(PS_EXCLUDE); - } - else if (getpid() == pid) + if (pid == 0 || pid == getpid()) + /* this test enables re-execing on a changed rcfile + * for pid == getpid() */ { - /* this test enables re-execing on a changed rcfile */ - if (argc == 2) - { + if (quitonly) { fprintf(stderr,GT_("fetchmail: no other fetchmail is running\n")); exit(PS_EXCLUDE); } } else if (kill(pid, SIGTERM) < 0) { - fprintf(stderr,GT_("fetchmail: error killing %s fetchmail at %d; bailing out.\n"), - bkgd ? GT_("background") : GT_("foreground"), pid); + fprintf(stderr,GT_("fetchmail: error killing %s fetchmail at %ld; bailing out.\n"), + bkgd ? GT_("background") : GT_("foreground"), (long)pid); exit(PS_EXCLUDE); } else { - fprintf(stderr,GT_("fetchmail: %s fetchmail at %d killed.\n"), - bkgd ? GT_("background") : GT_("foreground"), pid); - fm_lock_release(); - if (argc == 2) + int maxwait; + + if (outlevel > O_SILENT) + fprintf(stderr,GT_("fetchmail: %s fetchmail at %ld killed.\n"), + bkgd ? GT_("background") : GT_("foreground"), (long)pid); + /* We used to nuke the other process's lock here, with + * fm_lock_release(), which is broken. The other process + * needs to clear its lock by itself. */ + if (quitonly) exit(0); - else - pid = 0; + + /* wait for other process to exit */ + maxwait = 10; /* seconds */ + while (kill(pid, 0) == 0 && --maxwait >= 0) { + sleep(1); + } + pid = 0; } } @@ -429,20 +519,20 @@ int main(int argc, char **argv) else if (!implicitmode) { fprintf(stderr, - GT_("fetchmail: can't poll specified hosts with another fetchmail running at %d.\n"), - pid); + GT_("fetchmail: can't poll specified hosts with another fetchmail running at %ld.\n"), + (long)pid); return(PS_EXCLUDE); } else if (!bkgd) { fprintf(stderr, - GT_("fetchmail: another foreground fetchmail is running at %d.\n"), - pid); + GT_("fetchmail: another foreground fetchmail is running at %ld.\n"), + (long)pid); return(PS_EXCLUDE); } else if (getpid() == pid) /* this test enables re-execing on a changed rcfile */ - lock_assert(); + fm_lock_assert(); else if (argc > 1) { fprintf(stderr, @@ -452,8 +542,8 @@ int main(int argc, char **argv) else if (kill(pid, SIGUSR1) == 0) { fprintf(stderr, - GT_("fetchmail: background fetchmail at %d awakened.\n"), - pid); + GT_("fetchmail: background fetchmail at %ld awakened.\n"), + (long)pid); return(0); } else @@ -464,8 +554,8 @@ int main(int argc, char **argv) * SIGUSR1/SIGHUP transmission. */ fprintf(stderr, - GT_("fetchmail: elder sibling at %d died mysteriously.\n"), - pid); + GT_("fetchmail: elder sibling at %ld died mysteriously.\n"), + (long)pid); return(PS_UNDEFINED); } } @@ -486,10 +576,11 @@ int main(int argc, char **argv) const char* password_prompt = GT_("Enter password for %s@%s: "); size_t pplen = strlen(password_prompt) + strlen(ctl->remotename) + strlen(ctl->server.pollname) + 1; - xalloca(tmpbuf, char *, pplen); + tmpbuf = (char *)xmalloc(pplen); snprintf(tmpbuf, pplen, password_prompt, ctl->remotename, ctl->server.pollname); ctl->password = xstrdup((char *)fm_getpassword(tmpbuf)); + free(tmpbuf); } } } @@ -506,14 +597,29 @@ int main(int argc, char **argv) /* avoid zombies from plugins */ deal_with_sigchld(); + /* Fix up log destination - if the if() is true, the precedence rule + * above hasn't killed off the syslog option, because the logfile + * option is ineffective (because we're not detached or not in + * deamon mode), so kill it for the benefit of other parts of the + * code. */ + if (run.logfile && run.use_syslog) + run.logfile = 0; + /* * Maybe time to go to demon mode... */ if (run.poll_interval) { - if (!nodetach) - daemonize(run.logfile, terminate_run); - report(stdout, GT_("starting fetchmail %s daemon \n"), VERSION); + if (!nodetach) { + int rc; + + rc = daemonize(run.logfile); + if (rc) { + report(stderr, GT_("fetchmail: Cannot detach into background. Aborting.\n")); + exit(rc); + } + } + report(stdout, GT_("starting fetchmail %s daemon\n"), VERSION); /* * We'll set up a handler for these when we're sleeping, @@ -525,18 +631,17 @@ int main(int argc, char **argv) } else { - if (run.logfile && !nodetach && access(run.logfile, F_OK) == 0) - { + /* not in daemon mode */ + if (run.logfile) + { if (!freopen(run.logfile, "a", stdout)) - report(stderr, GT_("could not open %s to append logs to \n"), run.logfile); + report(stderr, GT_("could not open %s to append logs to\n"), run.logfile); if (!freopen(run.logfile, "a", stderr)) - report(stdout, GT_("could not open %s to append logs to \n"), run.logfile); + report(stdout, GT_("could not open %s to append logs to\n"), run.logfile); } } -#ifdef linux interface_init(); -#endif /* linux */ /* beyond here we don't want more than one fetchmail running per user */ umask(0077); @@ -544,11 +649,15 @@ int main(int argc, char **argv) set_signal_handler(SIGINT, terminate_run); set_signal_handler(SIGTERM, terminate_run); set_signal_handler(SIGALRM, terminate_run); - set_signal_handler(SIGPIPE, terminate_run); + set_signal_handler(SIGPIPE, SIG_IGN); set_signal_handler(SIGQUIT, terminate_run); /* here's the exclusion lock */ - lock_or_die(); + fm_lock_or_die(); + + if (check_only && outlevel >= O_VERBOSE) { + report(stdout, GT_("--check mode enabled, not fetching mail\n")); + } /* * Query all hosts. If there's only one, the error return will @@ -564,8 +673,9 @@ int main(int argc, char **argv) */ struct stat rcstat; - if (stat(rcfile, &rcstat) == -1) - { + if (strcmp(rcfile, "-") == 0) { + /* do nothing */ + } else if (stat(rcfile, &rcstat) == -1) { if (errno != ENOENT) report(stderr, GT_("couldn't time-check %s (error %d)\n"), @@ -608,19 +718,21 @@ int main(int argc, char **argv) report(stderr, GT_("attempt to re-exec fetchmail failed\n")); } -#if defined(HAVE_RES_SEARCH) && defined(USE_TCPIP_FOR_DNS) - /* - * This was an efficiency hack that backfired. The theory - * was that using TCP/IP for DNS queries would get us better - * reliability and shave off some per-UDP-packet costs. - * Unfortunately it interacted badly with diald, which effectively - * filters out DNS queries over TCP/IP for reasons having to do - * with some obscure Linux kernel problem involving bootstrapping of - * dynamically-addressed links. I don't understand this mess - * and don't want to, so it's "See ya!" to this hack. - */ - sethostent(TRUE); /* use TCP/IP for mailserver queries */ -#endif /* HAVE_RES_SEARCH */ +#ifdef HAVE_RES_SEARCH + /* Boldly assume that we also have res_init() if we have + * res_search(), and call res_init() to re-read the resolv.conf + * file, so that we can pick up changes to that file that are + * written by dhpccd, dhclient, pppd, openvpn and similar. */ + + /* NOTE: This assumes that /etc/resolv.conf is written + * atomically (i. e. a temporary file is written, flushed and + * then renamed into place). To fix Debian Bug#389270. */ + + /* NOTE: If this leaks memory or doesn't re-read + * /etc/resolv.conf, we're in trouble. The res_init() interface + * is only lightly documented :-( */ + res_init(); +#endif activecount = 0; batchcount = 0; @@ -651,14 +763,14 @@ int main(int argc, char **argv) } } -#if (defined(linux) && !defined(INET6_ENABLE)) || defined(__FreeBSD__) +#ifdef CAN_MONITOR /* * Don't do monitoring if we were woken by a signal. * Note that interface_approve() does its own error logging. */ if (!interface_approve(&ctl->server, !lastsig)) continue; -#endif /* (defined(linux) && !INET6_ENABLE) || defined(__FreeBSD__) */ +#endif /* CAN_MONITOR */ dofastuidl = 0; /* this is reset in the driver if required */ @@ -715,7 +827,7 @@ int main(int argc, char **argv) break; } -#if (defined(linux) && !defined(INET6_ENABLE)) || defined (__FreeBSD__) +#ifdef CAN_MONITOR if (ctl->server.monitor) { /* @@ -726,14 +838,10 @@ int main(int argc, char **argv) sleep(3); interface_note_activity(&ctl->server); } -#endif /* (defined(linux) && !INET6_ENABLE) || defined(__FreeBSD__) */ +#endif /* CAN_MONITOR */ } } -#if defined(HAVE_RES_SEARCH) && defined(USE_TCPIP_FOR_DNS) - endhostent(); /* release TCP/IP connection to nameserver */ -#endif /* HAVE_RES_SEARCH */ - /* close connections cleanly */ terminate_poll(0); @@ -762,9 +870,10 @@ int main(int argc, char **argv) exit(PS_AUTHFAIL); } - if (outlevel > O_SILENT) - report(stdout, - GT_("sleeping at %s\n"), timestamp()); + if ((outlevel > O_SILENT && !run.use_syslog && isatty(1)) + || outlevel > O_NORMAL) + report(stdout, + GT_("sleeping at %s for %d seconds\n"), timestamp(), run.poll_interval); /* * With this simple hack, we make it possible for a foreground @@ -774,7 +883,7 @@ int main(int argc, char **argv) * for people who think all system daemons wake up on SIGHUP. */ set_signal_handler(SIGUSR1, donothing); - if (getuid() != ROOT_UID) + if (getuid() == ROOT_UID) set_signal_handler(SIGHUP, donothing); /* @@ -797,11 +906,11 @@ int main(int argc, char **argv) ctl->wedged = FALSE; } - if (outlevel > O_SILENT) + if ((outlevel > O_SILENT && !run.use_syslog && isatty(1)) + || outlevel > O_NORMAL) report(stdout, GT_("awakened at %s\n"), timestamp()); } - } while - (run.poll_interval); + } while (run.poll_interval); if (outlevel >= O_VERBOSE) report(stdout, GT_("normal termination, status %d\n"), @@ -846,12 +955,7 @@ static void optmerge(struct query *h2, struct query *h1, int force) #define FLAG_MERGE(fld) if (force ? !!h1->fld : !h2->fld) h2->fld = h1->fld FLAG_MERGE(server.via); FLAG_MERGE(server.protocol); -#ifdef INET6_ENABLE FLAG_MERGE(server.service); - FLAG_MERGE(server.netsec); -#else /* INET6_ENABLE */ - FLAG_MERGE(server.port); -#endif /* INET6_ENABLE */ FLAG_MERGE(server.interval); FLAG_MERGE(server.authenticate); FLAG_MERGE(server.timeout); @@ -864,15 +968,16 @@ static void optmerge(struct query *h2, struct query *h1, int force) FLAG_MERGE(server.uidl); FLAG_MERGE(server.principal); -#if defined(linux) || defined(__FreeBSD__) +#ifdef CAN_MONITOR FLAG_MERGE(server.interface); - FLAG_MERGE(server.monitor); FLAG_MERGE(server.interface_pair); -#endif /* linux || defined(__FreeBSD__) */ + FLAG_MERGE(server.monitor); +#endif FLAG_MERGE(server.plugin); FLAG_MERGE(server.plugout); FLAG_MERGE(server.tracepolls); + FLAG_MERGE(server.badheader); FLAG_MERGE(wildcard); FLAG_MERGE(remotename); @@ -887,6 +992,7 @@ static void optmerge(struct query *h2, struct query *h1, int force) FLAG_MERGE(keep); FLAG_MERGE(flush); + FLAG_MERGE(limitflush); FLAG_MERGE(fetchall); FLAG_MERGE(rewrite); FLAG_MERGE(forcecr); @@ -908,7 +1014,9 @@ static void optmerge(struct query *h2, struct query *h1, int force) FLAG_MERGE(sslcert); FLAG_MERGE(sslproto); FLAG_MERGE(sslcertck); + FLAG_MERGE(sslcertfile); FLAG_MERGE(sslcertpath); + FLAG_MERGE(sslcommonname); FLAG_MERGE(sslfingerprint); #endif FLAG_MERGE(expunge); @@ -917,6 +1025,9 @@ static void optmerge(struct query *h2, struct query *h1, int force) #undef FLAG_MERGE } +/** Load configuration files. + * \return - true if no servers found on the command line + * - false if servers found on the command line */ static int load_params(int argc, char **argv, int optind) { int implicitmode, st; @@ -926,6 +1037,7 @@ static int load_params(int argc, char **argv, int optind) char *p; run.bouncemail = TRUE; + run.softbounce = TRUE; /* treat permanent errors as temporary */ run.spambounce = FALSE; /* don't bounce back to innocent bystanders */ memset(&def_opts, '\0', sizeof(struct query)); @@ -935,16 +1047,17 @@ static int load_params(int argc, char **argv, int optind) def_opts.server.protocol = P_AUTO; def_opts.server.timeout = CLIENT_TIMEOUT; def_opts.server.esmtp_name = user; + def_opts.server.badheader = BHREJECT; def_opts.warnings = WARNING_INTERVAL; def_opts.remotename = user; def_opts.listener = SMTP_MODE; def_opts.fetchsizelimit = 100; - def_opts.fastuidl = 10; + def_opts.fastuidl = 4; /* get the location of rcfile */ rcfiledir[0] = 0; p = strrchr (rcfile, '/'); - if (p && (p - rcfile) < sizeof (rcfiledir)) { + if (p && (size_t)(p - rcfile) < sizeof (rcfiledir)) { *p = 0; /* replace '/' by '0' */ strlcpy (rcfiledir, rcfile, sizeof(rcfiledir)); *p = '/'; /* restore '/' */ @@ -953,11 +1066,14 @@ static int load_params(int argc, char **argv, int optind) } /* note the parse time, so we can pick up on modifications */ - parsetime = 0; /* foil compiler warnings */ - if (stat(rcfile, &rcstat) != -1) - parsetime = rcstat.st_mtime; - else if (errno != ENOENT) - report(stderr, GT_("couldn't time-check the run-control file\n")); + if (strcmp(rcfile, "-") == 0) + parsetime = time(NULL); + else { + if (stat(rcfile, &rcstat) != -1) + parsetime = rcstat.st_mtime; + else if (errno != ENOENT) + report(stderr, GT_("couldn't time-check the run-control file\n")); + } /* this builds the host list */ if ((st = prc_parse_file(rcfile, !versioninfo)) != 0) @@ -989,7 +1105,7 @@ static int load_params(int argc, char **argv, int optind) || str_in_list(&ctl->server.akalist, argv[optind], TRUE)) { /* Is this correct? */ - if (predeclared && outlevel == O_VERBOSE) + if (predeclared && outlevel >= O_VERBOSE) fprintf(stderr,GT_("Warning: multiple mentions of host %s in config file\n"),argv[optind]); ctl->active = TRUE; predeclared = TRUE; @@ -1012,18 +1128,27 @@ static int load_params(int argc, char **argv, int optind) /* * If there's a defaults record, merge it and lose it. + * FIXME: we don't currently free all entries that might be in struct query. */ if (querylist && strcmp(querylist->server.pollname, "defaults") == 0) { + struct query *tmpq; + for (ctl = querylist->next; ctl; ctl = ctl->next) optmerge(ctl, querylist, FALSE); + tmpq = querylist; querylist = querylist->next; + free(tmpq->server.pollname); + free(tmpq); } /* don't allow a defaults record after the first */ - for (ctl = querylist; ctl; ctl = ctl->next) - if (ctl != querylist && strcmp(ctl->server.pollname, "defaults") == 0) + for (ctl = querylist; ctl; ctl = ctl->next) { + if (ctl != querylist && strcmp(ctl->server.pollname, "defaults") == 0) { + fprintf(stderr, GT_("fetchmail: Error: multiple \"defaults\" records in config file.\n")); exit(PS_SYNTAX); + } + } /* use localhost if we never fetch the FQDN of this host */ fetchmailhost = "localhost"; @@ -1033,19 +1158,23 @@ static int load_params(int argc, char **argv, int optind) run.logfile = cmd_run.logfile; if (cmd_run.idfile) run.idfile = cmd_run.idfile; + if (cmd_run.pidfile) + run.pidfile = cmd_run.pidfile; /* do this before the keep/fetchall test below, otherwise -d0 may fail */ if (cmd_run.poll_interval >= 0) run.poll_interval = cmd_run.poll_interval; if (cmd_run.invisible) - run.invisible = cmd_run.invisible; + run.invisible = (cmd_run.invisible == FLAG_TRUE); if (cmd_run.showdots) - run.showdots = cmd_run.showdots; + run.showdots = (cmd_run.showdots == FLAG_TRUE); if (cmd_run.use_syslog) run.use_syslog = (cmd_run.use_syslog == FLAG_TRUE); if (cmd_run.postmaster) run.postmaster = cmd_run.postmaster; if (cmd_run.bouncemail) - run.bouncemail = cmd_run.bouncemail; + run.bouncemail = (cmd_run.bouncemail == FLAG_TRUE); + if (cmd_run.softbounce) + run.softbounce = (cmd_run.softbounce == FLAG_TRUE); /* check and daemon options are not compatible */ if (check_only && run.poll_interval) @@ -1070,10 +1199,13 @@ static int load_params(int argc, char **argv, int optind) || ctl->server.authenticate == A_KERBEROS_V4 || ctl->server.authenticate == A_KERBEROS_V5)) { - fetchmailhost = host_fqdn(); + fetchmailhost = host_fqdn(1); break; } + if (!ctl) /* list exhausted */ + fetchmailhost = host_fqdn(0); + /* this code enables flags to be turned off */ #define DEFAULT(flag, dflt) if (flag == FLAG_TRUE)\ flag = TRUE;\ @@ -1081,8 +1213,6 @@ static int load_params(int argc, char **argv, int optind) flag = FALSE;\ else\ flag = (dflt) - /* one global gets treated specially */ - DEFAULT(run.showdots, run.poll_interval==0 || nodetach); /* merge in wired defaults, do sanity checks and prepare internal fields */ for (ctl = querylist; ctl; ctl = ctl->next) @@ -1116,6 +1246,7 @@ static int load_params(int argc, char **argv, int optind) DEFAULT(ctl->keep, FALSE); DEFAULT(ctl->fetchall, FALSE); DEFAULT(ctl->flush, FALSE); + DEFAULT(ctl->limitflush, FALSE); DEFAULT(ctl->rewrite, TRUE); DEFAULT(ctl->stripcr, (ctl->mda != (char *)NULL)); DEFAULT(ctl->forcecr, FALSE); @@ -1141,12 +1272,30 @@ static int load_params(int argc, char **argv, int optind) } #endif /* SSL_ENABLE */ #undef DEFAULT +#ifndef KERBEROS_V4 + if (ctl->server.authenticate == A_KERBEROS_V4) { + report(stderr, GT_("KERBEROS v4 support is configured, but not compiled in.\n")); + exit(PS_SYNTAX); + } +#endif +#ifndef KERBEROS_V5 + if (ctl->server.authenticate == A_KERBEROS_V5) { + report(stderr, GT_("KERBEROS v5 support is configured, but not compiled in.\n")); + exit(PS_SYNTAX); + } +#endif +#ifndef GSSAPI + if (ctl->server.authenticate == A_GSSAPI) { + report(stderr, GT_("GSSAPI support is configured, but not compiled in.\n")); + exit(PS_SYNTAX); + } +#endif /* * Make sure we have a nonempty host list to forward to. */ if (!ctl->smtphunt) - save_str(&ctl->smtphunt, fetchmailhost, FALSE); + save_str(&ctl->smtphunt, "localhost", FALSE); /* * Make sure we have a nonempty list of domains to fetch from. @@ -1162,14 +1311,14 @@ static int load_params(int argc, char **argv, int optind) if (!ctl->localnames) /* for local delivery via SMTP */ save_str_pair(&ctl->localnames, user, NULL); -#if !defined(HAVE_GETHOSTBYNAME) || !defined(HAVE_RES_SEARCH) +#ifndef HAVE_RES_SEARCH /* can't handle multidrop mailboxes unless we can do DNS lookups */ if (MULTIDROP(ctl) && ctl->server.dns) { ctl->server.dns = FALSE; report(stderr, GT_("fetchmail: warning: no DNS available to check multidrop fetches from %s\n"), ctl->server.pollname); } -#endif /* !HAVE_GETHOSTBYNAME || !HAVE_RES_SEARCH */ +#endif /* !HAVE_RES_SEARCH */ /* * can't handle multidrop mailboxes without "envelope" @@ -1187,24 +1336,26 @@ static int load_params(int argc, char **argv, int optind) save_str(&ctl->mailboxes, (char *)NULL, 0); /* maybe user overrode timeout on command line? */ - if (ctl->server.timeout == -1) + if (ctl->server.timeout == -1) ctl->server.timeout = CLIENT_TIMEOUT; -#ifndef INET6_ENABLE /* sanity checks */ - if (ctl->server.port < 0) - { - (void) fprintf(stderr, - GT_("%s configuration invalid, port number cannot be negative\n"), - ctl->server.pollname); - exit(PS_SYNTAX); - } - if (ctl->server.protocol == P_RPOP && ctl->server.port >= 1024) - { - (void) fprintf(stderr, - GT_("%s configuration invalid, RPOP requires a privileged port\n"), - ctl->server.pollname); - exit(PS_SYNTAX); + if (ctl->server.service) { + int port = servport(ctl->server.service); + if (port < 0) + { + (void) fprintf(stderr, + GT_("fetchmail: %s configuration invalid, specify positive port number for service or port\n"), + ctl->server.pollname); + exit(PS_SYNTAX); + } + if (ctl->server.protocol == P_RPOP && port >= 1024) + { + (void) fprintf(stderr, + GT_("fetchmail: %s configuration invalid, RPOP requires a privileged port\n"), + ctl->server.pollname); + exit(PS_SYNTAX); + } } if (ctl->listener == LMTP_MODE) { @@ -1214,8 +1365,9 @@ static int load_params(int argc, char **argv, int optind) { char *cp; - if (!(cp = strrchr(idp->id, '/')) || - (atoi(++cp) == SMTP_PORT)) + if (!(cp = strrchr(idp->id, '/')) + || (0 == strcmp(cp + 1, SMTP_PORT)) + || servport(cp + 1) == SMTP_PORT_NUM) { (void) fprintf(stderr, GT_("%s configuration invalid, LMTP can't use default SMTP port\n"), @@ -1224,29 +1376,19 @@ static int load_params(int argc, char **argv, int optind) } } } -#endif /* !INET6_ENABLE */ /* - * "I beg to you, have mercy on the week minds like myself." + * "I beg to you, have mercy on the we[a]k minds like myself." * wrote Pehr Anderson. Your petition is granted. */ - if (ctl->fetchall && ctl->keep && run.poll_interval && !nodetach) + if (ctl->fetchall && ctl->keep && (run.poll_interval || ctl->idle) && !nodetach && !configdump) { (void) fprintf(stderr, - GT_("Both fetchall and keep on in daemon mode is a mistake!\n")); - exit(PS_SYNTAX); + GT_("Both fetchall and keep on in daemon or idle mode is a mistake!\n")); } } } -#ifdef POP3_ENABLE - /* initialize UID handling */ - if (!versioninfo && (st = prc_filecheck(run.idfile, !versioninfo)) != 0) - exit(st); - else - initialize_saved_lists(querylist, run.idfile); -#endif /* POP3_ENABLE */ - /* * If the user didn't set a last-resort user to get misaddressed * multidrop mail, set an appropriate default here. @@ -1265,38 +1407,9 @@ static int load_params(int argc, char **argv, int optind) static RETSIGTYPE terminate_poll(int sig) /* to be executed at the end of a poll cycle */ { - /* - * Close all SMTP delivery sockets. For optimum performance - * we'd like to hold them open til end of run, but (1) this - * loses if our poll interval is longer than the MTA's inactivity - * timeout, and (2) some MTAs (like smail) don't deliver after - * each message, but rather queue up mail and wait to actually - * deliver it until the input socket is closed. - * - * Sending SMTP QUIT on signal is theoretically nice, but led to a - * subtle bug. If fetchmail was terminated by signal while it was - * shipping message text, it would hang forever waiting for a - * command acknowledge. In theory we could enable the QUIT - * only outside of the message send. In practice, we don't - * care. All mailservers hang up on a dropped TCP/IP connection - * anyway. - */ if (sig != 0) report(stdout, GT_("terminated with signal %d\n"), sig); - else - { - struct query *ctl; - - /* terminate all SMTP connections cleanly */ - for (ctl = querylist; ctl; ctl = ctl->next) - if (ctl->smtp_socket != -1) - { - /* don't send QUIT for ODMR case because we're acting - as a proxy between the SMTP server and client. */ - smtp_close(ctl, ctl->server.protocol != P_ODMR); - } - } #ifdef POP3_ENABLE /* @@ -1331,7 +1444,7 @@ static RETSIGTYPE terminate_run(int sig) if (ctl->password) memset(ctl->password, '\0', strlen(ctl->password)); -#if !defined(HAVE_ATEXIT) && !defined(HAVE_ON_EXIT) +#if !defined(HAVE_ATEXIT) fm_lock_release(); #endif @@ -1360,7 +1473,8 @@ static const int autoprobe[] = static int query_host(struct query *ctl) /* perform fetch transaction with single host */ { - int i, st = 0; + size_t i; + int st = 0; /* * If we're syslogging the progress messages are automatically timestamped. @@ -1424,12 +1538,7 @@ static int query_host(struct query *ctl) report(stderr, GT_("ETRN support is not configured.\n")); st = PS_PROTOCOL; #else -#ifdef HAVE_GETHOSTBYNAME st = doETRN(ctl); -#else - report(stderr, GT_("Cannot support ETRN without gethostbyname(2).\n")); - st = PS_PROTOCOL; -#endif /* HAVE_GETHOSTBYNAME */ break; #endif /* ETRN_ENABLE */ case P_ODMR: @@ -1437,12 +1546,7 @@ static int query_host(struct query *ctl) report(stderr, GT_("ODMR support is not configured.\n")); st = PS_PROTOCOL; #else -#ifdef HAVE_GETHOSTBYNAME st = doODMR(ctl); -#else - report(stderr, GT_("Cannot support ODMR without gethostbyname(2).\n")); - st = PS_PROTOCOL; -#endif /* HAVE_GETHOSTBYNAME */ #endif /* ODMR_ENABLE */ break; default: @@ -1495,6 +1599,11 @@ static void dump_params (struct runctl *runp, else if (outlevel >= O_VERBOSE) printf(GT_("Fetchmail will direct error mail to the sender.\n")); + if (!runp->softbounce) + printf(GT_("Fetchmail will treat permanent errors as permanent (drop messages).\n")); + else if (outlevel >= O_VERBOSE) + printf(GT_("Fetchmail will treat permanent errors as temporary (keep messages).\n")); + for (ctl = querylist; ctl; ctl = ctl->next) { if (!ctl->active || (implicit && ctl->server.skip)) @@ -1535,26 +1644,15 @@ static void dump_params (struct runctl *runp, } if (ctl->server.protocol == P_POP3 -#ifdef INET6_ENABLE && ctl->server.service && !strcmp(ctl->server.service, KPOP_PORT) -#else /* INET6_ENABLE */ - && ctl->server.port == KPOP_PORT -#endif /* INET6_ENABLE */ && (ctl->server.authenticate == A_KERBEROS_V4 || ctl->server.authenticate == A_KERBEROS_V5)) printf(GT_(" Protocol is KPOP with Kerberos %s authentication"), ctl->server.authenticate == A_KERBEROS_V5 ? "V" : "IV"); else printf(GT_(" Protocol is %s"), showproto(ctl->server.protocol)); -#ifdef INET6_ENABLE if (ctl->server.service) printf(GT_(" (using service %s)"), ctl->server.service); - if (ctl->server.netsec) - printf(GT_(" (using network security options %s)"), ctl->server.netsec); -#else /* INET6_ENABLE */ - if (ctl->server.port) - printf(GT_(" (using port %d)"), ctl->server.port); -#endif /* INET6_ENABLE */ else if (outlevel >= O_VERBOSE) printf(GT_(" (using default port)")); if (ctl->server.uidl && MAILBOX_PROTOCOL(ctl)) @@ -1569,6 +1667,9 @@ static void dump_params (struct runctl *runp, case A_PASSWORD: printf(GT_(" Password authentication will be forced.\n")); break; + case A_MSN: + printf(GT_(" MSN authentication will be forced.\n")); + break; case A_NTLM: printf(GT_(" NTLM authentication will be forced.\n")); break; @@ -1576,7 +1677,7 @@ static void dump_params (struct runctl *runp, printf(GT_(" OTP authentication will be forced.\n")); break; case A_CRAM_MD5: - printf(GT_(" CRAM-Md5 authentication will be forced.\n")); + printf(GT_(" CRAM-MD5 authentication will be forced.\n")); break; case A_GSSAPI: printf(GT_(" GSSAPI authentication will be forced.\n")); @@ -1600,9 +1701,13 @@ static void dump_params (struct runctl *runp, printf(GT_(" SSL protocol: %s.\n"), ctl->sslproto); if (ctl->sslcertck) { printf(GT_(" SSL server certificate checking enabled.\n")); - if (ctl->sslcertpath != NULL) - printf(GT_(" SSL trusted certificate directory: %s\n"), ctl->sslcertpath); } + if (ctl->sslcertfile != NULL) + printf(GT_(" SSL trusted certificate file: %s\n"), ctl->sslcertfile); + if (ctl->sslcertpath != NULL) + printf(GT_(" SSL trusted certificate directory: %s\n"), ctl->sslcertpath); + if (ctl->sslcommonname != NULL) + printf(GT_(" SSL server CommonName: %s\n"), ctl->sslcommonname); if (ctl->sslfingerprint != NULL) printf(GT_(" SSL key fingerprint (checked against the server key): %s\n"), ctl->sslfingerprint); #endif @@ -1623,7 +1728,7 @@ static void dump_params (struct runctl *runp, printf(GT_(" Selected mailboxes are:")); for (idp = ctl->mailboxes; idp; idp = idp->next) - printf(" %s", (char *)idp->id); + printf(" %s", idp->id); printf("\n"); } printf(ctl->fetchall @@ -1635,6 +1740,9 @@ static void dump_params (struct runctl *runp, printf(ctl->flush ? GT_(" Old messages will be flushed before message retrieval (--flush on).\n") : GT_(" Old messages will not be flushed before message retrieval (--flush off).\n")); + printf(ctl->limitflush + ? GT_(" Oversized messages will be flushed before message retrieval (--limitflush on).\n") + : GT_(" Oversized messages will not be flushed before message retrieval (--limitflush off).\n")); printf(ctl->rewrite ? GT_(" Rewrite of server-local addresses is enabled (--norewrite off).\n") : GT_(" Rewrite of server-local addresses is disabled (--norewrite on).\n")); @@ -1658,7 +1766,8 @@ static void dump_params (struct runctl *runp, : GT_(" Nonempty Status lines will be kept (dropstatus off)\n")); printf(ctl->dropdelivered ? GT_(" Delivered-To lines will be discarded (dropdelivered on)\n") - : GT_(" Delivered-To lines will be kept (dropdelivered off)\n")); if (NUM_NONZERO(ctl->limit)) + : GT_(" Delivered-To lines will be kept (dropdelivered off)\n")); + if (NUM_NONZERO(ctl->limit)) { if (NUM_NONZERO(ctl->limit)) printf(GT_(" Message size limit is %d octets (--limit %d).\n"), @@ -1709,7 +1818,7 @@ static void dump_params (struct runctl *runp, printf(GT_(" Domains for which mail will be fetched are:")); for (idp = ctl->domainlist; idp; idp = idp->next) { - printf(" %s", (char *)idp->id); + printf(" %s", idp->id); if (!idp->val.status.mark) printf(GT_(" (default)")); } @@ -1729,7 +1838,7 @@ static void dump_params (struct runctl *runp, ctl->listener); for (idp = ctl->smtphunt; idp; idp = idp->next) { - printf(" %s", (char *)idp->id); + printf(" %s", idp->id); if (!idp->val.status.mark) printf(GT_(" (default)")); } @@ -1787,9 +1896,9 @@ static void dump_params (struct runctl *runp, { for (idp = ctl->localnames; idp; idp = idp->next) if (idp->val.id2) - printf("\t%s -> %s\n", (char *)idp->id, (char *)idp->val.id2); + printf("\t%s -> %s\n", idp->id, idp->val.id2); else - printf("\t%s\n", (char *)idp->id); + printf("\t%s\n", idp->id); if (ctl->wildcard) fputs("\t*\n", stdout); } @@ -1811,9 +1920,9 @@ static void dump_params (struct runctl *runp, else { printf(GT_(" Envelope header is assumed to be: %s\n"), - ctl->server.envelope ? ctl->server.envelope:GT_("Received")); - if (ctl->server.envskip > 1 || outlevel >= O_VERBOSE) - printf(GT_(" Number of envelope header to be parsed: %d\n"), + ctl->server.envelope ? ctl->server.envelope : "Received"); + if (ctl->server.envskip || outlevel >= O_VERBOSE) + printf(GT_(" Number of envelope headers to be skipped over: %d\n"), ctl->server.envskip); if (ctl->server.qvirtual) printf(GT_(" Prefix %s will be removed from user id\n"), @@ -1824,26 +1933,23 @@ static void dump_params (struct runctl *runp, if (ctl->server.akalist) { - struct idlist *idp; - printf(GT_(" Predeclared mailserver aliases:")); for (idp = ctl->server.akalist; idp; idp = idp->next) - printf(" %s", (char *)idp->id); + printf(" %s", idp->id); putchar('\n'); } + if (ctl->server.localdomains) { - struct idlist *idp; - printf(GT_(" Local domains:")); for (idp = ctl->server.localdomains; idp; idp = idp->next) - printf(" %s", (char *)idp->id); + printf(" %s", idp->id); putchar('\n'); } } } } -#if defined(linux) || defined(__FreeBSD__) +#ifdef CAN_MONITOR if (ctl->server.interface) printf(GT_(" Connection must be through interface %s.\n"), ctl->server.interface); else if (outlevel >= O_VERBOSE) @@ -1878,14 +1984,24 @@ static void dump_params (struct runctl *runp, printf(GT_(" %d UIDs saved.\n"), count); if (outlevel >= O_VERBOSE) for (idp = ctl->oldsaved; idp; idp = idp->next) - printf("\t%s\n", (char *)idp->id); + printf("\t%s\n", idp->id); } } if (ctl->server.tracepolls) printf(GT_(" Poll trace information will be added to the Received header.\n")); else if (outlevel >= O_VERBOSE) - printf(GT_(" No poll trace information will be added to the Received header.\n.\n")); + printf(GT_(" No poll trace information will be added to the Received header.\n")); + + switch (ctl->server.badheader) { + case BHREJECT: + if (outlevel >= O_VERBOSE) + printf(GT_(" Messages with bad headers will be rejected.\n")); + break; + case BHACCEPT: + printf(GT_(" Messages with bad headers will be passed on.\n")); + break; + } if (ctl->properties) printf(GT_(" Pass-through properties \"%s\".\n"),