diff options
| author | Dan Winship <danw@src.gnome.org> | 2000-08-02 10:56:48 +0800 | 
|---|---|---|
| committer | Dan Winship <danw@src.gnome.org> | 2000-08-02 10:56:48 +0800 | 
| commit | 324b1e8675ed32dcdc051a2fd916158bd02a9893 (patch) | |
| tree | 341c0f634e335e1deba13131ffaac4545f6c43e3 | |
| parent | fad59bb3a344526f343b8e28a8d28059e81a42a9 (diff) | |
| download | gsoc2013-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/ChangeLog | 27 | ||||
| -rw-r--r-- | mail/Makefile.am | 1 | ||||
| -rw-r--r-- | mail/mail-crypto.c | 405 | ||||
| -rw-r--r-- | mail/mail-display.c | 42 | ||||
| -rw-r--r-- | mail/mail-display.h | 6 | ||||
| -rw-r--r-- | mail/mail-format.c | 414 | ||||
| -rw-r--r-- | mail/mail.h | 6 | ||||
| -rw-r--r-- | mail/session.c | 71 | 
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 | 
