aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Winship <danw@src.gnome.org>2000-08-02 10:56:48 +0800
committerDan Winship <danw@src.gnome.org>2000-08-02 10:56:48 +0800
commit324b1e8675ed32dcdc051a2fd916158bd02a9893 (patch)
tree341c0f634e335e1deba13131ffaac4545f6c43e3
parentfad59bb3a344526f343b8e28a8d28059e81a42a9 (diff)
downloadgsoc2013-evolution-324b1e8675ed32dcdc051a2fd916158bd02a9893.tar.gz
gsoc2013-evolution-324b1e8675ed32dcdc051a2fd916158bd02a9893.tar.zst
gsoc2013-evolution-324b1e8675ed32dcdc051a2fd916158bd02a9893.zip
New code to spawn off GPG/PGP to do stuff. Currently only deals with
* mail-crypto.c: New code to spawn off GPG/PGP to do stuff. Currently only deals with decryption. From Nathan Thompson-Amato <ndt@jps.net>, with bunches of changes from me. * session.c (mail_request_dialog): Expose the password dialog to the rest of the app (for use by the GPG/PGP code). * mail-format.c (handle_text_plain): Handle special inline data types. (Currently uuencoding, BinHex, and PGP encryption.) This is not the best way to deal with it, but it works for now. (try_inline_pgp): Convert an inline PGP-encrypted message into a multipart/encrypted part. (try_inline_binhex): Convert an inline BinHex attachment into an application/mac-binhex40 part (which we currently don't deal with...) (try_uudecoding): Convert a uuencoded attachment to an application/octet-stream part. (handle_multipart_encrypted): Deal with RFC2015 MIME-encoded PGP encrypted messages. (From ndt.) * mail-display.c (mail_text_write, mail_error_write): New utility functions. * Makefile.am (evolution_mail_SOURCES): add mail-crypto.c svn path=/trunk/; revision=4466
-rw-r--r--mail/ChangeLog27
-rw-r--r--mail/Makefile.am1
-rw-r--r--mail/mail-crypto.c405
-rw-r--r--mail/mail-display.c42
-rw-r--r--mail/mail-display.h6
-rw-r--r--mail/mail-format.c414
-rw-r--r--mail/mail.h6
-rw-r--r--mail/session.c71
8 files changed, 879 insertions, 93 deletions
diff --git a/mail/ChangeLog b/mail/ChangeLog
index fe12023521..2d89ced95d 100644
--- a/mail/ChangeLog
+++ b/mail/ChangeLog
@@ -1,3 +1,30 @@
+2000-08-01 Dan Winship <danw@helixcode.com>
+
+ * mail-crypto.c: New code to spawn off GPG/PGP to do stuff.
+ Currently only deals with decryption. From Nathan Thompson-Amato
+ <ndt@jps.net>, with bunches of changes from me.
+
+ * session.c (mail_request_dialog): Expose the password dialog to
+ the rest of the app (for use by the GPG/PGP code).
+
+ * mail-format.c (handle_text_plain): Handle special inline data
+ types. (Currently uuencoding, BinHex, and PGP encryption.) This is
+ not the best way to deal with it, but it works for now.
+ (try_inline_pgp): Convert an inline PGP-encrypted message into a
+ multipart/encrypted part.
+ (try_inline_binhex): Convert an inline BinHex attachment into an
+ application/mac-binhex40 part (which we currently don't deal
+ with...)
+ (try_uudecoding): Convert a uuencoded attachment to an
+ application/octet-stream part.
+ (handle_multipart_encrypted): Deal with RFC2015 MIME-encoded PGP
+ encrypted messages. (From ndt.)
+
+ * mail-display.c (mail_text_write, mail_error_write): New utility
+ functions.
+
+ * Makefile.am (evolution_mail_SOURCES): add mail-crypto.c
+
2000-07-31 Christopher James Lahey <clahey@helixcode.com>
* component-factory.c, folder-browser.c: Fixed some warnings.
diff --git a/mail/Makefile.am b/mail/Makefile.am
index c5752ac822..7204c237dd 100644
--- a/mail/Makefile.am
+++ b/mail/Makefile.am
@@ -41,6 +41,7 @@ evolution_mail_SOURCES = \
folder-browser.h \
folder-browser-factory.c \
mail-config.c \
+ mail-crypto.c \
mail-display.c \
mail-display.h \
mail-format.c \
diff --git a/mail/mail-crypto.c b/mail/mail-crypto.c
new file mode 100644
index 0000000000..4db4357b44
--- /dev/null
+++ b/mail/mail-crypto.c
@@ -0,0 +1,405 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+
+/*
+ * mail-crypto.h: OpenPGP en/decryption & signature code
+ *
+ * FIXME FIXME FIXME: This should be in its own library or component
+ */
+
+/*
+ * Authors:
+ * Nathan Thompson-Amato <ndt@jps.net>
+ * Dan Winship <danw@helixcode.com>
+ *
+ * Copyright 2000, Helix Code, Inc. (http://www.helixcode.com)
+ * Copyright 2000, Nathan Thompson-Amato
+ * Copyright 1999, 2000, Anthony Mulcahy
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#ifdef PGP_PROGRAM
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <gnome.h>
+
+#include "mail.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+#include <signal.h>
+
+static int
+cleanup_child (pid_t child)
+{
+ int status;
+ pid_t wait_result;
+ sigset_t mask, omask;
+
+ /* 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 (child, &status, 0);
+ alarm (0);
+ sigprocmask (SIG_SETMASK, &omask, NULL);
+
+ if (wait_result == -1 && errno == EINTR) {
+ /* The child is hanging: send a friendly reminder. */
+ kill (child, SIGTERM);
+ sleep (1);
+ wait_result = waitpid (child, &status, WNOHANG);
+ if (wait_result == 0) {
+ /* Still hanging; use brute force. */
+ kill (child, SIGKILL);
+ sleep (1);
+ wait_result = waitpid (child, &status, WNOHANG);
+ }
+ }
+
+ if (wait_result != -1 && WIFEXITED (status))
+ return WEXITSTATUS (status);
+ else
+ return -1;
+}
+
+static void
+cleanup_before_exec (int fd)
+{
+ int maxfd, i;
+
+ maxfd = sysconf (_SC_OPEN_MAX);
+ if (maxfd < 0)
+ return;
+
+ /* Loop over all fds. */
+ for (i = 0; i < maxfd; i++) {
+ if ((STDIN_FILENO != i) &&
+ (STDOUT_FILENO != i) &&
+ (STDERR_FILENO != i) &&
+ (fd != i))
+ close (i);
+ }
+}
+
+static int
+crypto_exec_with_passwd (char *path, char *argv[], const char *input,
+ int passwd_fds[], const char *passphrase,
+ char **output, char **diagnostics)
+{
+ fd_set fdset, write_fdset;
+ int ip_fds[2], op_fds[2], diag_fds[2];
+ int select_result, read_len, write_len;
+ size_t tmp_len;
+ pid_t child;
+ char *buf, *diag_buf;
+ const char *passwd_next, *input_next;
+ size_t size, alloc_size, diag_size, diag_alloc_size;
+ gboolean eof_seen, diag_eof_seen, passwd_eof_seen, input_eof_seen;
+ size_t passwd_remaining, passwd_incr, input_remaining, input_incr;
+ struct timeval timeout;
+ long tmp;
+
+ if ((pipe (ip_fds) < 0 ) ||
+ (pipe (op_fds) < 0 ) ||
+ (pipe (diag_fds) < 0 )) {
+ *diagnostics = g_strdup_printf ("Couldn't create pipe to %s: "
+ "%s", PGP_PROGRAM,
+ g_strerror (errno));
+ return 0;
+ }
+
+ if (!(child = fork ())) {
+ /* In child */
+
+ if ((dup2 (ip_fds[0], STDIN_FILENO) < 0 ) ||
+ (dup2 (op_fds[1], STDOUT_FILENO) < 0 ) ||
+ (dup2 (diag_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 excess fds */
+ cleanup_before_exec(passwd_fds[0]);
+
+ execvp (path, argv);
+ fprintf (stderr, "Could not execute %s: %s\n", argv[0],
+ g_strerror (errno));
+ _exit (255);
+ } else if (child < 0) {
+ *diagnostics = g_strdup_printf ("Cannot fork %s: %s",
+ argv[0], g_strerror (errno));
+ return 0;
+ }
+
+ /* Parent */
+ close (ip_fds[0]);
+ close (op_fds[1]);
+ close (diag_fds[1]);
+ close (passwd_fds[0]);
+
+ timeout.tv_sec = 10; /* timeout in seconds */
+ timeout.tv_usec = 0;
+
+ size = diag_size = 0;
+ alloc_size = 4096;
+ diag_alloc_size = 1024;
+ eof_seen = diag_eof_seen = FALSE;
+
+ buf = g_malloc (alloc_size);
+ diag_buf = g_malloc (diag_alloc_size);
+
+ passwd_next = passphrase;
+ passwd_remaining = strlen (passphrase);
+ passwd_incr = fpathconf (passwd_fds[1], _PC_PIPE_BUF);
+ /* Use a reasonable default value on error. */
+ if (passwd_incr <= 0)
+ passwd_incr = 1024;
+ passwd_eof_seen = FALSE;
+
+ input_next = input;
+ input_remaining = strlen (input);
+ input_incr = fpathconf (ip_fds[1], _PC_PIPE_BUF);
+ if (input_incr <= 0)
+ input_incr = 1024;
+ input_eof_seen = FALSE;
+
+ while (!(eof_seen && diag_eof_seen)) {
+ FD_ZERO (&fdset);
+ if (!eof_seen)
+ FD_SET (op_fds[0], &fdset);
+ if (!diag_eof_seen)
+ FD_SET (diag_fds[0], &fdset);
+
+ FD_ZERO (&write_fdset);
+ if (!passwd_eof_seen)
+ FD_SET (passwd_fds[1], &write_fdset);
+ if (!input_eof_seen)
+ FD_SET (ip_fds[1], &write_fdset);
+
+ select_result = select (FD_SETSIZE, &fdset, &write_fdset,
+ NULL, &timeout);
+ if (select_result < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ if (select_result == 0) {
+ /* timeout */
+ break;
+ }
+
+ if (FD_ISSET (op_fds[0], &fdset)) {
+ /* More output is available. */
+
+ if (size + 4096 > alloc_size) {
+ alloc_size += 4096;
+ buf = g_realloc (buf , alloc_size);
+ }
+ read_len = read (op_fds[0], &buf[size],
+ alloc_size - size - 1);
+ if (read_len < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ if (read_len == 0)
+ eof_seen = TRUE;
+ size += read_len;
+ }
+
+ if (FD_ISSET(diag_fds[0], &fdset) ) {
+ /* More stderr is available. */
+
+ if (diag_size + 1024 > diag_alloc_size) {
+ diag_alloc_size += 1024;
+ diag_buf = g_realloc (diag_buf,
+ diag_alloc_size);
+ }
+
+ read_len = read (diag_fds[0], &diag_buf[diag_size],
+ diag_alloc_size - diag_size - 1);
+ if (read_len < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ if (read_len == 0)
+ diag_eof_seen = TRUE;
+ diag_size += read_len;
+ }
+
+ if (FD_ISSET(passwd_fds[1], &write_fdset)) {
+ /* Ready for more password input. */
+
+ tmp_len = passwd_incr;
+ if (tmp_len > passwd_remaining)
+ tmp_len = passwd_remaining;
+ write_len = write (passwd_fds[1], passwd_next,
+ tmp_len);
+ if (write_len < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ passwd_next += write_len;
+ passwd_remaining -= write_len;
+ if (passwd_remaining == 0) {
+ close (passwd_fds[1]);
+ passwd_eof_seen = TRUE;
+ }
+ }
+
+ if (FD_ISSET(ip_fds[1], &write_fdset)) {
+ /* Ready for more ciphertext input. */
+
+ tmp_len = input_incr;
+ if (tmp_len > input_remaining)
+ tmp_len = input_remaining;
+ write_len = write (ip_fds[1], input_next, tmp_len);
+ if (write_len < 0) {
+ if (errno == EINTR)
+ continue;
+ break;
+ }
+ input_next += write_len;
+ input_remaining -= write_len;
+ if (input_remaining == 0 ) {
+ close (ip_fds[1]);
+ input_eof_seen = TRUE;
+ }
+ }
+ }
+
+ buf[size] = 0;
+ diag_buf[diag_size] = 0;
+ close (op_fds[0]);
+ close (diag_fds[0]);
+
+ *output = buf;
+ *diagnostics = diag_buf;
+
+ return cleanup_child (child);
+}
+
+/*----------------------------------------------------------------------*
+ * Public crypto functions
+ *----------------------------------------------------------------------*/
+
+
+char *
+mail_crypto_openpgp_decrypt (const char *ciphertext, const char *passphrase,
+ CamelException *ex)
+{
+ int retval;
+ char *path, *argv[12];
+ int i;
+ char *plaintext = NULL;
+ char *diagnostics = NULL;
+ int passwd_fds[2];
+ char passwd_fd[32];
+
+#ifndef PGP_PROGRAM
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ "No GPG/PGP program available.");
+ return NULL;
+#endif
+
+ if (pipe (passwd_fds) < 0) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ _("Couldn't create pipe to GPG/PGP: %s"),
+ g_strerror (errno));
+ return NULL;
+ }
+
+ i = 0;
+#if defined(GPG_PATH)
+ path = GPG_PATH;
+
+ argv[i++] = "gpg";
+ argv[i++] = "--verbose";
+ argv[i++] = "--yes";
+ argv[i++] = "--batch";
+
+ argv[i++] = "--output";
+ argv[i++] = "-"; /* output to stdout */
+
+ argv[i++] = "--decrypt";
+
+ argv[i++] = "--passphrase-fd";
+ sprintf (passwd_fd, "%d", passwd_fds[0]);
+ argv[i++] = passwd_fd;
+#elif defined(PGP5_PATH)
+ path = PGP5_PATH;
+
+ argv[i++] = "pgpv";
+ argv[i++] = "-f";
+ argv[i++] = "+batchmode=1";
+
+ sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]);
+ putenv (passwd_fd);
+#else
+ path = PGP_PATH;
+
+ argv[i++] = "pgp";
+ argv[i++] = "-f";
+
+ sprintf (passwd_fd, "PGPPASSFD=%d", passwd_fds[0]);
+ putenv (passwd_fd);
+#endif
+ argv[i++] = NULL;
+
+ retval = crypto_exec_with_passwd (path, argv, ciphertext, passwd_fds,
+ passphrase, &plaintext,
+ &diagnostics);
+ if (retval != 0 || !*plaintext) {
+ camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
+ "%s", diagnostics);
+ g_free (plaintext);
+ g_free (diagnostics);
+ return NULL;
+ }
+
+ g_free (diagnostics);
+ return plaintext;
+}
+
+#endif /* PGP_PROGRAM */
diff --git a/mail/mail-display.c b/mail/mail-display.c
index a0f6acbbe0..3af2ec1b15 100644
--- a/mail/mail-display.c
+++ b/mail/mail-display.c
@@ -16,6 +16,7 @@
#include <gnome.h>
#include "e-util/e-setup.h"
#include "e-util/e-util.h"
+#include "e-util/e-html-utils.h"
#include "mail-display.h"
#include "mail.h"
@@ -355,6 +356,47 @@ mail_html_write (GtkHTML *html, GtkHTMLStream *stream,
g_free (buf);
}
+void
+mail_text_write (GtkHTML *html, GtkHTMLStream *stream,
+ const char *format, ...)
+{
+ char *buf, *htmltext;
+ va_list ap;
+
+ va_start (ap, format);
+ buf = g_strdup_vprintf (format, ap);
+ va_end (ap);
+
+ htmltext = e_text_to_html (buf,
+ E_TEXT_TO_HTML_CONVERT_URLS |
+ E_TEXT_TO_HTML_CONVERT_NL |
+ E_TEXT_TO_HTML_CONVERT_SPACES);
+ gtk_html_write (html, stream, "<tt>", 4);
+ gtk_html_write (html, stream, htmltext, strlen (htmltext));
+ gtk_html_write (html, stream, "</tt>", 5);
+ g_free (htmltext);
+ g_free (buf);
+}
+
+void
+mail_error_write (GtkHTML *html, GtkHTMLStream *stream,
+ const char *format, ...)
+{
+ char *buf, *htmltext;
+ va_list ap;
+
+ va_start (ap, format);
+ buf = g_strdup_vprintf (format, ap);
+ va_end (ap);
+
+ htmltext = e_text_to_html (buf, E_TEXT_TO_HTML_CONVERT_NL);
+ gtk_html_write (html, stream, "<em><font color=red>", 20);
+ gtk_html_write (html, stream, htmltext, strlen (htmltext));
+ gtk_html_write (html, stream, "</font></em><br>", 16);
+ g_free (htmltext);
+ g_free (buf);
+}
+
/**
* mail_display_set_message:
diff --git a/mail/mail-display.h b/mail/mail-display.h
index 7348029c47..d839978371 100644
--- a/mail/mail-display.h
+++ b/mail/mail-display.h
@@ -45,5 +45,11 @@ void mail_display_set_message (MailDisplay *mail_display,
void mail_html_write (GtkHTML *html,
GtkHTMLStream *stream,
const char *format, ...);
+void mail_text_write (GtkHTML *html,
+ GtkHTMLStream *stream,
+ const char *format, ...);
+void mail_error_write (GtkHTML *html,
+ GtkHTMLStream *stream,
+ const char *format, ...);
#endif /* _MAIL_DISPLAY_H_ */
diff --git a/mail/mail-format.c b/mail/mail-format.c
index 5304e05e2a..df09cace9c 100644
--- a/mail/mail-format.c
+++ b/mail/mail-format.c
@@ -43,6 +43,10 @@ struct mail_format_data {
GtkHTMLStream *stream;
};
+static char *try_inline_pgp (char *start, struct mail_format_data *mfd);
+static char *try_uudecoding (char *start, struct mail_format_data *mfd);
+static char *try_inline_binhex (char *start, struct mail_format_data *mfd);
+
static gboolean handle_text_plain (CamelMimePart *part,
const char *mime_type,
struct mail_format_data *mfd);
@@ -69,6 +73,9 @@ static gboolean handle_multipart_alternative (CamelMimePart *part,
static gboolean handle_multipart_appledouble (CamelMimePart *part,
const char *mime_type,
struct mail_format_data *mfd);
+static gboolean handle_multipart_encrypted (CamelMimePart *part,
+ const char *mime_type,
+ struct mail_format_data *mfd);
static gboolean handle_audio (CamelMimePart *part,
const char *mime_type,
struct mail_format_data *mfd);
@@ -171,6 +178,54 @@ get_cid (CamelMimePart *part, struct mail_format_data *mfd)
return cid;
}
+static const char *
+get_url_for_icon (const char *icon_name, struct mail_format_data *mfd)
+{
+ static GHashTable *icons;
+ char *icon_path, buf[1024], *url;
+ GByteArray *ba;
+
+ if (!icons)
+ icons = g_hash_table_new (g_str_hash, g_str_equal);
+
+ if (*icon_name == '/')
+ icon_path = g_strdup (icon_name);
+ else {
+ icon_path = gnome_pixmap_file (icon_name);
+ if (!icon_path)
+ return "file:///dev/null";
+ }
+
+ ba = g_hash_table_lookup (icons, icon_path);
+ if (!ba) {
+ int fd, nread;
+
+ fd = open (icon_path, O_RDONLY);
+ if (fd == -1) {
+ g_free (icon_path);
+ return "file:///dev/null";
+ }
+
+ ba = g_byte_array_new ();
+
+ while (1) {
+ nread = read (fd, buf, sizeof (buf));
+ if (nread < 1)
+ break;
+ g_byte_array_append (ba, buf, nread);
+ }
+ close (fd);
+
+ g_hash_table_insert (icons, icon_path, ba);
+ }
+ g_free (icon_path);
+
+ url = g_strdup_printf ("x-evolution-data:%p", ba);
+ g_hash_table_insert (mfd->urls, url, ba);
+
+ return url;
+}
+
/* We're maintaining a hashtable of mimetypes -> functions;
@@ -218,6 +273,8 @@ setup_function_table (void)
handle_multipart_mixed);
g_hash_table_insert (mime_function_table, "multipart/appledouble",
handle_multipart_appledouble);
+ g_hash_table_insert (mime_function_table, "multipart/encrypted",
+ handle_multipart_encrypted);
/* RFC 2046 says unrecognized text subtypes can be treated
* as text/plain (as long as you recognize the character set),
@@ -476,38 +533,77 @@ get_data_wrapper_text (CamelDataWrapper *data)
* Mime handling functions
*----------------------------------------------------------------------*/
+struct {
+ char *start;
+ char * (*handler) (char *start, struct mail_format_data *mfd);
+} text_specials[] = {
+ { "-----BEGIN PGP MESSAGE-----\n", try_inline_pgp },
+ { "begin ", try_uudecoding },
+ { "(This file must be converted with BinHex 4.0)\n", try_inline_binhex }
+};
+#define NSPECIALS (sizeof (text_specials) / sizeof (*text_specials))
+
static gboolean
handle_text_plain (CamelMimePart *part, const char *mime_type,
struct mail_format_data *mfd)
{
CamelDataWrapper *wrapper =
camel_medium_get_content_object (CAMEL_MEDIUM (part));
- char *text, *htmltext;
+ char *text, *p, *start, *subtext;
GMimeContentField *type;
const char *format;
+ int i;
text = get_data_wrapper_text (wrapper);
if (!text)
return FALSE;
-
+
/* Check for RFC 2646 flowed text. */
type = camel_mime_part_get_content_type (part);
format = gmime_content_field_get_parameter (type, "format");
if (format && !g_strcasecmp (format, "flowed"))
return handle_text_plain_flowed (text, mfd);
- mail_html_write (mfd->html, mfd->stream,
- "\n<!-- text/plain -->\n<tt>\n");
+ mail_html_write (mfd->html, mfd->stream, "\n<!-- text/plain -->\n");
- htmltext = e_text_to_html (text,
- E_TEXT_TO_HTML_CONVERT_URLS |
- E_TEXT_TO_HTML_CONVERT_NL |
- E_TEXT_TO_HTML_CONVERT_SPACES);
- g_free (text);
- mail_html_write (mfd->html, mfd->stream, "%s", htmltext);
- g_free (htmltext);
+ p = text;
+ while (p) {
+ /* Look for special cases. */
+ for (i = 0; i < NSPECIALS; i++) {
+ start = strstr (p, text_specials[i].start);
+ if (start && (start == p || start[-1] == '\n'))
+ break;
+ }
+ if (!start)
+ break;
- mail_html_write (mfd->html, mfd->stream, "</tt>\n");
+ /* Deal with special case */
+ if (start != p) {
+ subtext = g_strndup (p, start - p);
+ mail_text_write (mfd->html, mfd->stream,
+ "%s", subtext);
+ g_free (subtext);
+ }
+ p = text_specials[i].handler (start, mfd);
+ if (p == start) {
+ /* Oops. That failed. Output this line normally and
+ * skip over it.
+ */
+ p = strchr (start, '\n');
+ if (!p++)
+ break;
+ subtext = g_strndup (start, p - start);
+ mail_text_write (mfd->html, mfd->stream,
+ "%s", subtext);
+ g_free (subtext);
+ } else if (p)
+ mail_html_write (mfd->html, mfd->stream, "<hr>");
+ }
+ /* Finish up (or do the whole thing if there were no specials). */
+ if (p)
+ mail_text_write (mfd->html, mfd->stream, "%s", p);
+
+ g_free (text);
return TRUE;
}
@@ -573,6 +669,148 @@ handle_text_plain_flowed (char *buf, struct mail_format_data *mfd)
return TRUE;
}
+static CamelMimePart *
+fake_mime_part_from_data (const char *data, int len, const char *type)
+{
+ CamelStream *memstream;
+ CamelDataWrapper *wrapper;
+ CamelMimePart *part;
+
+ memstream = camel_stream_mem_new_with_buffer (data, len);
+ wrapper = camel_data_wrapper_new ();
+ camel_data_wrapper_construct_from_stream (wrapper, memstream);
+ camel_data_wrapper_set_mime_type (wrapper, type);
+ gtk_object_unref (GTK_OBJECT (memstream));
+ part = camel_mime_part_new ();
+ camel_medium_set_content_object (CAMEL_MEDIUM (part), wrapper);
+ gtk_object_unref (GTK_OBJECT (wrapper));
+ return part;
+}
+
+static void
+destroy_part (GtkObject *root, GtkObject *part)
+{
+ gtk_object_unref (part);
+}
+
+static char *
+try_inline_pgp (char *start, struct mail_format_data *mfd)
+{
+ char *end;
+ CamelMimePart *part;
+ CamelMultipart *mp;
+
+ /* FIXME: This should deal with converting to multipart/signed
+ * as well.
+ */
+
+ end = strstr (start, "-----END PGP MESSAGE-----");
+ if (!end)
+ return start;
+
+ end += sizeof ("-----END PGP MESSAGE-----") - 1;
+
+ /* Build a multipart/encrypted. */
+ mp = camel_multipart_new ();
+ camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (mp),
+ "multipart/encrypted");
+
+ part = fake_mime_part_from_data ("Version: 1\n", 11,
+ "application/pgp-encrypted");
+ camel_multipart_add_part (mp, part);
+ gtk_object_unref (GTK_OBJECT (part));
+
+ part = fake_mime_part_from_data (start, end - start,
+ "application/octet-stream");
+ camel_multipart_add_part (mp, part);
+ gtk_object_unref (GTK_OBJECT (part));
+
+ part = camel_mime_part_new ();
+ camel_medium_set_content_object (CAMEL_MEDIUM (part),
+ CAMEL_DATA_WRAPPER (mp));
+ gtk_object_unref (GTK_OBJECT (mp));
+
+ gtk_signal_connect (GTK_OBJECT (mfd->root), "destroy",
+ destroy_part, part);
+ mail_html_write (mfd->html, mfd->stream, "<hr>");
+ call_handler_function (part, mfd);
+
+ return end;
+}
+
+static char *
+try_uudecoding (char *start, struct mail_format_data *mfd)
+{
+ int mode, len, state = 0;
+ char *filename, *estart, *p, *out, uulen = 0;
+ guint32 save = 0;
+ CamelMimePart *part;
+
+ /* Make sure it's a real uudecode begin line:
+ * begin [0-7]+ .*
+ */
+ mode = strtoul (start + 6, &p, 8);
+ if (p == start + 6 || *p != ' ')
+ return start;
+ estart = strchr (start, '\n');
+ if (!estart)
+ return start;
+
+ while (isspace ((unsigned char)*p))
+ p++;
+ filename = g_strndup (p, estart++ - p);
+
+ /* Make sure there's an end line. */
+ p = strstr (p, "\nend\n");
+ if (!p) {
+ g_free (filename);
+ return start;
+ }
+
+ out = g_malloc (p - estart);
+ len = uudecode_step (estart, p - estart, out, &state, &save, &uulen);
+
+ part = fake_mime_part_from_data (out, len, "application/octet-stream");
+ g_free (out);
+ camel_mime_part_set_filename (part, filename);
+ g_free (filename);
+ gtk_signal_connect (GTK_OBJECT (mfd->root), "destroy",
+ destroy_part, part);
+
+ mail_html_write (mfd->html, mfd->stream, "<hr>");
+ call_handler_function (part, mfd);
+
+ return p + 4;
+}
+
+static char *
+try_inline_binhex (char *start, struct mail_format_data *mfd)
+{
+ char *p;
+ CamelMimePart *part;
+
+ /* Find data start. */
+ p = strstr (start, "\n:");
+ if (!p)
+ return start;
+
+ /* And data end. */
+ p = strchr (p + 2, ':');
+ if (!p || (*(p + 1) != '\n' && *(p + 1) != '\0'))
+ return start;
+ p += 2;
+
+ part = fake_mime_part_from_data (start, p - start,
+ "application/mac-binhex40");
+ gtk_signal_connect (GTK_OBJECT (mfd->root), "destroy",
+ destroy_part, part);
+
+ mail_html_write (mfd->html, mfd->stream, "<hr>");
+ call_handler_function (part, mfd);
+
+ return p;
+}
+
static void
free_byte_array (GtkWidget *widget, gpointer user_data)
{
@@ -788,6 +1026,110 @@ handle_multipart_mixed (CamelMimePart *part, const char *mime_type,
return TRUE;
}
+static gboolean
+is_rfc2015 (CamelMimePart *part)
+{
+ int nparts;
+ char *text;
+ CamelDataWrapper *wrapper;
+ CamelMultipart *mp;
+ GMimeContentField *type;
+
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part));
+ mp = CAMEL_MULTIPART (wrapper);
+ nparts = camel_multipart_get_number (mp);
+ if (nparts != 2)
+ return FALSE;
+
+ /* Check for application/pgp-encrypted in the first part. */
+ part = camel_multipart_get_part (mp, 0);
+ type = camel_mime_part_get_content_type (part);
+ if (!gmime_content_field_is_type (type, "application", "pgp-encrypted"))
+ return FALSE;
+
+ /* Check version. */
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part));
+ text = get_data_wrapper_text (wrapper);
+ if (!text || !strstr(text, "Version: 1")) {
+ g_free(text);
+ return FALSE;
+ }
+ g_free(text);
+
+ /* Check for application/octet-stream in the second part. */
+ part = camel_multipart_get_part(mp, 1);
+ type = camel_mime_part_get_content_type (part);
+ if (!gmime_content_field_is_type (type, "application", "octet-stream"))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean
+handle_multipart_encrypted (CamelMimePart *part, const char *mime_type,
+ struct mail_format_data *mfd)
+{
+ CamelDataWrapper *wrapper =
+ camel_medium_get_content_object (CAMEL_MEDIUM (part));
+ CamelMultipart *mp;
+ char *ciphertext, *passphrase, *plaintext;
+ CamelException ex;
+
+ g_return_val_if_fail (CAMEL_IS_MULTIPART (wrapper), FALSE);
+ mp = CAMEL_MULTIPART (wrapper);
+
+ /* Currently we only handle RFC2015-style PGP encryption. */
+ if (!is_rfc2015 (part))
+ return handle_multipart_mixed (part, mime_type, mfd);
+
+ part = camel_multipart_get_part (mp, 1);
+ wrapper = camel_medium_get_content_object (CAMEL_MEDIUM (part));
+ ciphertext = get_data_wrapper_text (wrapper);
+ if (!ciphertext)
+ return FALSE;
+
+ camel_exception_init (&ex);
+
+#ifdef PGP_PROGRAM
+ /* Get the passphrase. */
+ passphrase = mail_request_dialog (
+ "Please enter your PGP/GPG passphrase.", TRUE, "pgp");
+ if (passphrase) {
+ plaintext = mail_crypto_openpgp_decrypt (ciphertext,
+ passphrase, &ex);
+ g_free (passphrase);
+ } else {
+ camel_exception_set (&ex, CAMEL_EXCEPTION_SYSTEM,
+ "No password provided.");
+ }
+#else
+ camel_exception_set (&ex, CAMEL_EXCEPTION_SYSTEM,
+ "No GPG/PGP support available in this copy "
+ "of Evolution.");
+#endif
+ g_free (ciphertext);
+
+ if (camel_exception_is_set (&ex)) {
+ mail_html_write (mfd->html, mfd->stream,
+ "<table><tr valign=top><td>"
+ "<table border=2><tr><td>"
+ "<img src=\"%s\"></td></tr></table><td>",
+ get_url_for_icon ("gnome-lockscreen.png",
+ mfd));
+ mail_error_write (mfd->html, mfd->stream,
+ "(Encrypted message not displayed)\n\n%s",
+ camel_exception_get_description (&ex));
+ mail_html_write (mfd->html, mfd->stream, "</td></tr></table>");
+
+ camel_exception_clear (&ex);
+ } else {
+ mail_text_write (mfd->html, mfd->stream, "%s", plaintext);
+ g_free (plaintext);
+ }
+
+ return TRUE;
+}
+
/* As seen in RFC 2387! */
static gboolean
handle_multipart_related (CamelMimePart *part, const char *mime_type,
@@ -915,54 +1257,6 @@ handle_multipart_appledouble (CamelMimePart *part, const char *mime_type,
return call_handler_function (part, mfd);
}
-static const char *
-get_url_for_icon (const char *icon_name, struct mail_format_data *mfd)
-{
- static GHashTable *icons;
- char *icon_path, buf[1024], *url;
- GByteArray *ba;
-
- if (!icons)
- icons = g_hash_table_new (g_str_hash, g_str_equal);
-
- if (*icon_name == '/')
- icon_path = g_strdup (icon_name);
- else {
- icon_path = gnome_pixmap_file (icon_name);
- if (!icon_path)
- return "file:///dev/null";
- }
-
- ba = g_hash_table_lookup (icons, icon_path);
- if (!ba) {
- int fd, nread;
-
- fd = open (icon_path, O_RDONLY);
- if (fd == -1) {
- g_free (icon_path);
- return "file:///dev/null";
- }
-
- ba = g_byte_array_new ();
-
- while (1) {
- nread = read (fd, buf, sizeof (buf));
- if (nread < 1)
- break;
- g_byte_array_append (ba, buf, nread);
- }
- close (fd);
-
- g_hash_table_insert (icons, icon_path, ba);
- }
- g_free (icon_path);
-
- url = g_strdup_printf ("x-evolution-data:%p", ba);
- g_hash_table_insert (mfd->urls, url, ba);
-
- return url;
-}
-
static void
handle_mystery (CamelMimePart *part, struct mail_format_data *mfd,
const char *url, const char *icon_name, const char *id,
diff --git a/mail/mail.h b/mail/mail.h
index 9cf857cd7f..83f5001605 100644
--- a/mail/mail.h
+++ b/mail/mail.h
@@ -32,6 +32,11 @@ BonoboControl *folder_browser_factory_new_control (const char *uri);
/* folder-browser */
CamelFolder *mail_uri_to_folder (const char *uri);
+/* mail-crypto */
+char *mail_crypto_openpgp_decrypt (const char *ciphertext,
+ const char *passphrase,
+ CamelException *ex);
+/* FIXME: add encryption & signing functions */
/* mail-format */
void mail_format_mime_message (CamelMimeMessage *mime_message,
@@ -63,5 +68,6 @@ void providers_config (BonoboUIHandler *uih, void *user_data, const char *path);
/* session */
void session_init (void);
+char *mail_request_dialog (const char *prompt, gboolean secret, const char *key);
void forget_passwords (BonoboUIHandler *uih, void *user_data, const char *path);
extern CamelSession *session;
diff --git a/mail/session.c b/mail/session.c
index 53ec9370db..7eaefd390c 100644
--- a/mail/session.c
+++ b/mail/session.c
@@ -37,15 +37,44 @@ request_callback (gchar *string, gpointer data)
}
#endif
-static char *
-evolution_auth_callback (CamelAuthCallbackMode mode, char *data,
- gboolean secret, CamelService *service, char *item,
- CamelException *ex)
+char *
+mail_request_dialog (const char *prompt, gboolean secret, const char *key)
{
#ifndef ASYNC_AUTH_CALLBACK
GtkWidget *dialog;
#endif
+ char *ans;
+
+ if (!passwords)
+ passwords = g_hash_table_new (g_str_hash, g_str_equal);
+
+ ans = g_hash_table_lookup (passwords, key);
+ if (ans)
+ return g_strdup (ans);
+
+#ifndef ASYNC_AUTH_CALLBACK
+ /* XXX parent window? */
+ dialog = gnome_request_dialog (secret, prompt, NULL, 0,
+ request_callback, &ans, NULL);
+ if (!dialog)
+ return NULL;
+ if (gnome_dialog_run_and_close (GNOME_DIALOG (dialog)) == -1 ||
+ ans == NULL)
+ return NULL;
+#else
+ if (!mail_op_get_password (data, secret, &ans))
+ return NULL;
+#endif
+
+ g_hash_table_insert (passwords, g_strdup (key), g_strdup (ans));
+ return ans;
+}
+
+static char *
+auth_callback (CamelAuthCallbackMode mode, char *data, gboolean secret,
+ CamelService *service, char *item, CamelException *ex)
+{
char *key, *ans;
if (!passwords)
@@ -75,38 +104,14 @@ evolution_auth_callback (CamelAuthCallbackMode mode, char *data,
return NULL;
}
- ans = g_hash_table_lookup (passwords, key);
- if (ans) {
- g_free (key);
- return g_strdup (ans);
- }
+ ans = mail_request_dialog (data, secret, key);
+ g_free (key);
-#ifndef ASYNC_AUTH_CALLBACK
- /* XXX parent window? */
- dialog = gnome_request_dialog (secret, data, NULL, 0,
- request_callback, &ans, NULL);
- if (!dialog) {
- camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
- "Could not create dialog box.");
- g_free (key);
- return NULL;
- }
- if (gnome_dialog_run_and_close (GNOME_DIALOG (dialog)) == -1 ||
- ans == NULL) {
+ if (!ans) {
camel_exception_set (ex, CAMEL_EXCEPTION_USER_CANCEL,
- "User cancelled query.");
- g_free (key);
- return NULL;
+ "User canceled operation.");
}
-#else
- if( mail_op_get_password( data, secret, &ans ) == FALSE ) {
- camel_exception_set( ex, CAMEL_EXCEPTION_USER_CANCEL, ans );
- g_free( key );
- return NULL;
- }
-#endif
- g_hash_table_insert (passwords, key, g_strdup (ans));
return ans;
}
@@ -116,7 +121,7 @@ session_init (void)
e_setup_base_dir ();
camel_init ();
- session = camel_session_new (evolution_auth_callback);
+ session = camel_session_new (auth_callback);
}
static gboolean