diff --git a/lib/mu-msg-json.c b/lib/mu-msg-json.c deleted file mode 100644 index 52b20f94..00000000 --- a/lib/mu-msg-json.c +++ /dev/null @@ -1,524 +0,0 @@ -/* -** Copyright (C) 2018 Dirk-Jan C. Binnema -** -** This program is free software; you can redistribute it and/or modify it -** under the terms of the GNU General Public License as published by the -** Free Software Foundation; either version 3, or (at your option) any -** later version. -** -** This program is distributed in the hope that it will be useful, -** but WITHOUT ANY WARRANTY; without even the implied warranty of -** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -** GNU General Public License for more details. -** -** You should have received a copy of the GNU General Public License -** along with this program; if not, write to the Free Software Foundation, -** Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -** -*/ - -#include -#include - -#include - -#include "mu-msg.h" -#include "mu-msg-iter.h" -#include "mu-msg-part.h" -#include "mu-maildir.h" - -static void -add_list_member (JsonBuilder *bob, const char* elm, const GSList *lst) -{ - const GSList *cur; - - if (!lst) - return; /* empty list, don't include */ - - bob = json_builder_set_member_name (bob, elm); - bob = json_builder_begin_array (bob); - - for (cur = lst; cur; cur = g_slist_next(cur)) - bob = json_builder_add_string_value (bob, (const char*)cur->data); - - bob = json_builder_end_array (bob); -} - -static void -add_string_member (JsonBuilder *bob, const char* elm, const char *str) -{ - if (!str) - return; /* don't include */ - - bob = json_builder_set_member_name (bob, elm); - bob = json_builder_add_string_value (bob, str); -} - -static void -add_int_member (JsonBuilder *bob, const char* elm, gint64 num) -{ - bob = json_builder_set_member_name (bob, elm); - bob = json_builder_add_int_value (bob, num); -} - -static void -add_bool_member (JsonBuilder *bob, const char* elm, gboolean b) -{ - bob = json_builder_set_member_name (bob, elm); - bob = json_builder_add_boolean_value (bob, b); -} - - -static void -consume_array_member (JsonBuilder *bob, const char* elm, JsonArray *arr) -{ - JsonNode *node; - - if (!arr) - return; /* nothing to do */ - - node = json_node_new (JSON_NODE_ARRAY); - json_node_init_array (node, arr); - json_array_unref (arr); - - bob = json_builder_set_member_name (bob, elm); - bob = json_builder_add_value (bob, node); /* consumes */ -} - - - -typedef struct { - JsonArray *from, *to, *cc, *bcc, *reply_to; -} ContactData; - -static void -add_contact (JsonArray **arr, MuMsgContact *c) -{ - JsonObject *cell; - - if (!*arr) - *arr = json_array_new (); - - cell = json_object_new (); - if (c->name) - json_object_set_string_member (cell, "name", c->name); - if (c->email) - json_object_set_string_member (cell, "email", c->email); - - json_array_add_object_element (*arr, cell); /* consumes */ -} - -static gboolean -each_contact (MuMsgContact *c, ContactData *cdata) -{ - switch (mu_msg_contact_type (c)) { - - case MU_MSG_CONTACT_TYPE_FROM: - add_contact(&cdata->from, c); - break; - case MU_MSG_CONTACT_TYPE_TO: - add_contact(&cdata->to ,c); - break; - case MU_MSG_CONTACT_TYPE_CC: - add_contact(&cdata->cc, c); - break; - case MU_MSG_CONTACT_TYPE_BCC: - add_contact(&cdata->bcc, c); - break; - case MU_MSG_CONTACT_TYPE_REPLY_TO: - add_contact(&cdata->reply_to, c); - break; - default: g_return_val_if_reached (FALSE); - } - - return TRUE; -} - - -static void -maybe_append_list_post_as_reply_to (JsonBuilder *bob, MuMsg *msg) -{ - /* some mailing lists do not set the reply-to; see pull #1278. So for - * those cases, check the List-Post address and use that instead */ - - GMatchInfo *minfo; - GRegex *rx; - const char* list_post; - - list_post = mu_msg_get_header (msg, "List-Post"); - if (!list_post) - return; - - rx = g_regex_new ("^(?", G_REGEX_CASELESS, 0, NULL); - g_return_if_fail(rx); - - if (g_regex_match (rx, list_post, 0, &minfo)) { - char *addr; - addr = g_match_info_fetch (minfo, 2); - - bob = json_builder_set_member_name (bob, "reply-to"); - bob = json_builder_begin_array(bob); - bob = json_builder_begin_object(bob); - add_string_member(bob, "email", addr); - g_free (addr); - - bob = json_builder_end_object(bob); - bob = json_builder_end_array(bob); - } - - g_match_info_free (minfo); - g_regex_unref (rx); -} - - -static void -add_contacts (JsonBuilder *bob, MuMsg *msg) -{ - ContactData cdata; - memset (&cdata, 0, sizeof(cdata)); - - mu_msg_contact_foreach (msg, - (MuMsgContactForeachFunc)each_contact, - &cdata); - - consume_array_member (bob, "to" , cdata.to); - consume_array_member (bob, "from" , cdata.from); - consume_array_member (bob, "cc" , cdata.cc); - consume_array_member (bob, "bcc" , cdata.bcc); - consume_array_member (bob, "reply-to", cdata.reply_to); - - if (!cdata.reply_to) - maybe_append_list_post_as_reply_to (bob, msg); -} - -struct _FlagData { - JsonBuilder *bob; - MuFlags msgflags; -}; -typedef struct _FlagData FlagData; - -static void -each_flag (MuFlags flag, FlagData *fdata) -{ - if (!(flag & fdata->msgflags)) - return; - - json_builder_add_string_value (fdata->bob, - mu_flag_name(flag)); -} - -static void -add_flags (JsonBuilder *bob, MuMsg *msg) -{ - FlagData fdata; - - fdata.msgflags = mu_msg_get_flags (msg); - fdata.bob = bob; - - bob = json_builder_set_member_name (bob, "flags"); - - bob = json_builder_begin_array (bob); - mu_flags_foreach ((MuFlagsForeachFunc)each_flag, &fdata); - bob = json_builder_end_array (bob); - -} - -static char* -get_temp_file (MuMsg *msg, MuMsgOptions opts, unsigned index) -{ - char *path; - GError *err; - - err = NULL; - path = mu_msg_part_get_cache_path (msg, opts, index, &err); - if (!path) - goto errexit; - - if (!mu_msg_part_save (msg, opts, path, index, &err)) - goto errexit; - - return path; - -errexit: - g_warning ("failed to save mime part: %s", - err->message ? err->message : "something went wrong"); - g_clear_error (&err); - g_free (path); - return NULL; -} - - -static gchar* -get_temp_file_maybe (MuMsg *msg, MuMsgPart *part, MuMsgOptions opts) -{ - opts |= MU_MSG_OPTION_USE_EXISTING; - - if (!(opts & MU_MSG_OPTION_EXTRACT_IMAGES) || - g_ascii_strcasecmp (part->type, "image") != 0) - return NULL; - - return get_temp_file (msg, opts, part->index); -} - - -struct _PartInfo { - JsonBuilder *bob; - MuMsgOptions opts; -}; -typedef struct _PartInfo PartInfo; - - -static void -add_part_crypto (JsonBuilder *bob, MuMsgPart *mpart, PartInfo *pinfo) -{ - const char *verdict; - MuMsgPartSigStatusReport *report; - - - add_string_member (bob, "decryption", - pinfo->opts & MU_MSG_PART_TYPE_DECRYPTED ? "ok" : - pinfo->opts & MU_MSG_PART_TYPE_ENCRYPTED ? "failed" : - NULL); - - report = mpart->sig_status_report; - if (!report) - return; - - switch (report->verdict) { - case MU_MSG_PART_SIG_STATUS_GOOD: verdict = "verified"; break; - case MU_MSG_PART_SIG_STATUS_BAD: verdict = "bad"; break; - case MU_MSG_PART_SIG_STATUS_ERROR: verdict = "unverified"; break; - default: verdict = NULL; - } - - add_string_member (bob, "signature", verdict); - add_string_member (bob, "signers", report->signers); -} - -static void -add_part_type (JsonBuilder *bob, MuMsgPartType ptype) -{ - unsigned u; - struct PartTypes { - MuMsgPartType ptype; - const char* name; - } ptypes[] = { - { MU_MSG_PART_TYPE_LEAF, "leaf" }, - { MU_MSG_PART_TYPE_MESSAGE, "message" }, - { MU_MSG_PART_TYPE_INLINE, "inline" }, - { MU_MSG_PART_TYPE_ATTACHMENT, "attachment" }, - { MU_MSG_PART_TYPE_SIGNED, "signed" }, - { MU_MSG_PART_TYPE_ENCRYPTED, "encrypted" } - }; - - bob = json_builder_set_member_name (bob, "type"); - bob = json_builder_begin_array(bob); - - for (u = 0; u!= G_N_ELEMENTS(ptypes); ++u) - if (ptype & ptypes[u].ptype) - json_builder_add_string_value (bob, ptypes[u].name); - - bob = json_builder_end_array(bob); -} - - -static void -each_part (MuMsg *msg, MuMsgPart *part, PartInfo *pinfo) -{ - char *name, *tmpfile; - - pinfo->bob = json_builder_begin_object(pinfo->bob); - - name = mu_msg_part_get_filename (part, TRUE); - tmpfile = get_temp_file_maybe (msg, part, pinfo->opts); - - add_int_member (pinfo->bob, "index", part->index); - add_string_member (pinfo->bob, "name", name); - - if (part->type && part->subtype) { - char *mime_type = - g_strdup_printf ("%s/%s", part->type, part->subtype); - add_string_member (pinfo->bob, "mime-type", mime_type); - g_free(mime_type); - } - - add_string_member (pinfo->bob, "temp", tmpfile); - add_part_type (pinfo->bob, part->part_type); - - if (mu_msg_part_maybe_attachment (part)) - add_bool_member (pinfo->bob, "attachment", TRUE); - - add_string_member (pinfo->bob, "cid", mu_msg_part_get_content_id(part)); - add_int_member (pinfo->bob, "size", part->size); - - add_part_crypto (pinfo->bob, part, pinfo); - - g_free (name); - g_free (tmpfile); - - pinfo->bob = json_builder_end_object(pinfo->bob); -} - - -static void -add_parts (JsonBuilder *bob, MuMsg *msg, MuMsgOptions opts) -{ - PartInfo pinfo; - - pinfo.opts = opts; - bob = json_builder_set_member_name (bob, "parts"); - bob = json_builder_begin_array (bob); - - mu_msg_part_foreach (msg, opts, (MuMsgPartForeachFunc)each_part, &pinfo); - - bob = json_builder_end_array (bob); -} - -static void -add_thread_info (JsonBuilder *bob, const MuMsgIterThreadInfo *ti) -{ - bob = json_builder_set_member_name (bob, "thread"); - bob = json_builder_begin_object(bob); - - add_string_member (bob, "path", ti->threadpath); - add_int_member (bob, "level", ti->level); - - bob = json_builder_set_member_name (bob, "flags"); - bob = json_builder_begin_array (bob); - - if (ti->prop & MU_MSG_ITER_THREAD_PROP_FIRST_CHILD) - bob = json_builder_add_string_value (bob, "first-child"); - if (ti->prop & MU_MSG_ITER_THREAD_PROP_LAST_CHILD) - bob = json_builder_add_string_value (bob, "last-child"); - if (ti->prop & MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT) - bob = json_builder_add_string_value (bob, "empty-parent"); - if (ti->prop & MU_MSG_ITER_THREAD_PROP_DUP) - bob = json_builder_add_string_value (bob, "duplicate"); - if (ti->prop & MU_MSG_ITER_THREAD_PROP_HAS_CHILD) - bob = json_builder_add_string_value (bob, "has-child"); - - bob = json_builder_end_array (bob); - bob = json_builder_end_object(bob); -} - -static void -add_body_txt_params (JsonBuilder *bob, MuMsg *msg, MuMsgOptions opts) -{ - const GSList *params; - - params = mu_msg_get_body_text_content_type_parameters (msg, opts); - if (!params) - return; - - bob = json_builder_set_member_name (bob, "body-txt-params"); - bob = json_builder_begin_array (bob); - - while (params) { - const char *key, *val; - - key = (const char *)params->data; - params = g_slist_next(params); - if (!params) - break; - val = (const char *)params->data; - - if (key && val) { - bob = json_builder_begin_object(bob); - add_string_member(bob, key, val); - bob = json_builder_end_object(bob); - } - - params = g_slist_next(params); - } - - bob = json_builder_end_array(bob); -} - -static void /* ie., parts that require opening the message file */ -add_file_parts (JsonBuilder *bob, MuMsg *msg, MuMsgOptions opts) -{ - const char *str; - GError *err; - - err = NULL; - - if (!mu_msg_load_msg_file (msg, &err)) { - g_warning ("failed to load message file: %s", - err ? err->message : "some error occurred"); - g_clear_error (&err); - return; - } - - add_parts (bob, msg, opts); - add_contacts (bob, msg); - - /* add the user-agent / x-mailer */ - str = mu_msg_get_header (msg, "User-Agent"); - if (!str) - str = mu_msg_get_header (msg, "X-Mailer"); - add_string_member (bob, "user-agent", str); - add_body_txt_params (bob, msg, opts); - add_string_member (bob, "body-txt", mu_msg_get_body_text(msg, opts)); - add_string_member (bob, "body-html", mu_msg_get_body_html(msg, opts)); -} - -struct _JsonNode* -mu_msg_to_json (MuMsg *msg, unsigned docid, const MuMsgIterThreadInfo *ti, - MuMsgOptions opts) -{ - JsonNode *node; - JsonBuilder *bob; - - time_t t; - size_t s; - - g_return_val_if_fail (msg, NULL); - g_return_val_if_fail (!((opts & MU_MSG_OPTION_HEADERS_ONLY) && - (opts & MU_MSG_OPTION_EXTRACT_IMAGES)),NULL); - bob = json_builder_new (); - bob = json_builder_begin_object (bob); - - if (ti) - add_thread_info (bob, ti); - - add_string_member (bob, "subject", mu_msg_get_subject (msg)); - - /* in the no-headers-only case (see below) we get a more complete list - * of contacts, so no need to get them here if that's the case */ - if (opts & MU_MSG_OPTION_HEADERS_ONLY) - add_contacts (bob, msg); - - t = mu_msg_get_date (msg); - if (t != (time_t)-1) - add_int_member (bob, "date", t); - - s = mu_msg_get_size (msg); - if (s != (size_t)-1) - add_int_member (bob, "size", s); - - add_string_member (bob, "message-id", mu_msg_get_msgid (msg)); - add_string_member (bob, "mailing-list", mu_msg_get_mailing_list (msg)); - add_string_member (bob, "path", mu_msg_get_path (msg)); - add_string_member (bob, "maildir", mu_msg_get_maildir (msg)); - add_string_member (bob, "priority", mu_msg_prio_name(mu_msg_get_prio(msg))); - - add_flags (bob, msg); - - add_list_member (bob, "tags", mu_msg_get_tags(msg)); - add_list_member (bob, "references", mu_msg_get_references (msg)); - add_string_member (bob, "in-reply-to", - mu_msg_get_header (msg, "In-Reply-To")); - - /* headers are retrieved from the database, views from the - * message file file attr things can only be gotten from the - * file (ie., mu view), not from the database (mu find). */ - if (!(opts & MU_MSG_OPTION_HEADERS_ONLY)) - add_file_parts (bob, msg, opts); - - bob = json_builder_end_object (bob); - node = json_builder_get_root (bob); - - g_clear_object (&bob); - - return node; -}