summaryrefslogtreecommitdiffabout
path: root/libetpan/src/engine
Side-by-side diff
Diffstat (limited to 'libetpan/src/engine') (more/less context) (ignore whitespace changes)
-rw-r--r--libetpan/src/engine/mailengine.c1636
-rw-r--r--libetpan/src/engine/mailengine.h190
-rw-r--r--libetpan/src/engine/mailprivacy.c949
-rw-r--r--libetpan/src/engine/mailprivacy.h117
-rw-r--r--libetpan/src/engine/mailprivacy_gnupg.c2614
-rw-r--r--libetpan/src/engine/mailprivacy_gnupg.h46
-rw-r--r--libetpan/src/engine/mailprivacy_smime.c1755
-rw-r--r--libetpan/src/engine/mailprivacy_smime.h84
-rw-r--r--libetpan/src/engine/mailprivacy_tools.c1283
-rw-r--r--libetpan/src/engine/mailprivacy_tools.h102
-rw-r--r--libetpan/src/engine/mailprivacy_types.h82
11 files changed, 8858 insertions, 0 deletions
diff --git a/libetpan/src/engine/mailengine.c b/libetpan/src/engine/mailengine.c
new file mode 100644
index 0000000..be4df38
--- a/dev/null
+++ b/libetpan/src/engine/mailengine.c
@@ -0,0 +1,1636 @@
+/*
+ * libEtPan! -- a mail library
+ *
+ * Copyright (C) 2001, 2005 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include "mailengine.h"
+
+#ifndef CONFIG_H
+#define CONFIG_H
+#include "config.h"
+#endif
+
+#include "mailfolder.h"
+#include "maildriver.h"
+#include "imapdriver_cached.h"
+#include "mailstorage.h"
+#include "imapdriver_cached_message.h"
+#include <stdlib.h>
+#include "mailprivacy.h"
+#ifdef LIBETPAN_REENTRANT
+#include <pthread.h>
+#endif
+#include <string.h>
+
+/* ************************************************************* */
+/* Message finder */
+
+#if 0
+struct message_folder_finder {
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_t lock;
+#endif
+
+ /* msg => folder */
+ chash * message_hash;
+};
+
+static int message_folder_finder_init(struct message_folder_finder * finder)
+{
+ int r;
+
+#ifdef LIBETPAN_REENTRANT
+ r = pthread_mutex_init(&finder->lock, NULL);
+ if (r != 0)
+ return MAIL_ERROR_MEMORY;
+#endif
+
+ finder->message_hash = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
+ if (finder->message_hash == NULL)
+ return MAIL_ERROR_MEMORY;
+
+ return MAIL_NO_ERROR;
+}
+
+static void message_folder_finder_done(struct message_folder_finder * finder)
+{
+ chash_free(finder->message_hash);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_destroy(&finder->lock);
+#endif
+}
+
+static struct mailfolder *
+message_folder_finder_lookup(struct message_folder_finder * finder,
+ mailmessage * msg)
+{
+ int r;
+ chashdatum key;
+ chashdatum data;
+ struct mailfolder * folder;
+
+ key.data = &msg;
+ key.len = sizeof(msg);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_lock(&finder->lock);
+#endif
+ r = chash_get(finder->message_hash, &key, &data);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_unlock(&finder->lock);
+#endif
+ if (r < 0)
+ return NULL;
+
+ folder = data.data;
+
+ return folder;
+}
+
+static inline int
+message_folder_finder_store_no_lock(struct message_folder_finder * finder,
+ mailmessage * msg, struct mailfolder * folder)
+{
+ int r;
+ chashdatum key;
+ chashdatum data;
+
+ key.data = &msg;
+ key.len = sizeof(msg);
+ data.data = folder;
+ data.len = 0;
+ r = chash_set(finder->message_hash, &key, &data, NULL);
+ if (r < 0)
+ return MAIL_ERROR_MEMORY;
+
+ return MAIL_NO_ERROR;
+}
+
+static int
+message_folder_finder_store(struct message_folder_finder * finder,
+ mailmessage * msg, struct mailfolder * folder)
+{
+ int r;
+
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_lock(&finder->lock);
+#endif
+ r = message_folder_finder_store_no_lock(finder, msg, folder);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_unlock(&finder->lock);
+#endif
+
+ return r;
+}
+
+static inline void
+message_folder_finder_delete_no_lock(struct message_folder_finder * finder,
+ mailmessage * msg)
+{
+ chashdatum key;
+
+ key.data = &msg;
+ key.len = sizeof(msg);
+ chash_delete(finder->message_hash, &key, NULL);
+}
+
+static void
+message_folder_finder_delete(struct message_folder_finder * finder,
+ mailmessage * msg)
+{
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_lock(&finder->lock);
+#endif
+ message_folder_finder_delete_no_lock(finder, msg);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_unlock(&finder->lock);
+#endif
+}
+#endif
+
+/* ************************************************************* */
+/* Message ref info */
+
+struct message_ref_elt {
+ mailmessage * msg;
+ int ref_count;
+ int mime_ref_count;
+ struct mailfolder * folder;
+ int lost;
+};
+
+static struct message_ref_elt *
+message_ref_elt_new(struct mailfolder * folder, mailmessage * msg)
+{
+ struct message_ref_elt * ref;
+
+ ref = malloc(sizeof(* ref));
+ if (ref == NULL)
+ return NULL;
+
+ ref->msg = msg;
+ ref->ref_count = 0;
+ ref->mime_ref_count = 0;
+ ref->folder = folder;
+ ref->lost = 0;
+
+ return ref;
+}
+
+static void message_ref_elt_free(struct message_ref_elt * ref_elt)
+{
+ free(ref_elt);
+}
+
+static inline int message_ref(struct message_ref_elt * ref_elt)
+{
+ ref_elt->ref_count ++;
+
+ return ref_elt->ref_count;
+}
+
+static inline int message_unref(struct message_ref_elt * ref_elt)
+{
+ ref_elt->ref_count --;
+
+ return ref_elt->ref_count;
+}
+
+
+static inline int message_mime_ref(struct mailprivacy * privacy,
+ struct message_ref_elt * ref_elt)
+{
+ int r;
+
+ if (ref_elt->mime_ref_count == 0) {
+ struct mailmime * mime;
+
+ r = mailprivacy_msg_get_bodystructure(privacy, ref_elt->msg, &mime);
+ if (r != MAIL_NO_ERROR)
+ return -r;
+ }
+
+ message_ref(ref_elt);
+
+ ref_elt->mime_ref_count ++;
+
+ return ref_elt->mime_ref_count;
+}
+
+static inline int message_mime_unref(struct mailprivacy * privacy,
+ struct message_ref_elt * ref_elt)
+{
+ message_unref(ref_elt);
+
+ ref_elt->mime_ref_count --;
+
+ if (ref_elt->mime_ref_count == 0)
+ mailprivacy_msg_flush(privacy, ref_elt->msg);
+
+ return ref_elt->mime_ref_count;
+}
+
+
+/* ************************************************************* */
+/* Folder ref info */
+
+struct folder_ref_info {
+ struct mailfolder * folder;
+#if 0
+ struct message_folder_finder * msg_folder_finder;
+#endif
+
+ /* msg => msg_ref_info */
+ chash * msg_hash;
+
+ /* uid => msg */
+ chash * uid_hash;
+
+ int lost_session;
+};
+
+static struct folder_ref_info *
+folder_ref_info_new(struct mailfolder * folder
+ /*, struct message_folder_finder * msg_folder_finder */)
+{
+ struct folder_ref_info * ref_info;
+
+ ref_info = malloc(sizeof(* ref_info));
+ if (ref_info == NULL)
+ goto err;
+
+ ref_info->folder = folder;
+#if 0
+ ref_info->msg_folder_finder = msg_folder_finder;
+#endif
+
+ ref_info->msg_hash = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
+ if (ref_info->msg_hash == NULL)
+ goto free;
+
+ ref_info->uid_hash = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYNONE);
+ if (ref_info->uid_hash == NULL)
+ goto free_msg_hash;
+
+ ref_info->lost_session = 1;
+
+ return ref_info;
+
+ free_msg_hash:
+ chash_free(ref_info->msg_hash);
+ free:
+ free(ref_info);
+ err:
+ return NULL;
+}
+
+static void folder_ref_info_free(struct folder_ref_info * ref_info)
+{
+ chash_free(ref_info->uid_hash);
+ chash_free(ref_info->msg_hash);
+ free(ref_info);
+}
+
+static struct message_ref_elt *
+folder_info_get_msg_ref(struct folder_ref_info * ref_info, mailmessage * msg)
+{
+ chashdatum key;
+ chashdatum data;
+ struct message_ref_elt * ref_elt;
+ int r;
+
+ key.data = &msg;
+ key.len = sizeof(msg);
+ r = chash_get(ref_info->msg_hash, &key, &data);
+ if (r < 0)
+ return NULL;
+
+ ref_elt = data.data;
+
+ return ref_elt;
+}
+
+static mailmessage *
+folder_info_get_msg_by_uid(struct folder_ref_info * ref_info,
+ char * uid)
+{
+ chashdatum key;
+ chashdatum data;
+ mailmessage * msg;
+ int r;
+
+ key.data = uid;
+ key.len = strlen(uid);
+ r = chash_get(ref_info->uid_hash, &key, &data);
+ if (r < 0)
+ return NULL;
+
+ msg = data.data;
+
+ return msg;
+}
+
+static int folder_message_ref(struct folder_ref_info * ref_info,
+ mailmessage * msg)
+{
+ struct message_ref_elt * msg_ref;
+
+ msg_ref = folder_info_get_msg_ref(ref_info, msg);
+ return message_ref(msg_ref);
+}
+
+static void folder_message_remove(struct folder_ref_info * ref_info,
+ mailmessage * msg);
+
+#ifdef DEBUG_ENGINE
+#include "etpan-app.h"
+
+void * engine_app = NULL;
+#endif
+
+static int folder_message_unref(struct folder_ref_info * ref_info,
+ mailmessage * msg)
+{
+ struct message_ref_elt * msg_ref;
+ int count;
+
+ msg_ref = folder_info_get_msg_ref(ref_info, msg);
+
+ if (msg_ref->ref_count == 0) {
+#ifdef ETPAN_APP_DEBUG
+ ETPAN_APP_DEBUG((engine_app, "** BUG detected negative ref count !"));
+#endif
+ }
+
+ count = message_unref(msg_ref);
+ if (count == 0) {
+ folder_message_remove(ref_info, msg);
+ mailmessage_free(msg);
+ }
+
+ return count;
+}
+
+static int folder_message_mime_ref(struct mailprivacy * privacy,
+ struct folder_ref_info * ref_info,
+ mailmessage * msg)
+{
+ struct message_ref_elt * msg_ref;
+
+ msg_ref = folder_info_get_msg_ref(ref_info, msg);
+
+ return message_mime_ref(privacy, msg_ref);
+}
+
+static int folder_message_mime_unref(struct mailprivacy * privacy,
+ struct folder_ref_info * ref_info,
+ mailmessage * msg)
+{
+ struct message_ref_elt * msg_ref;
+
+ msg_ref = folder_info_get_msg_ref(ref_info, msg);
+ return message_mime_unref(privacy, msg_ref);
+}
+
+static int folder_message_add(struct folder_ref_info * ref_info,
+ mailmessage * msg)
+{
+ chashdatum key;
+ chashdatum data;
+ struct message_ref_elt * msg_ref;
+ int r;
+
+ /*
+ r = message_folder_finder_store(ref_info->msg_folder_finder,
+ msg, ref_info->folder);
+ if (r != MAIL_NO_ERROR)
+ goto err;
+ */
+
+ msg_ref = message_ref_elt_new(ref_info->folder, msg);
+ if (msg_ref == NULL)
+ goto msg_folder_remove;
+
+ key.data = &msg;
+ key.len = sizeof(msg);
+ data.data = msg_ref;
+ data.len = 0;
+
+ r = chash_set(ref_info->msg_hash, &key, &data, NULL);
+ if (r < 0)
+ goto free_msg_ref;
+
+ if (msg->msg_uid != NULL) {
+ key.data = msg->msg_uid;
+ key.len = strlen(msg->msg_uid);
+ data.data = msg;
+ data.len = 0;
+
+ r = chash_set(ref_info->uid_hash, &key, &data, NULL);
+ if (r < 0)
+ goto remove_msg_ref;
+ }
+
+ return MAIL_NO_ERROR;
+
+ remove_msg_ref:
+ key.data = &msg;
+ key.len = sizeof(msg);
+ chash_delete(ref_info->msg_hash, &key, NULL);
+ free_msg_ref:
+ message_ref_elt_free(msg_ref);
+ msg_folder_remove:
+ /*
+ message_folder_finder_delete(ref_info->msg_folder_finder, msg);
+ */
+ err:
+ return MAIL_ERROR_MEMORY;
+}
+
+
+static void folder_message_remove(struct folder_ref_info * ref_info,
+ mailmessage * msg)
+{
+ chashdatum key;
+ struct message_ref_elt * msg_ref;
+
+ if (msg->msg_uid != NULL) {
+ key.data = msg->msg_uid;
+ key.len = strlen(msg->msg_uid);
+
+ chash_delete(ref_info->uid_hash, &key, NULL);
+ }
+
+ msg_ref = folder_info_get_msg_ref(ref_info, msg);
+ message_ref_elt_free(msg_ref);
+
+ key.data = &msg;
+ key.len = sizeof(msg);
+
+ chash_delete(ref_info->msg_hash, &key, NULL);
+
+ /*
+ message_folder_finder_delete(ref_info->msg_folder_finder, msg);
+ */
+}
+
+
+static int folder_update_msg_list(struct folder_ref_info * ref_info,
+ struct mailmessage_list ** p_new_msg_list,
+ struct mailmessage_list ** p_lost_msg_list)
+{
+ int r;
+ int res;
+ struct mailmessage_list * new_env_list;
+ unsigned int i;
+ carray * lost_msg_tab;
+ struct mailmessage_list * lost_msg_list;
+ unsigned int free_start_index;
+ chashiter * iter;
+ unsigned int lost_count;
+
+ r = mailfolder_get_messages_list(ref_info->folder, &new_env_list);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ for(iter = chash_begin(ref_info->msg_hash) ; iter != NULL ;
+ iter = chash_next(ref_info->msg_hash, iter)) {
+ struct message_ref_elt * msg_ref;
+ chashdatum data;
+
+ chash_value(iter, &data);
+ msg_ref = data.data;
+ msg_ref->lost = 1;
+ }
+
+ lost_count = chash_count(ref_info->msg_hash);
+
+ for(i = 0 ; i < carray_count(new_env_list->msg_tab) ; i ++) {
+ mailmessage * msg;
+ mailmessage * old_msg;
+
+ msg = carray_get(new_env_list->msg_tab, i);
+
+ if (msg->msg_uid == NULL)
+ continue;
+
+ old_msg = folder_info_get_msg_by_uid(ref_info, msg->msg_uid);
+ if (old_msg != NULL) {
+ struct message_ref_elt * msg_ref;
+
+ /* replace old message */
+ old_msg->msg_index = msg->msg_index;
+ carray_set(new_env_list->msg_tab, i, old_msg);
+ mailmessage_free(msg);
+
+ msg_ref = folder_info_get_msg_ref(ref_info, old_msg);
+ msg_ref->lost = 0;
+ lost_count --;
+ }
+ else {
+ /* set new message */
+ r = folder_message_add(ref_info, msg);
+ if (r != MAIL_NO_ERROR) {
+ free_start_index = i;
+ res = r;
+ goto free_remaining;
+ }
+ }
+ }
+
+ /* build the table of lost messages */
+ lost_msg_tab = carray_new(lost_count);
+ if (lost_msg_tab == NULL) {
+ res = MAIL_ERROR_MEMORY;
+ goto free_env_list;
+ }
+
+ carray_set_size(lost_msg_tab, lost_count);
+
+ i = 0;
+ for(iter = chash_begin(ref_info->msg_hash) ; iter != NULL ;
+ iter = chash_next(ref_info->msg_hash, iter)) {
+ struct message_ref_elt * msg_ref;
+ chashdatum key;
+ chashdatum value;
+ mailmessage * msg;
+
+ chash_key(iter, &key);
+ memcpy(&msg, key.data, sizeof(msg));
+
+ chash_value(iter, &value);
+ msg_ref = value.data;
+ if (msg_ref->lost) {
+ carray_set(lost_msg_tab, i, msg);
+ i ++;
+ }
+ }
+
+ lost_msg_list = mailmessage_list_new(lost_msg_tab);
+ if (lost_msg_list == NULL) {
+ res = MAIL_ERROR_MEMORY;
+ goto free_lost_msg_tab;
+ }
+
+ /* reference messages */
+ for(i = 0 ; i < carray_count(new_env_list->msg_tab) ; i ++) {
+ mailmessage * msg;
+
+ msg = carray_get(new_env_list->msg_tab, i);
+ folder_message_ref(ref_info, msg);
+ }
+
+ * p_new_msg_list = new_env_list;
+ * p_lost_msg_list = lost_msg_list;
+
+ return MAIL_NO_ERROR;
+
+ free_lost_msg_tab:
+ carray_free(lost_msg_tab);
+ free_env_list:
+ for(i = 0 ; i < carray_count(new_env_list->msg_tab) ; i ++) {
+ mailmessage * msg;
+ struct message_ref_elt * msg_ref;
+
+ msg = carray_get(new_env_list->msg_tab, i);
+ msg_ref = folder_info_get_msg_ref(ref_info, msg);
+ if (msg_ref != NULL) {
+ if (msg_ref->ref_count == 0)
+ folder_message_remove(ref_info, msg);
+ }
+ }
+ carray_set_size(new_env_list->msg_tab, 0);
+ mailmessage_list_free(new_env_list);
+ goto err;
+ free_remaining:
+ for(i = 0 ; i < carray_count(new_env_list->msg_tab) ; i ++) {
+ mailmessage * msg;
+ struct message_ref_elt * msg_ref;
+
+ msg = carray_get(new_env_list->msg_tab, i);
+ msg_ref = folder_info_get_msg_ref(ref_info, msg);
+ if (msg_ref != NULL) {
+ if (msg_ref->ref_count == 0)
+ folder_message_remove(ref_info, msg);
+ }
+ }
+ for(i = free_start_index ; i < carray_count(new_env_list->msg_tab) ; i ++) {
+ mailmessage * msg;
+
+ msg = carray_get(new_env_list->msg_tab, i);
+ mailmessage_free(msg);
+ }
+ carray_set_size(new_env_list->msg_tab, 0);
+ mailmessage_list_free(new_env_list);
+ err:
+ return res;
+}
+
+/*
+ folder_fetch_env_list()
+*/
+
+static int folder_fetch_env_list(struct folder_ref_info * ref_info,
+ struct mailmessage_list * msg_list)
+{
+ return mailfolder_get_envelopes_list(ref_info->folder, msg_list);
+}
+
+static void folder_free_msg_list(struct folder_ref_info * ref_info,
+ struct mailmessage_list * env_list)
+{
+ unsigned int i;
+
+ for(i = 0 ; i < carray_count(env_list->msg_tab) ; i ++) {
+ mailmessage * msg;
+ int count;
+
+ msg = carray_get(env_list->msg_tab, i);
+
+ count = folder_message_unref(ref_info, msg);
+ }
+ carray_set_size(env_list->msg_tab, 0);
+ mailmessage_list_free(env_list);
+}
+
+
+/* ************************************************************* */
+/* Storage ref info */
+
+struct storage_ref_info {
+ struct mailstorage * storage;
+#if 0
+ struct message_folder_finder * msg_folder_finder;
+#endif
+
+#if 0
+ /* msg => folder */
+ chash * msg_ref;
+#endif
+
+ /* folder => folder_ref_info */
+ chash * folder_ref_info;
+};
+
+static struct storage_ref_info *
+storage_ref_info_new(struct mailstorage * storage
+ /*, struct message_folder_finder * msg_folder_finder */)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = malloc(sizeof(* ref_info));
+ if (ref_info == NULL)
+ goto err;
+
+ ref_info->storage = storage;
+#if 0
+ ref_info->msg_folder_finder = msg_folder_finder;
+#endif
+
+ ref_info->folder_ref_info = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
+ if (ref_info->folder_ref_info == NULL)
+ goto free;
+
+ return ref_info;
+
+ free:
+ free(ref_info);
+ err:
+ return NULL;
+}
+
+static void storage_ref_info_free(struct storage_ref_info * ref_info)
+{
+#if 0
+ chash_free(ref_info->msg_ref);
+#endif
+ chash_free(ref_info->folder_ref_info);
+ free(ref_info);
+}
+
+
+static struct folder_ref_info *
+storage_get_folder_ref(struct storage_ref_info * ref_info,
+ struct mailfolder * folder)
+{
+ struct folder_ref_info * folder_ref;
+ chashdatum key;
+ chashdatum value;
+ int r;
+
+ key.data = &folder;
+ key.len = sizeof(folder);
+ r = chash_get(ref_info->folder_ref_info, &key, &value);
+ if (r < 0)
+ return NULL;
+
+ folder_ref = value.data;
+
+ return folder_ref;
+}
+
+static struct folder_ref_info *
+storage_folder_add_ref(struct storage_ref_info * ref_info,
+ struct mailfolder * folder)
+{
+ struct folder_ref_info * folder_ref;
+ chashdatum key;
+ chashdatum value;
+ int r;
+
+ folder_ref = folder_ref_info_new(folder /*, ref_info->msg_folder_finder */);
+ if (folder_ref == NULL)
+ goto err;
+
+ key.data = &folder;
+ key.len = sizeof(folder);
+ value.data = folder_ref;
+ value.len = 0;
+ r = chash_set(ref_info->folder_ref_info, &key, &value, NULL);
+ if (r < 0)
+ goto free;
+
+ return folder_ref;
+
+ free:
+ folder_ref_info_free(folder_ref);
+ err:
+ return NULL;
+}
+
+
+static void storage_folder_remove_ref(struct storage_ref_info * ref_info,
+ struct mailfolder * folder)
+{
+ struct folder_ref_info * folder_ref;
+ chashdatum key;
+ chashdatum value;
+ int r;
+
+ key.data = &folder;
+ key.len = sizeof(folder);
+ r = chash_get(ref_info->folder_ref_info, &key, &value);
+ if (r < 0)
+ return;
+
+ folder_ref = value.data;
+
+ if (folder_ref == NULL)
+ return;
+
+ folder_ref_info_free(folder_ref);
+
+ chash_delete(ref_info->folder_ref_info, &key, &value);
+}
+
+static int storage_folder_get_msg_list(struct storage_ref_info * ref_info,
+ struct mailfolder * folder,
+ struct mailmessage_list ** p_new_msg_list,
+ struct mailmessage_list ** p_lost_msg_list)
+{
+ struct folder_ref_info * folder_ref_info;
+
+ folder_ref_info = storage_get_folder_ref(ref_info, folder);
+ if (folder_ref_info == NULL)
+ return MAIL_ERROR_INVAL;
+
+ return folder_update_msg_list(folder_ref_info,
+ p_new_msg_list, p_lost_msg_list);
+}
+
+static int storage_folder_fetch_env_list(struct storage_ref_info * ref_info,
+ struct mailfolder * folder,
+ struct mailmessage_list * msg_list)
+{
+ struct folder_ref_info * folder_ref_info;
+
+ folder_ref_info = storage_get_folder_ref(ref_info, folder);
+ if (folder_ref_info == NULL)
+ return MAIL_ERROR_INVAL;
+
+ return folder_fetch_env_list(folder_ref_info, msg_list);
+}
+
+static void
+storage_folder_free_msg_list(struct storage_ref_info * ref_info,
+ struct mailfolder * folder,
+ struct mailmessage_list * env_list)
+{
+ struct folder_ref_info * folder_ref_info;
+
+ folder_ref_info = storage_get_folder_ref(ref_info, folder);
+
+ folder_free_msg_list(folder_ref_info, env_list);
+}
+
+
+/* connection and disconnection */
+
+static void
+folder_restore_session(struct folder_ref_info * ref_info)
+{
+ chashiter * iter;
+ mailsession * session;
+
+ session = ref_info->folder->fld_session;
+
+ for(iter = chash_begin(ref_info->msg_hash) ; iter != NULL ;
+ iter = chash_next(ref_info->msg_hash, iter)) {
+ chashdatum key;
+ mailmessage * msg;
+
+ chash_key(iter, &key);
+ memcpy(&msg, key.data, sizeof(msg));
+ msg->msg_session = session;
+
+ if (msg->msg_driver == imap_cached_message_driver) {
+ struct imap_cached_session_state_data * imap_cached_data;
+ mailmessage * ancestor_msg;
+
+ imap_cached_data = ref_info->folder->fld_session->sess_data;
+ ancestor_msg = msg->msg_data;
+ ancestor_msg->msg_session = imap_cached_data->imap_ancestor;
+ }
+ }
+}
+
+static void
+storage_restore_message_session(struct storage_ref_info * ref_info)
+{
+ chashiter * iter;
+
+ for(iter = chash_begin(ref_info->folder_ref_info) ; iter != NULL ;
+ iter = chash_next(ref_info->folder_ref_info, iter)) {
+ chashdatum data;
+ struct folder_ref_info * folder_ref_info;
+
+ chash_value(iter, &data);
+ folder_ref_info = data.data;
+ if (folder_ref_info->lost_session) {
+ if (folder_ref_info->folder->fld_session != NULL) {
+ /* restore folder session */
+ folder_restore_session(folder_ref_info);
+
+ folder_ref_info->lost_session = 0;
+ }
+ }
+ }
+}
+
+
+static int do_storage_connect(struct storage_ref_info * ref_info)
+{
+ int r;
+
+ r = mailstorage_connect(ref_info->storage);
+ if (r != MAIL_NO_ERROR)
+ return r;
+
+ return MAIL_NO_ERROR;
+}
+
+static void do_storage_disconnect(struct storage_ref_info * ref_info)
+{
+ clistiter * cur;
+
+ /* storage is disconnected, session is lost */
+ for(cur = clist_begin(ref_info->storage->sto_shared_folders) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ struct folder_ref_info * folder_ref_info;
+ struct mailfolder * folder;
+
+ folder = clist_content(cur);
+ /* folder is disconnected (in storage), session is lost */
+
+ folder_ref_info = storage_get_folder_ref(ref_info, folder);
+ folder_ref_info->lost_session = 1;
+ }
+
+ /* storage is disconnected */
+ mailstorage_disconnect(ref_info->storage);
+}
+
+
+
+static int folder_connect(struct storage_ref_info * ref_info,
+ struct mailfolder * folder)
+{
+ int r;
+ struct folder_ref_info * folder_ref_info;
+
+ r = do_storage_connect(ref_info);
+ if (r != MAIL_NO_ERROR)
+ return r;
+
+ r = mailfolder_connect(folder);
+ if (r != MAIL_NO_ERROR)
+ return r;
+
+ folder_ref_info = storage_get_folder_ref(ref_info, folder);
+
+ return MAIL_NO_ERROR;
+}
+
+
+static void folder_disconnect(struct storage_ref_info * ref_info,
+ struct mailfolder * folder)
+{
+ struct folder_ref_info * folder_ref_info;
+
+ folder_ref_info = storage_get_folder_ref(ref_info, folder);
+
+ /* folder is disconnected, session is lost */
+ folder_ref_info->lost_session = 1;
+ mailfolder_disconnect(folder);
+
+ if (folder->fld_shared_session)
+ do_storage_disconnect(ref_info);
+}
+
+
+static int storage_folder_connect(struct storage_ref_info * ref_info,
+ struct mailfolder * folder)
+{
+ int r;
+ int res;
+ struct folder_ref_info * folder_ref_info;
+
+ folder_ref_info = storage_get_folder_ref(ref_info, folder);
+ if (folder_ref_info == NULL) {
+ folder_ref_info = storage_folder_add_ref(ref_info, folder);
+ if (folder_ref_info == NULL)
+ return MAIL_ERROR_MEMORY;
+ }
+
+ /* connect folder */
+
+ r = folder_connect(ref_info, folder);
+ if (r == MAIL_ERROR_STREAM) {
+ /* properly handles disconnection */
+
+ /* reconnect */
+ folder_disconnect(ref_info, folder);
+ r = folder_connect(ref_info, folder);
+ }
+
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ /* test folder connection */
+ r = mailfolder_noop(folder);
+ if (r == MAIL_ERROR_STREAM) {
+ /* reconnect */
+ folder_disconnect(ref_info, folder);
+ r = folder_connect(ref_info, folder);
+ }
+
+ if ((r != MAIL_ERROR_NOT_IMPLEMENTED) && (r != MAIL_NO_ERROR)) {
+ res = r;
+ goto disconnect;
+ }
+
+ storage_restore_message_session(ref_info);
+
+ return MAIL_NO_ERROR;
+
+ disconnect:
+ folder_disconnect(ref_info, folder);
+ err:
+ return res;
+}
+
+static void storage_folder_disconnect(struct storage_ref_info * ref_info,
+ struct mailfolder * folder)
+{
+ mailfolder_disconnect(folder);
+ storage_folder_remove_ref(ref_info, folder);
+}
+
+static int storage_connect(struct storage_ref_info * ref_info)
+{
+ int r;
+ int res;
+
+ /* connect storage */
+
+ /* properly handles disconnection */
+ r = do_storage_connect(ref_info);
+ if (r == MAIL_ERROR_STREAM) {
+ /* reconnect storage */
+ do_storage_disconnect(ref_info);
+ r = do_storage_connect(ref_info);
+ }
+
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto disconnect;
+ }
+
+ /* test storage connection */
+
+ r = mailsession_noop(ref_info->storage->sto_session);
+ if ((r != MAIL_ERROR_NOT_IMPLEMENTED) && (r != MAIL_NO_ERROR)) {
+ /* properly handles disconnection */
+
+ /* reconnect storage */
+ do_storage_disconnect(ref_info);
+ r = do_storage_connect(ref_info);
+ }
+
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto disconnect;
+ }
+
+ storage_restore_message_session(ref_info);
+
+ return MAIL_NO_ERROR;
+
+ disconnect:
+ do_storage_disconnect(ref_info);
+ return res;
+}
+
+
+static void storage_disconnect(struct storage_ref_info * ref_info)
+{
+ chashiter * iter;
+
+ /* disconnect folders */
+ while ((iter = chash_begin(ref_info->folder_ref_info)) != NULL) {
+ chashdatum key;
+ struct mailfolder * folder;
+
+ chash_key(iter, &key);
+ memcpy(&folder, key.data, sizeof(folder));
+
+ storage_folder_disconnect(ref_info, folder);
+ }
+
+ /* disconnect storage */
+ do_storage_disconnect(ref_info);
+}
+
+
+/* ************************************************************* */
+/* interface for mailengine */
+
+struct mailengine {
+ struct mailprivacy * privacy;
+
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_t storage_hash_lock;
+#endif
+ /* storage => storage_ref_info */
+ chash * storage_hash;
+
+#if 0
+ struct message_folder_finder msg_folder_finder;
+#endif
+};
+
+static struct storage_ref_info *
+get_storage_ref_info(struct mailengine * engine,
+ struct mailstorage * storage)
+{
+ chashdatum key;
+ chashdatum data;
+ int r;
+ struct storage_ref_info * ref_info;
+
+ key.data = &storage;
+ key.len = sizeof(storage);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_lock(&engine->storage_hash_lock);
+#endif
+ r = chash_get(engine->storage_hash, &key, &data);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_unlock(&engine->storage_hash_lock);
+#endif
+ if (r < 0)
+ return NULL;
+
+ ref_info = data.data;
+
+ return ref_info;
+}
+
+static struct storage_ref_info *
+add_storage_ref_info(struct mailengine * engine,
+ struct mailstorage * storage)
+{
+ chashdatum key;
+ chashdatum data;
+ int r;
+ struct storage_ref_info * ref_info;
+
+ ref_info = storage_ref_info_new(storage
+ /* , &engine->msg_folder_finder */);
+ if (ref_info == NULL)
+ goto err;
+
+ key.data = &storage;
+ key.len = sizeof(storage);
+ data.data = ref_info;
+ data.len = 0;
+
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_lock(&engine->storage_hash_lock);
+#endif
+ r = chash_set(engine->storage_hash, &key, &data, NULL);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_unlock(&engine->storage_hash_lock);
+#endif
+ if (r < 0)
+ goto free;
+
+ ref_info = data.data;
+
+ return ref_info;
+
+ free:
+ storage_ref_info_free(ref_info);
+ err:
+ return NULL;
+}
+
+static void
+remove_storage_ref_info(struct mailengine * engine,
+ struct mailstorage * storage)
+{
+ chashdatum key;
+ chashdatum data;
+ struct storage_ref_info * ref_info;
+
+ key.data = &storage;
+ key.len = sizeof(storage);
+
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_lock(&engine->storage_hash_lock);
+#endif
+
+ chash_get(engine->storage_hash, &key, &data);
+ ref_info = data.data;
+
+ if (ref_info != NULL) {
+ storage_ref_info_free(ref_info);
+
+ chash_delete(engine->storage_hash, &key, NULL);
+ }
+
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_unlock(&engine->storage_hash_lock);
+#endif
+}
+
+struct mailengine *
+libetpan_engine_new(struct mailprivacy * privacy)
+{
+ struct mailengine * engine;
+ int r;
+
+ engine = malloc(sizeof(* engine));
+ if (engine == NULL)
+ goto err;
+
+ engine->privacy = privacy;
+
+#if 0
+ r = message_folder_finder_init(&engine->msg_folder_finder);
+ if (r != MAIL_NO_ERROR)
+ goto free;
+#endif
+
+#ifdef LIBETPAN_REENTRANT
+ r = pthread_mutex_init(&engine->storage_hash_lock, NULL);
+ if (r != 0)
+ goto free;
+#endif
+
+ engine->storage_hash = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
+ if (engine->storage_hash == NULL)
+ goto destroy_mutex;
+
+ return engine;
+
+ destroy_mutex:
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_destroy(&engine->storage_hash_lock);
+#endif
+#if 0
+ finder_done:
+ message_folder_finder_done(&engine->msg_folder_finder);
+#endif
+ free:
+ free(engine);
+ err:
+ return NULL;
+}
+
+void libetpan_engine_free(struct mailengine * engine)
+{
+ chash_free(engine->storage_hash);
+#ifdef LIBETPAN_REENTRANT
+ pthread_mutex_destroy(&engine->storage_hash_lock);
+#endif
+#if 0
+ message_folder_finder_done(&engine->msg_folder_finder);
+#endif
+ free(engine);
+}
+
+#if 0
+static struct folder_ref_info *
+message_get_folder_ref(struct mailengine * engine,
+ mailmessage * msg)
+{
+ struct mailfolder * folder;
+ struct mailstorage * storage;
+ struct storage_ref_info * storage_ref_info;
+ struct folder_ref_info * folder_ref_info;
+
+ folder = message_folder_finder_lookup(&engine->msg_folder_finder, msg);
+ if (folder == NULL)
+ storage = NULL;
+ else
+ storage = folder->fld_storage;
+
+ storage_ref_info = get_storage_ref_info(engine, storage);
+
+ folder_ref_info = storage_get_folder_ref(storage_ref_info, folder);
+
+ return folder_ref_info;
+}
+#endif
+
+static struct folder_ref_info *
+message_get_folder_ref(struct mailengine * engine,
+ mailmessage * msg)
+{
+ struct mailfolder * folder;
+ struct mailstorage * storage;
+ struct storage_ref_info * storage_ref_info;
+ struct folder_ref_info * folder_ref_info;
+
+ folder = msg->msg_folder;
+ if (folder == NULL)
+ storage = NULL;
+ else
+ storage = folder->fld_storage;
+
+ storage_ref_info = get_storage_ref_info(engine, storage);
+
+ folder_ref_info = storage_get_folder_ref(storage_ref_info, folder);
+
+ return folder_ref_info;
+}
+
+int libetpan_message_ref(struct mailengine * engine,
+ mailmessage * msg)
+{
+ struct folder_ref_info * ref_info;
+
+ ref_info = message_get_folder_ref(engine, msg);
+
+ return folder_message_ref(ref_info, msg);
+}
+
+int libetpan_message_unref(struct mailengine * engine,
+ mailmessage * msg)
+{
+ struct folder_ref_info * ref_info;
+
+ ref_info = message_get_folder_ref(engine, msg);
+
+ return folder_message_unref(ref_info, msg);
+}
+
+
+int libetpan_message_mime_ref(struct mailengine * engine,
+ mailmessage * msg)
+{
+ struct folder_ref_info * ref_info;
+
+ ref_info = message_get_folder_ref(engine, msg);
+
+ return folder_message_mime_ref(engine->privacy, ref_info, msg);
+}
+
+int libetpan_message_mime_unref(struct mailengine * engine,
+ mailmessage * msg)
+{
+ struct folder_ref_info * ref_info;
+
+ ref_info = message_get_folder_ref(engine, msg);
+
+ return folder_message_mime_unref(engine->privacy, ref_info, msg);
+}
+
+int libetpan_folder_get_msg_list(struct mailengine * engine,
+ struct mailfolder * folder,
+ struct mailmessage_list ** p_new_msg_list,
+ struct mailmessage_list ** p_lost_msg_list)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = get_storage_ref_info(engine, folder->fld_storage);
+
+ return storage_folder_get_msg_list(ref_info, folder,
+ p_new_msg_list, p_lost_msg_list);
+}
+
+int libetpan_folder_fetch_env_list(struct mailengine * engine,
+ struct mailfolder * folder,
+ struct mailmessage_list * msg_list)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = get_storage_ref_info(engine, folder->fld_storage);
+
+ return storage_folder_fetch_env_list(ref_info, folder, msg_list);
+}
+
+void libetpan_folder_free_msg_list(struct mailengine * engine,
+ struct mailfolder * folder,
+ struct mailmessage_list * env_list)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = get_storage_ref_info(engine, folder->fld_storage);
+
+ storage_folder_free_msg_list(ref_info, folder, env_list);
+}
+
+
+int libetpan_storage_add(struct mailengine * engine,
+ struct mailstorage * storage)
+{
+ struct storage_ref_info * storage_ref_info;
+ struct folder_ref_info * folder_ref_info;
+
+ storage_ref_info = add_storage_ref_info(engine, storage);
+ if (storage_ref_info == NULL)
+ goto err;
+
+ if (storage == NULL) {
+ folder_ref_info = storage_folder_add_ref(storage_ref_info, NULL);
+ if (folder_ref_info == NULL)
+ goto remove_storage_ref_info;
+ }
+
+ return MAIL_NO_ERROR;
+
+ remove_storage_ref_info:
+ remove_storage_ref_info(engine, storage);
+ err:
+ return MAIL_ERROR_MEMORY;
+}
+
+void libetpan_storage_remove(struct mailengine * engine,
+ struct mailstorage * storage)
+{
+ struct storage_ref_info * storage_ref_info;
+
+ storage_ref_info = get_storage_ref_info(engine, storage);
+ if (storage == NULL) {
+ storage_folder_remove_ref(storage_ref_info, NULL);
+ }
+
+ remove_storage_ref_info(engine, storage);
+}
+
+int libetpan_storage_connect(struct mailengine * engine,
+ struct mailstorage * storage)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = get_storage_ref_info(engine, storage);
+
+ return storage_connect(ref_info);
+}
+
+
+void libetpan_storage_disconnect(struct mailengine * engine,
+ struct mailstorage * storage)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = get_storage_ref_info(engine, storage);
+
+ storage_disconnect(ref_info);
+}
+
+int libetpan_storage_used(struct mailengine * engine,
+ struct mailstorage * storage)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = get_storage_ref_info(engine, storage);
+
+ return (chash_count(ref_info->folder_ref_info) != 0);
+}
+
+
+int libetpan_folder_connect(struct mailengine * engine,
+ struct mailfolder * folder)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = get_storage_ref_info(engine, folder->fld_storage);
+
+ return storage_folder_connect(ref_info, folder);
+}
+
+
+void libetpan_folder_disconnect(struct mailengine * engine,
+ struct mailfolder * folder)
+{
+ struct storage_ref_info * ref_info;
+
+ ref_info = get_storage_ref_info(engine, folder->fld_storage);
+
+ storage_folder_disconnect(ref_info, folder);
+}
+
+
+struct mailfolder *
+libetpan_message_get_folder(struct mailengine * engine,
+ mailmessage * msg)
+{
+#if 0
+ return message_folder_finder_lookup(&engine->msg_folder_finder, msg);
+#endif
+ return msg->msg_folder;
+}
+
+
+struct mailstorage *
+libetpan_message_get_storage(struct mailengine * engine,
+ mailmessage * msg)
+{
+ struct mailfolder * folder;
+
+ folder = libetpan_message_get_folder(engine, msg);
+
+ if (folder == NULL)
+ return NULL;
+ else
+ return folder->fld_storage;
+}
+
+
+int libetpan_message_register(struct mailengine * engine,
+ struct mailfolder * folder,
+ mailmessage * msg)
+{
+ struct storage_ref_info * storage_ref_info;
+ int r;
+ int res;
+ struct folder_ref_info * folder_ref_info;
+ struct mailstorage * storage;
+
+#if 0
+ r = message_folder_finder_store(&engine->msg_folder_finder, msg, folder);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+#endif
+
+ if (folder != NULL)
+ storage = folder->fld_storage;
+ else
+ storage = NULL;
+
+ storage_ref_info = get_storage_ref_info(engine, storage);
+
+ folder_ref_info = storage_get_folder_ref(storage_ref_info, folder);
+
+ r = folder_message_add(folder_ref_info, msg);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto delete;
+ }
+
+ return MAIL_NO_ERROR;
+
+ delete:
+#if 0
+ message_folder_finder_delete(&engine->msg_folder_finder, msg);
+#endif
+ err:
+ return res;
+}
+
+struct mailprivacy *
+libetpan_engine_get_privacy(struct mailengine * engine)
+{
+ return engine->privacy;
+}
+
+
+static void folder_debug(struct folder_ref_info * folder_ref_info, FILE * f)
+{
+ fprintf(f, "folder debug -- begin\n");
+ if (folder_ref_info->folder == NULL) {
+ fprintf(f, "NULL folder\n");
+ }
+ else {
+ if (folder_ref_info->folder->fld_virtual_name != NULL)
+ fprintf(f, "folder %s\n", folder_ref_info->folder->fld_virtual_name);
+ else
+ fprintf(f, "folder [no name]\n");
+ }
+
+ fprintf(f, "message count: %i\n", chash_count(folder_ref_info->msg_hash));
+ fprintf(f, "UID count: %i\n", chash_count(folder_ref_info->uid_hash));
+ fprintf(f, "folder debug -- end\n");
+}
+
+static void storage_debug(struct storage_ref_info * storage_ref_info, FILE * f)
+{
+ chashiter * iter;
+
+ fprintf(f, "storage debug -- begin\n");
+ if (storage_ref_info->storage == NULL) {
+ fprintf(f, "NULL storage\n");
+ }
+ else {
+ if (storage_ref_info->storage->sto_id != NULL)
+ fprintf(f, "storage %s\n", storage_ref_info->storage->sto_id);
+ else
+ fprintf(f, "storage [no name]\n");
+ }
+ fprintf(f, "folder count: %i\n",
+ chash_count(storage_ref_info->folder_ref_info));
+
+ for(iter = chash_begin(storage_ref_info->folder_ref_info) ; iter != NULL ;
+ iter = chash_next(storage_ref_info->folder_ref_info, iter)) {
+ chashdatum data;
+ struct folder_ref_info * folder_ref_info;
+
+ chash_value(iter, &data);
+ folder_ref_info = data.data;
+
+ folder_debug(folder_ref_info, f);
+ }
+ fprintf(f, "storage debug -- end\n");
+}
+
+void libetpan_engine_debug(struct mailengine * engine, FILE * f)
+{
+ chashiter * iter;
+
+ fprintf(f, "mail engine debug -- begin\n");
+
+ for(iter = chash_begin(engine->storage_hash) ; iter != NULL ;
+ iter = chash_next(engine->storage_hash, iter)) {
+ chashdatum data;
+ struct storage_ref_info * storage_ref_info;
+
+ chash_value(iter, &data);
+ storage_ref_info = data.data;
+
+ storage_debug(storage_ref_info, f);
+ }
+
+#if 0
+ fprintf(f, "global message references: %i\n",
+ chash_count(engine->msg_folder_finder.message_hash));
+#endif
+ fprintf(f, "mail engine debug -- end\n");
+}
+
diff --git a/libetpan/src/engine/mailengine.h b/libetpan/src/engine/mailengine.h
new file mode 100644
index 0000000..acb6a16
--- a/dev/null
+++ b/libetpan/src/engine/mailengine.h
@@ -0,0 +1,190 @@
+/*
+ * libEtPan! -- a mail library
+ *
+ * Copyright (C) 2001, 2005 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef MAILENGINE_H
+
+#define MAILENGINE_H
+
+#include <libetpan/mailmessage.h>
+#include <libetpan/mailfolder.h>
+#include <libetpan/mailprivacy_types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ to run things in thread, you must protect the storage again concurrency.
+*/
+
+
+/*
+ storage data
+*/
+
+struct mailengine *
+libetpan_engine_new(struct mailprivacy * privacy);
+
+void libetpan_engine_free(struct mailengine * engine);
+
+
+struct mailprivacy *
+libetpan_engine_get_privacy(struct mailengine * engine);
+
+
+/*
+ message ref and unref
+*/
+
+/*
+ these function can only take messages returned by get_msg_list()
+ as arguments.
+
+ these functions cannot fail.
+*/
+
+int libetpan_message_ref(struct mailengine * engine,
+ mailmessage * msg);
+
+int libetpan_message_unref(struct mailengine * engine,
+ mailmessage * msg);
+
+
+/*
+ when you want to access the MIME structure of the message
+ with msg->mime, you have to call libetpan_message_mime_ref()
+ and libetpan_message_mime_unref() when you have finished.
+
+ if libetpan_mime_ref() returns a value <= 0, it means this failed.
+ the value is -MAIL_ERROR_XXX
+*/
+
+int libetpan_message_mime_ref(struct mailengine * engine,
+ mailmessage * msg);
+
+int libetpan_message_mime_unref(struct mailengine * engine,
+ mailmessage * msg);
+
+/*
+ message list
+*/
+
+/*
+ libetpan_folder_get_msg_list()
+
+ This function returns two list.
+ - List of lost message (the messages that were previously returned
+ but that does no more exist) (p_lost_msg_list)
+ - List of valid messages (p_new_msg_list).
+
+ These two list can only be freed by libetpan_folder_free_msg_list()
+*/
+
+int libetpan_folder_get_msg_list(struct mailengine * engine,
+ struct mailfolder * folder,
+ struct mailmessage_list ** p_new_msg_list,
+ struct mailmessage_list ** p_lost_msg_list);
+
+int libetpan_folder_fetch_env_list(struct mailengine * engine,
+ struct mailfolder * folder,
+ struct mailmessage_list * msg_list);
+
+void libetpan_folder_free_msg_list(struct mailengine * engine,
+ struct mailfolder * folder,
+ struct mailmessage_list * env_list);
+
+
+/*
+ connect and disconnect storage
+*/
+
+int libetpan_storage_add(struct mailengine * engine,
+ struct mailstorage * storage);
+
+void libetpan_storage_remove(struct mailengine * engine,
+ struct mailstorage * storage);
+
+int libetpan_storage_connect(struct mailengine * engine,
+ struct mailstorage * storage);
+
+void libetpan_storage_disconnect(struct mailengine * engine,
+ struct mailstorage * storage);
+
+int libetpan_storage_used(struct mailengine * engine,
+ struct mailstorage * storage);
+
+
+/*
+ libetpan_folder_connect()
+ libetpan_folder_disconnect()
+
+ You can disconnect the folder only when you have freed all the message
+ you were given.
+*/
+
+int libetpan_folder_connect(struct mailengine * engine,
+ struct mailfolder * folder);
+
+void libetpan_folder_disconnect(struct mailengine * engine,
+ struct mailfolder * folder);
+
+
+struct mailfolder *
+libetpan_message_get_folder(struct mailengine * engine,
+ mailmessage * msg);
+
+struct mailstorage *
+libetpan_message_get_storage(struct mailengine * engine,
+ mailmessage * msg);
+
+
+/*
+ register a message
+*/
+
+int libetpan_message_register(struct mailengine * engine,
+ struct mailfolder * folder,
+ mailmessage * msg);
+
+
+void libetpan_engine_debug(struct mailengine * engine, FILE * f);
+
+extern void * engine_app;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/libetpan/src/engine/mailprivacy.c b/libetpan/src/engine/mailprivacy.c
new file mode 100644
index 0000000..99cc50f
--- a/dev/null
+++ b/libetpan/src/engine/mailprivacy.c
@@ -0,0 +1,949 @@
+/*
+ * libEtPan! -- a mail library
+ *
+ * Copyright (C) 2001, 2005 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include "mailprivacy.h"
+
+#include <libetpan/libetpan.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mailprivacy_tools.h"
+
+carray * mailprivacy_get_protocols(struct mailprivacy * privacy)
+{
+ return privacy->protocols;
+}
+
+static int recursive_check_privacy(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime);
+
+static int check_tmp_dir(char * tmp_dir)
+{
+ struct stat stat_info;
+ int r;
+
+ r = stat(tmp_dir, &stat_info);
+ if (r < 0)
+ return MAIL_ERROR_FILE;
+
+ /* check if the directory belongs to the user */
+ if (stat_info.st_uid != getuid())
+ return MAIL_ERROR_INVAL;
+
+ if ((stat_info.st_mode & 00777) != 0700) {
+ r = chmod(tmp_dir, 0700);
+ if (r < 0)
+ return MAIL_ERROR_FILE;
+ }
+
+ return MAIL_NO_ERROR;
+}
+
+struct mailprivacy * mailprivacy_new(char * tmp_dir, int make_alternative)
+{
+ struct mailprivacy * privacy;
+ int r;
+
+#if 0
+ r = check_tmp_dir(tmp_dir);
+ if (r != MAIL_NO_ERROR)
+ goto err;
+#endif
+
+ privacy = malloc(sizeof(* privacy));
+ if (privacy == NULL)
+ goto err;
+
+ privacy->tmp_dir = strdup(tmp_dir);
+ if (privacy->tmp_dir == NULL)
+ goto free;
+
+ privacy->msg_ref = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
+ if (privacy->msg_ref == NULL)
+ goto free_tmp_dir;
+
+ privacy->mmapstr = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
+ if (privacy->mmapstr == NULL)
+ goto free_msg_ref;
+
+ privacy->mime_ref = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYKEY);
+ if (privacy->mime_ref == NULL)
+ goto free_mmapstr;
+
+ privacy->protocols = carray_new(16);
+ if (privacy->protocols == NULL)
+ goto free_mime_ref;
+
+ privacy->make_alternative = make_alternative;
+
+ return privacy;
+
+ free_mime_ref:
+ chash_free(privacy->mime_ref);
+ free_mmapstr:
+ chash_free(privacy->mmapstr);
+ free_msg_ref:
+ chash_free(privacy->msg_ref);
+ free_tmp_dir:
+ free(privacy->tmp_dir);
+ free:
+ free(privacy);
+ err:
+ return NULL;
+}
+
+void mailprivacy_free(struct mailprivacy * privacy)
+{
+ carray_free(privacy->protocols);
+ chash_free(privacy->mime_ref);
+ chash_free(privacy->mmapstr);
+ chash_free(privacy->msg_ref);
+ free(privacy->tmp_dir);
+ free(privacy);
+}
+
+static int msg_is_modified(struct mailprivacy * privacy,
+ mailmessage * msg)
+{
+ chashdatum key;
+ chashdatum data;
+ int r;
+
+ if (privacy == NULL)
+ return 0;
+
+ key.data = &msg;
+ key.len = sizeof(msg);
+
+ r = chash_get(privacy->msg_ref, &key, &data);
+ if (r < 0)
+ return 0;
+ else
+ return 1;
+}
+
+static int register_msg(struct mailprivacy * privacy,
+ mailmessage * msg)
+{
+ chashdatum key;
+ chashdatum data;
+ int r;
+
+ if (privacy == NULL)
+ return MAIL_NO_ERROR;
+
+ key.data = &msg;
+ key.len = sizeof(msg);
+ data.data = msg;
+ data.len = 0;
+
+ r = chash_set(privacy->msg_ref, &key, &data, NULL);
+ if (r < 0)
+ return MAIL_ERROR_MEMORY;
+ else
+ return MAIL_NO_ERROR;
+}
+
+static void unregister_message(struct mailprivacy * privacy,
+ mailmessage * msg)
+{
+ chashdatum key;
+
+ key.data = &msg;
+ key.len = sizeof(msg);
+
+ chash_delete(privacy->msg_ref, &key, NULL);
+}
+
+static int result_is_mmapstr(struct mailprivacy * privacy, char * str)
+{
+ chashdatum key;
+ chashdatum data;
+ int r;
+
+ key.data = &str;
+ key.len = sizeof(str);
+
+ r = chash_get(privacy->mmapstr, &key, &data);
+ if (r < 0)
+ return 0;
+ else
+ return 1;
+}
+
+static int register_result_mmapstr(struct mailprivacy * privacy,
+ char * content)
+{
+ chashdatum key;
+ chashdatum data;
+ int r;
+
+ key.data = &content;
+ key.len = sizeof(content);
+ data.data = content;
+ data.len = 0;
+
+ r = chash_set(privacy->mmapstr, &key, &data, NULL);
+ if (r < 0)
+ return MAIL_ERROR_MEMORY;
+
+ return 0;
+}
+
+static void unregister_result_mmapstr(struct mailprivacy * privacy,
+ char * str)
+{
+ chashdatum key;
+
+ mmap_string_unref(str);
+
+ key.data = &str;
+ key.len = sizeof(str);
+
+ chash_delete(privacy->mmapstr, &key, NULL);
+}
+
+static int register_mime(struct mailprivacy * privacy,
+ struct mailmime * mime)
+{
+ chashdatum key;
+ chashdatum data;
+ int r;
+
+ key.data = &mime;
+ key.len = sizeof(mime);
+ data.data = mime;
+ data.len = 0;
+
+ r = chash_set(privacy->mime_ref, &key, &data, NULL);
+ if (r < 0)
+ return MAIL_ERROR_MEMORY;
+ else
+ return MAIL_NO_ERROR;
+}
+
+static void unregister_mime(struct mailprivacy * privacy,
+ struct mailmime * mime)
+{
+ chashdatum key;
+
+ key.data = &mime;
+ key.len = sizeof(mime);
+
+ chash_delete(privacy->mime_ref, &key, NULL);
+}
+
+static int mime_is_registered(struct mailprivacy * privacy,
+ struct mailmime * mime)
+{
+ chashdatum key;
+ chashdatum data;
+ int r;
+
+ key.data = &mime;
+ key.len = sizeof(mime);
+
+ r = chash_get(privacy->mime_ref, &key, &data);
+ if (r < 0)
+ return 0;
+ else
+ return 1;
+}
+
+static int recursive_register_mime(struct mailprivacy * privacy,
+ struct mailmime * mime)
+{
+ clistiter * cur;
+ int r;
+
+ r = register_mime(privacy, mime);
+ if (r != MAIL_NO_ERROR)
+ return r;
+
+ switch (mime->mm_type) {
+ case MAILMIME_SINGLE:
+ break;
+
+ case MAILMIME_MULTIPLE:
+ for(cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list) ;
+ cur != NULL ; cur = clist_next(cur)) {
+ struct mailmime * child;
+
+ child = clist_content(cur);
+
+ r = recursive_register_mime(privacy, child);
+ if (r != MAIL_NO_ERROR)
+ return r;
+ }
+ break;
+
+ case MAILMIME_MESSAGE:
+ if (mime->mm_data.mm_message.mm_msg_mime) {
+ r = recursive_register_mime(privacy,
+ mime->mm_data.mm_message.mm_msg_mime);
+ if (r != MAIL_NO_ERROR)
+ return r;
+ }
+ break;
+ }
+
+ return MAIL_NO_ERROR;
+}
+
+void mailprivacy_recursive_unregister_mime(struct mailprivacy * privacy,
+ struct mailmime * mime)
+{
+ clistiter * cur;
+
+ unregister_mime(privacy, mime);
+
+ switch (mime->mm_type) {
+ case MAILMIME_SINGLE:
+ break;
+
+ case MAILMIME_MULTIPLE:
+ for(cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list) ;
+ cur != NULL ; cur = clist_next(cur)) {
+ struct mailmime * child;
+
+ child = clist_content(cur);
+
+ mailprivacy_recursive_unregister_mime(privacy, child);
+ }
+ break;
+
+ case MAILMIME_MESSAGE:
+ if (mime->mm_data.mm_message.mm_msg_mime)
+ mailprivacy_recursive_unregister_mime(privacy,
+ mime->mm_data.mm_message.mm_msg_mime);
+ break;
+ }
+}
+
+static void recursive_clear_registered_mime(struct mailprivacy * privacy,
+ struct mailmime * mime)
+{
+ clistiter * cur;
+ struct mailmime_data * data;
+
+ switch (mime->mm_type) {
+ case MAILMIME_SINGLE:
+ if (mime_is_registered(privacy, mime)) {
+ data = mime->mm_data.mm_single;
+ if (data != NULL) {
+ if (data->dt_type == MAILMIME_DATA_FILE)
+ unlink(data->dt_data.dt_filename);
+ }
+ }
+ break;
+
+ case MAILMIME_MULTIPLE:
+ if (mime_is_registered(privacy, mime)) {
+ data = mime->mm_data.mm_multipart.mm_preamble;
+ if (data != NULL) {
+ if (data->dt_type == MAILMIME_DATA_FILE)
+ unlink(data->dt_data.dt_filename);
+ }
+ data = mime->mm_data.mm_multipart.mm_epilogue;
+ if (data != NULL) {
+ if (data->dt_type == MAILMIME_DATA_FILE)
+ unlink(data->dt_data.dt_filename);
+ }
+ }
+ for(cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list) ;
+ cur != NULL ; cur = clist_next(cur)) {
+ struct mailmime * child;
+
+ child = clist_content(cur);
+
+ recursive_clear_registered_mime(privacy, child);
+ }
+ break;
+
+ case MAILMIME_MESSAGE:
+ if (mime->mm_data.mm_message.mm_msg_mime)
+ recursive_clear_registered_mime(privacy,
+ mime->mm_data.mm_message.mm_msg_mime);
+ break;
+ }
+ unregister_mime(privacy, mime);
+}
+
+
+/* **************************************************** */
+/* fetch operations start here */
+
+
+static void recursive_clear_registered_mime(struct mailprivacy * privacy,
+ struct mailmime * mime);
+
+#if 0
+static void display_recursive_part(struct mailmime * mime)
+{
+ clistiter * cur;
+
+ fprintf(stderr, "part %p\n", mime->mm_body);
+ switch (mime->mm_type) {
+ case MAILMIME_SINGLE:
+ fprintf(stderr, "single %p - %i\n", mime->mm_data.mm_single,
+ mime->mm_data.mm_single->dt_type);
+ if (mime->mm_data.mm_single->dt_type == MAILMIME_DATA_TEXT) {
+ fprintf(stderr, "data : %p %i\n",
+ mime->mm_data.mm_single->dt_data.dt_text.dt_data,
+ mime->mm_data.mm_single->dt_data.dt_text.dt_length);
+ }
+ break;
+ case MAILMIME_MESSAGE:
+ fprintf(stderr, "message %p\n", mime->mm_data.mm_message.mm_msg_mime);
+ display_recursive_part(mime->mm_data.mm_message.mm_msg_mime);
+ break;
+ case MAILMIME_MULTIPLE:
+ for(cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list) ; cur != NULL ;
+ cur = clist_next(cur)) {
+
+ fprintf(stderr, "multipart\n");
+ display_recursive_part(clist_content(cur));
+ }
+ break;
+ }
+}
+#endif
+
+int mailprivacy_msg_get_bodystructure(struct mailprivacy * privacy,
+ mailmessage * msg_info,
+ struct mailmime ** result)
+{
+ int r;
+ struct mailmime * mime;
+
+ if (msg_info->msg_mime != NULL)
+ return MAIL_NO_ERROR;
+
+ if (msg_is_modified(privacy, msg_info))
+ return MAIL_NO_ERROR;
+
+ r = mailmessage_get_bodystructure(msg_info, &mime);
+ if (r != MAIL_NO_ERROR)
+ return r;
+
+ /* modification on message if necessary */
+ r = recursive_check_privacy(privacy, msg_info, msg_info->msg_mime);
+ if (r != MAIL_NO_ERROR) {
+ * result = msg_info->msg_mime;
+ return MAIL_NO_ERROR;
+ }
+
+ r = register_msg(privacy, msg_info);
+ if (r != MAIL_NO_ERROR) {
+ recursive_clear_registered_mime(privacy, mime);
+ mailmessage_flush(msg_info);
+ return MAIL_ERROR_MEMORY;
+ }
+
+ * result = msg_info->msg_mime;
+
+ return MAIL_NO_ERROR;
+}
+
+void mailprivacy_msg_flush(struct mailprivacy * privacy,
+ mailmessage * msg_info)
+{
+ if (msg_is_modified(privacy, msg_info)) {
+ /* remove all modified parts */
+ if (msg_info->msg_mime != NULL)
+ recursive_clear_registered_mime(privacy, msg_info->msg_mime);
+ unregister_message(privacy, msg_info);
+ }
+
+ mailmessage_flush(msg_info);
+}
+
+static int fetch_registered_part(struct mailprivacy * privacy,
+ int (* fetch_section)(mailmessage *, struct mailmime *,
+ char **, size_t *),
+ struct mailmime * mime,
+ char ** result, size_t * result_len)
+{
+ mailmessage * dummy_msg;
+ int res;
+ char * content;
+ size_t content_len;
+ int r;
+
+ dummy_msg = mime_message_init(NULL);
+ if (dummy_msg == NULL) {
+ res = MAIL_ERROR_MEMORY;
+ goto err;
+ }
+
+ r = mime_message_set_tmpdir(dummy_msg, privacy->tmp_dir);
+ if (r != MAIL_NO_ERROR) {
+ res = MAIL_ERROR_MEMORY;
+ goto free_msg;
+ }
+
+ r = fetch_section(dummy_msg, mime, &content, &content_len);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto free_msg;
+ }
+
+ r = register_result_mmapstr(privacy, content);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto free_fetch;
+ }
+
+ mailmessage_free(dummy_msg);
+
+ * result = content;
+ * result_len = content_len;
+
+ return MAIL_NO_ERROR;
+
+ free_fetch:
+ mailmessage_fetch_result_free(dummy_msg, content);
+ free_msg:
+ mailmessage_free(dummy_msg);
+ err:
+ return res;
+}
+
+int mailprivacy_msg_fetch_section(struct mailprivacy * privacy,
+ mailmessage * msg_info,
+ struct mailmime * mime,
+ char ** result, size_t * result_len)
+{
+ if (msg_is_modified(privacy, msg_info) &&
+ mime_is_registered(privacy, mime)) {
+ return fetch_registered_part(privacy, mailmessage_fetch_section,
+ mime, result, result_len);
+ }
+
+ return mailmessage_fetch_section(msg_info, mime, result, result_len);
+}
+
+int mailprivacy_msg_fetch_section_header(struct mailprivacy * privacy,
+ mailmessage * msg_info,
+ struct mailmime * mime,
+ char ** result,
+ size_t * result_len)
+{
+ if (msg_is_modified(privacy, msg_info) &&
+ mime_is_registered(privacy, mime)) {
+ return fetch_registered_part(privacy, mailmessage_fetch_section_header,
+ mime, result, result_len);
+ }
+
+ return mailmessage_fetch_section_header(msg_info, mime, result, result_len);
+}
+
+int mailprivacy_msg_fetch_section_mime(struct mailprivacy * privacy,
+ mailmessage * msg_info,
+ struct mailmime * mime,
+ char ** result,
+ size_t * result_len)
+{
+ if (msg_is_modified(privacy, msg_info) &&
+ mime_is_registered(privacy, mime)) {
+ return fetch_registered_part(privacy, mailmessage_fetch_section_mime,
+ mime, result, result_len);
+ }
+
+ return mailmessage_fetch_section_mime(msg_info, mime, result, result_len);
+}
+
+int mailprivacy_msg_fetch_section_body(struct mailprivacy * privacy,
+ mailmessage * msg_info,
+ struct mailmime * mime,
+ char ** result,
+ size_t * result_len)
+{
+ if (msg_is_modified(privacy, msg_info) &&
+ mime_is_registered(privacy, mime)) {
+ return fetch_registered_part(privacy, mailmessage_fetch_section_body,
+ mime, result, result_len);
+ }
+
+ return mailmessage_fetch_section_body(msg_info, mime, result, result_len);
+}
+
+void mailprivacy_msg_fetch_result_free(struct mailprivacy * privacy,
+ mailmessage * msg_info,
+ char * msg)
+{
+ if (msg == NULL)
+ return;
+
+ if (msg_is_modified(privacy, msg_info)) {
+ if (result_is_mmapstr(privacy, msg)) {
+ unregister_result_mmapstr(privacy, msg);
+ return;
+ }
+ }
+
+ mailmessage_fetch_result_free(msg_info, msg);
+}
+
+int mailprivacy_msg_fetch(struct mailprivacy * privacy,
+ mailmessage * msg_info,
+ char ** result,
+ size_t * result_len)
+{
+ return mailmessage_fetch(msg_info, result, result_len);
+}
+
+int mailprivacy_msg_fetch_header(struct mailprivacy * privacy,
+ mailmessage * msg_info,
+ char ** result,
+ size_t * result_len)
+{
+ return mailmessage_fetch_header(msg_info, result, result_len);
+}
+
+/* end of fetch operations */
+/* **************************************************** */
+
+static int privacy_handler(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime, struct mailmime ** result);
+
+
+
+static struct mailmime *
+mime_add_alternative(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime,
+ struct mailmime * alternative)
+{
+ struct mailmime * multipart;
+ int r;
+ struct mailmime * mime_copy;
+ char original_filename[PATH_MAX];
+
+ if (mime->mm_parent == NULL)
+ goto err;
+
+ r = mailmime_new_with_content("multipart/alternative", NULL, &multipart);
+ if (r != MAILIMF_NO_ERROR)
+ goto err;
+
+ r = mailmime_smart_add_part(multipart, alternative);
+ if (r != MAILIMF_NO_ERROR) {
+ goto free_multipart;
+ }
+
+ /* get copy of mime part "mime" and set parts */
+
+ r = mailprivacy_fetch_mime_body_to_file(privacy,
+ original_filename, sizeof(original_filename),
+ msg, mime);
+ if (r != MAIL_NO_ERROR)
+ goto detach_alternative;
+
+ r = mailprivacy_get_part_from_file(privacy, 0,
+ original_filename, &mime_copy);
+ unlink(original_filename);
+ if (r != MAIL_NO_ERROR) {
+ goto detach_alternative;
+ }
+
+ r = mailmime_smart_add_part(multipart, mime_copy);
+ if (r != MAILIMF_NO_ERROR) {
+ goto free_mime_copy;
+ }
+
+ r = recursive_register_mime(privacy, multipart);
+ if (r != MAIL_NO_ERROR)
+ goto detach_mime_copy;
+
+ mailmime_substitute(mime, multipart);
+
+ mailmime_free(mime);
+
+ return multipart;
+
+ detach_mime_copy:
+ mailprivacy_recursive_unregister_mime(privacy, multipart);
+ mailmime_remove_part(alternative);
+ free_mime_copy:
+ mailprivacy_mime_clear(mime_copy);
+ mailmime_free(mime_copy);
+ detach_alternative:
+ mailmime_remove_part(alternative);
+ free_multipart:
+ mailmime_free(multipart);
+ err:
+ return NULL;
+}
+
+/*
+ recursive_check_privacy returns MAIL_NO_ERROR if at least one
+ part is using a privacy protocol.
+*/
+
+static int recursive_check_privacy(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime)
+{
+ int r;
+ clistiter * cur;
+ struct mailmime * alternative;
+ int res;
+ struct mailmime * multipart;
+
+ if (privacy == NULL)
+ return MAIL_NO_ERROR;
+
+ if (mime_is_registered(privacy, mime))
+ return MAIL_ERROR_INVAL;
+
+ r = privacy_handler(privacy, msg, mime, &alternative);
+ if (r == MAIL_NO_ERROR) {
+ if (privacy->make_alternative) {
+ multipart = mime_add_alternative(privacy, msg, mime, alternative);
+ if (multipart == NULL) {
+ mailprivacy_mime_clear(alternative);
+ mailmime_free(alternative);
+ return MAIL_ERROR_MEMORY;
+ }
+ }
+ else {
+ mailmime_substitute(mime, alternative);
+ mailmime_free(mime);
+ mime = NULL;
+ }
+
+ return MAIL_NO_ERROR;
+ }
+ else {
+ switch (mime->mm_type) {
+ case MAILMIME_SINGLE:
+ return MAIL_ERROR_INVAL;
+
+ case MAILMIME_MULTIPLE:
+ res = MAIL_ERROR_INVAL;
+
+ for(cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list) ;
+ cur != NULL ; cur = clist_next(cur)) {
+ struct mailmime * child;
+
+ child = clist_content(cur);
+
+ r = recursive_check_privacy(privacy, msg, child);
+ if (r == MAIL_NO_ERROR)
+ res = MAIL_NO_ERROR;
+ }
+
+ return res;
+
+ case MAILMIME_MESSAGE:
+ if (mime->mm_data.mm_message.mm_msg_mime != NULL)
+ return recursive_check_privacy(privacy, msg,
+ mime->mm_data.mm_message.mm_msg_mime);
+ return MAIL_ERROR_INVAL;
+
+ default:
+ return MAIL_ERROR_INVAL;
+ }
+ }
+}
+
+static int privacy_handler(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ int r;
+ struct mailmime * alternative_mime;
+ unsigned int i;
+
+ alternative_mime = NULL;
+ for(i = 0 ; i < carray_count(privacy->protocols) ; i ++) {
+ struct mailprivacy_protocol * protocol;
+
+ protocol = carray_get(privacy->protocols, i);
+
+ if (protocol->decrypt != NULL) {
+ r = protocol->decrypt(privacy, msg, mime, &alternative_mime);
+ if (r == MAIL_NO_ERROR) {
+
+ * result = alternative_mime;
+
+ return MAIL_NO_ERROR;
+ }
+ }
+ }
+
+ return MAIL_ERROR_INVAL;
+}
+
+int mailprivacy_register(struct mailprivacy * privacy,
+ struct mailprivacy_protocol * protocol)
+{
+ int r;
+
+ r = carray_add(privacy->protocols, protocol, NULL);
+ if (r < 0)
+ return MAIL_ERROR_MEMORY;
+
+ return MAIL_NO_ERROR;
+}
+
+void mailprivacy_unregister(struct mailprivacy * privacy,
+ struct mailprivacy_protocol * protocol)
+{
+ unsigned int i;
+
+ for(i = 0 ; i < carray_count(privacy->protocols) ; i ++) {
+ if (carray_get(privacy->protocols, i) == protocol) {
+ carray_delete(privacy->protocols, i);
+ return;
+ }
+ }
+}
+
+static struct mailprivacy_protocol *
+get_protocol(struct mailprivacy * privacy, char * privacy_driver)
+{
+ unsigned int i;
+
+ for(i = 0 ; i < carray_count(privacy->protocols) ; i ++) {
+ struct mailprivacy_protocol * protocol;
+
+ protocol = carray_get(privacy->protocols, i);
+ if (strcasecmp(protocol->name, privacy_driver) == 0)
+ return protocol;
+ }
+
+ return NULL;
+}
+
+static struct mailprivacy_encryption *
+get_encryption(struct mailprivacy_protocol * protocol,
+ char * privacy_encryption)
+{
+ int i;
+
+ for(i = 0 ; i < protocol->encryption_count ; i ++) {
+ struct mailprivacy_encryption * encryption;
+
+ encryption = &protocol->encryption_tab[i];
+ if (strcasecmp(encryption->name, privacy_encryption) == 0)
+ return encryption;
+ }
+
+ return NULL;
+}
+
+int mailprivacy_encrypt(struct mailprivacy * privacy,
+ char * privacy_driver, char * privacy_encryption,
+ struct mailmime * mime,
+ struct mailmime ** result)
+{
+ struct mailprivacy_protocol * protocol;
+ struct mailprivacy_encryption * encryption;
+ int r;
+
+ protocol = get_protocol(privacy, privacy_driver);
+ if (protocol == NULL)
+ return MAIL_ERROR_INVAL;
+
+ encryption = get_encryption(protocol, privacy_encryption);
+ if (encryption == NULL)
+ return MAIL_ERROR_INVAL;
+
+ if (encryption->encrypt == NULL)
+ return MAIL_ERROR_NOT_IMPLEMENTED;
+
+ r = encryption->encrypt(privacy, mime, result);
+ if (r != MAIL_NO_ERROR)
+ return r;
+
+ return MAIL_NO_ERROR;
+}
+
+char * mailprivacy_get_encryption_name(struct mailprivacy * privacy,
+ char * privacy_driver, char * privacy_encryption)
+{
+ struct mailprivacy_protocol * protocol;
+ struct mailprivacy_encryption * encryption;
+
+ protocol = get_protocol(privacy, privacy_driver);
+ if (protocol == NULL)
+ return NULL;
+
+ encryption = get_encryption(protocol, privacy_encryption);
+ if (encryption == NULL)
+ return NULL;
+
+ return encryption->description;
+}
+
+int mailprivacy_is_encrypted(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime)
+{
+ unsigned int i;
+
+ if (mime_is_registered(privacy, mime))
+ return 0;
+
+ for(i = 0 ; i < carray_count(privacy->protocols) ; i ++) {
+ struct mailprivacy_protocol * protocol;
+
+ protocol = carray_get(privacy->protocols, i);
+
+ if (protocol->is_encrypted != NULL) {
+ if (protocol->is_encrypted(privacy, msg, mime))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+void mailprivacy_debug(struct mailprivacy * privacy, FILE * f)
+{
+ fprintf(f, "privacy debug -- begin\n");
+ fprintf(f, "registered message: %i\n", chash_count(privacy->msg_ref));
+ fprintf(f, "registered MMAPStr: %i\n", chash_count(privacy->mmapstr));
+ fprintf(f, "registered mailmime: %i\n", chash_count(privacy->mime_ref));
+ fprintf(f, "privacy debug -- end\n");
+}
diff --git a/libetpan/src/engine/mailprivacy.h b/libetpan/src/engine/mailprivacy.h
new file mode 100644
index 0000000..1f77331
--- a/dev/null
+++ b/libetpan/src/engine/mailprivacy.h
@@ -0,0 +1,117 @@
+/*
+ * libEtPan! -- a mail library
+ *
+ * Copyright (C) 2001, 2005 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef MAILPRIVACY_H
+
+#define MAILPRIVACY_H
+
+#include <libetpan/mailmessage.h>
+#include <libetpan/mailprivacy_types.h>
+#include <libetpan/mailprivacy_tools.h>
+
+struct mailprivacy * mailprivacy_new(char * tmp_dir, int make_alternative);
+
+void mailprivacy_free(struct mailprivacy * privacy);
+
+int mailprivacy_msg_get_bodystructure(struct mailprivacy * privacy,
+ mailmessage * msg_info,
+ struct mailmime ** result);
+
+void mailprivacy_msg_flush(struct mailprivacy * privacy,
+ mailmessage * msg_info);
+
+int mailprivacy_msg_fetch_section(struct mailprivacy * privacy,
+ mailmessage * msg_info,
+ struct mailmime * mime,
+ char ** result, size_t * result_len);
+
+int mailprivacy_msg_fetch_section_header(struct mailprivacy * privacy,
+ mailmessage * msg_info,
+ struct mailmime * mime,
+ char ** result,
+ size_t * result_len);
+
+int mailprivacy_msg_fetch_section_mime(struct mailprivacy * privacy,
+ mailmessage * msg_info,
+ struct mailmime * mime,
+ char ** result,
+ size_t * result_len);
+
+int mailprivacy_msg_fetch_section_body(struct mailprivacy * privacy,
+ mailmessage * msg_info,
+ struct mailmime * mime,
+ char ** result,
+ size_t * result_len);
+
+void mailprivacy_msg_fetch_result_free(struct mailprivacy * privacy,
+ mailmessage * msg_info,
+ char * msg);
+
+int mailprivacy_msg_fetch(struct mailprivacy * privacy,
+ mailmessage * msg_info,
+ char ** result,
+ size_t * result_len);
+
+int mailprivacy_msg_fetch_header(struct mailprivacy * privacy,
+ mailmessage * msg_info,
+ char ** result,
+ size_t * result_len);
+
+int mailprivacy_register(struct mailprivacy * privacy,
+ struct mailprivacy_protocol * protocol);
+
+void mailprivacy_unregister(struct mailprivacy * privacy,
+ struct mailprivacy_protocol * protocol);
+
+char * mailprivacy_get_encryption_name(struct mailprivacy * privacy,
+ char * privacy_driver, char * privacy_encryption);
+
+int mailprivacy_encrypt(struct mailprivacy * privacy,
+ char * privacy_driver, char * privacy_encryption,
+ struct mailmime * mime,
+ struct mailmime ** result);
+
+void mailprivacy_debug(struct mailprivacy * privacy, FILE * f);
+
+carray * mailprivacy_get_protocols(struct mailprivacy * privacy);
+
+int mailprivacy_is_encrypted(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime);
+
+void mailprivacy_recursive_unregister_mime(struct mailprivacy * privacy,
+ struct mailmime * mime);
+
+#endif
diff --git a/libetpan/src/engine/mailprivacy_gnupg.c b/libetpan/src/engine/mailprivacy_gnupg.c
new file mode 100644
index 0000000..01dcc80
--- a/dev/null
+++ b/libetpan/src/engine/mailprivacy_gnupg.c
@@ -0,0 +1,2614 @@
+/*
+ * libEtPan! -- a mail library
+ *
+ * Copyright (C) 2001, 2005 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include "mailprivacy_gnupg.h"
+
+#include "mailprivacy.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <stdlib.h>
+#include "mailprivacy_tools.h"
+#include <libetpan/mailmime.h>
+#include <libetpan/libetpan-config.h>
+
+static int pgp_is_encrypted(struct mailmime * mime)
+{
+ if (mime->mm_content_type != NULL) {
+ clistiter * cur;
+
+ if (strcasecmp(mime->mm_content_type->ct_subtype, "encrypted") != 0)
+ return 0;
+
+ for(cur = clist_begin(mime->mm_content_type->ct_parameters) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ struct mailmime_parameter * param;
+
+ param = clist_content(cur);
+
+ if ((strcasecmp(param->pa_name, "protocol") == 0) &&
+ (strcasecmp(param->pa_value, "application/pgp-encrypted") == 0))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int pgp_is_signed(struct mailmime * mime)
+{
+ if (mime->mm_content_type != NULL) {
+ clistiter * cur;
+
+ if (strcasecmp(mime->mm_content_type->ct_subtype, "signed") != 0)
+ return 0;
+
+ for(cur = clist_begin(mime->mm_content_type->ct_parameters) ;
+ cur != NULL ; cur = clist_next(cur)) {
+ struct mailmime_parameter * param;
+
+ param = clist_content(cur);
+
+ if ((strcasecmp(param->pa_name, "protocol") == 0) &&
+ (strcasecmp(param->pa_value, "application/pgp-signature") == 0))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+#define PGP_SIGNED "-----BEGIN PGP SIGNED MESSAGE-----"
+
+int pgp_is_clearsigned(char * data, size_t len)
+{
+ if (len >= strlen(PGP_SIGNED))
+ if (strncmp(data, PGP_SIGNED, sizeof(PGP_SIGNED) - 1) == 0)
+ return 1;
+
+ return 0;
+}
+
+#define PGP_CRYPTED "-----BEGIN PGP MESSAGE-----"
+
+int pgp_is_crypted_armor(char * data, size_t len)
+{
+ if (len >= strlen(PGP_CRYPTED))
+ if (strncmp(data, PGP_CRYPTED, sizeof(PGP_CRYPTED) - 1) == 0)
+ return 1;
+
+ return 0;
+}
+
+
+#define BUF_SIZE 1024
+
+enum {
+ NO_ERROR_PGP = 0,
+ ERROR_PGP_CHECK,
+ ERROR_PGP_COMMAND,
+ ERROR_PGP_FILE,
+};
+
+/* write output to a file */
+
+static int get_pgp_output(FILE * dest_f, char * command)
+{
+ FILE * p;
+ char buf[BUF_SIZE];
+ size_t size;
+ int res;
+ int status;
+ char command_redirected[PATH_MAX];
+
+ snprintf(command_redirected, sizeof(command_redirected), "%s 2>&1", command);
+
+ /*
+ flush buffer so that it is not flushed more than once when forking
+ */
+ fflush(dest_f);
+
+ p = popen(command_redirected, "r");
+ if (p == NULL) {
+ res = ERROR_PGP_COMMAND;
+ goto err;
+ }
+
+ while ((size = fread(buf, 1, sizeof(buf), p)) != 0) {
+ size_t written;
+
+ written = fwrite(buf, 1, size, dest_f);
+ if (written != size) {
+ res = ERROR_PGP_FILE;
+ goto close;
+ }
+ }
+ status = pclose(p);
+
+ if (WEXITSTATUS(status) != 0)
+ return ERROR_PGP_CHECK;
+ else
+ return NO_ERROR_PGP;
+
+ close:
+ pclose(p);
+ err:
+ return res;
+}
+
+#define PGP_DECRYPT_DESCRIPTION "PGP encrypted part\r\n"
+#define PGP_DECRYPT_FAILED "PGP decryption FAILED\r\n"
+#define PGP_DECRYPT_SUCCESS "PGP decryption success\r\n"
+
+/* extracted from mailprivacy_smime.c -- begin */
+
+static char * get_first_from_addr(struct mailmime * mime)
+{
+ clistiter * cur;
+ struct mailimf_single_fields single_fields;
+ struct mailimf_fields * fields;
+ struct mailimf_mailbox * mb;
+
+ if (mime->mm_type != MAILMIME_MESSAGE)
+ return NULL;
+
+ fields = mime->mm_data.mm_message.mm_fields;
+ if (fields == NULL)
+ return NULL;
+
+ mailimf_single_fields_init(&single_fields, fields);
+
+ if (single_fields.fld_from == NULL)
+ return NULL;
+
+ cur = clist_begin(single_fields.fld_from->frm_mb_list->mb_list);
+ if (cur == NULL)
+ return NULL;
+
+ mb = clist_content(cur);
+
+ return mb->mb_addr_spec;
+}
+
+/* extracted from mailprivacy_smime.c -- end */
+
+static int pgp_decrypt(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ char default_key[PATH_MAX];
+ struct mailmime * version_mime;
+ struct mailmime * encrypted_mime;
+ clistiter * cur;
+ char encrypted_filename[PATH_MAX];
+ char description_filename[PATH_MAX];
+ FILE * description_f;
+ char decrypted_filename[PATH_MAX];
+ FILE * decrypted_f;
+ char command[PATH_MAX];
+ struct mailmime * description_mime;
+ struct mailmime * decrypted_mime;
+ int r;
+ int res;
+ int decrypt_ok;
+ char quoted_decrypted_filename[PATH_MAX];
+ char quoted_encrypted_filename[PATH_MAX];
+ struct mailmime * multipart;
+ char * email;
+
+ /* get the two parts of the PGP message */
+
+ cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list);
+ if (cur == NULL) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ version_mime = clist_content(cur);
+ cur = clist_next(cur);
+ if (cur == NULL) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ encrypted_mime = clist_content(cur);
+
+ /* fetch the second section, that's the useful one */
+
+ r = mailprivacy_fetch_decoded_to_file(privacy,
+ encrypted_filename, sizeof(encrypted_filename),
+ msg, encrypted_mime);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ /* we are in a safe directory */
+
+ decrypted_f = mailprivacy_get_tmp_file(privacy,
+ decrypted_filename,
+ sizeof(decrypted_filename));
+ if (decrypted_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_encrypted;
+ }
+ fclose(decrypted_f);
+
+ /* description */
+
+ description_f = mailprivacy_get_tmp_file(privacy,
+ description_filename,
+ sizeof(description_filename));
+ if (description_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_decrypted;
+ }
+
+ fprintf(description_f, PGP_DECRYPT_DESCRIPTION);
+
+ /* get encryption key */
+
+ * default_key = '\0';
+ email = get_first_from_addr(mime);
+ if (email != NULL)
+ snprintf(default_key, sizeof(default_key),
+ "--default-key %s", email);
+
+ /* run the command */
+
+ r = mail_quote_filename(quoted_encrypted_filename,
+ sizeof(quoted_encrypted_filename), encrypted_filename);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ r = mail_quote_filename(quoted_decrypted_filename,
+ sizeof(quoted_decrypted_filename), decrypted_filename);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ decrypt_ok = 0;
+ snprintf(command, sizeof(command),
+ "gpg -q --batch --yes --out %s %s --decrypt %s",
+ quoted_decrypted_filename, default_key, quoted_encrypted_filename);
+
+ r = get_pgp_output(description_f, command);
+ switch (r) {
+ case NO_ERROR_PGP:
+ decrypt_ok = 1;
+ break;
+ case ERROR_PGP_CHECK:
+ decrypt_ok = 0;
+ break;
+ case ERROR_PGP_COMMAND:
+ fclose(description_f);
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_description;
+ case ERROR_PGP_FILE:
+ fclose(description_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_description;
+ }
+ if (decrypt_ok)
+ fprintf(description_f, PGP_DECRYPT_SUCCESS);
+ else
+ fprintf(description_f, PGP_DECRYPT_FAILED);
+ fclose(description_f);
+
+ /* building multipart */
+
+ r = mailmime_new_with_content("multipart/x-decrypted", NULL, &multipart);
+ if (r != MAILIMF_NO_ERROR) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* building the description part */
+
+ description_mime = mailprivacy_new_file_part(privacy,
+ description_filename,
+ "text/plain", MAILMIME_MECHANISM_8BIT);
+ if (description_mime == NULL) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* adds the description part */
+
+ r = mailmime_smart_add_part(multipart, description_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(description_mime);
+ mailmime_free(description_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* building the decrypted part */
+
+ r = mailprivacy_get_part_from_file(privacy, 1,
+ decrypted_filename, &decrypted_mime);
+ if (r == MAIL_NO_ERROR) {
+ /* adds the decrypted part */
+
+ r = mailmime_smart_add_part(multipart, decrypted_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(decrypted_mime);
+ mailmime_free(decrypted_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+ }
+
+ unlink(description_filename);
+ unlink(decrypted_filename);
+ unlink(encrypted_filename);
+
+ * result = multipart;
+
+ return MAIL_NO_ERROR;
+
+ unlink_description:
+ unlink(description_filename);
+ unlink_decrypted:
+ unlink(decrypted_filename);
+ unlink_encrypted:
+ unlink(encrypted_filename);
+ err:
+ return res;
+}
+
+#define PGP_VERIFY_DESCRIPTION "PGP verify signed message\r\n"
+#define PGP_VERIFY_FAILED "PGP verification FAILED\r\n"
+#define PGP_VERIFY_SUCCESS "PGP verification success\r\n"
+
+static int
+pgp_verify(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ struct mailmime * signed_mime;
+ struct mailmime * signature_mime;
+ char signed_filename[PATH_MAX];
+ char signature_filename[PATH_MAX];
+ int res;
+ int r;
+ clistiter * cur;
+ char command[PATH_MAX];
+ int sign_ok;
+ struct mailmime * description_mime;
+ FILE * description_f;
+ char description_filename[PATH_MAX];
+ char quoted_signed_filename[PATH_MAX];
+ char quoted_signature_filename[PATH_MAX];
+ struct mailmime * multipart;
+ struct mailmime * signed_msg_mime;
+
+ /* get the two parts of the PGP message */
+
+ cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list);
+ if (cur == NULL) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ signed_mime = clist_content(cur);
+ cur = clist_next(cur);
+ if (cur == NULL) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ signature_mime = clist_content(cur);
+
+ /* fetch signed part and write it to a file */
+
+ r = mailprivacy_fetch_mime_body_to_file(privacy,
+ signed_filename, sizeof(signed_filename),
+ msg, signed_mime);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ /* fetch signed part and write it to a file */
+
+ r = mailprivacy_fetch_decoded_to_file(privacy,
+ signature_filename, sizeof(signature_filename),
+ msg, signature_mime);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto unlink_signed;
+ }
+
+ /* description */
+
+ description_f = mailprivacy_get_tmp_file(privacy,
+ description_filename,
+ sizeof(description_filename));
+ if (description_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_signature;
+ }
+
+ fprintf(description_f, PGP_VERIFY_DESCRIPTION);
+
+ /* run the command */
+
+ r = mail_quote_filename(quoted_signature_filename,
+ sizeof(quoted_signature_filename), signature_filename);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ r = mail_quote_filename(quoted_signed_filename,
+ sizeof(quoted_signed_filename), signed_filename);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ sign_ok = 0;
+ snprintf(command, sizeof(command), "gpg -q --batch --yes --verify %s %s",
+ quoted_signature_filename, quoted_signed_filename);
+
+ r = get_pgp_output(description_f, command);
+ switch (r) {
+ case NO_ERROR_PGP:
+ sign_ok = 1;
+ break;
+ case ERROR_PGP_CHECK:
+ sign_ok = 0;
+ break;
+ case ERROR_PGP_COMMAND:
+ fclose(description_f);
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_description;
+ case ERROR_PGP_FILE:
+ fclose(description_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_description;
+ }
+
+ if (sign_ok)
+ fprintf(description_f, PGP_VERIFY_SUCCESS);
+ else
+ fprintf(description_f, PGP_VERIFY_FAILED);
+ fclose(description_f);
+
+ /* building multipart */
+
+ r = mailmime_new_with_content("multipart/x-verified", NULL, &multipart);
+ if (r != MAILIMF_NO_ERROR) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* building the description part */
+
+ description_mime = mailprivacy_new_file_part(privacy,
+ description_filename,
+ "text/plain", MAILMIME_MECHANISM_8BIT);
+ if (description_mime == NULL) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* adds the description part */
+
+ r = mailmime_smart_add_part(multipart, description_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(description_mime);
+ mailmime_free(description_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ r = mailprivacy_get_part_from_file(privacy, 1,
+ signed_filename, &signed_msg_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ r = mailmime_smart_add_part(multipart, signed_msg_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(signed_msg_mime);
+ mailmime_free(signed_msg_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ unlink(description_filename);
+ unlink(signature_filename);
+ unlink(signed_filename);
+
+ * result = multipart;
+
+ return MAIL_NO_ERROR;
+
+ unlink_description:
+ unlink(description_filename);
+ unlink_signature:
+ unlink(signature_filename);
+ unlink_signed:
+ unlink(signed_filename);
+ err:
+ return res;
+}
+
+
+#define PGP_CLEAR_VERIFY_DESCRIPTION "PGP verify clear signed message\r\n"
+#define PGP_CLEAR_VERIFY_FAILED "PGP verification FAILED\r\n"
+#define PGP_CLEAR_VERIFY_SUCCESS "PGP verification success\r\n"
+
+static int pgp_verify_clearsigned(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime,
+ char * content, size_t content_len, struct mailmime ** result)
+{
+ int r;
+ char command[PATH_MAX];
+ int res;
+ int sign_ok;
+ size_t written;
+ char signed_filename[PATH_MAX];
+ FILE * signed_f;
+ char stripped_filename[PATH_MAX];
+ FILE * stripped_f;
+ char description_filename[PATH_MAX];
+ FILE * description_f;
+ char quoted_signed_filename[PATH_MAX];
+ char quoted_stripped_filename[PATH_MAX];
+ struct mailmime * stripped_mime;
+ struct mailmime * description_mime;
+ struct mailmime * multipart;
+ struct mailmime_content * content_type;
+
+ if (mime->mm_parent == NULL) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ if (mime->mm_parent->mm_type == MAILMIME_SINGLE) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ signed_f = mailprivacy_get_tmp_file(privacy,
+ signed_filename, sizeof(signed_filename));
+ if (signed_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+
+ written = fwrite(content, 1, content_len, signed_f);
+ if (written != content_len) {
+ fclose(signed_f);
+ unlink(signed_filename);
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+ fclose(signed_f);
+
+ /* XXX - prepare file for PGP, remove trailing WS */
+
+ stripped_f = mailprivacy_get_tmp_file(privacy,
+ stripped_filename, sizeof(stripped_filename));
+ if (stripped_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_signed;
+ }
+ fclose(stripped_f);
+
+ /* description */
+
+ description_f = mailprivacy_get_tmp_file(privacy,
+ description_filename,
+ sizeof(description_filename));
+ if (description_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_stripped;
+ }
+
+ fprintf(description_f, PGP_CLEAR_VERIFY_DESCRIPTION);
+
+ r = mail_quote_filename(quoted_stripped_filename,
+ sizeof(quoted_stripped_filename), stripped_filename);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ r = mail_quote_filename(quoted_signed_filename,
+ sizeof(quoted_signed_filename), signed_filename);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ snprintf(command, sizeof(command),
+ "gpg -q --batch --yes --out %s --decrypt %s",
+ quoted_stripped_filename, quoted_signed_filename);
+
+ sign_ok = 0;
+ r = get_pgp_output(description_f, command);
+ switch (r) {
+ case NO_ERROR_PGP:
+ sign_ok = 1;
+ break;
+ case ERROR_PGP_CHECK:
+ sign_ok = 0;
+ break;
+ case ERROR_PGP_COMMAND:
+ res = MAIL_ERROR_COMMAND;
+ fclose(description_f);
+ goto unlink_description;
+ case ERROR_PGP_FILE:
+ res = MAIL_ERROR_FILE;
+ fclose(description_f);
+ goto unlink_description;
+ }
+ if (sign_ok)
+ fprintf(description_f, PGP_CLEAR_VERIFY_SUCCESS);
+ else
+ fprintf(description_f, PGP_CLEAR_VERIFY_FAILED);
+ fclose(description_f);
+
+ /* building multipart */
+
+ r = mailmime_new_with_content("multipart/x-verified", NULL, &multipart);
+ if (r != MAILIMF_NO_ERROR) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* building the description part */
+
+ description_mime = mailprivacy_new_file_part(privacy,
+ description_filename,
+ "text/plain", MAILMIME_MECHANISM_8BIT);
+ if (description_mime == NULL) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* adds the description part */
+
+ r = mailmime_smart_add_part(multipart, description_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(description_mime);
+ mailmime_free(description_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* building the signature stripped part */
+
+ stripped_mime = mailprivacy_new_file_part(privacy,
+ stripped_filename,
+ "application/octet-stream",
+ MAILMIME_MECHANISM_8BIT);
+ if (stripped_mime == NULL) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* place original content type */
+
+ content_type = mailmime_content_dup(mime->mm_content_type);
+ if (content_type == NULL) {
+ mailprivacy_mime_clear(stripped_mime);
+ mailmime_free(stripped_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ mailmime_content_free(stripped_mime->mm_content_type);
+ stripped_mime->mm_content_type = content_type;
+
+ /* place original MIME fields */
+
+ if (mime->mm_mime_fields != NULL) {
+ struct mailmime_fields * mime_fields;
+ clistiter * cur;
+
+ mime_fields = mailprivacy_mime_fields_dup(privacy, mime->mm_mime_fields);
+ if (mime_fields == NULL) {
+ mailprivacy_mime_clear(stripped_mime);
+ mailmime_free(stripped_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+ for(cur = clist_begin(mime_fields->fld_list) ;
+ cur != NULL ; cur = clist_next(cur)) {
+ struct mailmime_field * field;
+
+ field = clist_content(cur);
+ if (field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING) {
+ mailmime_field_free(field);
+ clist_delete(mime_fields->fld_list, cur);
+ break;
+ }
+ }
+ clist_concat(stripped_mime->mm_mime_fields->fld_list,
+ mime_fields->fld_list);
+ mailmime_fields_free(mime_fields);
+ }
+
+ /* adds the stripped part */
+
+ r = mailmime_smart_add_part(multipart, stripped_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(stripped_mime);
+ mailmime_free(stripped_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ unlink(description_filename);
+ unlink(stripped_filename);
+ unlink(signed_filename);
+
+ * result = multipart;
+
+ return MAIL_NO_ERROR;
+
+ unlink_description:
+ unlink(description_filename);
+ unlink_stripped:
+ unlink(stripped_filename);
+ unlink_signed:
+ unlink(signed_filename);
+ err:
+ return res;
+}
+
+
+#define PGP_DECRYPT_ARMOR_DESCRIPTION "PGP ASCII armor encrypted part\r\n"
+#define PGP_DECRYPT_ARMOR_FAILED "PGP ASCII armor decryption FAILED\r\n"
+#define PGP_DECRYPT_ARMOR_SUCCESS "PGP ASCII armor decryption success\r\n"
+
+static int pgp_decrypt_armor(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime,
+ char * content, size_t content_len, struct mailmime ** result)
+{
+ char default_key[PATH_MAX];
+ FILE * encrypted_f;
+ char encrypted_filename[PATH_MAX];
+ char description_filename[PATH_MAX];
+ FILE * description_f;
+ char decrypted_filename[PATH_MAX];
+ FILE * decrypted_f;
+ size_t written;
+ char command[PATH_MAX];
+ struct mailmime * description_mime;
+ struct mailmime * decrypted_mime;
+ struct mailmime * multipart;
+ int r;
+ int res;
+ int sign_ok;
+ char quoted_decrypted_filename[PATH_MAX];
+ char quoted_encrypted_filename[PATH_MAX];
+ char * email;
+
+ if (mime->mm_parent == NULL) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ if (mime->mm_parent->mm_type == MAILMIME_SINGLE) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ encrypted_f = mailprivacy_get_tmp_file(privacy,
+ encrypted_filename,
+ sizeof(encrypted_filename));
+ if (encrypted_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+
+ written = fwrite(content, 1, content_len, encrypted_f);
+ if (written != content_len) {
+ fclose(encrypted_f);
+ unlink(encrypted_filename);
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+
+ fclose(encrypted_f);
+
+ /* we are in a safe directory */
+
+ decrypted_f = mailprivacy_get_tmp_file(privacy,
+ decrypted_filename,
+ sizeof(decrypted_filename));
+ if (decrypted_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_encrypted;
+ }
+ fclose(decrypted_f);
+
+ /* description */
+
+ description_f = mailprivacy_get_tmp_file(privacy,
+ description_filename,
+ sizeof(description_filename));
+ if (description_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_decrypted;
+ }
+
+ fprintf(description_f, PGP_DECRYPT_ARMOR_DESCRIPTION);
+
+ /* get encryption key */
+
+ * default_key = '\0';
+ email = get_first_from_addr(mime);
+ if (email != NULL)
+ snprintf(default_key, sizeof(default_key),
+ "--default-key %s", email);
+
+ /* run the command */
+
+ r = mail_quote_filename(quoted_encrypted_filename,
+ sizeof(quoted_encrypted_filename), encrypted_filename);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ r = mail_quote_filename(quoted_decrypted_filename,
+ sizeof(quoted_decrypted_filename), decrypted_filename);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ sign_ok = 0;
+ snprintf(command, sizeof(command),
+ "gpg -q --batch --yes --out %s %s --decrypt %s",
+ quoted_decrypted_filename, default_key, quoted_encrypted_filename);
+
+ r = get_pgp_output(description_f, command);
+ switch (r) {
+ case NO_ERROR_PGP:
+ sign_ok = 1;
+ break;
+ case ERROR_PGP_CHECK:
+ sign_ok = 0;
+ break;
+ case ERROR_PGP_COMMAND:
+ fclose(description_f);
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_description;
+ case ERROR_PGP_FILE:
+ fclose(description_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_description;
+ }
+ if (sign_ok)
+ fprintf(description_f, PGP_DECRYPT_ARMOR_SUCCESS);
+ else
+ fprintf(description_f, PGP_DECRYPT_ARMOR_FAILED);
+ fclose(description_f);
+
+ /* building multipart */
+
+ r = mailmime_new_with_content("multipart/x-decrypted", NULL, &multipart);
+ if (r != MAILIMF_NO_ERROR) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* building the description part */
+
+ description_mime = mailprivacy_new_file_part(privacy,
+ description_filename,
+ "text/plain", MAILMIME_MECHANISM_8BIT);
+ if (description_mime == NULL) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* adds the description part */
+
+ r = mailmime_smart_add_part(multipart, description_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(description_mime);
+ mailmime_free(description_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* building the decrypted part */
+
+ r = mailprivacy_get_part_from_file(privacy, 1,
+ decrypted_filename, &decrypted_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = r;
+ goto unlink_description;
+ }
+
+ /* adds the decrypted part */
+
+ r = mailmime_smart_add_part(multipart, decrypted_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(decrypted_mime);
+ mailmime_free(decrypted_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ unlink(description_filename);
+ unlink(decrypted_filename);
+ unlink(encrypted_filename);
+
+ * result = multipart;
+
+ return MAIL_NO_ERROR;
+
+ unlink_description:
+ unlink(description_filename);
+ unlink_decrypted:
+ unlink(decrypted_filename);
+ unlink_encrypted:
+ unlink(encrypted_filename);
+ err:
+ return res;
+}
+
+
+static int mime_is_text(struct mailmime * build_info)
+{
+ if (build_info->mm_type == MAILMIME_SINGLE) {
+ if (build_info->mm_content_type != NULL) {
+ if (build_info->mm_content_type->ct_type->tp_type ==
+ MAILMIME_TYPE_DISCRETE_TYPE) {
+ if (build_info->mm_content_type->ct_type->tp_data.tp_discrete_type->dt_type ==
+ MAILMIME_DISCRETE_TYPE_TEXT)
+ return 1;
+ }
+ }
+ else
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int pgp_test_encrypted(struct mailprivacy * privacy,
+ mailmessage * msg, struct mailmime * mime)
+{
+ int r;
+ int res;
+
+ switch (mime->mm_type) {
+ case MAILMIME_MULTIPLE:
+ return (pgp_is_encrypted(mime) || pgp_is_signed(mime));
+
+ case MAILMIME_SINGLE:
+ /* clear sign or ASCII armor encryption */
+ if (mime_is_text(mime)) {
+ char * content;
+ size_t content_len;
+ char * parsed_content;
+ size_t parsed_content_len;
+ size_t cur_token;
+ int encoding;
+ struct mailmime_single_fields single_fields;
+
+ r = mailprivacy_msg_fetch_section(privacy, msg, mime,
+ &content, &content_len);
+ if (r != MAIL_NO_ERROR)
+ return 0;
+
+ mailmime_single_fields_init(&single_fields, mime->mm_mime_fields,
+ mime->mm_content_type);
+ if (single_fields.fld_encoding != NULL)
+ encoding = single_fields.fld_encoding->enc_type;
+ else
+ encoding = MAILMIME_MECHANISM_8BIT;
+
+ cur_token = 0;
+ r = mailmime_part_parse(content, content_len, &cur_token,
+ encoding, &parsed_content, &parsed_content_len);
+ mailprivacy_msg_fetch_result_free(privacy, msg, content);
+
+ if (r != MAILIMF_NO_ERROR)
+ return 0;
+
+ res = 0;
+
+ if (pgp_is_clearsigned(parsed_content, parsed_content_len))
+ res = 1;
+ else if (pgp_is_crypted_armor(parsed_content, parsed_content_len))
+ res = 1;
+
+ mmap_string_unref(parsed_content);
+
+ return res;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static int pgp_handler(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ int r;
+ struct mailmime * alternative_mime;
+
+ alternative_mime = NULL;
+ switch (mime->mm_type) {
+ case MAILMIME_MULTIPLE:
+ r = MAIL_ERROR_INVAL;
+ if (pgp_is_encrypted(mime)) {
+ r = pgp_decrypt(privacy, msg, mime, &alternative_mime);
+ }
+ else if (pgp_is_signed(mime)) {
+ r = pgp_verify(privacy, msg, mime, &alternative_mime);
+ }
+
+ if (r != MAIL_NO_ERROR)
+ return r;
+
+ * result = alternative_mime;
+
+ return MAIL_NO_ERROR;
+
+ case MAILMIME_SINGLE:
+ /* clear sign or ASCII armor encryption */
+ if (mime_is_text(mime)) {
+ char * content;
+ size_t content_len;
+ char * parsed_content;
+ size_t parsed_content_len;
+ size_t cur_token;
+ int encoding;
+ struct mailmime_single_fields single_fields;
+
+ r = mailprivacy_msg_fetch_section(privacy, msg, mime,
+ &content, &content_len);
+ if (r != MAIL_NO_ERROR)
+ return MAIL_ERROR_FETCH;
+
+ mailmime_single_fields_init(&single_fields, mime->mm_mime_fields,
+ mime->mm_content_type);
+ if (single_fields.fld_encoding != NULL)
+ encoding = single_fields.fld_encoding->enc_type;
+ else
+ encoding = MAILMIME_MECHANISM_8BIT;
+
+ cur_token = 0;
+ r = mailmime_part_parse(content, content_len, &cur_token,
+ encoding, &parsed_content, &parsed_content_len);
+ mailprivacy_msg_fetch_result_free(privacy, msg, content);
+
+ if (r != MAILIMF_NO_ERROR)
+ return MAIL_ERROR_PARSE;
+
+ r = MAIL_ERROR_INVAL;
+ if (pgp_is_clearsigned(parsed_content,
+ parsed_content_len)) {
+ r = pgp_verify_clearsigned(privacy,
+ msg, mime, parsed_content, parsed_content_len, &alternative_mime);
+ }
+ else if (pgp_is_crypted_armor(parsed_content,
+ parsed_content_len)) {
+ r = pgp_decrypt_armor(privacy,
+ msg, mime, parsed_content, parsed_content_len, &alternative_mime);
+ }
+
+ mmap_string_unref(parsed_content);
+
+ if (r != MAIL_NO_ERROR)
+ return r;
+
+ * result = alternative_mime;
+
+ return MAIL_NO_ERROR;
+ }
+ break;
+ }
+
+ return MAIL_ERROR_INVAL;
+}
+
+
+#if 0
+static void prepare_mime_single(struct mailmime * mime)
+{
+ struct mailmime_single_fields single_fields;
+ int encoding;
+ int r;
+
+ if (mime->mime_fields != NULL) {
+ mailmime_single_fields_init(&single_fields, mime->mime_fields,
+ mime->content_type);
+ if (single_fields.encoding != NULL) {
+ encoding = single_fields.encoding->type;
+ switch (encoding) {
+ case MAILMIME_MECHANISM_8BIT:
+ case MAILMIME_MECHANISM_7BIT:
+ case MAILMIME_MECHANISM_BINARY:
+ single_fields.encoding->type = MAILMIME_MECHANISM_QUOTED_PRINTABLE;
+ break;
+ }
+ }
+ else {
+ struct mailmime_mechanism * mechanism;
+ struct mailmime_field * field;
+
+ mechanism =
+ mailmime_mechanism_new(MAILMIME_MECHANISM_QUOTED_PRINTABLE, NULL);
+ if (mechanism == NULL)
+ return;
+
+ field = mailmime_field_new(MAILMIME_FIELD_TRANSFER_ENCODING,
+ NULL, mechanism, NULL, NULL, 0, NULL, NULL);
+ if (field == NULL) {
+ mailmime_mechanism_free(mechanism);
+ return;
+ }
+
+ r = clist_append(mime->mime_fields->list, field);
+ if (r < 0) {
+ mailmime_field_free(field);
+ return;
+ }
+ }
+ }
+
+ switch (mime->body->encoding) {
+ case MAILMIME_MECHANISM_8BIT:
+ case MAILMIME_MECHANISM_7BIT:
+ case MAILMIME_MECHANISM_BINARY:
+ mime->body->encoding = MAILMIME_MECHANISM_QUOTED_PRINTABLE;
+ mime->body->encoded = 0;
+ break;
+ }
+}
+
+/*
+ prepare_mime()
+
+ we assume we built ourself the message.
+*/
+
+static void prepare_mime(struct mailmime * mime)
+{
+ clistiter * cur;
+
+ switch (mime->type) {
+ case MAILMIME_SINGLE:
+ if (mime->body != NULL) {
+ prepare_mime_single(mime);
+ }
+ break;
+
+ case MAILMIME_MULTIPLE:
+ for(cur = clist_begin(mime->list) ; cur != NULL ; cur = clist_next(cur)) {
+ struct mailmime * child;
+
+ child = cur->data;
+
+ prepare_mime(child);
+ }
+ break;
+
+ case MAILMIME_MESSAGE:
+ if (mime->msg_mime) {
+ prepare_mime(mime->msg_mime);
+ }
+ break;
+ }
+}
+#endif
+
+static int pgp_sign_mime(struct mailprivacy * privacy,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ char signed_filename[PATH_MAX];
+ FILE * signed_f;
+ int res;
+ int r;
+ int col;
+ char signature_filename[PATH_MAX];
+ FILE * signature_f;
+ char command[PATH_MAX];
+ char quoted_signature_filename[PATH_MAX];
+ char quoted_signed_filename[PATH_MAX];
+ char default_key[PATH_MAX];
+ struct mailmime * signature_mime;
+ struct mailmime * multipart;
+ struct mailmime_content * content;
+ struct mailmime_parameter * param;
+ struct mailmime * signed_msg_mime;
+ char * dup_signature_filename;
+ char * email;
+
+ /* get signing key */
+
+ * default_key = '\0';
+ email = get_first_from_addr(mime);
+ if (email != NULL)
+ snprintf(default_key, sizeof(default_key),
+ "--default-key %s", email);
+
+ /* part to sign */
+
+ /* encode quoted printable all text parts */
+
+ mailprivacy_prepare_mime(mime);
+
+ signed_f = mailprivacy_get_tmp_file(privacy,
+ signed_filename, sizeof(signed_filename));
+ if (signed_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+
+ col = 0;
+ r = mailmime_write(signed_f, &col, mime);
+ if (r != MAILIMF_NO_ERROR) {
+ fclose(signed_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_signed;
+ }
+
+ fclose(signed_f);
+
+ /* prepare destination file for signature */
+
+ signature_f = mailprivacy_get_tmp_file(privacy,
+ signature_filename,
+ sizeof(signature_filename));
+ if (signature_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_signed;
+ }
+ fclose(signature_f);
+
+ r = mail_quote_filename(quoted_signed_filename,
+ sizeof(quoted_signed_filename), signed_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ r = mail_quote_filename(quoted_signature_filename,
+ sizeof(quoted_signature_filename), signature_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ snprintf(command, sizeof(command),
+ "gpg -q -a --batch --yes --digest-algo sha1 --out %s %s -b %s 2>/dev/null",
+ quoted_signature_filename, default_key, quoted_signed_filename);
+
+ r = system(command);
+ if (WEXITSTATUS(r) != 0) {
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_signature;
+ }
+
+ /* multipart */
+
+ multipart = mailprivacy_new_file_part(privacy, NULL,
+ "multipart/signed", -1);
+
+ content = multipart->mm_content_type;
+
+ param = mailmime_param_new_with_data("micalg", "pgp-sha1");
+ if (param == NULL) {
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ r = clist_append(content->ct_parameters, param);
+ if (r < 0) {
+ mailmime_parameter_free(param);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ param = mailmime_param_new_with_data("protocol",
+ "application/pgp-signature");
+ if (param == NULL) {
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ r = clist_append(content->ct_parameters, param);
+ if (r < 0) {
+ mailmime_parameter_free(param);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ /* signed part */
+
+ r = mailprivacy_get_part_from_file(privacy, 1,
+ signed_filename, &signed_msg_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = r;
+ goto unlink_signature;
+ }
+
+ mailprivacy_prepare_mime(signed_msg_mime);
+
+ r = mailmime_smart_add_part(multipart, signed_msg_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(signed_msg_mime);
+ mailmime_free(signed_msg_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ /* signature part */
+
+ /* reencode the signature file with CRLF */
+ dup_signature_filename = mailprivacy_dup_imf_file(privacy,
+ signature_filename);
+ if (dup_signature_filename == NULL) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_FILE;
+ goto unlink_signature;
+ }
+
+ /* replace the signature file */
+ unlink(signature_filename);
+ strncpy(signature_filename,
+ dup_signature_filename, sizeof(signature_filename));
+ signature_filename[sizeof(signature_filename) - 1] = '\0';
+
+ signature_mime = mailprivacy_new_file_part(privacy,
+ signature_filename,
+ "application/pgp-signature",
+ MAILMIME_MECHANISM_8BIT);
+ if (signature_mime == NULL) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ r = mailmime_smart_add_part(multipart, signature_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(signature_mime);
+ mailmime_free(signature_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ unlink(signature_filename);
+ unlink(signed_filename);
+
+ * result = multipart;
+
+ return MAIL_NO_ERROR;
+
+ unlink_signature:
+ unlink(signature_filename);
+ unlink_signed:
+ unlink(signed_filename);
+ err:
+ return res;
+}
+
+
+/* ********************************************************************* */
+/* find GPG recipient */
+
+static int recipient_add_mb(char * recipient, size_t * len,
+ struct mailimf_mailbox * mb)
+{
+ char buffer[PATH_MAX];
+ size_t buflen;
+
+ if (mb->mb_addr_spec != NULL) {
+ snprintf(buffer, sizeof(buffer), "-r %s ", mb->mb_addr_spec);
+ buflen = strlen(buffer);
+ if (buflen >= * len)
+ return MAIL_ERROR_MEMORY;
+
+ strncat(recipient, buffer, * len);
+ (* len) -= buflen;
+ }
+
+ return MAIL_NO_ERROR;
+}
+
+static int recipient_add_mb_list(char * recipient, size_t * len,
+ struct mailimf_mailbox_list * mb_list)
+{
+ clistiter * cur;
+ int r;
+
+ for(cur = clist_begin(mb_list->mb_list) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ struct mailimf_mailbox * mb;
+
+ mb = clist_content(cur);
+
+ r = recipient_add_mb(recipient, len, mb);
+ if (r != MAIL_NO_ERROR)
+ return r;
+ }
+
+ return MAIL_NO_ERROR;
+}
+
+static int recipient_add_group(char * recipient, size_t * len,
+ struct mailimf_group * group)
+{
+ return recipient_add_mb_list(recipient, len, group->grp_mb_list);
+}
+
+static int recipient_add_addr(char * recipient, size_t * len,
+ struct mailimf_address * addr)
+{
+ int r;
+
+ switch (addr->ad_type) {
+ case MAILIMF_ADDRESS_MAILBOX:
+ r = recipient_add_mb(recipient, len, addr->ad_data.ad_mailbox);
+ break;
+ case MAILIMF_ADDRESS_GROUP:
+ r = recipient_add_group(recipient, len, addr->ad_data.ad_group);
+ break;
+ default:
+ r = MAIL_ERROR_INVAL;
+ }
+
+ return r;
+}
+
+static int recipient_add_addr_list(char * recipient, size_t * len,
+ struct mailimf_address_list * addr_list)
+{
+ clistiter * cur;
+ int r;
+
+ for(cur = clist_begin(addr_list->ad_list) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ struct mailimf_address * addr;
+
+ addr = clist_content(cur);
+
+ r = recipient_add_addr(recipient, len, addr);
+ if (r != MAIL_NO_ERROR)
+ return r;
+ }
+
+ return MAIL_NO_ERROR;
+}
+
+
+static int collect_recipient(char * recipient, size_t size,
+ struct mailimf_fields * fields)
+{
+ struct mailimf_single_fields single_fields;
+ int r;
+ size_t remaining;
+ int res;
+
+ * recipient = '\0';
+ remaining = size;
+
+ if (fields != NULL)
+ mailimf_single_fields_init(&single_fields, fields);
+
+ if (single_fields.fld_to != NULL) {
+ r = recipient_add_addr_list(recipient, &remaining,
+ single_fields.fld_to->to_addr_list);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ }
+
+ if (single_fields.fld_cc != NULL) {
+ r = recipient_add_addr_list(recipient, &remaining,
+ single_fields.fld_cc->cc_addr_list);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ }
+
+ if (single_fields.fld_bcc != NULL) {
+ if (single_fields.fld_bcc->bcc_addr_list != NULL) {
+ r = recipient_add_addr_list(recipient, &remaining,
+ single_fields.fld_bcc->bcc_addr_list);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ }
+ }
+
+ return MAIL_NO_ERROR;
+
+ err:
+ return res;
+}
+
+
+#define PGP_VERSION "Version: 1\r\n"
+
+static int pgp_sign_encrypt_mime(struct mailprivacy * privacy,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ char original_filename[PATH_MAX];
+ FILE * original_f;
+ int res;
+ int r;
+ int col;
+ char encrypted_filename[PATH_MAX];
+ FILE * encrypted_f;
+ char version_filename[PATH_MAX];
+ FILE * version_f;
+ char command[PATH_MAX];
+ char quoted_original_filename[PATH_MAX];
+ char quoted_encrypted_filename[PATH_MAX];
+ char default_key[PATH_MAX];
+ struct mailmime * version_mime;
+ struct mailmime * multipart;
+ struct mailmime_content * content;
+ struct mailmime_parameter * param;
+ struct mailmime * encrypted_mime;
+ char recipient[PATH_MAX];
+ struct mailimf_fields * fields;
+ struct mailmime * root;
+ size_t written;
+ char * email;
+
+ root = mime;
+ while (root->mm_parent != NULL)
+ root = root->mm_parent;
+
+ fields = NULL;
+ if (root->mm_type == MAILMIME_MESSAGE)
+ fields = root->mm_data.mm_message.mm_fields;
+
+ /* recipient */
+
+ collect_recipient(recipient, sizeof(recipient), fields);
+
+ /* get signing key */
+
+ * default_key = '\0';
+ email = get_first_from_addr(mime);
+ if (email != NULL)
+ snprintf(default_key, sizeof(default_key),
+ "--default-key %s", email);
+
+ /* part to encrypt */
+
+ /* encode quoted printable all text parts */
+
+ mailprivacy_prepare_mime(mime);
+
+ original_f = mailprivacy_get_tmp_file(privacy, original_filename,
+ sizeof(original_filename));
+ if (original_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+
+ col = 0;
+ r = mailmime_write(original_f, &col, mime);
+ if (r != MAILIMF_NO_ERROR) {
+ fclose(original_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_original;
+ }
+
+ fclose(original_f);
+
+ /* prepare destination file for encryption */
+
+ encrypted_f = mailprivacy_get_tmp_file(privacy,
+ encrypted_filename,
+ sizeof(encrypted_filename));
+ if (encrypted_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_original;
+ }
+ fclose(encrypted_f);
+
+ r = mail_quote_filename(quoted_original_filename,
+ sizeof(quoted_original_filename), original_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ r = mail_quote_filename(quoted_encrypted_filename,
+ sizeof(quoted_encrypted_filename), encrypted_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ snprintf(command, sizeof(command),
+ "gpg -q %s -a --batch --yes --digest-algo sha1 --out %s -s %s -e %s 2>/dev/null",
+ recipient, quoted_encrypted_filename, default_key,
+ quoted_original_filename);
+
+ r = system(command);
+ if (WEXITSTATUS(r) != 0) {
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_encrypted;
+ }
+
+ /* multipart */
+
+ multipart = mailprivacy_new_file_part(privacy, NULL,
+ "multipart/encrypted", -1);
+
+ content = multipart->mm_content_type;
+
+ param = mailmime_param_new_with_data("protocol",
+ "application/pgp-encrypted");
+ if (param == NULL) {
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ r = clist_append(content->ct_parameters, param);
+ if (r < 0) {
+ mailmime_parameter_free(param);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ /* version part */
+
+ version_f = mailprivacy_get_tmp_file(privacy,
+ version_filename,
+ sizeof(version_filename));
+ if (version_f == NULL) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_FILE;
+ goto unlink_encrypted;
+ }
+ written = fwrite(PGP_VERSION, 1, sizeof(PGP_VERSION) - 1, version_f);
+ if (written != sizeof(PGP_VERSION) - 1) {
+ fclose(version_f);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_FILE;
+ goto unlink_encrypted;
+ }
+ fclose(version_f);
+
+ version_mime = mailprivacy_new_file_part(privacy,
+ version_filename,
+ "application/pgp-encrypted",
+ MAILMIME_MECHANISM_8BIT);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = r;
+ goto unlink_version;
+ }
+
+ r = mailmime_smart_add_part(multipart, version_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(version_mime);
+ mailmime_free(version_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_version;
+ }
+
+ /* encrypted part */
+
+ encrypted_mime = mailprivacy_new_file_part(privacy,
+ encrypted_filename,
+ "application/octet-stream",
+ MAILMIME_MECHANISM_8BIT);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = r;
+ goto unlink_version;
+ }
+
+ r = mailmime_smart_add_part(multipart, encrypted_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(encrypted_mime);
+ mailmime_free(encrypted_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_version;
+ }
+
+ unlink(version_filename);
+ unlink(encrypted_filename);
+ unlink(original_filename);
+
+ * result = multipart;
+
+ return MAIL_NO_ERROR;
+
+ unlink_version:
+ unlink(version_filename);
+ unlink_encrypted:
+ unlink(encrypted_filename);
+ unlink_original:
+ unlink(original_filename);
+ err:
+ return res;
+}
+
+
+static int pgp_encrypt_mime(struct mailprivacy * privacy,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ char original_filename[PATH_MAX];
+ FILE * original_f;
+ int res;
+ int r;
+ int col;
+ char encrypted_filename[PATH_MAX];
+ FILE * encrypted_f;
+ char version_filename[PATH_MAX];
+ FILE * version_f;
+ char command[PATH_MAX];
+ char quoted_original_filename[PATH_MAX];
+ char quoted_encrypted_filename[PATH_MAX];
+ struct mailmime * version_mime;
+ struct mailmime * multipart;
+ struct mailmime_content * content;
+ struct mailmime_parameter * param;
+ struct mailmime * encrypted_mime;
+ char recipient[PATH_MAX];
+ struct mailimf_fields * fields;
+ struct mailmime * root;
+ size_t written;
+
+ root = mime;
+ while (root->mm_parent != NULL)
+ root = root->mm_parent;
+
+ fields = NULL;
+ if (root->mm_type == MAILMIME_MESSAGE)
+ fields = root->mm_data.mm_message.mm_fields;
+
+ /* recipient */
+
+ collect_recipient(recipient, sizeof(recipient), fields);
+
+ /* part to encrypt */
+
+ /* encode quoted printable all text parts */
+
+ mailprivacy_prepare_mime(mime);
+
+ original_f = mailprivacy_get_tmp_file(privacy,
+ original_filename, sizeof(original_filename));
+ if (original_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+
+ col = 0;
+ r = mailmime_write(original_f, &col, mime);
+ if (r != MAILIMF_NO_ERROR) {
+ fclose(original_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_original;
+ }
+
+ fclose(original_f);
+
+ /* prepare destination file for encryption */
+
+ encrypted_f = mailprivacy_get_tmp_file(privacy,
+ encrypted_filename,
+ sizeof(encrypted_filename));
+ if (encrypted_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_original;
+ }
+ fclose(encrypted_f);
+
+ r = mail_quote_filename(quoted_original_filename,
+ sizeof(quoted_original_filename), original_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ r = mail_quote_filename(quoted_encrypted_filename,
+ sizeof(quoted_encrypted_filename), encrypted_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ snprintf(command, sizeof(command),
+ "gpg -q %s -a --batch --yes --out %s -e %s 2>/dev/null",
+ recipient, quoted_encrypted_filename, quoted_original_filename);
+
+ r = system(command);
+ if (WEXITSTATUS(r) != 0) {
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_encrypted;
+ }
+
+ /* multipart */
+
+ multipart = mailprivacy_new_file_part(privacy, NULL,
+ "multipart/encrypted", -1);
+
+ content = multipart->mm_content_type;
+
+ param = mailmime_param_new_with_data("protocol",
+ "application/pgp-encrypted");
+ if (param == NULL) {
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ r = clist_append(content->ct_parameters, param);
+ if (r < 0) {
+ mailmime_parameter_free(param);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ /* version part */
+
+ version_f = mailprivacy_get_tmp_file(privacy,
+ version_filename, sizeof(version_filename));
+ if (version_f == NULL) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_FILE;
+ goto unlink_encrypted;
+ }
+ written = fwrite(PGP_VERSION, 1, sizeof(PGP_VERSION) - 1, version_f);
+ if (written != sizeof(PGP_VERSION) - 1) {
+ fclose(version_f);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_FILE;
+ goto unlink_encrypted;
+ }
+ fclose(version_f);
+
+ version_mime = mailprivacy_new_file_part(privacy,
+ version_filename,
+ "application/pgp-encrypted",
+ MAILMIME_MECHANISM_8BIT);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = r;
+ goto unlink_version;
+ }
+
+ r = mailmime_smart_add_part(multipart, version_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(version_mime);
+ mailmime_free(version_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_version;
+ }
+
+ /* encrypted part */
+
+ encrypted_mime = mailprivacy_new_file_part(privacy,
+ encrypted_filename,
+ "application/octet-stream",
+ MAILMIME_MECHANISM_8BIT);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = r;
+ goto unlink_version;
+ }
+
+ r = mailmime_smart_add_part(multipart, encrypted_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(encrypted_mime);
+ mailmime_free(encrypted_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_version;
+ }
+
+ unlink(version_filename);
+ unlink(encrypted_filename);
+ unlink(original_filename);
+
+ * result = multipart;
+
+ return MAIL_NO_ERROR;
+
+ unlink_version:
+ unlink(version_filename);
+ unlink_encrypted:
+ unlink(encrypted_filename);
+ unlink_original:
+ unlink(original_filename);
+ err:
+ return res;
+}
+
+static int pgp_clear_sign(struct mailprivacy * privacy,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ char default_key[PATH_MAX];
+ char original_filename[PATH_MAX];
+ FILE * original_f;
+ char signed_filename[PATH_MAX];
+ FILE * signed_f;
+ char quoted_original_filename[PATH_MAX];
+ char quoted_signed_filename[PATH_MAX];
+ int col;
+ struct mailmime * signed_mime;
+ int res;
+ int r;
+ char command[PATH_MAX];
+ struct mailmime_content * content_type;
+ char * email;
+
+ if (mime->mm_type != MAILMIME_SINGLE) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ if (mime->mm_data.mm_single == NULL) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ /* get signing key */
+
+ * default_key = '\0';
+ email = get_first_from_addr(mime);
+ if (email != NULL)
+ snprintf(default_key, sizeof(default_key),
+ "--default-key %s", email);
+
+ /* get part to sign */
+
+ original_f = mailprivacy_get_tmp_file(privacy,
+ original_filename,
+ sizeof(original_filename));
+ if (original_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+
+ col = 0;
+ r = mailmime_data_write(original_f, &col, mime->mm_data.mm_single, 1);
+ if (r != MAILIMF_NO_ERROR) {
+ fclose(original_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_original;
+ }
+ fclose(original_f);
+
+ signed_f = mailprivacy_get_tmp_file(privacy,
+ signed_filename,
+ sizeof(signed_filename));
+ if (signed_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_original;
+ }
+ fclose(signed_f);
+
+ r = mail_quote_filename(quoted_original_filename,
+ sizeof(quoted_original_filename), original_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signed;
+ }
+
+ r = mail_quote_filename(quoted_signed_filename,
+ sizeof(quoted_signed_filename), signed_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signed;
+ }
+
+ snprintf(command, sizeof(command),
+ "gpg -q --batch --yes --digest-algo sha1 --out %s %s --clearsign %s 2>/dev/null",
+ quoted_signed_filename, default_key, quoted_original_filename);
+
+ r = system(command);
+ if (WEXITSTATUS(r) != 0) {
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_signed;
+ }
+
+ /* building the signed part */
+
+ signed_mime = mailprivacy_new_file_part(privacy, signed_filename,
+ NULL, MAILMIME_MECHANISM_8BIT);
+ if (signed_mime == NULL) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signed;
+ }
+
+ /* place original content type */
+
+ content_type = mailmime_content_dup(mime->mm_content_type);
+ if (content_type == NULL) {
+ mailprivacy_mime_clear(signed_mime);
+ mailmime_free(signed_mime);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signed;
+ }
+
+ mailmime_content_free(signed_mime->mm_content_type);
+ signed_mime->mm_content_type = content_type;
+
+ /* place original MIME fields */
+
+ if (mime->mm_mime_fields != NULL) {
+ struct mailmime_fields * mime_fields;
+ clistiter * cur;
+
+ mime_fields = mailprivacy_mime_fields_dup(privacy,
+ mime->mm_mime_fields);
+ if (mime_fields == NULL) {
+ mailprivacy_mime_clear(signed_mime);
+ mailmime_free(signed_mime);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signed;
+ }
+ for(cur = clist_begin(mime_fields->fld_list) ;
+ cur != NULL ; cur = clist_next(cur)) {
+ struct mailmime_field * field;
+
+ field = clist_content(cur);
+ if (field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING) {
+ mailmime_field_free(field);
+ clist_delete(mime_fields->fld_list, cur);
+ break;
+ }
+ }
+ clist_concat(signed_mime->mm_mime_fields->fld_list,
+ mime_fields->fld_list);
+ mailmime_fields_free(mime_fields);
+ }
+
+ unlink(signed_filename);
+ unlink(original_filename);
+
+ * result = signed_mime;
+
+ return MAIL_NO_ERROR;
+
+ unlink_signed:
+ unlink(signed_filename);
+ unlink_original:
+ unlink(original_filename);
+ err:
+ return res;
+}
+
+
+static int pgp_armor_encrypt(struct mailprivacy * privacy,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ char original_filename[PATH_MAX];
+ FILE * original_f;
+ char encrypted_filename[PATH_MAX];
+ FILE * encrypted_f;
+ char quoted_original_filename[PATH_MAX];
+ char quoted_encrypted_filename[PATH_MAX];
+ int col;
+ struct mailmime * encrypted_mime;
+ int res;
+ int r;
+ char command[PATH_MAX];
+ struct mailmime_content * content_type;
+ struct mailmime * root;
+ struct mailimf_fields * fields;
+ char recipient[PATH_MAX];
+
+ if (mime->mm_type != MAILMIME_SINGLE) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ if (mime->mm_data.mm_single == NULL) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ root = mime;
+ while (root->mm_parent != NULL)
+ root = root->mm_parent;
+
+ fields = NULL;
+ if (root->mm_type == MAILMIME_MESSAGE)
+ fields = root->mm_data.mm_message.mm_fields;
+
+ /* recipient */
+
+ collect_recipient(recipient, sizeof(recipient), fields);
+
+ /* get part to encrypt */
+
+ original_f = mailprivacy_get_tmp_file(privacy, original_filename,
+ sizeof(original_filename));
+ if (original_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+
+ col = 0;
+ r = mailmime_data_write(original_f, &col, mime->mm_data.mm_single, 1);
+ if (r != MAILIMF_NO_ERROR) {
+ fclose(original_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_original;
+ }
+ fclose(original_f);
+
+ encrypted_f = mailprivacy_get_tmp_file(privacy,
+ encrypted_filename,
+ sizeof(encrypted_filename));
+ if (encrypted_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_original;
+ }
+ fclose(encrypted_f);
+
+ r = mail_quote_filename(quoted_original_filename,
+ sizeof(quoted_original_filename), original_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ r = mail_quote_filename(quoted_encrypted_filename,
+ sizeof(quoted_encrypted_filename), encrypted_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ snprintf(command, sizeof(command),
+ "gpg -q %s --batch --yes --out %s -e --armor %s 2>/dev/null",
+ recipient, quoted_encrypted_filename, quoted_original_filename);
+
+ r = system(command);
+ if (WEXITSTATUS(r) != 0) {
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_encrypted;
+ }
+
+ /* building the encrypted part */
+
+ encrypted_mime = mailprivacy_new_file_part(privacy,
+ encrypted_filename,
+ "application/octet-stream",
+ MAILMIME_MECHANISM_8BIT);
+ if (encrypted_mime == NULL) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ /* place original content type */
+
+ content_type = mailmime_content_dup(mime->mm_content_type);
+ if (content_type == NULL) {
+ mailprivacy_mime_clear(encrypted_mime);
+ mailmime_free(encrypted_mime);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ mailmime_content_free(encrypted_mime->mm_content_type);
+ encrypted_mime->mm_content_type = content_type;
+
+ /* place original MIME fields */
+
+ if (mime->mm_mime_fields != NULL) {
+ struct mailmime_fields * mime_fields;
+ clistiter * cur;
+
+ mime_fields = mailprivacy_mime_fields_dup(privacy, mime->mm_mime_fields);
+ if (mime_fields == NULL) {
+ mailprivacy_mime_clear(encrypted_mime);
+ mailmime_free(encrypted_mime);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+ for(cur = clist_begin(mime_fields->fld_list) ;
+ cur != NULL ; cur = clist_next(cur)) {
+ struct mailmime_field * field;
+
+ field = clist_content(cur);
+ if (field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING) {
+ mailmime_field_free(field);
+ clist_delete(mime_fields->fld_list, cur);
+ break;
+ }
+ }
+ clist_concat(encrypted_mime->mm_mime_fields->fld_list,
+ mime_fields->fld_list);
+ mailmime_fields_free(mime_fields);
+ }
+
+ unlink(encrypted_filename);
+ unlink(original_filename);
+
+ * result = encrypted_mime;
+
+ return MAIL_NO_ERROR;
+
+ unlink_encrypted:
+ unlink(encrypted_filename);
+ unlink_original:
+ unlink(original_filename);
+ err:
+ return res;
+}
+
+
+static int pgp_armor_sign_encrypt(struct mailprivacy * privacy,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ char default_key[PATH_MAX];
+ char original_filename[PATH_MAX];
+ FILE * original_f;
+ char encrypted_filename[PATH_MAX];
+ FILE * encrypted_f;
+ char quoted_original_filename[PATH_MAX];
+ char quoted_encrypted_filename[PATH_MAX];
+ int col;
+ struct mailmime * encrypted_mime;
+ int res;
+ int r;
+ char command[PATH_MAX];
+ struct mailmime_content * content_type;
+ struct mailmime * root;
+ struct mailimf_fields * fields;
+ char recipient[PATH_MAX];
+ char * email;
+
+ if (mime->mm_type != MAILMIME_SINGLE) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ if (mime->mm_data.mm_single == NULL) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ /* get signing key */
+
+ * default_key = '\0';
+ email = get_first_from_addr(mime);
+ if (email != NULL)
+ snprintf(default_key, sizeof(default_key),
+ "--default-key %s", email);
+
+ root = mime;
+ while (root->mm_parent != NULL)
+ root = root->mm_parent;
+
+ fields = NULL;
+ if (root->mm_type == MAILMIME_MESSAGE)
+ fields = root->mm_data.mm_message.mm_fields;
+
+ /* recipient */
+
+ collect_recipient(recipient, sizeof(recipient), fields);
+
+ /* get part to encrypt */
+
+ original_f = mailprivacy_get_tmp_file(privacy,
+ original_filename,
+ sizeof(original_filename));
+ if (original_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+
+ col = 0;
+ r = mailmime_data_write(original_f, &col, mime->mm_data.mm_single, 1);
+ if (r != MAILIMF_NO_ERROR) {
+ fclose(original_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_original;
+ }
+ fclose(original_f);
+
+ encrypted_f = mailprivacy_get_tmp_file(privacy,
+ encrypted_filename,
+ sizeof(encrypted_filename));
+ if (encrypted_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_original;
+ }
+ fclose(encrypted_f);
+
+ r = mail_quote_filename(quoted_original_filename,
+ sizeof(quoted_original_filename), original_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ r = mail_quote_filename(quoted_encrypted_filename,
+ sizeof(quoted_encrypted_filename), encrypted_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ snprintf(command, sizeof(command),
+ "gpg -q %s --batch --yes --digest-algo sha1 "
+ "--out %s %s -e -s -a %s 2>/dev/null",
+ recipient, encrypted_filename, default_key, original_filename);
+
+ r = system(command);
+ if (WEXITSTATUS(r) != 0) {
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_encrypted;
+ }
+
+ /* building the encrypted part */
+
+ encrypted_mime = mailprivacy_new_file_part(privacy,
+ encrypted_filename,
+ "application/octet-stream",
+ MAILMIME_MECHANISM_8BIT);
+ if (encrypted_mime == NULL) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ /* place original content type */
+
+ content_type = mailmime_content_dup(mime->mm_content_type);
+ if (content_type == NULL) {
+ mailprivacy_mime_clear(encrypted_mime);
+ mailmime_free(encrypted_mime);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ mailmime_content_free(encrypted_mime->mm_content_type);
+ encrypted_mime->mm_content_type = content_type;
+
+ /* place original MIME fields */
+
+ if (mime->mm_mime_fields != NULL) {
+ struct mailmime_fields * mime_fields;
+ clistiter * cur;
+
+ mime_fields = mailprivacy_mime_fields_dup(privacy, mime->mm_mime_fields);
+ if (mime_fields == NULL) {
+ mailprivacy_mime_clear(encrypted_mime);
+ mailmime_free(encrypted_mime);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+ for(cur = clist_begin(mime_fields->fld_list) ;
+ cur != NULL ; cur = clist_next(cur)) {
+ struct mailmime_field * field;
+
+ field = clist_content(cur);
+ if (field->fld_type == MAILMIME_FIELD_TRANSFER_ENCODING) {
+ mailmime_field_free(field);
+ clist_delete(mime_fields->fld_list, cur);
+ break;
+ }
+ }
+ clist_concat(encrypted_mime->mm_mime_fields->fld_list,
+ mime_fields->fld_list);
+ mailmime_fields_free(mime_fields);
+ }
+
+ unlink(encrypted_filename);
+ unlink(original_filename);
+
+ * result = encrypted_mime;
+
+ return MAIL_NO_ERROR;
+
+ unlink_encrypted:
+ unlink(encrypted_filename);
+ unlink_original:
+ unlink(original_filename);
+ err:
+ return res;
+}
+
+
+static struct mailprivacy_encryption pgp_encryption_tab[] = {
+ /* PGP signed part */
+ {
+ .name = "signed",
+ .description = "PGP signed part",
+ .encrypt = pgp_sign_mime,
+ },
+
+ /* PGP encrypted part */
+
+ {
+ .name = "encrypted",
+ .description = "PGP encrypted part",
+ .encrypt = pgp_encrypt_mime,
+ },
+
+ /* PGP signed & encrypted part */
+
+ {
+ .name = "signed-encrypted",
+ .description = "PGP signed & encrypted part",
+ .encrypt = pgp_sign_encrypt_mime,
+ },
+
+ /* PGP clear signed part */
+
+ {
+ .name = "clear-signed",
+ .description = "PGP clear signed part",
+ .encrypt = pgp_clear_sign,
+ },
+
+ /* PGP armor encrypted part */
+
+ {
+ .name = "encrypted-armor",
+ .description = "PGP ASCII armor encrypted part",
+ .encrypt = pgp_armor_encrypt,
+ },
+
+ /* PGP armor signed & encrypted part */
+
+ {
+ .name = "signed-encrypted-armor",
+ .description = "PGP ASCII armor signed & encrypted part",
+ .encrypt = pgp_armor_sign_encrypt,
+ },
+};
+
+static struct mailprivacy_protocol pgp_protocol = {
+ .name = "pgp",
+ .description = "OpenPGP",
+
+ .is_encrypted = pgp_test_encrypted,
+ .decrypt = pgp_handler,
+
+ .encryption_count =
+ (sizeof(pgp_encryption_tab) / sizeof(pgp_encryption_tab[0])),
+
+ .encryption_tab = pgp_encryption_tab,
+};
+
+int mailprivacy_gnupg_init(struct mailprivacy * privacy)
+{
+ return mailprivacy_register(privacy, &pgp_protocol);
+}
+
+void mailprivacy_gnupg_done(struct mailprivacy * privacy)
+{
+ mailprivacy_unregister(privacy, &pgp_protocol);
+}
diff --git a/libetpan/src/engine/mailprivacy_gnupg.h b/libetpan/src/engine/mailprivacy_gnupg.h
new file mode 100644
index 0000000..013c8a4
--- a/dev/null
+++ b/libetpan/src/engine/mailprivacy_gnupg.h
@@ -0,0 +1,46 @@
+/*
+ * etPan! -- a mail user agent
+ *
+ * Copyright (C) 2001, 2005 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef MAIL_PRIVACY_GNUPG_H
+
+#define MAIL_PRIVACY_GNUPG_H
+
+#include <libetpan/mailprivacy_types.h>
+
+int mailprivacy_gnupg_init(struct mailprivacy * privacy);
+
+void mailprivacy_gnupg_done(struct mailprivacy * privacy);
+
+#endif
diff --git a/libetpan/src/engine/mailprivacy_smime.c b/libetpan/src/engine/mailprivacy_smime.c
new file mode 100644
index 0000000..43eb69f
--- a/dev/null
+++ b/libetpan/src/engine/mailprivacy_smime.c
@@ -0,0 +1,1755 @@
+/*
+ * libEtPan! -- a mail library
+ *
+ * Copyright (C) 2001, 2005 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include "mailprivacy_smime.h"
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include "mailprivacy_tools.h"
+#include "mailprivacy.h"
+#include <stdlib.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <libetpan/libetpan-config.h>
+
+/*
+ global variable
+
+ TODO : instance of privacy drivers
+*/
+
+static char cert_dir[PATH_MAX] = "";
+static chash * certificates = NULL;
+static chash * private_keys = NULL;
+static char CAcert_dir[PATH_MAX] = "";
+static char * CAfile = NULL;
+static int CA_check = 1;
+static int store_cert = 0;
+static char private_keys_dir[PATH_MAX] = "";
+
+static char * get_cert_file(char * email);
+
+static char * get_private_key_file(char * email);
+
+
+static int smime_is_signed(struct mailmime * mime)
+{
+ if (mime->mm_content_type != NULL) {
+ clistiter * cur;
+
+ for(cur = clist_begin(mime->mm_content_type->ct_parameters) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ struct mailmime_parameter * param;
+
+ param = cur->data;
+
+ if ((strcasecmp(param->pa_name, "protocol") == 0) &&
+ ((strcasecmp(param->pa_value, "application/x-pkcs7-signature") == 0) ||
+ (strcasecmp(param->pa_value, "application/pkcs7-signature") == 0)))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int smime_is_encrypted(struct mailmime * mime)
+{
+ if (mime->mm_content_type != NULL) {
+ if ((strcasecmp(mime->mm_content_type->ct_subtype, "x-pkcs7-mime") == 0) ||
+ (strcasecmp(mime->mm_content_type->ct_subtype, "pkcs7-mime") == 0))
+ return 1;
+ }
+
+ return 0;
+}
+
+#define BUF_SIZE 1024
+
+enum {
+ NO_ERROR_SMIME = 0,
+ ERROR_SMIME_CHECK,
+ ERROR_SMIME_COMMAND,
+ ERROR_SMIME_FILE,
+};
+
+/* write output to a file */
+
+static int get_smime_output(FILE * dest_f, char * command)
+{
+ FILE * p;
+ char buf[BUF_SIZE];
+ size_t size;
+ int res;
+ int status;
+ char command_redirected[PATH_MAX];
+
+ snprintf(command_redirected, sizeof(command_redirected), "%s 2>&1", command);
+
+ /*
+ flush buffer so that it is not flushed more than once when forking
+ */
+ fflush(dest_f);
+
+ p = popen(command_redirected, "r");
+ if (p == NULL) {
+ res = ERROR_SMIME_COMMAND;
+ goto err;
+ }
+
+ while ((size = fread(buf, 1, sizeof(buf), p)) != 0) {
+ size_t written;
+
+ written = fwrite(buf, 1, size, dest_f);
+ if (written != size) {
+ res = ERROR_SMIME_FILE;
+ goto close;
+ }
+ }
+ status = pclose(p);
+
+ if (WEXITSTATUS(status) != 0)
+ return ERROR_SMIME_CHECK;
+ else
+ return NO_ERROR_SMIME;
+
+ close:
+ pclose(p);
+ err:
+ return res;
+}
+
+static char * get_first_from_addr(struct mailmime * mime)
+{
+ clistiter * cur;
+ struct mailimf_single_fields single_fields;
+ struct mailimf_fields * fields;
+ struct mailimf_mailbox * mb;
+
+ while (mime->mm_parent != NULL)
+ mime = mime->mm_parent;
+
+ if (mime->mm_type != MAILMIME_MESSAGE)
+ return NULL;
+
+ fields = mime->mm_data.mm_message.mm_fields;
+ if (fields == NULL)
+ return NULL;
+
+ mailimf_single_fields_init(&single_fields, fields);
+
+ if (single_fields.fld_from == NULL)
+ return NULL;
+
+ cur = clist_begin(single_fields.fld_from->frm_mb_list->mb_list);
+ if (cur == NULL)
+ return NULL;
+
+ mb = clist_content(cur);
+
+ return mb->mb_addr_spec;
+}
+
+#define SMIME_DECRYPT_DESCRIPTION "S/MIME encrypted part\r\n"
+#define SMIME_DECRYPT_FAILED "S/MIME decryption FAILED\r\n"
+#define SMIME_DECRYPT_SUCCESS "S/MIME decryption success\r\n"
+
+static int smime_decrypt(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ char smime_filename[PATH_MAX];
+ char quoted_smime_filename[PATH_MAX];
+ char description_filename[PATH_MAX];
+ FILE * description_f;
+ char decrypted_filename[PATH_MAX];
+ FILE * decrypted_f;
+ char command[PATH_MAX];
+ struct mailmime * description_mime;
+ struct mailmime * decrypted_mime;
+ int r;
+ int res;
+ int sign_ok;
+ char quoted_decrypted_filename[PATH_MAX];
+ struct mailmime * multipart;
+ char * smime_cert;
+ char * smime_key;
+ char quoted_smime_cert[PATH_MAX];
+ char quoted_smime_key[PATH_MAX];
+ char * email;
+ chashiter * iter;
+
+ /* fetch the whole multipart and write it to a file */
+
+ r = mailprivacy_fetch_mime_body_to_file(privacy,
+ smime_filename, sizeof(smime_filename),
+ msg, mime);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ /* we are in a safe directory */
+
+ decrypted_f = mailprivacy_get_tmp_file(privacy,
+ decrypted_filename,
+ sizeof(decrypted_filename));
+ if (decrypted_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_smime;
+ }
+ fclose(decrypted_f);
+
+ sign_ok = 0;
+ for(iter = chash_begin(private_keys) ; iter != NULL ;
+ iter = chash_next(private_keys, iter)) {
+ chashdatum key;
+ char email_buf[BUF_SIZE];
+
+ chash_key(iter, &key);
+
+ if (key.len >= sizeof(email_buf))
+ continue;
+
+ strncpy(email_buf, key.data, key.len);
+ email_buf[key.len] = '\0';
+ email = email_buf;
+
+ /* description */
+
+ description_f = mailprivacy_get_tmp_file(privacy,
+ description_filename,
+ sizeof(description_filename));
+ if (description_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_decrypted;
+ }
+
+ fprintf(description_f, SMIME_DECRYPT_DESCRIPTION);
+
+ /* get encryption key */
+
+#if 0
+ email = get_first_from_addr(mime);
+ if (email == NULL) {
+ fclose(description_f);
+ res = MAIL_ERROR_INVAL;
+ goto unlink_description;
+ }
+#endif
+
+ smime_key = get_private_key_file(email);
+ smime_cert = get_cert_file(email);
+ if ((smime_cert == NULL) || (smime_key == NULL)) {
+ fclose(description_f);
+ res = MAIL_ERROR_INVAL;
+ goto unlink_description;
+ }
+
+ r = mail_quote_filename(quoted_smime_cert, sizeof(quoted_smime_cert),
+ smime_cert);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ r = mail_quote_filename(quoted_smime_key, sizeof(quoted_smime_key),
+ smime_key);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* run the command */
+
+ r = mail_quote_filename(quoted_smime_filename,
+ sizeof(quoted_smime_filename), smime_filename);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ r = mail_quote_filename(quoted_decrypted_filename,
+ sizeof(quoted_decrypted_filename), decrypted_filename);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ sign_ok = 0;
+ snprintf(command, PATH_MAX,
+ "openssl smime -decrypt -in %s -out %s -inkey %s -recip %s",
+ quoted_smime_filename, quoted_decrypted_filename,
+ quoted_smime_key, quoted_smime_cert);
+
+ r = get_smime_output(description_f, command);
+ switch (r) {
+ case NO_ERROR_SMIME:
+ sign_ok = 1;
+ break;
+ case ERROR_SMIME_CHECK:
+ sign_ok = 0;
+ break;
+ case ERROR_SMIME_COMMAND:
+ fclose(description_f);
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_description;
+ case ERROR_SMIME_FILE:
+ fclose(description_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_description;
+ }
+
+ if (sign_ok) {
+ fprintf(description_f, SMIME_DECRYPT_SUCCESS);
+ fclose(description_f);
+ break;
+ }
+ else {
+ fclose(description_f);
+ unlink(description_filename);
+ }
+ }
+
+ if (!sign_ok) {
+ description_f = mailprivacy_get_tmp_file(privacy,
+ description_filename,
+ sizeof(description_filename));
+ if (description_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_decrypted;
+ }
+
+ fprintf(description_f, SMIME_DECRYPT_DESCRIPTION);
+ fprintf(description_f, SMIME_DECRYPT_FAILED);
+ fclose(description_f);
+ }
+
+ /* building multipart */
+
+ r = mailmime_new_with_content("multipart/x-decrypted", NULL, &multipart);
+ if (r != MAILIMF_NO_ERROR) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* building the description part */
+
+ description_mime = mailprivacy_new_file_part(privacy,
+ description_filename,
+ "text/plain", MAILMIME_MECHANISM_8BIT);
+ if (description_mime == NULL) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* adds the description part */
+
+ r = mailmime_smart_add_part(multipart, description_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(description_mime);
+ mailmime_free(description_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* building the decrypted part */
+
+ r = mailprivacy_get_part_from_file(privacy, 1,
+ decrypted_filename, &decrypted_mime);
+ if (r == MAIL_NO_ERROR) {
+ /* adds the decrypted part */
+
+ r = mailmime_smart_add_part(multipart, decrypted_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(decrypted_mime);
+ mailmime_free(decrypted_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+ }
+
+ unlink(description_filename);
+ unlink(decrypted_filename);
+ unlink(smime_filename);
+
+ * result = multipart;
+
+ return MAIL_NO_ERROR;
+
+ unlink_description:
+ unlink(description_filename);
+ unlink_decrypted:
+ unlink(decrypted_filename);
+ unlink_smime:
+ unlink(smime_filename);
+ err:
+ return res;
+}
+
+
+static int get_cert_from_sig(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime);
+
+#define SMIME_VERIFY_DESCRIPTION "S/MIME verify signed message\r\n"
+#define SMIME_VERIFY_FAILED "S/MIME verification FAILED\r\n"
+#define SMIME_VERIFY_SUCCESS "S/MIME verification success\r\n"
+
+static int
+smime_verify(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ char smime_filename[PATH_MAX];
+ char quoted_smime_filename[PATH_MAX];
+ int res;
+ int r;
+ char command[PATH_MAX];
+ int sign_ok;
+ struct mailmime * description_mime;
+ FILE * description_f;
+ char description_filename[PATH_MAX];
+ struct mailmime * multipart;
+ char stripped_filename[PATH_MAX];
+ struct mailmime * stripped_mime;
+ char quoted_stripped_filename[PATH_MAX];
+ FILE * stripped_f;
+ char check_CA[PATH_MAX];
+ char quoted_CAfile[PATH_MAX];
+ char noverify[PATH_MAX];
+
+ if (store_cert)
+ get_cert_from_sig(privacy, msg, mime);
+
+ * check_CA = '\0';
+ if (CAfile != NULL) {
+ r = mail_quote_filename(quoted_CAfile, sizeof(quoted_CAfile), CAfile);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto err;
+ }
+
+ snprintf(check_CA, sizeof(check_CA), "-CAfile %s", quoted_CAfile);
+ }
+
+ * noverify = '\0';
+ if (!CA_check) {
+ snprintf(noverify, sizeof(noverify), "-noverify");
+ }
+
+ /* fetch the whole multipart and write it to a file */
+
+ r = mailprivacy_fetch_mime_body_to_file(privacy,
+ smime_filename, sizeof(smime_filename),
+ msg, mime);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ stripped_f = mailprivacy_get_tmp_file(privacy,
+ stripped_filename,
+ sizeof(stripped_filename));
+ if (stripped_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_smime;
+ }
+ fclose(stripped_f);
+
+ /* description */
+
+ description_f = mailprivacy_get_tmp_file(privacy,
+ description_filename,
+ sizeof(description_filename));
+ if (description_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_stripped;
+ }
+
+ fprintf(description_f, SMIME_VERIFY_DESCRIPTION);
+
+ /* run the command */
+
+ r = mail_quote_filename(quoted_smime_filename,
+ sizeof(quoted_smime_filename), smime_filename);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ r = mail_quote_filename(quoted_stripped_filename,
+ sizeof(quoted_stripped_filename), stripped_filename);
+ if (r < 0) {
+ fclose(description_f);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ sign_ok = 0;
+ snprintf(command, PATH_MAX, "openssl smime -verify -in %s -out %s %s %s",
+ quoted_smime_filename, quoted_stripped_filename, check_CA, noverify);
+
+ r = get_smime_output(description_f, command);
+ switch (r) {
+ case NO_ERROR_SMIME:
+ sign_ok = 1;
+ break;
+ case ERROR_SMIME_CHECK:
+ sign_ok = 0;
+ break;
+ case ERROR_SMIME_COMMAND:
+ fclose(description_f);
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_description;
+ case ERROR_SMIME_FILE:
+ fclose(description_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_description;
+ }
+ if (sign_ok)
+ fprintf(description_f, SMIME_VERIFY_SUCCESS);
+ else
+ fprintf(description_f, SMIME_VERIFY_FAILED);
+ fclose(description_f);
+
+ /* building multipart */
+
+ r = mailmime_new_with_content("multipart/x-verified", NULL, &multipart);
+ if (r != MAILIMF_NO_ERROR) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* building the description part */
+
+ description_mime = mailprivacy_new_file_part(privacy,
+ description_filename,
+ "text/plain", MAILMIME_MECHANISM_8BIT);
+ if (description_mime == NULL) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ /* adds the description part */
+
+ r = mailmime_smart_add_part(multipart, description_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(description_mime);
+ mailmime_free(description_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ r = mailprivacy_get_part_from_file(privacy, 1,
+ stripped_filename, &stripped_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = r;
+ goto unlink_description;
+ }
+
+ r = mailmime_smart_add_part(multipart, stripped_mime);
+ if (r != MAIL_NO_ERROR) {
+ mailprivacy_mime_clear(stripped_mime);
+ mailmime_free(stripped_mime);
+ mailprivacy_mime_clear(multipart);
+ mailmime_free(multipart);
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_description;
+ }
+
+ unlink(description_filename);
+ unlink(stripped_filename);
+ unlink(smime_filename);
+
+ * result = multipart;
+
+ return MAIL_NO_ERROR;
+
+ unlink_description:
+ unlink(description_filename);
+ unlink_stripped:
+ unlink(stripped_filename);
+ unlink_smime:
+ unlink(smime_filename);
+ err:
+ return res;
+}
+
+static int smime_test_encrypted(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime)
+{
+ switch (mime->mm_type) {
+ case MAILMIME_MULTIPLE:
+ return smime_is_signed(mime);
+
+ case MAILMIME_SINGLE:
+ return smime_is_encrypted(mime);
+ }
+
+ return 0;
+}
+
+static int smime_handler(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ int r;
+ struct mailmime * alternative_mime;
+
+ alternative_mime = NULL;
+ switch (mime->mm_type) {
+ case MAILMIME_MULTIPLE:
+ r = MAIL_ERROR_INVAL;
+ if (smime_is_signed(mime))
+ r = smime_verify(privacy, msg, mime, &alternative_mime);
+
+ if (r != MAIL_NO_ERROR)
+ return r;
+
+ * result = alternative_mime;
+
+ return MAIL_NO_ERROR;
+
+ case MAILMIME_SINGLE:
+ r = MAIL_ERROR_INVAL;
+ if (smime_is_encrypted(mime))
+ r = smime_decrypt(privacy, msg, mime, &alternative_mime);
+
+ if (r != MAIL_NO_ERROR)
+ return r;
+
+ * result = alternative_mime;
+
+ return MAIL_NO_ERROR;
+ }
+
+ return MAIL_ERROR_INVAL;
+}
+
+
+
+
+static void strip_mime_headers(struct mailmime * mime)
+{
+ struct mailmime_fields * fields;
+ clistiter * cur;
+
+ fields = mime->mm_mime_fields;
+
+ if (fields == NULL)
+ return;
+
+ for(cur = clist_begin(fields->fld_list) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ struct mailmime_field * field;
+
+ field = clist_content(cur);
+
+ if (field->fld_type == MAILMIME_FIELD_VERSION) {
+ mailmime_field_free(field);
+ clist_delete(fields->fld_list, cur);
+ break;
+ }
+ }
+}
+
+static int smime_sign(struct mailprivacy * privacy,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ char signed_filename[PATH_MAX];
+ FILE * signed_f;
+ int res;
+ int r;
+ int col;
+ char signature_filename[PATH_MAX];
+ FILE * signature_f;
+ char command[PATH_MAX];
+ char quoted_signature_filename[PATH_MAX];
+ char quoted_signed_filename[PATH_MAX];
+ struct mailmime * signed_mime;
+ char * smime_cert;
+ char * smime_key;
+ char quoted_smime_cert[PATH_MAX];
+ char quoted_smime_key[PATH_MAX];
+ char * email;
+
+ /* get signing key */
+
+ email = get_first_from_addr(mime);
+ if (email == NULL) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ smime_key = get_private_key_file(email);
+ smime_cert = get_cert_file(email);
+ if ((smime_cert == NULL) || (smime_key == NULL)) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ /* part to sign */
+
+ /* encode quoted printable all text parts */
+
+ mailprivacy_prepare_mime(mime);
+
+ signed_f = mailprivacy_get_tmp_file(privacy,
+ signed_filename, sizeof(signed_filename));
+ if (signed_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+
+ col = 0;
+ r = mailmime_write(signed_f, &col, mime);
+ if (r != MAILIMF_NO_ERROR) {
+ fclose(signed_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_signed;
+ }
+
+ fclose(signed_f);
+
+ /* prepare destination file for signature */
+
+ signature_f = mailprivacy_get_tmp_file(privacy,
+ signature_filename,
+ sizeof(signature_filename));
+ if (signature_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_signed;
+ }
+ fclose(signature_f);
+
+ r = mail_quote_filename(quoted_signed_filename,
+ sizeof(quoted_signed_filename), signed_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ r = mail_quote_filename(quoted_signature_filename,
+ sizeof(quoted_signature_filename), signature_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ r = mail_quote_filename(quoted_smime_key,
+ sizeof(quoted_smime_key), smime_key);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ r = mail_quote_filename(quoted_smime_cert,
+ sizeof(quoted_smime_cert), smime_cert);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ snprintf(command, sizeof(command),
+ "openssl smime -sign -in %s -out %s -signer %s -inkey %s 2>/dev/null",
+ quoted_signed_filename, quoted_signature_filename,
+ quoted_smime_cert, quoted_smime_key);
+
+ r = system(command);
+ if (WEXITSTATUS(r) != 0) {
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_signature;
+ }
+
+ /* signature part */
+
+ r = mailprivacy_get_part_from_file(privacy, 0,
+ signature_filename, &signed_mime);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto unlink_signature;
+ }
+ strip_mime_headers(signed_mime);
+
+ unlink(signature_filename);
+ unlink(signed_filename);
+
+ * result = signed_mime;
+
+ return MAIL_NO_ERROR;
+
+ unlink_signature:
+ unlink(signature_filename);
+ unlink_signed:
+ unlink(signed_filename);
+ err:
+ return res;
+}
+
+
+/* ********************************************************************* */
+/* find S/MIME recipient */
+
+static int recipient_add_mb(char * recipient, size_t * len,
+ struct mailimf_mailbox * mb)
+{
+ char * filename;
+ char quoted_filename[PATH_MAX];
+ size_t buflen;
+ int r;
+
+ if (mb->mb_addr_spec == NULL)
+ return MAIL_NO_ERROR;
+
+ filename = get_cert_file(mb->mb_addr_spec);
+ if (filename == NULL)
+ return MAIL_ERROR_INVAL;
+
+ r = mail_quote_filename(quoted_filename, sizeof(quoted_filename),
+ filename);
+ if (r < 0)
+ return MAIL_ERROR_MEMORY;
+
+ buflen = strlen(quoted_filename + 1);
+ if (buflen >= * len)
+ return MAIL_ERROR_MEMORY;
+
+ strncat(recipient, quoted_filename, * len);
+ (* len) -= buflen;
+ strncat(recipient, " ", * len);
+ (* len) --;
+
+ return MAIL_NO_ERROR;
+}
+
+static int recipient_add_mb_list(char * recipient, size_t * len,
+ struct mailimf_mailbox_list * mb_list)
+{
+ clistiter * cur;
+ int r;
+
+ for(cur = clist_begin(mb_list->mb_list) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ struct mailimf_mailbox * mb;
+
+ mb = clist_content(cur);
+
+ r = recipient_add_mb(recipient, len, mb);
+ if (r != MAIL_NO_ERROR)
+ return r;
+ }
+
+ return MAIL_NO_ERROR;
+}
+
+static int recipient_add_group(char * recipient, size_t * len,
+ struct mailimf_group * group)
+{
+ return recipient_add_mb_list(recipient, len, group->grp_mb_list);
+}
+
+static int recipient_add_addr(char * recipient, size_t * len,
+ struct mailimf_address * addr)
+{
+ int r;
+
+ switch (addr->ad_type) {
+ case MAILIMF_ADDRESS_MAILBOX:
+ r = recipient_add_mb(recipient, len, addr->ad_data.ad_mailbox);
+ break;
+ case MAILIMF_ADDRESS_GROUP:
+ r = recipient_add_group(recipient, len, addr->ad_data.ad_group);
+ break;
+ default:
+ r = MAIL_ERROR_INVAL;
+ }
+
+ return r;
+}
+
+static int recipient_add_addr_list(char * recipient, size_t * len,
+ struct mailimf_address_list * addr_list)
+{
+ clistiter * cur;
+ int r;
+
+ for(cur = clist_begin(addr_list->ad_list) ; cur != NULL ;
+ cur = clist_next(cur)) {
+ struct mailimf_address * addr;
+
+ addr = clist_content(cur);
+
+ r = recipient_add_addr(recipient, len, addr);
+ if (r != MAIL_NO_ERROR)
+ return r;
+ }
+
+ return MAIL_NO_ERROR;
+}
+
+static int collect_smime_cert(char * recipient, size_t size,
+ struct mailimf_fields * fields)
+{
+ struct mailimf_single_fields single_fields;
+ int r;
+ size_t remaining;
+ int res;
+
+ * recipient = '\0';
+ remaining = size;
+
+ if (fields != NULL)
+ mailimf_single_fields_init(&single_fields, fields);
+
+ if (single_fields.fld_to != NULL) {
+ r = recipient_add_addr_list(recipient, &remaining,
+ single_fields.fld_to->to_addr_list);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ }
+
+ if (single_fields.fld_cc != NULL) {
+ r = recipient_add_addr_list(recipient, &remaining,
+ single_fields.fld_cc->cc_addr_list);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ }
+
+ if (single_fields.fld_bcc != NULL) {
+ if (single_fields.fld_bcc->bcc_addr_list != NULL) {
+ r = recipient_add_addr_list(recipient, &remaining,
+ single_fields.fld_bcc->bcc_addr_list);
+ if (r < 0) {
+ res = r;
+ goto err;
+ }
+ }
+ }
+
+ return MAIL_NO_ERROR;
+
+ err:
+ return res;
+}
+
+
+
+static int smime_encrypt(struct mailprivacy * privacy,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ char encrypted_filename[PATH_MAX];
+ FILE * encrypted_f;
+ int res;
+ int r;
+ int col;
+ char decrypted_filename[PATH_MAX];
+ FILE * decrypted_f;
+ char command[PATH_MAX];
+ char quoted_decrypted_filename[PATH_MAX];
+ char quoted_encrypted_filename[PATH_MAX];
+ struct mailmime * encrypted_mime;
+ struct mailmime * root;
+ struct mailimf_fields * fields;
+ char recipient[PATH_MAX];
+
+ root = mime;
+ while (root->mm_parent != NULL)
+ root = root->mm_parent;
+
+ fields = NULL;
+ if (root->mm_type == MAILMIME_MESSAGE)
+ fields = root->mm_data.mm_message.mm_fields;
+
+ /* recipient */
+ r = collect_smime_cert(recipient, sizeof(recipient), fields);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ /* part to encrypt */
+
+ /* encode quoted printable all text parts */
+
+ mailprivacy_prepare_mime(mime);
+
+ decrypted_f = mailprivacy_get_tmp_file(privacy,
+ decrypted_filename,
+ sizeof(decrypted_filename));
+ if (decrypted_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+
+ col = 0;
+ r = mailmime_write(decrypted_f, &col, mime);
+ if (r != MAILIMF_NO_ERROR) {
+ fclose(decrypted_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_decrypted;
+ }
+
+ fclose(decrypted_f);
+
+ /* prepare destination file for encryption */
+
+ encrypted_f = mailprivacy_get_tmp_file(privacy,
+ encrypted_filename,
+ sizeof(encrypted_filename));
+ if (encrypted_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_decrypted;
+ }
+ fclose(encrypted_f);
+
+ r = mail_quote_filename(quoted_decrypted_filename,
+ sizeof(quoted_decrypted_filename), decrypted_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ r = mail_quote_filename(quoted_encrypted_filename,
+ sizeof(quoted_encrypted_filename), encrypted_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ snprintf(command, sizeof(command),
+ "openssl smime -encrypt -in %s -out %s %s 2>/dev/null",
+ quoted_decrypted_filename, quoted_encrypted_filename, recipient);
+
+ r = system(command);
+ if (WEXITSTATUS(r) != 0) {
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_encrypted;
+ }
+
+ /* encrypted part */
+
+ r = mailprivacy_get_part_from_file(privacy, 0,
+ encrypted_filename, &encrypted_mime);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto unlink_encrypted;
+ }
+ strip_mime_headers(encrypted_mime);
+
+ unlink(encrypted_filename);
+ unlink(decrypted_filename);
+
+ * result = encrypted_mime;
+
+ return MAIL_NO_ERROR;
+
+ unlink_encrypted:
+ unlink(encrypted_filename);
+ unlink_decrypted:
+ unlink(decrypted_filename);
+ err:
+ return res;
+}
+
+
+static int smime_sign_encrypt(struct mailprivacy * privacy,
+ struct mailmime * mime, struct mailmime ** result)
+{
+ char encrypted_filename[PATH_MAX];
+ FILE * encrypted_f;
+ int res;
+ int r;
+ int col;
+ char signature_filename[PATH_MAX];
+ FILE * signature_f;
+ char decrypted_filename[PATH_MAX];
+ FILE * decrypted_f;
+ char command[PATH_MAX];
+ char quoted_decrypted_filename[PATH_MAX];
+ char quoted_encrypted_filename[PATH_MAX];
+ char quoted_signature_filename[PATH_MAX];
+ struct mailmime * encrypted_mime;
+ struct mailmime * root;
+ struct mailimf_fields * fields;
+ char recipient[PATH_MAX];
+ char * smime_cert;
+ char * smime_key;
+ char quoted_smime_cert[PATH_MAX];
+ char quoted_smime_key[PATH_MAX];
+ char * email;
+
+ root = mime;
+ while (root->mm_parent != NULL)
+ root = root->mm_parent;
+
+ fields = NULL;
+ if (root->mm_type == MAILMIME_MESSAGE)
+ fields = root->mm_data.mm_message.mm_fields;
+
+ /* recipient */
+ r = collect_smime_cert(recipient, sizeof(recipient), fields);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ /* get signing key */
+
+ email = get_first_from_addr(mime);
+ if (email == NULL) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ smime_key = get_private_key_file(email);
+ smime_cert = get_cert_file(email);
+ if ((smime_cert == NULL) || (smime_key == NULL)) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ /* part to encrypt */
+
+ /* encode quoted printable all text parts */
+
+ mailprivacy_prepare_mime(mime);
+
+ decrypted_f = mailprivacy_get_tmp_file(privacy,
+ decrypted_filename, sizeof(decrypted_filename));
+ if (decrypted_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+
+ col = 0;
+ r = mailmime_write(decrypted_f, &col, mime);
+ if (r != MAILIMF_NO_ERROR) {
+ fclose(decrypted_f);
+ res = MAIL_ERROR_FILE;
+ goto unlink_decrypted;
+ }
+
+ fclose(decrypted_f);
+
+ /* prepare destination file for signature */
+
+ signature_f = mailprivacy_get_tmp_file(privacy,
+ signature_filename,
+ sizeof(signature_filename));
+ if (signature_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_decrypted;
+ }
+ fclose(signature_f);
+
+ r = mail_quote_filename(quoted_decrypted_filename,
+ sizeof(quoted_decrypted_filename), decrypted_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ r = mail_quote_filename(quoted_signature_filename,
+ sizeof(quoted_signature_filename), signature_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ r = mail_quote_filename(quoted_smime_key,
+ sizeof(quoted_smime_key), smime_key);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ r = mail_quote_filename(quoted_smime_cert,
+ sizeof(quoted_smime_cert), smime_cert);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ snprintf(command, sizeof(command),
+ "openssl smime -sign -in %s -out %s -signer %s -inkey %s 2>/dev/null",
+ quoted_decrypted_filename, quoted_signature_filename,
+ quoted_smime_cert, quoted_smime_key);
+
+ r = system(command);
+ if (WEXITSTATUS(r) != 0) {
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_signature;
+ }
+
+
+ /* prepare destination file for encryption */
+
+ encrypted_f = mailprivacy_get_tmp_file(privacy,
+ encrypted_filename,
+ sizeof(encrypted_filename));
+ if (encrypted_f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto unlink_signature;
+ }
+ fclose(encrypted_f);
+
+ r = mail_quote_filename(quoted_encrypted_filename,
+ sizeof(quoted_encrypted_filename), encrypted_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_encrypted;
+ }
+
+ snprintf(command, sizeof(command),
+ "openssl smime -encrypt -in %s -out %s %s 2>/dev/null",
+ quoted_signature_filename, quoted_encrypted_filename, recipient);
+
+ r = system(command);
+ if (WEXITSTATUS(r) != 0) {
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_encrypted;
+ }
+
+ /* encrypted part */
+
+ r = mailprivacy_get_part_from_file(privacy, 0,
+ encrypted_filename, &encrypted_mime);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto unlink_encrypted;
+ }
+ strip_mime_headers(encrypted_mime);
+
+ unlink(encrypted_filename);
+ unlink(signature_filename);
+ unlink(decrypted_filename);
+
+ * result = encrypted_mime;
+
+ return MAIL_NO_ERROR;
+
+ unlink_encrypted:
+ unlink(encrypted_filename);
+ unlink_signature:
+ unlink(signature_filename);
+ unlink_decrypted:
+ unlink(decrypted_filename);
+ err:
+ return res;
+}
+
+
+
+static struct mailprivacy_encryption smime_encryption_tab[] = {
+ /* S/MIME signed part */
+ {
+ .name = "signed",
+ .description = "S/MIME signed part",
+ .encrypt = smime_sign,
+ },
+
+ /* S/MIME encrypted part */
+
+ {
+ .name = "encrypted",
+ .description = "S/MIME encrypted part",
+ .encrypt = smime_encrypt,
+ },
+
+ /* S/MIME signed & encrypted part */
+
+ {
+ .name = "signed-encrypted",
+ .description = "S/MIME signed & encrypted part",
+ .encrypt = smime_sign_encrypt,
+ },
+};
+
+static struct mailprivacy_protocol smime_protocol = {
+ .name = "smime",
+ .description = "S/MIME",
+
+ .is_encrypted = smime_test_encrypted,
+ .decrypt = smime_handler,
+
+ .encryption_count =
+ (sizeof(smime_encryption_tab) / sizeof(smime_encryption_tab[0])),
+
+ .encryption_tab = smime_encryption_tab,
+};
+
+int mailprivacy_smime_init(struct mailprivacy * privacy)
+{
+ certificates = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYALL);
+ if (certificates == NULL)
+ goto err;
+
+ private_keys = chash_new(CHASH_DEFAULTSIZE, CHASH_COPYALL);
+ if (private_keys == NULL)
+ goto free_cert;
+
+ CAcert_dir[0] = '\0';
+
+ return mailprivacy_register(privacy, &smime_protocol);
+
+ free_cert:
+ chash_free(certificates);
+ err:
+ return MAIL_ERROR_MEMORY;
+}
+
+void mailprivacy_smime_done(struct mailprivacy * privacy)
+{
+ mailprivacy_unregister(privacy, &smime_protocol);
+ chash_free(private_keys);
+ private_keys = NULL;
+ chash_free(certificates);
+ certificates = NULL;
+ if (CAfile != NULL) {
+ unlink(CAfile);
+ free(CAfile);
+ }
+ CAfile = NULL;
+ CAcert_dir[0] = '\0';
+}
+
+
+static void strip_string(char * str)
+{
+ char * p;
+ size_t len;
+
+ p = strchr(str, '\r');
+ if (p != NULL)
+ * p = 0;
+
+ p = strchr(str, '\n');
+ if (p != NULL)
+ * p = 0;
+
+ p = str;
+ while ((* p == ' ') || (* p == '\t')) {
+ p ++;
+ }
+
+ len = strlen(p);
+ memmove(str, p, len);
+ str[len] = 0;
+
+ if (len == 0)
+ return;
+
+ p = str;
+ len = len - 1;
+ while ((p[len] == ' ') || (p[len] == '\t')) {
+ p[len] = '\0';
+
+ if (len == 0)
+ break;
+
+ len --;
+ }
+}
+
+
+
+#define MAX_EMAIL_SIZE 1024
+
+static void set_file(chash * hash, char * email, char * filename)
+{
+ char * n;
+ char buf[MAX_EMAIL_SIZE];
+ chashdatum key;
+ chashdatum data;
+
+ strncpy(buf, email, sizeof(buf));
+ buf[sizeof(buf) - 1] = '\0';
+ for(n = buf ; * n != '\0' ; n ++)
+ * n = toupper((unsigned char) * n);
+ strip_string(buf);
+
+ key.data = buf;
+ key.len = strlen(buf);
+ data.data = filename;
+ data.len = strlen(filename) + 1;
+
+ chash_set(hash, &key, &data, NULL);
+}
+
+static char * get_file(chash * hash, char * email)
+{
+ chashdatum key;
+ chashdatum data;
+ char buf[MAX_EMAIL_SIZE];
+ char * n;
+ int r;
+
+ strncpy(buf, email, sizeof(buf));
+ buf[sizeof(buf) - 1] = '\0';
+ for(n = buf ; * n != '\0' ; n ++)
+ * n = toupper((unsigned char) * n);
+
+ strip_string(buf);
+ key.data = buf;
+ key.len = strlen(buf);
+ r = chash_get(hash, &key, &data);
+ if (r < 0)
+ return NULL;
+
+ return data.data;
+}
+
+void mailprivacy_smime_set_cert_dir(struct mailprivacy * privacy,
+ char * directory)
+{
+ DIR * dir;
+ struct dirent * ent;
+
+ chash_clear(certificates);
+
+ if (directory == NULL)
+ return;
+
+ if (* directory == '\0')
+ return;
+
+ strncpy(cert_dir, directory, sizeof(cert_dir));
+ cert_dir[sizeof(cert_dir) - 1] = '\0';
+
+ dir = opendir(directory);
+ if (dir == NULL)
+ return;
+
+ while ((ent = readdir(dir)) != NULL) {
+ char filename[PATH_MAX];
+ char command[PATH_MAX];
+ char buf[MAX_EMAIL_SIZE];
+ FILE * p;
+
+ snprintf(filename, sizeof(filename),
+ "%s/%s", directory, ent->d_name);
+
+ snprintf(command, sizeof(command),
+ "openssl x509 -email -noout -in %s 2>/dev/null", filename);
+
+ p = popen(command, "r");
+ if (p == NULL)
+ continue;
+
+ while (fgets(buf, sizeof(buf), p) != NULL)
+ set_file(certificates, buf, filename);
+
+ pclose(p);
+ }
+ closedir(dir);
+}
+
+static char * get_cert_file(char * email)
+{
+ return get_file(certificates, email);
+}
+
+static char * get_private_key_file(char * email)
+{
+ return get_file(private_keys, email);
+}
+
+void mail_private_smime_clear_private_keys(struct mailprivacy * privacy)
+{
+ chash_clear(private_keys);
+}
+
+#define MAX_BUF 1024
+
+void mailprivacy_smime_set_CA_dir(struct mailprivacy * privacy,
+ char * directory)
+{
+ DIR * dir;
+ struct dirent * ent;
+ FILE * f_CA;
+ char CA_filename[PATH_MAX];
+
+ if (directory == NULL)
+ return;
+
+ if (* directory == '\0')
+ return;
+
+ /* make a temporary file that contains all the CAs */
+
+ if (CAfile != NULL) {
+ unlink(CAfile);
+ free(CAfile);
+ CAfile = NULL;
+ }
+
+ f_CA = mailprivacy_get_tmp_file(privacy, CA_filename, sizeof(CA_filename));
+ if (f_CA == NULL)
+ return;
+
+ strncpy(CAcert_dir, directory, sizeof(CAcert_dir));
+ CAcert_dir[sizeof(CAcert_dir) - 1] = '\0';
+
+ dir = opendir(directory);
+ if (dir == NULL) {
+ fclose(f_CA);
+ goto unlink_CA;
+ }
+
+ while ((ent = readdir(dir)) != NULL) {
+ char filename[PATH_MAX];
+ char command[PATH_MAX];
+ char buf[MAX_BUF];
+ FILE * f;
+
+ snprintf(filename, sizeof(filename),
+ "%s/%s", directory, ent->d_name);
+
+ f = fopen(filename, "r");
+ if (f == NULL)
+ continue;
+
+ while (fgets(buf, sizeof(buf), f) != NULL)
+ fputs(buf, f_CA);
+
+ fclose(f);
+ }
+
+ closedir(dir);
+
+ fclose(f_CA);
+
+ CAfile = strdup(CA_filename);
+ if (CAfile == NULL)
+ goto unlink_CA;
+
+ return;
+
+ unlink_CA:
+ unlink(CA_filename);
+}
+
+void mailprivacy_smime_set_CA_check(struct mailprivacy * privacy,
+ int enabled)
+{
+ CA_check = enabled;
+}
+
+void mailprivacy_smime_set_store_cert(struct mailprivacy * privacy,
+ int enabled)
+{
+ store_cert = enabled;
+}
+
+static int get_cert_from_sig(struct mailprivacy * privacy,
+ mailmessage * msg,
+ struct mailmime * mime)
+{
+ clistiter * cur;
+ struct mailmime * signed_mime;
+ struct mailmime * signature_mime;
+ int res;
+ char signature_filename[PATH_MAX];
+ char quoted_signature_filename[PATH_MAX];
+ char * email;
+ char * cert_file;
+ char store_cert_filename[PATH_MAX];
+ char quoted_store_cert_filename[PATH_MAX];
+ int r;
+ char command[PATH_MAX];
+
+ if (* cert_dir == '\0')
+ return MAIL_ERROR_INVAL;
+
+ if (mime->mm_type != MAILMIME_MULTIPLE)
+ return MAIL_ERROR_INVAL;
+
+ email = get_first_from_addr(mime);
+ if (email == NULL)
+ return MAIL_ERROR_INVAL;
+
+ cert_file = get_cert_file(email);
+ if (cert_file != NULL)
+ return MAIL_NO_ERROR;
+
+ /* get the two parts of the S/MIME message */
+
+ cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list);
+ if (cur == NULL) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ signed_mime = cur->data;
+ cur = clist_next(cur);
+ if (cur == NULL) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ signature_mime = cur->data;
+
+ r = mailprivacy_fetch_decoded_to_file(privacy,
+ signature_filename, sizeof(signature_filename),
+ msg, signature_mime);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+
+ r = mail_quote_filename(quoted_signature_filename,
+ sizeof(quoted_signature_filename), signature_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ snprintf(store_cert_filename, sizeof(store_cert_filename),
+ "%s/%s-cert.pem", cert_dir, email);
+
+ r = mail_quote_filename(quoted_store_cert_filename,
+ sizeof(quoted_store_cert_filename), store_cert_filename);
+ if (r < 0) {
+ res = MAIL_ERROR_MEMORY;
+ goto unlink_signature;
+ }
+
+ snprintf(command, sizeof(command),
+ "openssl pkcs7 -inform DER -in %s -out %s -print_certs 2>/dev/null",
+ quoted_signature_filename, quoted_store_cert_filename);
+
+ r = system(command);
+ if (WEXITSTATUS(r) != 0) {
+ res = MAIL_ERROR_COMMAND;
+ goto unlink_signature;
+ }
+
+ unlink(signature_filename);
+
+ set_file(certificates, email, store_cert_filename);
+
+ return MAIL_NO_ERROR;
+
+ unlink_signature:
+ unlink(signature_filename);
+ err:
+ return res;
+}
+
+
+static void set_private_key(struct mailprivacy * privacy,
+ char * email, char * file)
+{
+ set_file(private_keys, email, file);
+}
+
+#define PRIVATE_KEY_SUFFIX "-private-key.pem"
+
+void mailprivacy_smime_set_private_keys_dir(struct mailprivacy * privacy,
+ char * directory)
+{
+ DIR * dir;
+ struct dirent * ent;
+
+ chash_clear(private_keys);
+
+ if (directory == NULL)
+ return;
+
+ if (* directory == '\0')
+ return;
+
+ strncpy(private_keys_dir, directory, sizeof(private_keys_dir));
+ private_keys_dir[sizeof(private_keys_dir) - 1] = '\0';
+
+ dir = opendir(directory);
+ if (dir == NULL)
+ return;
+
+ while ((ent = readdir(dir)) != NULL) {
+ char filename[PATH_MAX];
+ char email[PATH_MAX];
+ char * p;
+
+ snprintf(filename, sizeof(filename),
+ "%s/%s", directory, ent->d_name);
+
+ strncpy(email, ent->d_name, sizeof(email));
+ email[sizeof(email) - 1] = '\0';
+
+ p = strstr(email, PRIVATE_KEY_SUFFIX);
+ if (p == NULL)
+ continue;
+
+ if (strlen(p) != sizeof(PRIVATE_KEY_SUFFIX) - 1)
+ continue;
+
+ * p = 0;
+
+ if (* email == '\0')
+ continue;
+
+ set_private_key(privacy, email, filename);
+ }
+ closedir(dir);
+}
diff --git a/libetpan/src/engine/mailprivacy_smime.h b/libetpan/src/engine/mailprivacy_smime.h
new file mode 100644
index 0000000..85d9e40
--- a/dev/null
+++ b/libetpan/src/engine/mailprivacy_smime.h
@@ -0,0 +1,84 @@
+/*
+ * libEtPan! -- a mail library
+ *
+ * Copyright (C) 2001, 2005 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef MAILPRIVACY_SMIME_H
+
+#define MAILPRIVACY_SMIME_H
+
+#include <libetpan/mailprivacy_types.h>
+
+int mailprivacy_smime_init(struct mailprivacy * privacy);
+
+void mailprivacy_smime_done(struct mailprivacy * privacy);
+
+void mailprivacy_smime_set_cert_dir(struct mailprivacy * privacy,
+ char * directory);
+
+
+/*
+ set directory where certificates of authority certifications are
+ stored.
+*/
+
+void mailprivacy_smime_set_CA_dir(struct mailprivacy * privacy,
+ char * directory);
+
+
+/*
+ to disable the verification of signers certificates of a
+ signed message.
+*/
+
+void mailprivacy_smime_set_CA_check(struct mailprivacy * privacy,
+ int enabled);
+
+
+/*
+ to store certificates of signed messages
+*/
+
+void mailprivacy_smime_set_store_cert(struct mailprivacy * privacy,
+ int enabled);
+
+/*
+ set directory where private keys are stored.
+ name of the files in that directory must be in form :
+ [email-address]-private-key.pem
+*/
+
+void mailprivacy_smime_set_private_keys_dir(struct mailprivacy * privacy,
+ char * directory);
+
+#endif
diff --git a/libetpan/src/engine/mailprivacy_tools.c b/libetpan/src/engine/mailprivacy_tools.c
new file mode 100644
index 0000000..cae2ef8
--- a/dev/null
+++ b/libetpan/src/engine/mailprivacy_tools.c
@@ -0,0 +1,1283 @@
+/*
+ * libEtPan! -- a mail library
+ *
+ * Copyright (C) 2001, 2005 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#include "mailprivacy_tools.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <libetpan/mailmessage.h>
+#include <ctype.h>
+#include "mailprivacy.h"
+#include <libetpan/libetpan-config.h>
+#include <libetpan/data_message_driver.h>
+
+void mailprivacy_mime_clear(struct mailmime * mime)
+{
+ struct mailmime_data * data;
+ clistiter * cur;
+
+ switch (mime->mm_type) {
+ case MAILMIME_SINGLE:
+ data = mime->mm_data.mm_single;
+ if (data != NULL) {
+ if (data->dt_type == MAILMIME_DATA_FILE)
+ unlink(data->dt_data.dt_filename);
+ }
+ break;
+
+ case MAILMIME_MULTIPLE:
+ data = mime->mm_data.mm_multipart.mm_preamble;
+ if (data != NULL) {
+ if (data->dt_type == MAILMIME_DATA_FILE)
+ unlink(data->dt_data.dt_filename);
+ }
+ data = mime->mm_data.mm_multipart.mm_epilogue;
+ if (data != NULL) {
+ if (data->dt_type == MAILMIME_DATA_FILE)
+ unlink(data->dt_data.dt_filename);
+ }
+
+ for(cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list) ;
+ cur != NULL ; cur = clist_next(cur)) {
+ struct mailmime * submime;
+
+ submime = clist_content(cur);
+
+ mailprivacy_mime_clear(submime);
+ }
+ break;
+
+ case MAILMIME_MESSAGE:
+ if (mime->mm_data.mm_message.mm_msg_mime != NULL) {
+ mailprivacy_mime_clear(mime->mm_data.mm_message.mm_msg_mime);
+ }
+ break;
+ }
+}
+
+
+static FILE * get_tmp_file(char * filename)
+{
+ int fd;
+ mode_t old_mask;
+ FILE * f;
+
+ old_mask = umask(0077);
+ fd = mkstemp(filename);
+ umask(old_mask);
+ if (fd == -1)
+ return NULL;
+
+ f = fdopen(fd, "r+");
+ if (f == NULL) {
+ close(fd);
+ unlink(filename);
+ }
+
+ return f;
+}
+
+FILE * mailprivacy_get_tmp_file(struct mailprivacy * privacy,
+ char * filename, size_t size)
+{
+ snprintf(filename, size, "%s/libetpan-privacy-XXXXXX", privacy->tmp_dir);
+ return get_tmp_file(filename);
+}
+
+
+static char * dup_file(struct mailprivacy * privacy,
+ char * source_filename)
+{
+ char filename[PATH_MAX];
+ FILE * dest_f;
+ int r;
+ struct stat stat_info;
+ char * dest_filename;
+ char * mapping;
+ size_t written;
+ int fd;
+
+ dest_f = mailprivacy_get_tmp_file(privacy, filename, sizeof(filename));
+ if (dest_f == NULL)
+ goto err;
+
+ dest_filename = strdup(filename);
+ if (dest_filename == NULL)
+ goto close_dest;
+
+ fd = open(source_filename, O_RDONLY);
+ if (fd < 0)
+ goto free_dest;
+
+ r = fstat(fd, &stat_info);
+ if (r < 0)
+ goto close_src;
+
+ mapping = mmap(NULL, stat_info.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (mapping == MAP_FAILED)
+ goto close_src;
+
+ written = fwrite(mapping, 1, stat_info.st_size, dest_f);
+ if (written != (size_t) stat_info.st_size)
+ goto unmap;
+
+ munmap(mapping, stat_info.st_size);
+ close(fd);
+ fclose(dest_f);
+
+ return dest_filename;
+
+ unmap:
+ munmap(mapping, stat_info.st_size);
+ close_src:
+ close(fd);
+ free_dest:
+ free(dest_filename);
+ close_dest:
+ fclose(dest_f);
+ err:
+ return NULL;
+}
+
+
+/*
+ mime_data_replace()
+
+ write a mime part to a file and change the reference of mailmime_data
+ to the file.
+*/
+
+static int mime_data_replace(struct mailprivacy * privacy,
+ int encoding_type,
+ struct mailmime_data * data)
+{
+ char filename[PATH_MAX];
+ FILE * f;
+ size_t written;
+ char * dup_filename;
+ int res;
+ int r;
+ int decoded;
+
+ if (data->dt_type != MAILMIME_DATA_TEXT) {
+ res = MAIL_NO_ERROR;
+ goto err;
+ }
+
+ f = mailprivacy_get_tmp_file(privacy, filename, sizeof(filename));
+ if (f == NULL) {
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+
+ decoded = 0;
+ if (encoding_type != -1) {
+ char * content;
+ size_t content_len;
+ size_t cur_token;
+
+ cur_token = 0;
+ r = mailmime_part_parse(data->dt_data.dt_text.dt_data,
+ data->dt_data.dt_text.dt_length,
+ &cur_token, encoding_type, &content, &content_len);
+
+ if (r == MAILIMF_NO_ERROR) {
+ /* write decoded */
+ written = fwrite(content, 1, content_len, f);
+ if (written != content_len) {
+ fclose(f);
+ unlink(filename);
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+ mmap_string_unref(content);
+
+ decoded = 1;
+ data->dt_encoded = 0;
+ }
+ }
+
+ if (!decoded) {
+ written = fwrite(data->dt_data.dt_text.dt_data, 1,
+ data->dt_data.dt_text.dt_length, f);
+ if (written != data->dt_data.dt_text.dt_length) {
+ fclose(f);
+ unlink(filename);
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+ }
+
+ fclose(f);
+
+ dup_filename = strdup(filename);
+ if (dup_filename == NULL) {
+ unlink(filename);
+ res = MAIL_ERROR_MEMORY;
+ goto err;
+ }
+
+ data->dt_type = MAILMIME_DATA_FILE;
+ data->dt_data.dt_filename = dup_filename;
+
+ return MAIL_NO_ERROR;
+
+ err:
+ return res;
+}
+
+
+/*
+ recursive_replace_single_parts()
+
+ write all parts of the given mime part to file.
+*/
+
+static int recursive_replace_single_parts(struct mailprivacy * privacy,
+ struct mailmime * mime)
+{
+ int r;
+ int res;
+ clistiter * cur;
+
+ mime->mm_mime_start = NULL;
+
+ switch(mime->mm_type) {
+ case MAILMIME_SINGLE:
+ if (mime->mm_data.mm_single != NULL) {
+ int encoding_type;
+ struct mailmime_single_fields single_fields;
+
+ mailmime_single_fields_init(&single_fields, mime->mm_mime_fields,
+ mime->mm_content_type);
+
+ if (single_fields.fld_encoding != NULL)
+ encoding_type = single_fields.fld_encoding->enc_type;
+ else
+ encoding_type = -1;
+
+ r = mime_data_replace(privacy, encoding_type, mime->mm_data.mm_single);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ }
+ break;
+
+ case MAILMIME_MULTIPLE:
+ if (mime->mm_data.mm_multipart.mm_preamble != NULL) {
+ r = mime_data_replace(privacy, -1,
+ mime->mm_data.mm_multipart.mm_preamble);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ }
+
+ if (mime->mm_data.mm_multipart.mm_epilogue != NULL) {
+ r = mime_data_replace(privacy, -1,
+ mime->mm_data.mm_multipart.mm_epilogue);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ }
+
+ for(cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list) ;
+ cur != NULL ; cur = clist_next(cur)) {
+ struct mailmime * child;
+
+ child = clist_content(cur);
+
+ r = recursive_replace_single_parts(privacy, child);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ }
+
+ break;
+
+ case MAILMIME_MESSAGE:
+ if (mime->mm_data.mm_message.mm_msg_mime != NULL) {
+ r = recursive_replace_single_parts(privacy,
+ mime->mm_data.mm_message.mm_msg_mime);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto err;
+ }
+ }
+ break;
+ }
+
+ return MAIL_NO_ERROR;
+
+ err:
+ return res;
+}
+
+/*
+ mailprivacy_get_mime()
+
+ parse the message in MIME structure,
+ all single MIME parts are stored in files.
+
+ privacy can be set to NULL to disable privacy check.
+*/
+
+int mailprivacy_get_mime(struct mailprivacy * privacy,
+ int check_privacy,
+ char * content, size_t content_len,
+ struct mailmime ** result_mime)
+{
+ struct mailmime * mime;
+ mailmessage * msg;
+ int r;
+ int res;
+
+#if 0
+ int check_privacy;
+
+ check_privacy = (privacy != NULL);
+#endif
+
+ /*
+ use message data driver, get bodystructure and
+ convert all the data part in MAILMIME_SINGLE to files.
+ */
+
+ msg = data_message_init(content, content_len);
+ if (msg == NULL) {
+ res = MAIL_ERROR_MEMORY;
+ goto err;
+ }
+
+#if 0
+ if (msg->mime == NULL) {
+ if (check_privacy) {
+ r = mailprivacy_msg_get_bodystructure(privacy, msg, &mime);
+ }
+ else {
+ /*
+ don't use etpan_msg_get_bodystructure because it is not useful
+ and to avoid loops due to security part
+ */
+ r = mailmessage_get_bodystructure(msg, &mime);
+ }
+ }
+ else {
+ mime = msg->mime;
+ }
+#endif
+
+ if (check_privacy)
+ r = mailprivacy_msg_get_bodystructure(privacy, msg, &mime);
+ else
+ r = mailmessage_get_bodystructure(msg, &mime);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto free_msg;
+ }
+
+ /*
+ should be done so that the MIME structure need not to be unregistered.
+ */
+ mailprivacy_recursive_unregister_mime(privacy, mime);
+
+ r = recursive_replace_single_parts(privacy, mime);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto clear_mime;
+ }
+
+ data_message_detach_mime(msg);
+#if 0
+ if (check_privacy)
+ mailprivacy_msg_flush(privacy, msg);
+ else
+ mailmessage_flush(msg);
+#endif
+ mailprivacy_msg_flush(privacy, msg);
+ mailmessage_free(msg);
+
+ * result_mime = mime;
+
+ return MAIL_NO_ERROR;
+
+ clear_mime:
+ mailprivacy_mime_clear(mime);
+ mailprivacy_msg_flush(privacy, msg);
+ free_msg:
+ mailmessage_free(msg);
+ err:
+ return res;
+}
+
+#ifndef LIBETPAN_SYSTEM_BASENAME
+static char * libetpan_basename(char * filename)
+{
+ char * next;
+ char * p;
+
+ p = filename;
+ next = strchr(p, '/');
+
+ while (next != NULL) {
+ p = next;
+ next = strchr(p + 1, '/');
+ }
+
+ if (p == filename)
+ return filename;
+ else
+ return p + 1;
+}
+#else
+#define libetpan_basename(a) basename(a)
+#endif
+
+struct mailmime *
+mailprivacy_new_file_part(struct mailprivacy * privacy,
+ char * filename,
+ char * default_content_type, int default_encoding)
+{
+ char basename_buf[PATH_MAX];
+ char * name;
+ struct mailmime_mechanism * encoding;
+ struct mailmime_content * content;
+ struct mailmime * mime;
+ int r;
+ char * dup_filename;
+ struct mailmime_fields * mime_fields;
+ int encoding_type;
+ char * content_type_str;
+ int do_encoding;
+
+ if (filename != NULL) {
+ strncpy(basename_buf, filename, PATH_MAX);
+ name = libetpan_basename(basename_buf);
+ }
+ else {
+ name = NULL;
+ }
+
+ encoding = NULL;
+
+ /* default content-type */
+ if (default_content_type == NULL)
+ content_type_str = "application/octet-stream";
+ else
+ content_type_str = default_content_type;
+
+ content = mailmime_content_new_with_str(content_type_str);
+ if (content == NULL) {
+ goto free_content;
+ }
+
+ do_encoding = 1;
+ if (content->ct_type->tp_type == MAILMIME_TYPE_COMPOSITE_TYPE) {
+ struct mailmime_composite_type * composite;
+
+ composite = content->ct_type->tp_data.tp_composite_type;
+
+ switch (composite->ct_type) {
+ case MAILMIME_COMPOSITE_TYPE_MESSAGE:
+ if (strcasecmp(content->ct_subtype, "rfc822") == 0)
+ do_encoding = 0;
+ break;
+
+ case MAILMIME_COMPOSITE_TYPE_MULTIPART:
+ do_encoding = 0;
+ break;
+ }
+ }
+
+ if (do_encoding) {
+ if (default_encoding == -1)
+ encoding_type = MAILMIME_MECHANISM_BASE64;
+ else
+ encoding_type = default_encoding;
+
+ /* default Content-Transfer-Encoding */
+ encoding = mailmime_mechanism_new(encoding_type, NULL);
+ if (encoding == NULL) {
+ goto free_content;
+ }
+ }
+
+ mime_fields = mailmime_fields_new_with_data(encoding,
+ NULL, NULL, NULL, NULL);
+ if (mime_fields == NULL) {
+ goto free_content;
+ }
+
+ mime = mailmime_new_empty(content, mime_fields);
+ if (mime == NULL) {
+ goto free_mime_fields;
+ }
+
+ if ((filename != NULL) && (mime->mm_type == MAILMIME_SINGLE)) {
+ /*
+ duplicates the file so that the file can be deleted when
+ the MIME part is done
+ */
+ dup_filename = dup_file(privacy, filename);
+ if (dup_filename == NULL) {
+ goto free_mime;
+ }
+
+ r = mailmime_set_body_file(mime, dup_filename);
+ if (r != MAILIMF_NO_ERROR) {
+ free(dup_filename);
+ goto free_mime;
+ }
+ }
+
+ return mime;
+
+ free_mime:
+ mailmime_free(mime);
+ goto err;
+ free_mime_fields:
+ mailmime_fields_free(mime_fields);
+ mailmime_content_free(content);
+ goto err;
+ free_content:
+ if (encoding != NULL)
+ mailmime_mechanism_free(encoding);
+ if (content != NULL)
+ mailmime_content_free(content);
+ err:
+ return NULL;
+}
+
+
+int mailmime_substitute(struct mailmime * old_mime,
+ struct mailmime * new_mime)
+{
+ struct mailmime * parent;
+
+ parent = old_mime->mm_parent;
+ if (parent == NULL)
+ return MAIL_ERROR_INVAL;
+
+ if (old_mime->mm_parent_type == MAILMIME_MESSAGE)
+ parent->mm_data.mm_message.mm_msg_mime = new_mime;
+ else /* MAILMIME_MULTIPLE */
+ old_mime->mm_multipart_pos->data = new_mime;
+ new_mime->mm_parent = parent;
+ new_mime->mm_parent_type = old_mime->mm_parent_type;
+
+ /* detach old_mime */
+ old_mime->mm_parent = NULL;
+ old_mime->mm_parent_type = MAILMIME_NONE;
+
+ return MAIL_NO_ERROR;
+}
+
+
+
+/* write mime headers and body to a file, CR LF fixed */
+
+int mailprivacy_fetch_mime_body_to_file(struct mailprivacy * privacy,
+ char * filename, size_t size,
+ mailmessage * msg, struct mailmime * mime)
+{
+ int r;
+ int res;
+ FILE * f;
+ char * content;
+ size_t content_len;
+ int col;
+
+ if (mime->mm_parent_type == MAILMIME_NONE) {
+ res = MAIL_ERROR_INVAL;
+ goto err;
+ }
+
+ f = mailprivacy_get_tmp_file(privacy, filename, size);
+ if (f == NULL) {
+ res = MAIL_ERROR_FETCH;
+ goto err;
+ }
+
+ r = mailprivacy_msg_fetch_section_mime(privacy, msg, mime,
+ &content, &content_len);
+ if (r != MAIL_NO_ERROR) {
+ res = MAIL_ERROR_FETCH;
+ goto close;
+ }
+
+ col = 0;
+ r = mailimf_string_write(f, &col, content, content_len);
+ mailprivacy_msg_fetch_result_free(privacy, msg, content);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto close;
+ }
+
+ r = mailprivacy_msg_fetch_section(privacy, msg, mime,
+ &content, &content_len);
+ if (r != MAIL_NO_ERROR) {
+ res = MAIL_ERROR_FETCH;
+ goto close;
+ }
+
+ r = mailimf_string_write(f, &col, content, content_len);
+ mailprivacy_msg_fetch_result_free(privacy, msg, content);
+ if (r != MAILIMF_NO_ERROR) {
+ res = r;
+ goto close;
+ }
+
+ fclose(f);
+
+ return MAIL_NO_ERROR;
+
+ close:
+ fclose(f);
+ unlink(filename);
+ err:
+ return res;
+}
+
+
+int mailprivacy_get_part_from_file(struct mailprivacy * privacy,
+ int check_security, char * filename,
+ struct mailmime ** result_mime)
+{
+ int fd;
+ struct mailmime * mime;
+ int r;
+ struct stat stat_info;
+ int res;
+ char * mapping;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ res = MAIL_ERROR_FILE;
+ goto err;
+ }
+
+ r = fstat(fd, &stat_info);
+ if (r < 0) {
+ res = MAIL_ERROR_FILE;
+ goto close;
+ }
+
+ mapping = mmap(NULL, stat_info.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (mapping == MAP_FAILED) {
+ res = MAIL_ERROR_FILE;
+ goto close;
+ }
+
+ /* check recursive parts if privacy is set */
+ r = mailprivacy_get_mime(privacy, check_security,
+ mapping, stat_info.st_size, &mime);
+ if (r != MAIL_NO_ERROR) {
+ res = r;
+ goto unmap;
+ }
+
+ if (mime->mm_type == MAILMIME_MESSAGE) {
+ struct mailmime * submime;
+
+ submime = mime->mm_data.mm_message.mm_msg_mime;
+ if (mime->mm_data.mm_message.mm_msg_mime != NULL) {
+ mailmime_remove_part(submime);
+ mailmime_free(mime);
+
+ mime = submime;
+ }
+ }
+
+ munmap(mapping, stat_info.st_size);
+
+ close(fd);
+
+ * result_mime = mime;
+
+ return MAIL_NO_ERROR;
+
+ unmap:
+ munmap(mapping, stat_info.st_size);
+ close:
+ close(fd);
+ err:
+ return res;
+}
+
+int mail_quote_filename(char * result, size_t size, char * path)
+{
+ char * p;
+ char * result_p;
+ size_t remaining;
+
+ result_p = result;
+ remaining = size;
+
+ for(p = path ; * p != '\0' ; p ++) {
+
+ if (isalpha(* p) || isdigit(* p) || (* p == '/')) {
+ if (remaining > 0) {
+ * result_p = * p;
+ result_p ++;
+ remaining --;
+ }
+ else {
+ result[size - 1] = '\0';
+ return -1;
+ }
+ }
+ else {
+ if (remaining >= 2) {
+ * result_p = '\\';
+ result_p ++;
+ * result_p = * p;
+ result_p ++;
+ remaining -= 2;
+ }
+ else {
+ result[size - 1] = '\0';
+ return -1;
+ }
+ }
+ }
+ if (remaining > 0) {
+ * result_p = '\0';
+ }
+ else {
+ result[size - 1] = '\0';
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static void prepare_mime_single(struct mailmime * mime)
+{
+ struct mailmime_single_fields single_fields;
+ int encoding;
+ int r;
+
+ if (mime->mm_mime_fields != NULL) {
+ mailmime_single_fields_init(&single_fields, mime->mm_mime_fields,
+ mime->mm_content_type);
+ if (single_fields.fld_encoding != NULL) {
+ encoding = single_fields.fld_encoding->enc_type;
+ switch (encoding) {
+ case MAILMIME_MECHANISM_8BIT:
+ case MAILMIME_MECHANISM_7BIT:
+ case MAILMIME_MECHANISM_BINARY:
+ single_fields.fld_encoding->enc_type =
+ MAILMIME_MECHANISM_QUOTED_PRINTABLE;
+ break;
+ }
+ }
+ else {
+ struct mailmime_mechanism * mechanism;
+ struct mailmime_field * field;
+
+ mechanism =
+ mailmime_mechanism_new(MAILMIME_MECHANISM_QUOTED_PRINTABLE, NULL);
+ if (mechanism == NULL)
+ return;
+
+ field = mailmime_field_new(MAILMIME_FIELD_TRANSFER_ENCODING,
+ NULL, mechanism, NULL, NULL, 0, NULL, NULL);
+ if (field == NULL) {
+ mailmime_mechanism_free(mechanism);
+ return;
+ }
+
+ r = clist_append(mime->mm_mime_fields->fld_list, field);
+ if (r < 0) {
+ mailmime_field_free(field);
+ return;
+ }
+ }
+ }
+
+ if (mime->mm_type == MAILMIME_SINGLE) {
+ switch (mime->mm_data.mm_single->dt_encoding) {
+ case MAILMIME_MECHANISM_8BIT:
+ case MAILMIME_MECHANISM_7BIT:
+ case MAILMIME_MECHANISM_BINARY:
+ mime->mm_data.mm_single->dt_encoding =
+ MAILMIME_MECHANISM_QUOTED_PRINTABLE;
+ mime->mm_data.mm_single->dt_encoded = 0;
+ break;
+ }
+ }
+}
+
+/*
+ mailprivacy_prepare_mime()
+
+ we assume we built ourself the message.
+*/
+
+void mailprivacy_prepare_mime(struct mailmime * mime)
+{
+ clistiter * cur;
+
+ switch (mime->mm_type) {
+ case MAILMIME_SINGLE:
+ if (mime->mm_data.mm_single != NULL) {
+ prepare_mime_single(mime);
+ }
+ break;
+
+ case MAILMIME_MULTIPLE:
+ for(cur = clist_begin(mime->mm_data.mm_multipart.mm_mp_list) ;
+ cur != NULL ; cur = clist_next(cur)) {
+ struct mailmime * child;
+
+ child = clist_content(cur);
+
+ mailprivacy_prepare_mime(child);
+ }
+ break;
+
+ case MAILMIME_MESSAGE:
+ if (mime->mm_data.mm_message.mm_msg_mime) {
+ mailprivacy_prepare_mime(mime->mm_data.mm_message.mm_msg_mime);
+ }
+ break;
+ }
+}
+
+
+char * mailprivacy_dup_imf_file(struct mailprivacy * privacy,
+ char * source_filename)
+{
+ char filename[PATH_MAX];
+ FILE * dest_f;
+ int r;
+ struct stat stat_info;
+ char * dest_filename;
+ char * mapping;
+ int fd;
+ int col;
+
+ dest_f = mailprivacy_get_tmp_file(privacy,
+ filename, sizeof(filename));
+ if (dest_f == NULL)
+ goto err;
+
+ dest_filename = strdup(filename);
+ if (dest_filename == NULL)
+ goto close_dest;
+
+ fd = open(source_filename, O_RDONLY);
+ if (fd < 0)
+ goto free_dest;
+
+ r = fstat(fd, &stat_info);
+ if (r < 0)
+ goto close_src;
+
+ mapping = mmap(NULL, stat_info.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (mapping == MAP_FAILED)
+ goto close_src;
+
+ col = 0;
+ r = mailimf_string_write(dest_f, &col, mapping, stat_info.st_size);
+ if (r != MAILIMF_NO_ERROR)
+ goto unmap;
+
+ munmap(mapping, stat_info.st_size);
+ close(fd);
+ fclose(dest_f);
+
+ return dest_filename;
+
+ unmap:
+ munmap(mapping, stat_info.st_size);
+ close_src:
+ close(fd);
+ free_dest:
+ free(dest_filename);
+ close_dest:
+ fclose(dest_f);
+ err:
+ return NULL;
+}
+
+/* TODO : better function to duplicate mime fields, currenly such inelegant */
+
+struct mailmime_fields *
+mailprivacy_mime_fields_dup(struct mailprivacy * privacy,
+ struct mailmime_fields * mime_fields)
+{
+ FILE * f;
+ char tmp_file[PATH_MAX];
+ int col;
+ int r;
+ struct mailmime_fields * dup_mime_fields;
+ int fd;
+ char * mapping;
+ struct stat stat_info;
+ struct mailimf_fields * fields;
+ size_t cur_token;
+
+ f = mailprivacy_get_tmp_file(privacy, tmp_file, sizeof(tmp_file));
+ if (f == NULL)
+ goto err;
+
+ col = 0;
+ r = mailmime_fields_write(f, &col, mime_fields);
+ if (r != MAILIMF_NO_ERROR)
+ goto unlink;
+
+ fflush(f);
+
+ fd = fileno(f);
+ if (fd == -1)
+ goto unlink;
+
+ r = fstat(fd, &stat_info);
+ if (r < 0)
+ goto unlink;
+
+ mapping = mmap(NULL, stat_info.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (mapping == MAP_FAILED)
+ goto unlink;
+
+ cur_token = 0;
+ r = mailimf_optional_fields_parse(mapping, stat_info.st_size,
+ &cur_token, &fields);
+ if (r != MAILIMF_NO_ERROR)
+ goto unmap;
+
+ r = mailmime_fields_parse(fields, &dup_mime_fields);
+ mailimf_fields_free(fields);
+ if (r != MAILIMF_NO_ERROR)
+ goto unmap;
+
+ munmap(mapping, stat_info.st_size);
+ fclose(f);
+ unlink(tmp_file);
+
+ return dup_mime_fields;
+
+ unmap:
+ munmap(mapping, stat_info.st_size);
+ unlink:
+ fclose(f);
+ unlink(tmp_file);
+ err:
+ return NULL;
+}
+
+
+
+struct mailmime_parameter *
+mailmime_parameter_dup(struct mailmime_parameter * param)
+{
+ char * name;
+ char * value;
+ struct mailmime_parameter * dup_param;
+
+ name = strdup(param->pa_name);
+ if (name == NULL)
+ goto err;
+
+ value = strdup(param->pa_value);
+ if (value == NULL)
+ goto free_name;
+
+ dup_param = mailmime_parameter_new(name, value);
+ if (dup_param == NULL)
+ goto free_value;
+
+ return dup_param;
+
+ free_value:
+ free(value);
+ free_name:
+ free(name);
+ err:
+ return NULL;
+}
+
+struct mailmime_composite_type *
+mailmime_composite_type_dup(struct mailmime_composite_type * composite_type)
+{
+ struct mailmime_composite_type * dup_composite;
+ char * token;
+
+ token = NULL;
+ if (composite_type->ct_token != NULL) {
+ token = strdup(composite_type->ct_token);
+ if (token == NULL)
+ goto err;
+ }
+
+ dup_composite = mailmime_composite_type_new(composite_type->ct_type, token);
+ if (dup_composite == NULL)
+ goto free_token;
+
+ return dup_composite;
+
+ free_token:
+ if (token != NULL)
+ free(token);
+ err:
+ return NULL;
+}
+
+struct mailmime_discrete_type *
+mailmime_discrete_type_dup(struct mailmime_discrete_type * discrete_type)
+{
+ struct mailmime_discrete_type * dup_discrete;
+ char * extension;
+
+ extension = NULL;
+ if (discrete_type->dt_extension != NULL) {
+ extension = strdup(discrete_type->dt_extension);
+ if (extension == NULL)
+ goto err;
+ }
+
+ dup_discrete = mailmime_discrete_type_new(discrete_type->dt_type, extension);
+ if (dup_discrete == NULL)
+ goto free_extension;
+
+ return dup_discrete;
+
+ free_extension:
+ if (extension != NULL)
+ free(extension);
+ err:
+ return NULL;
+}
+
+struct mailmime_type * mailmime_type_dup(struct mailmime_type * type)
+{
+ struct mailmime_type * dup_type;
+ struct mailmime_discrete_type * discrete_type;
+ struct mailmime_composite_type * composite_type;
+
+ discrete_type = NULL;
+ composite_type = NULL;
+ switch (type->tp_type) {
+ case MAILMIME_TYPE_DISCRETE_TYPE:
+ discrete_type =
+ mailmime_discrete_type_dup(type->tp_data.tp_discrete_type);
+ if (discrete_type == NULL)
+ goto err;
+ break;
+
+ composite_type =
+ mailmime_composite_type_dup(type->tp_data.tp_composite_type);
+ if (composite_type == NULL)
+ goto free_discrete;
+ }
+
+ dup_type = mailmime_type_new(type->tp_type, discrete_type, composite_type);
+ if (dup_type == NULL)
+ goto free_composite;
+
+ return dup_type;
+
+ free_composite:
+ if (composite_type != NULL)
+ mailmime_composite_type_free(composite_type);
+ free_discrete:
+ if (discrete_type != NULL)
+ mailmime_discrete_type_free(discrete_type);
+ err:
+ return NULL;
+}
+
+struct mailmime_content *
+mailmime_content_dup(struct mailmime_content * content)
+{
+ clist * list;
+ struct mailmime_type * type;
+ int r;
+ struct mailmime_content * dup_content;
+ char * subtype;
+
+ type = mailmime_type_dup(content->ct_type);
+ if (type == NULL)
+ goto err;
+
+ subtype = strdup(content->ct_subtype);
+ if (subtype == NULL)
+ goto free_type;
+
+ list = clist_new();
+ if (list == NULL)
+ goto free_subtype;
+
+ if (content->ct_parameters != NULL) {
+ clistiter * cur;
+
+ for(cur = clist_begin(content->ct_parameters) ;
+ cur != NULL ; cur = clist_next(cur)) {
+ struct mailmime_parameter * param;
+
+ param = mailmime_parameter_dup(clist_content(cur));
+ if (param == NULL)
+ goto free_list;
+
+ r = clist_append(list, param);
+ if (r < 0) {
+ mailmime_parameter_free(param);
+ goto free_list;
+ }
+ }
+ }
+
+ dup_content = mailmime_content_new(type, subtype, list);
+ if (dup_content == NULL)
+ goto free_list;
+
+ return dup_content;
+
+ free_list:
+ clist_foreach(list, (clist_func) mailmime_parameter_free, NULL);
+ free_subtype:
+ free(subtype);
+ free_type:
+ mailmime_type_free(type);
+ err:
+ return NULL;
+}
+
+
+struct mailmime_parameter *
+mailmime_param_new_with_data(char * name, char * value)
+{
+ char * param_name;
+ char * param_value;
+ struct mailmime_parameter * param;
+
+ param_name = strdup(name);
+ if (param_name == NULL)
+ goto err;
+
+ param_value = strdup(value);
+ if (param_value == NULL)
+ goto free_name;
+
+ param = mailmime_parameter_new(param_name, param_value);
+ if (param == NULL)
+ goto free_value;
+
+ return param;
+
+ free_value:
+ free(param_value);
+ free_name:
+ free(param_name);
+ err:
+ return NULL;
+}
+
+
+int mailprivacy_fetch_decoded_to_file(struct mailprivacy * privacy,
+ char * filename, size_t size,
+ mailmessage * msg, struct mailmime * mime)
+{
+ int r;
+ int res;
+ FILE * f;
+ char * content;
+ size_t content_len;
+ size_t written;
+ struct mailmime_single_fields single_fields;
+ int encoding;
+ size_t cur_token;
+ char * parsed_content;
+ size_t parsed_content_len;
+
+ mailmime_single_fields_init(&single_fields, mime->mm_mime_fields,
+ mime->mm_content_type);
+ if (single_fields.fld_encoding != NULL)
+ encoding = single_fields.fld_encoding->enc_type;
+ else
+ encoding = MAILMIME_MECHANISM_8BIT;
+
+ r = mailprivacy_msg_fetch_section(privacy, msg, mime,
+ &content, &content_len);
+ if (r != MAIL_NO_ERROR) {
+ res = MAIL_ERROR_FETCH;
+ goto err;
+ }
+
+ cur_token = 0;
+ r = mailmime_part_parse(content, content_len, &cur_token,
+ encoding, &parsed_content, &parsed_content_len);
+ mailprivacy_msg_fetch_result_free(privacy, msg, content);
+ if (r != MAILIMF_NO_ERROR) {
+ res = MAIL_ERROR_PARSE;
+ goto err;
+ }
+
+ f = mailprivacy_get_tmp_file(privacy, filename, size);
+ if (f == NULL) {
+ res = MAIL_ERROR_FETCH;
+ goto free_fetch;
+ }
+ written = fwrite(parsed_content, 1, parsed_content_len, f);
+ if (written != parsed_content_len) {
+ res = MAIL_ERROR_FILE;
+ goto close;
+ }
+ fclose(f);
+
+ mmap_string_unref(parsed_content);
+
+ return MAIL_NO_ERROR;
+
+ close:
+ fclose(f);
+ unlink(filename);
+ free_fetch:
+ mmap_string_unref(parsed_content);
+ err:
+ return res;
+}
+
diff --git a/libetpan/src/engine/mailprivacy_tools.h b/libetpan/src/engine/mailprivacy_tools.h
new file mode 100644
index 0000000..1bf27c4
--- a/dev/null
+++ b/libetpan/src/engine/mailprivacy_tools.h
@@ -0,0 +1,102 @@
+/*
+ * libEtPan! -- a mail library
+ *
+ * Copyright (C) 2001, 2005 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef MAIL_PRIVACY_TOOLS_H
+
+#define MAIL_PRIVACY_TOOLS_H
+
+#include <libetpan/mailmessage.h>
+#include <libetpan/mailprivacy_types.h>
+
+void mailprivacy_mime_clear(struct mailmime * mime);
+
+FILE * mailprivacy_get_tmp_file(struct mailprivacy * privacy,
+ char * filename, size_t size);
+
+int mailprivacy_get_mime(struct mailprivacy * privacy,
+ int check_privacy,
+ char * content, size_t content_len,
+ struct mailmime ** result_mime);
+
+struct mailmime *
+mailprivacy_new_file_part(struct mailprivacy * privacy,
+ char * filename,
+ char * default_content_type, int default_encoding);
+
+int mailmime_substitute(struct mailmime * old_mime,
+ struct mailmime * new_mime);
+
+int mailprivacy_fetch_mime_body_to_file(struct mailprivacy * privacy,
+ char * filename, size_t size,
+ mailmessage * msg, struct mailmime * mime);
+
+int mailprivacy_get_part_from_file(struct mailprivacy * privacy,
+ int check_privacy,
+ char * filename,
+ struct mailmime ** result_mime);
+
+int mail_quote_filename(char * result, size_t size, char * path);
+
+void mailprivacy_prepare_mime(struct mailmime * mime);
+
+char * mailprivacy_dup_imf_file(struct mailprivacy * privacy,
+ char * source_filename);
+
+struct mailmime_fields *
+mailprivacy_mime_fields_dup(struct mailprivacy * privacy,
+ struct mailmime_fields * mime_fields);
+
+struct mailmime_parameter *
+mailmime_parameter_dup(struct mailmime_parameter * param);
+
+struct mailmime_composite_type *
+mailmime_composite_type_dup(struct mailmime_composite_type * composite_type);
+
+struct mailmime_discrete_type *
+mailmime_discrete_type_dup(struct mailmime_discrete_type * discrete_type);
+
+struct mailmime_type * mailmime_type_dup(struct mailmime_type * type);
+
+struct mailmime_content *
+mailmime_content_dup(struct mailmime_content * content);
+
+struct mailmime_parameter *
+mailmime_param_new_with_data(char * name, char * value);
+
+int mailprivacy_fetch_decoded_to_file(struct mailprivacy * privacy,
+ char * filename, size_t size,
+ mailmessage * msg, struct mailmime * mime);
+
+#endif
diff --git a/libetpan/src/engine/mailprivacy_types.h b/libetpan/src/engine/mailprivacy_types.h
new file mode 100644
index 0000000..e53f226
--- a/dev/null
+++ b/libetpan/src/engine/mailprivacy_types.h
@@ -0,0 +1,82 @@
+/*
+ * libEtPan! -- a mail library
+ *
+ * Copyright (C) 2001, 2005 - DINH Viet Hoa
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the libEtPan! project nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef MAIL_PRIVACY_TYPES_H
+
+#define MAIL_PRIVACY_TYPES_H
+
+#include <libetpan/chash.h>
+#include <libetpan/carray.h>
+#include <libetpan/mailmessage.h>
+#include <libetpan/mailmime.h>
+
+struct mailprivacy {
+ char * tmp_dir; /* working tmp directory */
+ chash * msg_ref; /* mailmessage => present or not */
+ chash * mmapstr; /* mmapstring => present or not present */
+ chash * mime_ref; /* mime => present or not */
+ carray * protocols;
+ int make_alternative;
+ /* if make_alternative is 0, replaces the part with decrypted
+ part, if 1, adds a multipart/alternative and put the decrypted
+ and encrypted part as subparts.
+ */
+};
+
+struct mailprivacy_encryption {
+ char * name;
+ char * description;
+
+ int (* encrypt)(struct mailprivacy *,
+ struct mailmime *, struct mailmime **);
+};
+
+struct mailprivacy_protocol {
+ char * name;
+ char * description;
+
+ /* introduce to easy the port to sylpheed */
+ int (* is_encrypted)(struct mailprivacy *,
+ mailmessage *, struct mailmime *);
+
+ int (* decrypt)(struct mailprivacy *,
+ mailmessage *, struct mailmime *,
+ struct mailmime **);
+
+ int encryption_count;
+ struct mailprivacy_encryption * encryption_tab;
+};
+
+#endif