* mu-msg-part: clean up mime-part parsing

This commit is contained in:
djcb 2012-07-24 22:12:22 +03:00
parent 535b1107c4
commit c7eaaf9475
3 changed files with 212 additions and 150 deletions

View File

@ -88,49 +88,94 @@ struct _PartData {
MuMsgPartForeachFunc _func;
gpointer _user_data;
GMimePart *_body_part;
MuMsgOptions _opts;
MuMsgOptions _opts;
};
typedef struct _PartData PartData;
static gchar *mime_message_to_string (GMimeMessage *mimemsg);
static void
each_mime_part_get_text (GMimeObject *parent, GMimeObject *part,
GString **gstr)
{
char *txt;
txt = NULL;
if (GMIME_IS_MESSAGE(part)) {
txt = mime_message_to_string (GMIME_MESSAGE(part));
if (txt)
*gstr = g_string_append (*gstr, txt);
}
if (GMIME_IS_PART (part)) {
GMimeContentType *ctype;
gboolean err;
ctype = g_mime_object_get_content_type (part);
if (!g_mime_content_type_is_type (ctype, "text", "plain"))
return; /* not plain text */
txt = mu_msg_mime_part_to_string
((GMimePart*)part, &err);
}
if (txt) {
*gstr = g_string_append_c (*gstr, '\n');
*gstr = g_string_append (*gstr, txt);
g_free (txt);
}
}
static gchar *
mime_message_to_string (GMimeMessage *mimemsg)
{
GString *data;
InternetAddressList *addresses;
gchar *adrs;
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');
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);
g_free (adrs);
/* recurse through all text parts */
g_mime_message_foreach
(mimemsg,
(GMimeObjectForeachFunc)each_mime_part_get_text,
&data);
return g_string_free (data, FALSE);
}
char*
mu_msg_part_get_text (MuMsgPart *self, gboolean *err)
{
GMimeObject *mobj;
g_return_val_if_fail (self && self->data, NULL);
mobj = (GMimeObject*)self->data;
if (GMIME_IS_PART(mobj))
if (GMIME_IS_PART(mobj)) {
/* ignore all but plain text */
if ((strcasecmp (self->type, "text") == 0) &&
(strcasecmp (self->subtype, "plain") == 0))
return NULL;
return mu_msg_mime_part_to_string ((GMimePart*)mobj, err);
else if (GMIME_IS_MESSAGE(mobj)) {
/* when it's an (embedded) message, the text is just
* of the message metadata, so we can index it.
*/
GString *data;
GMimeMessage *msg;
InternetAddressList *addresses;
gchar *adrs;
}
msg = (GMimeMessage*)mobj;
data = g_string_sized_new (512); /* just a guess */
g_string_append (data, g_mime_message_get_sender(msg));
g_string_append_c (data, '\n');
g_string_append (data, g_mime_message_get_subject(msg));
g_string_append_c (data, '\n');
addresses = g_mime_message_get_all_recipients (msg);
adrs = internet_address_list_to_string (addresses, FALSE);
g_object_unref(G_OBJECT(addresses));
g_string_append (data, adrs);
g_free (adrs);
return g_string_free (data, FALSE);
} else
if (GMIME_IS_MESSAGE(mobj))
return mime_message_to_string ((GMimeMessage*)mobj);
g_return_val_if_reached (NULL);
return NULL;
}
@ -157,91 +202,6 @@ get_part_size (GMimePart *part)
}
static gboolean
init_msg_part_from_mime_part (GMimePart *part, MuMsgPart *pi)
{
const gchar *fname, *descr;
GMimeContentType *ct;
ct = g_mime_object_get_content_type ((GMimeObject*)part);
if (GMIME_IS_CONTENT_TYPE(ct)) {
pi->type = (char*)g_mime_content_type_get_media_type (ct);
pi->subtype = (char*)g_mime_content_type_get_media_subtype (ct);
}
pi->disposition = (char*)g_mime_object_get_disposition
((GMimeObject*)part);
fname = g_mime_part_get_filename (part);
pi->file_name = fname ? mu_str_utf8ify (fname) : NULL;
descr = g_mime_part_get_content_description (part);
pi->description = descr ? mu_str_utf8ify (descr) : NULL;
pi->size = get_part_size (part);
pi->is_leaf = TRUE;
pi->is_msg = FALSE;
return TRUE;
}
static gchar*
get_filename_for_mime_message_part (GMimeMessage *mmsg)
{
gchar *name, *cur;
name = (char*)g_mime_message_get_subject (mmsg);
if (!name)
name = "message";
name = g_strconcat (name, ".eml", NULL);
/* remove slashes... */
for (cur = name ; *cur; ++cur) {
if (*cur == '/' || *cur == ' ' || *cur == ':')
*cur = '-';
}
return name;
}
static gboolean
init_msg_part_from_mime_message_part (GMimeMessage *mmsg, MuMsgPart *pi)
{
pi->disposition = GMIME_DISPOSITION_ATTACHMENT;
/* pseudo-file name... */
pi->file_name = get_filename_for_mime_message_part (mmsg);
pi->description = g_strdup ("message");
pi->type = "message";
pi->subtype = "rfc822";
pi->size = 0;
pi->is_leaf = TRUE;
pi->is_msg = TRUE;
return TRUE;
}
static void
msg_part_free (MuMsgPart *pi)
{
if (!pi)
return;
g_free (pi->file_name);
g_free (pi->description);
#ifdef BUILD_CRYPTO
mu_msg_part_free_sig_infos (pi->sig_infos);
#endif /*BUILD_CRYPTO*/
}
#ifdef BUILD_CRYPTO
static void
check_signature_maybe (GMimeObject *parent, GMimeObject *mobj, MuMsgPart *pi,
@ -274,9 +234,104 @@ check_signature_maybe (GMimeObject *parent, GMimeObject *mobj, MuMsgPart *pi,
g_clear_error (&err);
}
}
#endif /*BUILD_CRYPTO*/
static gboolean
init_msg_part_from_mime_part (MuMsgOptions opts, GMimeObject *parent,
GMimePart *part, MuMsgPart *pi)
{
const gchar *fname, *descr;
GMimeContentType *ct;
ct = g_mime_object_get_content_type ((GMimeObject*)part);
if (GMIME_IS_CONTENT_TYPE(ct)) {
pi->type = (char*)g_mime_content_type_get_media_type (ct);
pi->subtype = (char*)g_mime_content_type_get_media_subtype (ct);
}
pi->disposition = (char*)g_mime_object_get_disposition
((GMimeObject*)part);
fname = g_mime_part_get_filename (part);
pi->file_name = fname ? mu_str_utf8ify (fname) : NULL;
descr = g_mime_part_get_content_description (part);
pi->description = descr ? mu_str_utf8ify (descr) : NULL;
pi->size = get_part_size (part);
pi->part_type = MU_MSG_PART_TYPE_LEAF;
if (!pi->disposition ||
g_ascii_strcasecmp (pi->disposition,
GMIME_DISPOSITION_INLINE) == 0)
pi->part_type |= MU_MSG_PART_TYPE_INLINE;
if (GMIME_IS_MULTIPART_SIGNED (parent))
pi->part_type |= MU_MSG_PART_TYPE_SIGNED;
if (GMIME_IS_MULTIPART_ENCRYPTED (parent))
pi->part_type |= MU_MSG_PART_TYPE_ENCRYPTED;
/* 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,
pi, opts);
#endif /*BUILD_CRYPTO*/
return TRUE;
}
static gchar*
get_filename_for_mime_message_part (GMimeMessage *mmsg)
{
gchar *name, *cur;
name = (char*)g_mime_message_get_subject (mmsg);
if (!name)
name = "message";
name = g_strconcat (name, ".eml", NULL);
/* remove slashes... */
for (cur = name ; *cur; ++cur) {
if (*cur == '/' || *cur == ' ' || *cur == ':')
*cur = '-';
}
return name;
}
static gboolean
init_msg_part_from_mime_message_part (GMimeMessage *mmsg, MuMsgPart *pi)
{
pi->disposition = GMIME_DISPOSITION_ATTACHMENT;
/* pseudo-file name... */
pi->file_name = get_filename_for_mime_message_part (mmsg);
pi->description = g_strdup ("message");
pi->type = "message";
pi->subtype = "rfc822";
pi->size = 0;
pi->part_type = MU_MSG_PART_TYPE_MESSAGE;
return TRUE;
}
static void
msg_part_free (MuMsgPart *pi)
{
if (!pi)
return;
g_free (pi->file_name);
g_free (pi->description);
#ifdef BUILD_CRYPTO
mu_msg_part_free_sig_infos (pi->sig_infos);
#endif /*BUILD_CRYPTO*/
}
static void
@ -292,34 +347,25 @@ part_foreach_cb (GMimeObject *parent, GMimeObject *mobj, PartData *pdata)
memset (&pi, 0, sizeof(pi));
pi.index = pdata->_idx++;
pi.content_id = (char*)g_mime_object_get_content_id (mobj);
pi.data = (gpointer)mobj;
/* check if this is the body part */
pi.is_body = ((void*)pdata->_body_part == (void*)mobj);
if ((void*)pdata->_body_part == (void*)mobj)
pi.part_type |= MU_MSG_PART_TYPE_BODY;
if (GMIME_IS_PART(mobj))
rv = init_msg_part_from_mime_part ((GMimePart*)mobj, &pi);
else if (GMIME_IS_MESSAGE_PART(mobj)) {
if (GMIME_IS_PART(mobj)) {
pi.data = (gpointer)mobj;
rv = init_msg_part_from_mime_part
(pdata->_opts, parent, (GMimePart*)mobj, &pi);
} else if (GMIME_IS_MESSAGE_PART(mobj)) {
GMimeMessage *mmsg;
mmsg = g_mime_message_part_get_message ((GMimeMessagePart*)mobj);
if (!mmsg)
return;
/* use the message, not the message part */
pi.data = (gpointer)mmsg;
rv = init_msg_part_from_mime_message_part (mmsg, &pi);
if (rv && (pdata->_opts && MU_MSG_OPTION_RECURSE_RFC822))
/* NOTE: this screws up the counting
* (pdata->_idx); happily, we only use it
* where we don't care about that */
g_mime_message_foreach /* recurse */
(mmsg, (GMimeObjectForeachFunc)part_foreach_cb,
pdata);
} else
rv = FALSE; /* ignore */
/* if we have crypto support, check the signature if there is one */
#ifdef BUILD_CRYPTO
if (pdata->_opts & MU_MSG_OPTION_CHECK_SIGNATURES)
check_signature_maybe (parent, mobj, &pi, pdata->_opts);
#endif /*BUILD_CRYPTO*/
if (rv)
pdata->_func(pdata->_msg, &pi, pdata->_user_data);
@ -710,17 +756,15 @@ mu_msg_part_find_files (MuMsg *msg, const GRegex *pattern)
gboolean
mu_msg_part_looks_like_attachment (MuMsgPart *part, gboolean include_inline)
mu_msg_part_looks_like_attachment (MuMsgPart *part,
gboolean include_inline)
{
g_return_val_if_fail (part, FALSE);
if (part->is_body||!part->disposition||!part->type)
if (part->part_type & MU_MSG_PART_TYPE_BODY)
return FALSE;
if (!include_inline && (part->part_type & MU_MSG_PART_TYPE_INLINE))
return FALSE;
if (include_inline ||
g_ascii_strcasecmp (part->disposition,
GMIME_DISPOSITION_ATTACHMENT) == 0)
return TRUE;
return FALSE;
return TRUE;
}

View File

@ -27,6 +27,25 @@
G_BEGIN_DECLS
enum _MuMsgPartType {
MU_MSG_PART_TYPE_NONE = 0,
/* look like the message body (heuristic) ? */
MU_MSG_PART_TYPE_BODY = 1 << 0,
/* MIME part without children */
MU_MSG_PART_TYPE_LEAF = 1 << 1,
/* an RFC822 message part? */
MU_MSG_PART_TYPE_MESSAGE = 1 << 2,
/* disposition inline? */
MU_MSG_PART_TYPE_INLINE = 1 << 3,
/* a signed part? */
MU_MSG_PART_TYPE_SIGNED = 1 << 5,
/* an encrypted part? */
MU_MSG_PART_TYPE_ENCRYPTED = 1 << 6
};
typedef enum _MuMsgPartType MuMsgPartType;
struct _MuMsgPart {
/* index of this message part */
@ -55,11 +74,7 @@ struct _MuMsgPart {
gpointer data; /* opaque data */
gboolean is_body; /* TRUE if this is probably the
* message body*/
gboolean is_leaf; /* if the body is a leaf part (MIME
* Part), not eg. a multipart/ */
gboolean is_msg; /* part is a message/rfc822 */
MuMsgPartType part_type;
/* crypto stuff */
GSList *sig_infos; /* list of MuMsgPartSig */
@ -122,6 +137,7 @@ char* mu_msg_part_get_text (MuMsgPart *part, gboolean *err);
gboolean mu_msg_part_looks_like_attachment (MuMsgPart *part,
gboolean include_inline);
/**
* save a specific attachment to some targetdir
*

View File

@ -35,19 +35,21 @@ typedef struct _MuMsg MuMsg;
/* options for various functions */
enum _MuMsgOptions {
MU_MSG_OPTION_NONE = 0,
MU_MSG_OPTION_RECURSE_RFC822 = 1 << 0, /* recurse into submessages */
/* 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 << 3,
MU_MSG_OPTION_AUTO_RETRIEVE_KEY = 1 << 4,
MU_MSG_OPTION_USE_AGENT = 1 << 5,
MU_MSG_OPTION_USE_PKCS7 = 1 << 6 /* gpg is the default */
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;