diff -ru a/configure.ac b/configure.ac --- a/configure.ac 2006-11-19 13:28:48.000000000 -0500 +++ b/configure.ac 2007-01-06 19:03:47.000000000 -0500 @@ -94,6 +94,17 @@ AM_GNU_GETTEXT_VERSION(0.14.3) dnl end i18n +# libpwmd support. +AC_ARG_ENABLE(libpwmd, + [ --enable-libpwmd compile with libpwmd support],,[enable_libpwmd=no]) + +if test "$enable_libpwmd" = "yes"; then + AC_CHECK_LIB(pwmd, pwmd_connect,, AC_MSG_ERROR([libpwmd not found])) + AC_DEFINE(HAVE_LIBPWMD, 1, [Define if you have libpwmd installed.]) + LDFLAGS="$LDFLAGS -L/usr/local/lib -lpwmd" + CFLAGS="$CFLAGS -I/usr/local/include" +fi + # Under sysV68, socket and friends are provided by the C library. # -linet does not provide socket, but causes multiple definition # errors at link-time. It is thus better to only use the C library. diff -ru a/fetchmail.c b/fetchmail.c --- a/fetchmail.c 2006-09-18 05:04:15.000000000 -0400 +++ b/fetchmail.c 2007-01-06 19:01:10.000000000 -0500 @@ -1,5 +1,5 @@ /* - * fetchmail.c -- main driver module for fetchmail + r fetchmail.c -- main driver module for fetchmail * * For license terms, see the file COPYING in this directory. */ @@ -140,6 +140,24 @@ const char *iana_charset; +#ifdef HAVE_LIBPWMD +void exit_with_pwmd_error() +{ + if (pwmd_ret == PWMD_PERROR) { + fprintf(stderr, GT_("pwmd: %s\n"), pwmd_strerror(pwmd_error)); + exit(PS_AUTHFAIL); + } + else if (pwmd_ret == PWMD_AGENT_ERROR) { + fprintf(stderr, GT_("pwmd: gpg-agent error. Is GPG_AGENT_INFO set? Read gpg-agent(1) for more info.\n")); + exit(PS_AUTHFAIL); + } + else + fprintf(stderr, GT_("pwmd: %s\n"), strerror(pwmd_error)); + + exit(PS_UNDEFINED); +} +#endif + int main(int argc, char **argv) { int bkgd = FALSE; @@ -264,6 +282,9 @@ #ifdef ENABLE_NLS "+NLS" #endif /* ENABLE_NLS */ +#ifdef HAVE_LIBPWMD + "+PWMD" +#endif ".\n"; printf(GT_("This is fetchmail release %s"), VERSION); fputs(features, stdout); @@ -284,8 +305,64 @@ } /* avoid parsing the config file if all we're doing is killing a daemon */ - if (!quitonly) + if (!quitonly) { +#ifdef HAVE_LIBPWMD + /* + * We need to connect to the server here before load_params is called. + */ + if (pwmd_file) { + /* + * Cannot get an element path without a service. + */ + if (cmd_opts.server.protocol == 0 || cmd_opts.server.protocol == 1) { + fprintf(stderr, GT_("Option --pwmd-file needs a service (-p) parameter.\n")); + exit(PS_SYNTAX); + } + + /* + * Try and connect to pwmd. + */ + if ((pwm = pwmd_connect(pwmd_socket, &pwmd_error)) == NULL) + exit_with_pwmd_error(); + + /* + * Use gpg-agent for password retrieval. In version 0.3 of pwmd + * there is an option to push files into the file cache when the + * daemon starts up. For a daemonized fetchmail, this may be + * required because there won't be a way to get the key without a + * tty. + */ + if ((pwmd_ret = pwmd_command(pwm, &pwmd_result, &pwmd_error, PWMD_SETOPT, + PWMD_OPTION_USEAGENT, 1)) != PWMD_OK) + exit_with_pwmd_error(); + + /* + * Set the text to be used in the pinentry dialog. + */ + if ((pwmd_ret = pwmd_command(pwm, &pwmd_result, &pwmd_error, PWMD_SETOPT, + PWMD_OPTION_TITLE, "Fetchmail")) != PWMD_OK) + exit_with_pwmd_error(); + + snprintf(pwmd_buf, sizeof(pwmd_buf), + "A password is needed to open the file \"%s\".", pwmd_file); + + if ((pwmd_ret = pwmd_command(pwm, &pwmd_result, &pwmd_error, PWMD_SETOPT, + PWMD_OPTION_DESC, pwmd_buf)) != PWMD_OK) + exit_with_pwmd_error(); + + /* + * Try and open the file so we can later get account/server credentials. If + * the file is not cached on the server, gpg-agent will ask for the + * password. + */ + if ((pwmd_ret = pwmd_command(pwm, &pwmd_result, &pwmd_error, PWMD_OPEN, + pwmd_file)) != PWMD_OK) + exit_with_pwmd_error(); + } +#endif + implicitmode = load_params(argc, argv, optind); + } #if defined(HAVE_SYSLOG) /* logging should be set up early in case we were restarted from exec */ @@ -510,6 +587,30 @@ if (ctl->active && !(implicitmode && ctl->server.skip) && !NO_PASSWORD(ctl) && !ctl->password) { +#ifdef HAVE_LIBPWMD + /* + * Try and find the account. The element tree must be in the form + * of: account->password + */ + if (pwm) { + snprintf(pwmd_buf, sizeof(pwmd_buf), "%s\tpassword", pwmd_account); + + if ((pwmd_ret = pwmd_command(pwm, &pwmd_result, &pwmd_error, + PWMD_GET, pwmd_buf)) != PWMD_OK) { + if (pwmd_ret == PWMD_ERROR) + fprintf(stderr, GT_("pwmd: %s\n"), strerror(pwmd_error)); + else if (pwmd_ret == PWMD_PERROR) + fprintf(stderr, GT_("pwmd (password): %s"), + pwmd_strerror(pwmd_error)); + } + else { + ctl->password = xstrdup((char *)pwmd_result); + memset(pwmd_result, 0, strlen((char *)pwmd_result)); + free(pwmd_result); + continue; + } + } +#endif if (!isatty(0)) { fprintf(stderr, @@ -529,6 +630,15 @@ } } +#ifdef HAVE_LIBPWMD + /* + * We should have all the needed data from the server, go ahead and close + * the connection. + */ + if (pwm) + pwmd_close(pwm); +#endif + /* * Time to initiate the SOCKS library (this is not mandatory: it just * registers the correct application name for logging purpose. If you @@ -865,7 +975,7 @@ exit(PS_NOMAIL); } -static void list_merge(struct idlist **dstl, struct idlist **srcl, int force) +void list_merge(struct idlist **dstl, struct idlist **srcl, int force) { /* * If force is off, modify dstl fields only when they're empty (treat srcl @@ -880,7 +990,7 @@ } } -static void optmerge(struct query *h2, struct query *h1, int force) +void optmerge(struct query *h2, struct query *h1, int force) /* merge two options records */ { list_merge(&h2->server.localdomains, &h1->server.localdomains, force); @@ -960,16 +1070,40 @@ #undef FLAG_MERGE } +#ifdef HAVE_LIBPWMD +char *protocol_to_service(int protocol) +{ + switch (protocol) { + case P_POP2: + return "POP2"; + case P_POP3: + return "POP3"; + case P_APOP: + return "APOP"; + case P_RPOP: + return "RPOP"; + case P_ETRN: + return "ETRN"; + case P_ODMR: + return "ODMR"; + default: + return NULL; + } + + return NULL; +} +#endif + /** 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 load_params(int argc, char **argv, int optind) { int implicitmode, st; struct passwd *pw; struct query def_opts, *ctl; struct stat rcstat; - char *p; + char *p, *prot; run.bouncemail = TRUE; run.spambounce = FALSE; /* don't bounce back to innocent bystanders */ @@ -1049,8 +1183,112 @@ * call later on. */ ctl = hostalloc((struct query *)NULL); +#ifdef HAVE_LIBPWMD + if (pwm) { + prot = protocol_to_service(cmd_opts.server.protocol); + pwmd_account = xstrdup(argv[optind]); + + /* + * Get the hostname for this protocol. Element path must + * be account->[protocol]->hostname. + */ + snprintf(pwmd_buf, sizeof(pwmd_buf), "%s\t%s\thostname", pwmd_account, prot); + + if ((pwmd_ret = pwmd_command(pwm, &pwmd_result, &pwmd_error, + PWMD_GET, pwmd_buf)) != PWMD_OK) + exit_with_pwmd_error(); + + ctl->server.via = + ctl->server.pollname = xstrdup((char *)pwmd_result); + memset(pwmd_result, 0, strlen((char *)pwmd_result)); + free(pwmd_result); + + /* + * Get the remote username. Element must be + * account->username. + */ + snprintf(pwmd_buf, sizeof(pwmd_buf), "%s\tusername", pwmd_account); + + if ((pwmd_ret = pwmd_command(pwm, &pwmd_result, &pwmd_error, + PWMD_GET, pwmd_buf)) != PWMD_OK) + exit_with_pwmd_error(); + + def_opts.remotename = xstrdup((char *)pwmd_result); + def_opts.server.esmtp_name = xstrdup(def_opts.remotename); + memset(pwmd_result, 0, strlen((char *)pwmd_result)); + free(pwmd_result); + + /* + * If there is a ssl element and set to 1, enable ssl for + * this account. Element path must be + * account->[protocol]->ssl. + */ + snprintf(pwmd_buf, sizeof(pwmd_buf), "%s\t%s\tssl", pwmd_account, prot); + + if ((pwmd_ret = pwmd_command(pwm, &pwmd_result, &pwmd_error, + PWMD_GET, pwmd_buf)) != PWMD_OK) { + if (pwmd_ret != PWMD_PERROR) + exit_with_pwmd_error(); + else + fprintf(stderr, GT_("pwmd (ssl): %s"), pwmd_strerror(pwmd_error)); + } + else { + p = xstrdup((char *)pwmd_result); + ctl->use_ssl = atoi(p); + memset(pwmd_result, 0, strlen((char *)pwmd_result)); + free(pwmd_result); + free(p); + } + + /* + * account->[protocol]->sslfingerprint. + */ + snprintf(pwmd_buf, sizeof(pwmd_buf), "%s\t%s\tsslfingerprint", + pwmd_account, prot); + + if ((pwmd_ret = pwmd_command(pwm, &pwmd_result, &pwmd_error, + PWMD_GET, pwmd_buf)) != PWMD_OK) { + if (pwmd_ret != PWMD_PERROR) + exit_with_pwmd_error(); + else + fprintf(stderr, GT_("pwmd (sslfingerprint): %s"), + pwmd_strerror(pwmd_error)); + } + else { + p = xstrdup((char *)pwmd_result); + ctl->sslfingerprint = p; + memset(pwmd_result, 0, strlen((char *)pwmd_result)); + free(pwmd_result); + } + + /* + * Server port. Element path must be + * account->[protocol]->port. + */ + snprintf(pwmd_buf, sizeof(pwmd_buf), "%s\t%s\tport", pwmd_account, prot); + + if ((pwmd_ret = pwmd_command(pwm, &pwmd_result, &pwmd_error, + PWMD_GET, pwmd_buf)) != PWMD_OK) { + if (pwmd_ret != PWMD_PERROR) + exit_with_pwmd_error(); + else + fprintf(stderr, GT_("pwmd (port): %s"), + pwmd_strerror(pwmd_error)); + } + else { + p = xstrdup((char *)pwmd_result); + ctl->server.service = p; + memset(pwmd_result, 0, strlen((char *)pwmd_result)); + free(pwmd_result); + } + } + else + ctl->server.via = + ctl->server.pollname = xstrdup(argv[optind]); +#else ctl->server.via = ctl->server.pollname = xstrdup(argv[optind]); +#endif ctl->active = TRUE; ctl->server.lead_server = (struct hostdata *)NULL; } diff -ru a/fetchmail.h b/fetchmail.h --- a/fetchmail.h 2006-11-01 16:56:18.000000000 -0500 +++ b/fetchmail.h 2007-01-06 18:52:56.000000000 -0500 @@ -39,6 +39,10 @@ # include "trio/trio.h" #endif +#ifdef HAVE_LIBPWMD +#include +#endif + /* We need this for strstr */ #if !defined(HAVE_STRSTR) && !defined(strstr) char *strstr(const char *, const char *); @@ -456,6 +460,16 @@ extern char *sdps_envfrom; extern char *sdps_envto; #endif /* SDPS_ENABLE */ +#ifdef HAVE_LIBPWMD +pwm_t *pwm; /* the handle */ +char *pwmd_account; /* the account being processed */ +char *pwmd_socket; /* socket to connect to */ +char *pwmd_file; /* file to open on server */ +void *pwmd_result; /* server result */ +int pwmd_error; /* protocol or errno */ +int pwmd_ret; /* return value from pwmd_command() */ +char pwmd_buf[LINE_MAX]; /* element copy buffer */ +#endif extern const char *iana_charset; /* IANA assigned charset name */ diff -ru a/options.c b/options.c --- a/options.c 2006-08-14 19:04:02.000000000 -0400 +++ b/options.c 2007-01-06 18:52:56.000000000 -0500 @@ -54,11 +54,20 @@ }; /* options still left: CgGhHjJoORTWxXYz */ +#ifndef HAVE_LIBPWMD static const char *shortoptions = "?Vcsvd:NqL:f:i:p:UP:A:t:E:Q:u:akKFnl:r:S:Z:b:B:e:m:I:M:yw:D:"; +#else +static const char *shortoptions = + "C:G:?Vcsvd:NqL:f:i:p:UP:A:t:E:Q:u:akKFnl:r:S:Z:b:B:e:m:I:M:yw:D:"; +#endif static const struct option longoptions[] = { /* this can be const because all flag fields are 0 and will never get set */ +#ifdef HAVE_LIBPWMD + {"pwmd-socket", required_argument, (int *) 0, 'C' }, + {"pwmd-file", required_argument, (int *) 0, 'G' }, +#endif {"help", no_argument, (int *) 0, '?' }, {"version", no_argument, (int *) 0, 'V' }, {"check", no_argument, (int *) 0, 'c' }, @@ -286,6 +295,14 @@ xfree(rcfile); rcfile = prependdir (optarg, currentwd); break; +#ifdef HAVE_LIBPWMD + case 'C': + pwmd_socket = xstrdup(optarg); + break; + case 'G': + pwmd_file = xstrdup(optarg); + break; +#endif case 'i': rctl->idfile = prependdir (optarg, currentwd); break; @@ -628,6 +645,10 @@ P(GT_(" --principal mail service principal\n")); P(GT_(" --tracepolls add poll-tracing information to Received header\n")); +#ifdef HAVE_LIBPWMD + P(GT_(" -C, --pwmd-socket pwmd socket path (~/.pwmd/socket)\n")); + P(GT_(" -G, --pwmd-file filename to open on the pwmd server\n")); +#endif P(GT_(" -u, --username specify users's login on server\n")); P(GT_(" -a, --[fetch]all retrieve old and new messages\n")); P(GT_(" -K, --nokeep delete new messages after retrieval\n"));