From 8432156765d0bc82cec6fe3dc37c3de8f7ee7dba Mon Sep 17 00:00:00 2001 From: djcb Date: Fri, 27 Jul 2012 18:04:17 +0300 Subject: [PATCH] * crypto: implement automagic decryption when so requested (mainly by adding some smartness to various part_foreach functions, adding mu_msg_(get|set)_auto_decrypt to mark a message for auto-decryption) --- lib/mu-msg-file.c | 95 +++++++++++++++++++++++++++++++++---- lib/mu-msg-part.c | 116 ++++++++++++++++++++++++++++------------------ lib/mu-msg-part.h | 4 +- lib/mu-msg-priv.h | 31 +++++++++++-- lib/mu-msg.c | 25 ++++++++-- lib/mu-msg.h | 28 +++++++++-- 6 files changed, 234 insertions(+), 65 deletions(-) diff --git a/lib/mu-msg-file.c b/lib/mu-msg-file.c index 31c7d89a..46e81162 100644 --- a/lib/mu-msg-file.c +++ b/lib/mu-msg-file.c @@ -42,6 +42,10 @@ #include "mu-maildir.h" #include "mu-msg-priv.h" +#ifdef BUILD_CRYPTO +#include "mu-msg-crypto.h" +#endif /*BUILD_CRYPTO*/ + static gboolean init_file_metadata (MuMsgFile *self, const char* path, const char *mdir, GError **err); @@ -285,9 +289,10 @@ get_content_flags (MuMsgFile *self) flags = MU_FLAG_NONE; if (GMIME_IS_MESSAGE(self->_mime_msg)) - g_mime_message_foreach (self->_mime_msg, - (GMimeObjectForeachFunc)msg_cflags_cb, - &flags); + mu_mime_message_foreach (self->_mime_msg, + FALSE, /* never decrypt for this */ + (GMimeObjectForeachFunc)msg_cflags_cb, + &flags); return flags; } @@ -555,7 +560,8 @@ cleanup: GMimePart* -mu_msg_mime_get_body_part (GMimeMessage *msg, gboolean want_html) +mu_msg_mime_get_body_part (GMimeMessage *msg, gboolean decrypt, + gboolean want_html) { GetBodyData data; @@ -564,9 +570,10 @@ mu_msg_mime_get_body_part (GMimeMessage *msg, gboolean want_html) memset (&data, 0, sizeof(GetBodyData)); data._want_html = want_html; - g_mime_message_foreach (msg, - (GMimeObjectForeachFunc)get_body_cb, - &data); + mu_mime_message_foreach (msg, + decrypt, + (GMimeObjectForeachFunc)get_body_cb, + &data); if (want_html) return (GMimePart*)data._html_part; else @@ -583,7 +590,9 @@ get_body (MuMsgFile *self, gboolean want_html) g_return_val_if_fail (self, NULL); g_return_val_if_fail (GMIME_IS_MESSAGE(self->_mime_msg), NULL); - part = mu_msg_mime_get_body_part (self->_mime_msg, want_html); + part = mu_msg_mime_get_body_part (self->_mime_msg, + self->_auto_decrypt, + want_html); if (GMIME_IS_PART(part)) { gboolean err; gchar *str; @@ -656,7 +665,7 @@ get_concatenated_text (MuMsgFile *self) g_return_val_if_fail (GMIME_IS_MESSAGE(self->_mime_msg), NULL); txt = NULL; - g_mime_message_foreach (self->_mime_msg, + mu_mime_message_foreach (self->_mime_msg, self->_auto_decrypt, (GMimeObjectForeachFunc)append_text, &txt); return txt; @@ -851,7 +860,6 @@ mu_msg_file_get_num_field (MuMsgFile *self, const MuMsgFieldId mfid) } - const char* mu_msg_file_get_header (MuMsgFile *self, const char *header) { @@ -868,3 +876,70 @@ mu_msg_file_get_header (MuMsgFile *self, const char *header) return hdr ? free_string_later (self, mu_str_utf8ify(hdr)) : NULL; } + + +struct _ForeachData { + GMimeObjectForeachFunc user_func; + gpointer user_data; + gboolean decrypt; +}; +typedef struct _ForeachData ForeachData; + + +static void +foreach_cb (GMimeObject *parent, GMimeObject *part, ForeachData *fdata) +{ + fdata->user_func (parent, part, fdata->user_data); + +#ifdef BUILD_CRYPTO + /* maybe iterate over decrypted parts */ + if (fdata->decrypt && + GMIME_IS_MULTIPART_ENCRYPTED (part)) { + + GError *err; + GMimeObject *dec; + + err = NULL; + dec = mu_msg_crypto_decrypt_part + (GMIME_MULTIPART_ENCRYPTED(part), + MU_MSG_OPTION_NONE, &err); + if (!dec||err) { + g_printerr ("crypto error: %s\n", + err ? err->message : "something went wrong"); + g_clear_error(&err); + g_clear_object(&dec); + return; + } + + if (GMIME_IS_MULTIPART (dec)) + g_mime_multipart_foreach ( + (GMIME_MULTIPART(dec)), + (GMimeObjectForeachFunc)foreach_cb, + fdata); + else + foreach_cb (parent, dec, fdata); + + g_object_unref (dec); + } +#endif /*BUILD_CRYPTO*/ +} + + +void +mu_mime_message_foreach (GMimeMessage *msg, gboolean decrypt, + GMimeObjectForeachFunc func, gpointer user_data) +{ + ForeachData fdata; + + g_return_if_fail (GMIME_IS_MESSAGE (msg)); + g_return_if_fail (func); + + fdata.user_func = func; + fdata.user_data = user_data; + fdata.decrypt = decrypt; + + g_mime_message_foreach + (msg, + (GMimeObjectForeachFunc)foreach_cb, + &fdata); +} diff --git a/lib/mu-msg-part.c b/lib/mu-msg-part.c index a567e48c..8931ac94 100644 --- a/lib/mu-msg-part.c +++ b/lib/mu-msg-part.c @@ -76,9 +76,10 @@ find_part (MuMsg* msg, guint partidx) fpdata.idx = 0; fpdata.part = NULL; - g_mime_message_foreach (msg->_file->_mime_msg, - (GMimeObjectForeachFunc)find_part_cb, - &fpdata); + mu_mime_message_foreach (msg->_file->_mime_msg, + mu_msg_get_auto_decrypt(msg), + (GMimeObjectForeachFunc)find_part_cb, + &fpdata); return fpdata.part; } @@ -94,19 +95,28 @@ typedef struct _PartData PartData; -static gchar *mime_message_to_string (GMimeMessage *mimemsg); +struct _TxtData { + GString *gstr; + gboolean decrypt; +}; +typedef struct _TxtData TxtData; + +static gchar *mime_message_to_string (GMimeMessage *mimemsg, + gboolean decrypt); static void each_mime_part_get_text (GMimeObject *parent, GMimeObject *part, - GString **gstr) + TxtData *tdata) { char *txt; txt = NULL; if (GMIME_IS_MESSAGE(part)) { - txt = mime_message_to_string (GMIME_MESSAGE(part)); + txt = mime_message_to_string (GMIME_MESSAGE(part), + tdata->decrypt); if (txt) - *gstr = g_string_append (*gstr, txt); + tdata->gstr = + g_string_append (tdata->gstr, txt); } if (GMIME_IS_PART (part)) { @@ -120,47 +130,57 @@ each_mime_part_get_text (GMimeObject *parent, GMimeObject *part, } if (txt) { - *gstr = g_string_append_c (*gstr, '\n'); - *gstr = g_string_append (*gstr, txt); + tdata->gstr = g_string_append_c (tdata->gstr, '\n'); + tdata->gstr = g_string_append (tdata->gstr, txt); g_free (txt); } } -static gchar * -mime_message_to_string (GMimeMessage *mimemsg) +static gchar* +mime_message_to_string (GMimeMessage *mimemsg, gboolean decrypt) { - GString *data; + TxtData tdata; InternetAddressList *addresses; gchar *adrs; + const char *str; - data = g_string_sized_new (2048); /* just a guess */ - - g_string_append (data, g_mime_message_get_sender(mimemsg)); - g_string_append_c (data, '\n'); - g_string_append (data, g_mime_message_get_subject(mimemsg)); - g_string_append_c (data, '\n'); + tdata.gstr = g_string_sized_new (2048); /* just a guess */ + /* put sender, recipients and subject in the string, so they + * can be indexed as well */ + str = g_mime_message_get_sender(mimemsg); + if (str) { + g_string_append (tdata.gstr, str); + g_string_append_c (tdata.gstr, '\n'); + } + str = g_mime_message_get_subject(mimemsg); + if (str) { + g_string_append (tdata.gstr, str); + g_string_append_c (tdata.gstr, '\n'); + } addresses = g_mime_message_get_all_recipients (mimemsg); adrs = internet_address_list_to_string (addresses, FALSE); g_object_unref(G_OBJECT(addresses)); - - g_string_append (data, adrs); + if (adrs) + g_string_append (tdata.gstr, adrs); g_free (adrs); /* recurse through all text parts */ - g_mime_message_foreach - (mimemsg, + tdata.decrypt = decrypt; + mu_mime_message_foreach + (mimemsg, decrypt, (GMimeObjectForeachFunc)each_mime_part_get_text, - &data); + &tdata); - return g_string_free (data, FALSE); + return g_string_free (tdata.gstr, FALSE); } char* -mu_msg_part_get_text (MuMsgPart *self, gboolean *err) +mu_msg_part_get_text (MuMsg *msg, MuMsgPart *self, gboolean *err) { GMimeObject *mobj; + g_return_val_if_fail (msg, NULL); g_return_val_if_fail (self && self->data, NULL); mobj = (GMimeObject*)self->data; @@ -173,8 +193,8 @@ mu_msg_part_get_text (MuMsgPart *self, gboolean *err) } if (GMIME_IS_MESSAGE(mobj)) - return mime_message_to_string ((GMimeMessage*)mobj); - + return mime_message_to_string ((GMimeMessage*)mobj, + mu_msg_get_auto_decrypt(msg)); g_return_val_if_reached (NULL); return NULL; } @@ -234,6 +254,9 @@ check_signature_maybe (GMimeObject *parent, GMimeObject *mobj, MuMsgPart *pi, g_clear_error (&err); } } + + + #endif /*BUILD_CRYPTO*/ static gboolean @@ -273,7 +296,7 @@ init_msg_part_from_mime_part (MuMsgOptions opts, GMimeObject *parent, /* if we have crypto support, check the signature if there is one */ #ifdef BUILD_CRYPTO if (opts & MU_MSG_OPTION_CHECK_SIGNATURES) - check_signature_maybe (parent, (GMimeObject*)part, + check_signature_maybe (parent, (GMimeObject*)part, pi, opts); #endif /*BUILD_CRYPTO*/ @@ -332,14 +355,13 @@ msg_part_free (MuMsgPart *pi) #endif /*BUILD_CRYPTO*/ } - static void part_foreach_cb (GMimeObject *parent, GMimeObject *mobj, PartData *pdata) { MuMsgPart pi; gboolean rv; - /* ignore non-leaf / message parts */ + /* ignore other non-leaf / message parts */ if (!is_part_or_message_part (mobj)) return; @@ -348,13 +370,12 @@ part_foreach_cb (GMimeObject *parent, GMimeObject *mobj, PartData *pdata) pi.content_id = (char*)g_mime_object_get_content_id (mobj); if (GMIME_IS_PART(mobj)) { - pi.data = (gpointer)mobj; rv = init_msg_part_from_mime_part (pdata->_opts, parent, (GMimePart*)mobj, &pi); /* check if this is the body part */ - if ((void*)pdata->_body_part == (void*)mobj) + if (rv && (void*)pdata->_body_part == (void*)mobj) pi.part_type |= MU_MSG_PART_TYPE_BODY; } else if (GMIME_IS_MESSAGE_PART(mobj)) { @@ -393,14 +414,16 @@ mu_msg_part_foreach (MuMsg *msg, MuMsgPartForeachFunc func, gpointer user_data, pdata._msg = msg; pdata._idx = 0; - pdata._body_part = mu_msg_mime_get_body_part (mime_msg, FALSE); + pdata._body_part = mu_msg_mime_get_body_part + (mime_msg, mu_msg_get_auto_decrypt(msg), FALSE); pdata._func = func; pdata._user_data = user_data; pdata._opts = opts; - g_mime_message_foreach (msg->_file->_mime_msg, - (GMimeObjectForeachFunc)part_foreach_cb, - &pdata); + mu_mime_message_foreach (msg->_file->_mime_msg, + mu_msg_get_auto_decrypt(msg), + (GMimeObjectForeachFunc)part_foreach_cb, + &pdata); } @@ -658,21 +681,22 @@ part_match_foreach_cb (GMimeObject *parent, GMimeObject *part, } static int -msg_part_find_idx (GMimeMessage *msg, MatchFunc func, gpointer user_data) +msg_part_find_idx (GMimeMessage *mimemsg, gboolean auto_decrypt, + MatchFunc func, gpointer user_data) { MatchData mdata; - g_return_val_if_fail (msg, -1); - g_return_val_if_fail (GMIME_IS_MESSAGE(msg), -1); + g_return_val_if_fail (mimemsg, -1); + g_return_val_if_fail (GMIME_IS_MESSAGE(mimemsg), -1); mdata._idx = 0; mdata._found_idx = -1; mdata._matcher = func; mdata._user_data = user_data; - g_mime_message_foreach (msg, - (GMimeObjectForeachFunc)part_match_foreach_cb, - &mdata); + mu_mime_message_foreach (mimemsg, auto_decrypt, + (GMimeObjectForeachFunc)part_match_foreach_cb, + &mdata); return mdata._found_idx; } @@ -700,6 +724,7 @@ mu_msg_part_find_cid (MuMsg *msg, const char* sought_cid) sought_cid + 4 : sought_cid; return msg_part_find_idx (msg->_file->_mime_msg, + mu_msg_get_auto_decrypt(msg), (MatchFunc)match_content_id, (gpointer)(char*)cid); } @@ -752,9 +777,10 @@ mu_msg_part_find_files (MuMsg *msg, const GRegex *pattern) mdata._rx = pattern; mdata._idx = 0; - g_mime_message_foreach (msg->_file->_mime_msg, - (GMimeObjectForeachFunc)match_filename_rx, - &mdata); + mu_mime_message_foreach (msg->_file->_mime_msg, + mu_msg_get_auto_decrypt(msg), + (GMimeObjectForeachFunc)match_filename_rx, + &mdata); return mdata._lst; } diff --git a/lib/mu-msg-part.h b/lib/mu-msg-part.h index 66f289c9..d1657e48 100644 --- a/lib/mu-msg-part.h +++ b/lib/mu-msg-part.h @@ -123,7 +123,7 @@ typedef struct _MuMsgPart MuMsgPart; * * @return utf8 string for this MIME part, to be freed by caller */ -char* mu_msg_part_get_text (MuMsgPart *part, gboolean *err); +char* mu_msg_part_get_text (MuMsg *msg, MuMsgPart *part, gboolean *err); /** @@ -211,6 +211,8 @@ gchar* mu_msg_part_filepath_cache (MuMsg *msg, guint partid) */ int mu_msg_part_find_cid (MuMsg *msg, const char* content_id); + + /** * retrieve a list of indices for mime-parts with filenames matching a regex * diff --git a/lib/mu-msg-priv.h b/lib/mu-msg-priv.h index 68665a6d..732b7b7e 100644 --- a/lib/mu-msg-priv.h +++ b/lib/mu-msg-priv.h @@ -44,6 +44,9 @@ struct _MuMsgFile { char _path [PATH_MAX + 1]; char _maildir [PATH_MAX + 1]; + /* whether to attemp to automagically decrypt encrypted parts */ + gboolean _auto_decrypt; + /* list where we push allocated strings so we can * free them when the struct gets destroyed */ @@ -97,15 +100,25 @@ gboolean mu_msg_part_mime_save_object (GMimeObject *obj, const char *fullpath, * get the MIME part that's probably the body of the message (heuristic) * * @param self a MuMsg + * @param decrypt whether decryption should be attempted, if needed * @param want_html whether it should be a html type of body * * @return the MIME part, or NULL in case of error. */ -GMimePart* mu_msg_mime_get_body_part (GMimeMessage *msg, gboolean want_html); - - +GMimePart* mu_msg_mime_get_body_part (GMimeMessage *msg, gboolean decrypt, + gboolean want_html); +/** + * Like g_mime_message_foreach, but will recurse into encrypted parts + * + * @param msg + * @param decrypt whether to try to automatically decrypt + * @param func + * @param user_data + */ +void mu_mime_message_foreach (GMimeMessage *msg, gboolean decrypt, + GMimeObjectForeachFunc func, gpointer user_data); #ifdef BUILD_CRYPTO /** @@ -136,6 +149,18 @@ char* mu_msg_mime_decrypt (GMimeMultipartEncrypted *encpart, MuMsgOptions opts, GError **err); + +/** + * decrypt the given encrypted mime multipart + * + * @param enc encrypted part + * @param opts options + * @param err receives error data + * + * @return the decrypted part, or NULL in case of error + */ +GMimeObject* mu_msg_crypto_decrypt_part (GMimeMultipartEncrypted *enc, MuMsgOptions opts, + GError **err); #endif /*BUILD_CRYPTO*/ G_END_DECLS diff --git a/lib/mu-msg.c b/lib/mu-msg.c index b8c5cac9..a6d8aaf6 100644 --- a/lib/mu-msg.c +++ b/lib/mu-msg.c @@ -84,7 +84,8 @@ msg_new (void) } MuMsg* -mu_msg_new_from_file (const char *path, const char *mdir, GError **err) +mu_msg_new_from_file (const char *path, const char *mdir, + GError **err) { MuMsg *self; MuMsgFile *msgfile; @@ -169,6 +170,26 @@ mu_msg_unref (MuMsg *self) } +void +mu_msg_set_auto_decrypt (MuMsg *msg, gboolean autodecrypt) +{ + g_return_if_fail (msg); + mu_msg_load_msg_file (msg, NULL); + msg->_file->_auto_decrypt = autodecrypt; +} + + +gboolean +mu_msg_get_auto_decrypt (MuMsg *msg) +{ + g_return_val_if_fail (msg, FALSE); + if (!msg->_file) + return FALSE; + return msg->_file->_auto_decrypt; +} + + + /* use this instead of mu_msg_get_path so we don't get into infinite * regress...*/ static const char* @@ -377,8 +398,6 @@ mu_msg_get_timestamp (MuMsg *self) } - - const char* mu_msg_get_path (MuMsg *self) { diff --git a/lib/mu-msg.h b/lib/mu-msg.h index f3d34cfd..b1299c8a 100644 --- a/lib/mu-msg.h +++ b/lib/mu-msg.h @@ -38,18 +38,16 @@ enum _MuMsgOptions { /* 1 << 0 is still free! */ /* for -> sexp conversion */ - MU_MSG_OPTION_HEADERS_ONLY = 1 << 1, MU_MSG_OPTION_EXTRACT_IMAGES = 1 << 2, -/* 1 << 3 is still free! */ - /* below options are for checking signatures; only effective * if mu was built with crypto support */ MU_MSG_OPTION_CHECK_SIGNATURES = 1 << 4, MU_MSG_OPTION_AUTO_RETRIEVE_KEY = 1 << 5, MU_MSG_OPTION_USE_AGENT = 1 << 6, MU_MSG_OPTION_USE_PKCS7 = 1 << 7 /* gpg is the default */ + }; typedef enum _MuMsgOptions MuMsgOptions; @@ -138,6 +136,28 @@ MuMsg *mu_msg_ref (MuMsg *msg); void mu_msg_unref (MuMsg *msg); +/** + * should we set this message to 'autodecrypt'? if so, try to + * automatically decrypt encrypted message parts. + * + * @param msg a message + * @param autodecrypt TRUE or FALSE + * + * @return + */ +void mu_msg_set_auto_decrypt (MuMsg *msg, gboolean autodecrypt); + +/** + * get the autodecrypt status for this message. See @func mu_msg_set_auto_decrypt + * + * @param msg a message + * + * @return the auto-decrypt status + */ +gboolean mu_msg_get_auto_decrypt (MuMsg *msg); + + + /** * cache the values from the backend (file or db), so we don't the @@ -566,6 +586,8 @@ typedef gboolean (*MuMsgContactForeachFunc) (MuMsgContact* contact, void mu_msg_contact_foreach (MuMsg *msg, MuMsgContactForeachFunc func, gpointer user_data); + + G_END_DECLS #endif /*__MU_MSG_H__*/