diff options
Diffstat (limited to 'mail/mail-config.c')
-rw-r--r-- | mail/mail-config.c | 234 |
1 files changed, 225 insertions, 9 deletions
diff --git a/mail/mail-config.c b/mail/mail-config.c index bdda99103b..f7ca05d548 100644 --- a/mail/mail-config.c +++ b/mail/mail-config.c @@ -24,6 +24,16 @@ #include <config.h> #endif +/* this group of headers is for pgp detection */ +#include <signal.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <termios.h> +#include <unistd.h> +#include <errno.h> + #include <pwd.h> #include <ctype.h> @@ -1243,17 +1253,198 @@ mail_config_set_confirm_expunge (gboolean value) struct { char *bin; + char *version; CamelPgpType type; } binaries[] = { - { "gpg", CAMEL_PGP_TYPE_GPG }, - { "pgpv", CAMEL_PGP_TYPE_PGP5 }, - { "pgp", CAMEL_PGP_TYPE_PGP2 }, - { NULL, CAMEL_PGP_TYPE_NONE } + { "gpg", NULL, CAMEL_PGP_TYPE_GPG }, + { "pgp", "6.5.8", CAMEL_PGP_TYPE_PGP6 }, + { "pgp", "5.0", CAMEL_PGP_TYPE_PGP5 }, + { "pgp", "2.6", CAMEL_PGP_TYPE_PGP2 }, + { NULL, NULL, CAMEL_PGP_TYPE_NONE } }; -/* FIXME: what about PGP 6.x? And I assume we want to "prefer" GnuPG - over the other, which is done now, but after that do we have a - order-of-preference for the rest? */ + +typedef struct _PGPFILE { + FILE *fp; + pid_t pid; +} PGPFILE; + +static PGPFILE * +pgpopen (const char *command, const char *mode) +{ + int in_fds[2], out_fds[2]; + PGPFILE *pgp = NULL; + char **argv = NULL; + pid_t child; + int fd; + + g_return_val_if_fail (command != NULL, NULL); + + if (*mode != 'r' || *mode != 'w') + return NULL; + + argv = g_strsplit (command, " ", 0); + if (!argv) + return NULL; + + if (pipe (in_fds) == -1) + goto error; + + if (pipe (out_fds) == -1) { + close (in_fds[0]); + close (in_fds[1]); + goto error; + } + + if ((child = fork ()) == 0) { + /* In child */ + int maxfd; + + if ((dup2 (in_fds[0], STDIN_FILENO) < 0 ) || + (dup2 (out_fds[1], STDOUT_FILENO) < 0 ) || + (dup2 (out_fds[1], STDERR_FILENO) < 0 )) { + _exit (255); + } + + /* Dissociate from evolution-mail's controlling + * terminal so that pgp/gpg won't be able to read from + * it: PGP 2 will fall back to asking for the password + * on /dev/tty if the passed-in password is incorrect. + * This will make that fail rather than hanging. + */ + setsid (); + + /* close all open fds that we aren't using */ + maxfd = sysconf (_SC_OPEN_MAX); + for (fd = 0; fd < maxfd; fd++) { + if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) + close (fd); + } + + execvp (argv[0], argv); + fprintf (stderr, "Could not execute %s: %s\n", argv[0], + g_strerror (errno)); + _exit (255); + } else if (child < 0) { + close (in_fds[0]); + close (in_fds[1]); + close (out_fds[0]); + close (out_fds[1]); + goto error; + } + + /* Parent */ + g_strfreev (argv); + + close (in_fds[0]); /* pgp's stdin */ + close (out_fds[1]); /* pgp's stdout */ + + if (mode[0] == 'r') { + /* opening in read-mode */ + fd = out_fds[0]; + close (in_fds[1]); + } else { + /* opening in write-mode */ + fd = in_fds[1]; + close (out_fds[0]); + } + + pgp = g_new (PGPFILE, 1); + pgp->fp = fdopen (fd, mode); + pgp->pid = child; + + return pgp; + error: + g_strfreev (argv); + + return NULL; +} + +static int +pgpclose (PGPFILE *pgp) +{ + sigset_t mask, omask; + pid_t wait_result; + int status; + + if (pgp->fp) { + fclose (pgp->fp); + pgp->fp = NULL; + } + + /* PGP5 closes fds before exiting, meaning this might be called + * too early. So wait a bit for the result. + */ + sigemptyset (&mask); + sigaddset (&mask, SIGALRM); + sigprocmask (SIG_BLOCK, &mask, &omask); + alarm (1); + wait_result = waitpid (pgp->pid, &status, 0); + alarm (0); + sigprocmask (SIG_SETMASK, &omask, NULL); + + if (wait_result == -1 && errno == EINTR) { + /* PGP is hanging: send a friendly reminder. */ + kill (pgp->pid, SIGTERM); + sleep (1); + wait_result = waitpid (pgp->pid, &status, WNOHANG); + if (wait_result == 0) { + /* Still hanging; use brute force. */ + kill (pgp->pid, SIGKILL); + sleep (1); + wait_result = waitpid (pgp->pid, &status, WNOHANG); + } + } + + if (wait_result != -1 && WIFEXITED (status)) { + g_free (pgp); + return 0; + } else + return -1; +} + +CamelPgpType +mail_config_pgp_type_detect_from_path (const char *pgp) +{ + const char *bin = g_basename (pgp); + struct stat st; + int i; + + /* make sure the file exists *and* is executable? */ + if (stat (pgp, &st) == -1 || !(st.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR))) + return CAMEL_PGP_TYPE_NONE; + + for (i = 0; binaries[i].bin; i++) { + if (binaries[i].version) { + /* compare version strings */ + char buffer[256], *command; + gboolean found = FALSE; + PGPFILE *fp; + + command = g_strdup_printf ("%s --version", pgp); + fp = pgpopen (command, "r"); + g_free (command); + if (fp) { + while (!feof (fp->fp) && !found) { + memset (buffer, 0, sizeof (buffer)); + fgets (buffer, sizeof (buffer), fp->fp); + found = strstr (buffer, binaries[i].version) != NULL; + } + + pgpclose (fp); + + if (found) + return binaries[i].type; + } + } else if (!strcmp (binaries[i].bin, bin)) { + /* no version string to compare against... */ + return binaries[i].type; + } + } + + return CAMEL_PGP_TYPE_NONE; +} + static void auto_detect_pgp_variables (void) { @@ -1266,6 +1457,7 @@ auto_detect_pgp_variables (void) path = PATH; while (path && *path && !type) { const char *pend = strchr (path, ':'); + gboolean found = FALSE; char *dirname; int i; @@ -1293,8 +1485,32 @@ auto_detect_pgp_variables (void) pgp = g_strdup_printf ("%s/%s", dirname, binaries[i].bin); /* make sure the file exists *and* is executable? */ if (stat (pgp, &st) != -1 && st.st_mode & (S_IXOTH | S_IXGRP | S_IXUSR)) { - type = binaries[i].type; - break; + if (binaries[i].version) { + /* compare version strings */ + char buffer[256], *command; + PGPFILE *fp; + + command = g_strdup_printf ("%s --version", pgp); + fp = pgpopen (command, "r"); + g_free (command); + if (fp) { + while (!feof (fp->fp) && !found) { + memset (buffer, 0, sizeof (buffer)); + fgets (buffer, sizeof (buffer), fp->fp); + found = strstr (buffer, binaries[i].version) != NULL; + } + + pgpclose (fp); + } + } else { + /* no version string to compare against... */ + found = TRUE; + } + + if (found) { + type = binaries[i].type; + break; + } } g_free (pgp); |