-rw-r--r-- | libetpan/src/engine/mailengine.c | 1636 | ||||
-rw-r--r-- | libetpan/src/engine/mailengine.h | 190 | ||||
-rw-r--r-- | libetpan/src/engine/mailprivacy.c | 949 | ||||
-rw-r--r-- | libetpan/src/engine/mailprivacy.h | 117 | ||||
-rw-r--r-- | libetpan/src/engine/mailprivacy_gnupg.c | 2614 | ||||
-rw-r--r-- | libetpan/src/engine/mailprivacy_gnupg.h | 46 | ||||
-rw-r--r-- | libetpan/src/engine/mailprivacy_smime.c | 1755 | ||||
-rw-r--r-- | libetpan/src/engine/mailprivacy_smime.h | 84 | ||||
-rw-r--r-- | libetpan/src/engine/mailprivacy_tools.c | 1283 | ||||
-rw-r--r-- | libetpan/src/engine/mailprivacy_tools.h | 102 | ||||
-rw-r--r-- | libetpan/src/engine/mailprivacy_types.h | 82 |
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 |