lib: update msg->sexp to use programmatic s-expressions
Use the new mu-sexp capabilities.
This commit is contained in:
@ -100,7 +100,7 @@ libmu_la_SOURCES= \
|
||||
mu-msg-prio.c \
|
||||
mu-msg-prio.h \
|
||||
mu-msg-priv.h \
|
||||
mu-msg-sexp.c \
|
||||
mu-msg-sexp.cc \
|
||||
mu-msg.c \
|
||||
mu-msg.h \
|
||||
mu-msg.h \
|
||||
|
||||
@ -1,634 +0,0 @@
|
||||
/*
|
||||
** Copyright (C) 2011-2017 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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 <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "utils/mu-str.h"
|
||||
#include "mu-msg.h"
|
||||
#include "mu-msg-iter.h"
|
||||
#include "mu-msg-part.h"
|
||||
#include "mu-maildir.h"
|
||||
|
||||
static void
|
||||
append_sexp_attr_list (GString *gstr, const char* elm, const GSList *lst)
|
||||
{
|
||||
const GSList *cur;
|
||||
|
||||
if (!lst)
|
||||
return; /* empty list, don't include */
|
||||
|
||||
g_string_append_printf (gstr, "\t:%s ( ", elm);
|
||||
|
||||
for (cur = lst; cur; cur = g_slist_next(cur)) {
|
||||
char *str;
|
||||
str = mu_str_escape_c_literal
|
||||
((const gchar*)cur->data, TRUE);
|
||||
g_string_append_printf (gstr, "%s ", str);
|
||||
g_free (str);
|
||||
}
|
||||
|
||||
g_string_append (gstr, ")\n");
|
||||
}
|
||||
|
||||
static void
|
||||
append_sexp_attr (GString *gstr, const char* elm, const char *str)
|
||||
{
|
||||
gchar *esc, *utf8, *cur;
|
||||
|
||||
if (!str || strlen(str) == 0)
|
||||
return; /* empty: don't include */
|
||||
|
||||
|
||||
utf8 = mu_str_utf8ify (str);
|
||||
|
||||
for (cur = utf8; *cur; ++cur)
|
||||
if (iscntrl(*cur))
|
||||
*cur = ' ';
|
||||
|
||||
esc = mu_str_escape_c_literal (utf8, TRUE);
|
||||
g_free (utf8);
|
||||
|
||||
g_string_append_printf (gstr, "\t:%s %s\n", elm, esc);
|
||||
g_free (esc);
|
||||
}
|
||||
|
||||
static void
|
||||
append_sexp_body_attr (GString *gstr, const char* elm, const char *str)
|
||||
{
|
||||
gchar *esc;
|
||||
|
||||
if (!str || strlen(str) == 0)
|
||||
return; /* empty: don't include */
|
||||
|
||||
esc = mu_str_escape_c_literal (str, TRUE);
|
||||
|
||||
g_string_append_printf (gstr, "\t:%s %s\n", elm, esc);
|
||||
g_free (esc);
|
||||
}
|
||||
|
||||
struct _ContactData {
|
||||
gboolean from, to, cc, bcc, reply_to;
|
||||
GString *gstr;
|
||||
MuMsgContactType prev_ctype;
|
||||
};
|
||||
typedef struct _ContactData ContactData;
|
||||
|
||||
static gchar*
|
||||
get_name_email_pair (MuMsgContact *c)
|
||||
{
|
||||
gchar *name, *email, *pair;
|
||||
|
||||
name = (char*)mu_msg_contact_name(c);
|
||||
email = (char*)mu_msg_contact_email(c);
|
||||
|
||||
name = name ? mu_str_escape_c_literal (name, TRUE) : NULL;
|
||||
email = email ? mu_str_escape_c_literal (email, TRUE) : NULL;
|
||||
|
||||
pair = g_strdup_printf ("(%s . %s)",
|
||||
name ? name : "nil",
|
||||
email ? email : "nil");
|
||||
g_free (name);
|
||||
g_free (email);
|
||||
|
||||
return pair;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
add_prefix_maybe (GString *gstr, gboolean *field, const char *prefix)
|
||||
{
|
||||
/* if there's nothing in the field yet, add the prefix */
|
||||
if (!*field)
|
||||
g_string_append (gstr, prefix);
|
||||
|
||||
*field = TRUE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
each_contact (MuMsgContact *c, ContactData *cdata)
|
||||
{
|
||||
char *pair;
|
||||
MuMsgContactType ctype;
|
||||
|
||||
ctype = mu_msg_contact_type (c);
|
||||
|
||||
/* if the current type is not the previous type, close the
|
||||
* previous first */
|
||||
if (cdata->prev_ctype != ctype && cdata->prev_ctype != (unsigned)-1)
|
||||
g_string_append (cdata->gstr, ")\n");
|
||||
|
||||
switch (ctype) {
|
||||
|
||||
case MU_MSG_CONTACT_TYPE_FROM:
|
||||
add_prefix_maybe (cdata->gstr, &cdata->from, "\t:from (");
|
||||
break;
|
||||
case MU_MSG_CONTACT_TYPE_TO:
|
||||
add_prefix_maybe (cdata->gstr, &cdata->to, "\t:to (");
|
||||
break;
|
||||
case MU_MSG_CONTACT_TYPE_CC:
|
||||
add_prefix_maybe (cdata->gstr, &cdata->cc, "\t:cc (");
|
||||
break;
|
||||
case MU_MSG_CONTACT_TYPE_BCC:
|
||||
add_prefix_maybe (cdata->gstr, &cdata->bcc, "\t:bcc (");
|
||||
break;
|
||||
case MU_MSG_CONTACT_TYPE_REPLY_TO:
|
||||
add_prefix_maybe (cdata->gstr, &cdata->reply_to,
|
||||
"\t:reply-to (");
|
||||
break;
|
||||
default: g_return_val_if_reached (FALSE);
|
||||
}
|
||||
|
||||
cdata->prev_ctype = ctype;
|
||||
|
||||
pair = get_name_email_pair (c);
|
||||
g_string_append (cdata->gstr, pair);
|
||||
g_free (pair);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
maybe_append_list_post (GString *gstr, 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 ("<?mailto:([a-z0-9%+@._-]+)>?", 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, 1);
|
||||
g_string_append_printf (gstr,"\t:list-post ((nil . \"%s\"))\n", addr);
|
||||
g_free(addr);
|
||||
}
|
||||
|
||||
g_match_info_free (minfo);
|
||||
g_regex_unref (rx);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
append_sexp_contacts (GString *gstr, MuMsg *msg)
|
||||
{
|
||||
ContactData cdata;
|
||||
|
||||
cdata.from = cdata.to = cdata.cc = cdata.bcc
|
||||
= cdata.reply_to = FALSE;
|
||||
cdata.gstr = gstr;
|
||||
cdata.prev_ctype = (unsigned)-1;
|
||||
|
||||
mu_msg_contact_foreach (msg, (MuMsgContactForeachFunc)each_contact,
|
||||
&cdata);
|
||||
if (cdata.from || cdata.to || cdata.cc || cdata.bcc || cdata.reply_to)
|
||||
gstr = g_string_append (gstr, ")\n");
|
||||
|
||||
maybe_append_list_post (gstr, msg);
|
||||
}
|
||||
|
||||
struct _FlagData {
|
||||
char *flagstr;
|
||||
MuFlags msgflags;
|
||||
};
|
||||
typedef struct _FlagData FlagData;
|
||||
|
||||
static void
|
||||
each_flag (MuFlags flag, FlagData *fdata)
|
||||
{
|
||||
if (!(flag & fdata->msgflags))
|
||||
return;
|
||||
|
||||
if (!fdata->flagstr)
|
||||
fdata->flagstr = g_strdup (mu_flag_name(flag));
|
||||
else {
|
||||
gchar *tmp;
|
||||
tmp = g_strconcat (fdata->flagstr, " ",
|
||||
mu_flag_name(flag), NULL);
|
||||
g_free (fdata->flagstr);
|
||||
fdata->flagstr = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
append_sexp_flags (GString *gstr, MuMsg *msg)
|
||||
{
|
||||
FlagData fdata;
|
||||
|
||||
fdata.msgflags = mu_msg_get_flags (msg);
|
||||
fdata.flagstr = NULL;
|
||||
|
||||
mu_flags_foreach ((MuFlagsForeachFunc)each_flag, &fdata);
|
||||
if (fdata.flagstr)
|
||||
g_string_append_printf (gstr, "\t:flags (%s)\n",
|
||||
fdata.flagstr);
|
||||
g_free (fdata.flagstr);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
char *tmp, *tmpfile;
|
||||
|
||||
opts |= MU_MSG_OPTION_USE_EXISTING;
|
||||
|
||||
if (!(opts & MU_MSG_OPTION_EXTRACT_IMAGES) ||
|
||||
g_ascii_strcasecmp (part->type, "image") != 0)
|
||||
return NULL;
|
||||
|
||||
tmp = get_temp_file (msg, opts, part->index);
|
||||
if (!tmp)
|
||||
return NULL;
|
||||
|
||||
tmpfile = mu_str_escape_c_literal (tmp, TRUE);
|
||||
g_free (tmp);
|
||||
return tmpfile;
|
||||
}
|
||||
|
||||
|
||||
struct _PartInfo {
|
||||
char *parts;
|
||||
MuMsgOptions opts;
|
||||
};
|
||||
typedef struct _PartInfo PartInfo;
|
||||
|
||||
static char*
|
||||
sig_verdict (MuMsgPart *mpart)
|
||||
{
|
||||
char *signers, *s;
|
||||
const char *verdict;
|
||||
MuMsgPartSigStatusReport *report;
|
||||
|
||||
report = mpart->sig_status_report;
|
||||
if (!report)
|
||||
return g_strdup ("");
|
||||
|
||||
switch (report->verdict) {
|
||||
case MU_MSG_PART_SIG_STATUS_GOOD:
|
||||
verdict = ":signature verified";
|
||||
break;
|
||||
case MU_MSG_PART_SIG_STATUS_BAD:
|
||||
verdict = ":signature bad";
|
||||
break;
|
||||
case MU_MSG_PART_SIG_STATUS_ERROR:
|
||||
verdict = ":signature unverified";
|
||||
break;
|
||||
default:
|
||||
verdict = "";
|
||||
break;
|
||||
}
|
||||
|
||||
if (!report->signers)
|
||||
return g_strdup (verdict);
|
||||
|
||||
signers = mu_str_escape_c_literal (report->signers, TRUE);
|
||||
s = g_strdup_printf ("%s :signers %s", verdict, signers);
|
||||
g_free (signers);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
static const char*
|
||||
dec_verdict (MuMsgPart *mpart)
|
||||
{
|
||||
MuMsgPartType ptype;
|
||||
|
||||
ptype = mpart->part_type;
|
||||
|
||||
if (ptype & MU_MSG_PART_TYPE_DECRYPTED)
|
||||
return ":decryption succeeded";
|
||||
else if (ptype & MU_MSG_PART_TYPE_ENCRYPTED)
|
||||
return ":decryption failed";
|
||||
else
|
||||
return "";
|
||||
}
|
||||
|
||||
|
||||
static gchar *
|
||||
get_part_type_string (MuMsgPartType ptype)
|
||||
{
|
||||
GString *gstr;
|
||||
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" }
|
||||
};
|
||||
|
||||
gstr = g_string_sized_new (100); /* more than enough */
|
||||
gstr = g_string_append_c (gstr, '(');
|
||||
|
||||
for (u = 0; u!= G_N_ELEMENTS(ptypes); ++u) {
|
||||
if (ptype & ptypes[u].ptype) {
|
||||
if (gstr->len > 1)
|
||||
gstr = g_string_append_c (gstr, ' ');
|
||||
gstr = g_string_append (gstr, ptypes[u].name);
|
||||
}
|
||||
}
|
||||
|
||||
gstr = g_string_append_c (gstr, ')');
|
||||
|
||||
return g_string_free (gstr, FALSE);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
each_part (MuMsg *msg, MuMsgPart *part, PartInfo *pinfo)
|
||||
{
|
||||
char *name, *encname, *tmp, *parttype;
|
||||
char *tmpfile, *cidesc, *verdict;
|
||||
const char *cid;
|
||||
|
||||
name = mu_msg_part_get_filename (part, TRUE);
|
||||
encname = name ?
|
||||
mu_str_escape_c_literal(name, TRUE) :
|
||||
g_strdup("\"noname\"");
|
||||
g_free (name);
|
||||
|
||||
tmpfile = get_temp_file_maybe (msg, part, pinfo->opts);
|
||||
parttype = get_part_type_string (part->part_type);
|
||||
verdict = sig_verdict (part);
|
||||
|
||||
cid = mu_msg_part_get_content_id(part);
|
||||
cidesc = cid ? mu_str_escape_c_literal(cid, TRUE) : NULL;
|
||||
|
||||
tmp = g_strdup_printf
|
||||
("%s(:index %d :name %s :mime-type \"%s/%s\"%s%s "
|
||||
":type %s "
|
||||
":attachment %s %s%s :size %i %s %s)",
|
||||
pinfo->parts ? pinfo->parts: "",
|
||||
part->index,
|
||||
encname,
|
||||
part->type ? part->type : "application",
|
||||
part->subtype ? part->subtype : "octet-stream",
|
||||
tmpfile ? " :temp" : "", tmpfile ? tmpfile : "",
|
||||
parttype,
|
||||
mu_msg_part_maybe_attachment (part) ? "t" : "nil",
|
||||
cidesc ? " :cid" : "", cidesc ? cidesc : "",
|
||||
(int)part->size,
|
||||
verdict,
|
||||
dec_verdict (part));
|
||||
|
||||
g_free (encname);
|
||||
g_free (tmpfile);
|
||||
g_free (parttype);
|
||||
g_free (verdict);
|
||||
g_free (cidesc);
|
||||
|
||||
g_free (pinfo->parts);
|
||||
pinfo->parts = tmp;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
append_sexp_parts (GString *gstr, MuMsg *msg, MuMsgOptions opts)
|
||||
{
|
||||
PartInfo pinfo;
|
||||
|
||||
pinfo.parts = NULL;
|
||||
pinfo.opts = opts;
|
||||
|
||||
if (!mu_msg_part_foreach (msg, opts, (MuMsgPartForeachFunc)each_part,
|
||||
&pinfo)) {
|
||||
/* do nothing */
|
||||
} else if (pinfo.parts) {
|
||||
g_string_append_printf (gstr, "\t:parts (%s)\n", pinfo.parts);
|
||||
g_free (pinfo.parts);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
append_sexp_thread_info (GString *gstr, const MuMsgIterThreadInfo *ti)
|
||||
{
|
||||
g_string_append_printf
|
||||
(gstr, "\t:thread (:path \"%s\" :level %u%s%s%s%s%s)\n",
|
||||
ti->threadpath,
|
||||
ti->level,
|
||||
ti->prop & MU_MSG_ITER_THREAD_PROP_FIRST_CHILD ?
|
||||
" :first-child t" : "",
|
||||
ti->prop & MU_MSG_ITER_THREAD_PROP_LAST_CHILD ?
|
||||
" :last-child t" : "",
|
||||
ti->prop & MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT ?
|
||||
" :empty-parent t" : "",
|
||||
ti->prop & MU_MSG_ITER_THREAD_PROP_DUP ?
|
||||
" :duplicate t" : "",
|
||||
ti->prop & MU_MSG_ITER_THREAD_PROP_HAS_CHILD ?
|
||||
" :has-child t" : "");
|
||||
}
|
||||
|
||||
static void
|
||||
append_sexp_param (GString *gstr, const GSList *param)
|
||||
{
|
||||
for (;param; param = g_slist_next (param)) {
|
||||
const char *str;
|
||||
char *key, *value;
|
||||
|
||||
str = param->data;
|
||||
key = str ? mu_str_escape_c_literal (str, FALSE) : g_strdup ("");
|
||||
|
||||
param = g_slist_next (param);
|
||||
str = param->data;
|
||||
value = str ? mu_str_escape_c_literal (str, FALSE) : g_strdup ("");
|
||||
|
||||
g_string_append_printf (gstr, "(\"%s\" . \"%s\")", key, value);
|
||||
g_free (key);
|
||||
g_free (value);
|
||||
|
||||
if (param->next)
|
||||
g_string_append_c (gstr, ' ');
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
append_message_file_parts (GString *gstr, MuMsg *msg, MuMsgOptions opts)
|
||||
{
|
||||
const char *str;
|
||||
GError *err;
|
||||
const GSList *params;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
append_sexp_parts (gstr, msg, opts);
|
||||
append_sexp_contacts (gstr, 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")))
|
||||
append_sexp_attr (gstr, "user-agent", str);
|
||||
|
||||
params = mu_msg_get_body_text_content_type_parameters (msg, opts);
|
||||
if (params) {
|
||||
g_string_append_printf (gstr, "\t:body-txt-params (");
|
||||
append_sexp_param (gstr, params);
|
||||
g_string_append_printf (gstr, ")\n");
|
||||
}
|
||||
|
||||
append_sexp_body_attr (gstr, "body-txt",
|
||||
mu_msg_get_body_text(msg, opts));
|
||||
append_sexp_body_attr (gstr, "body-html",
|
||||
mu_msg_get_body_html(msg, opts));
|
||||
}
|
||||
|
||||
static void
|
||||
append_sexp_date_and_size (GString *gstr, MuMsg *msg)
|
||||
{
|
||||
time_t t;
|
||||
size_t s;
|
||||
|
||||
t = mu_msg_get_date (msg);
|
||||
if (t == (time_t)-1) /* invalid date? */
|
||||
t = 0;
|
||||
|
||||
s = mu_msg_get_size (msg);
|
||||
if (s == (size_t)-1) /* invalid size? */
|
||||
s = 0;
|
||||
|
||||
g_string_append_printf
|
||||
(gstr,
|
||||
"\t:date (%d %u 0)\n\t:size %u\n",
|
||||
(unsigned)(t >> 16),
|
||||
(unsigned)(t & 0xffff),
|
||||
(unsigned)s);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
append_sexp_tags (GString *gstr, MuMsg *msg)
|
||||
{
|
||||
const GSList *tags, *t;
|
||||
gchar *tagesc;
|
||||
GString *tagstr = g_string_new("");
|
||||
|
||||
tags = mu_msg_get_tags (msg);
|
||||
|
||||
for(t = tags; t; t = t->next) {
|
||||
if (t != tags)
|
||||
g_string_append(tagstr, " ");
|
||||
|
||||
tagesc = mu_str_escape_c_literal((const gchar *)t->data, TRUE);
|
||||
g_string_append(tagstr, tagesc);
|
||||
|
||||
g_free(tagesc);
|
||||
}
|
||||
|
||||
if (tagstr->len > 0)
|
||||
g_string_append_printf (gstr, "\t:tags (%s)\n",
|
||||
tagstr->str);
|
||||
g_string_free (tagstr, TRUE);
|
||||
}
|
||||
|
||||
char*
|
||||
mu_msg_to_sexp (MuMsg *msg, unsigned docid, const MuMsgIterThreadInfo *ti,
|
||||
MuMsgOptions opts)
|
||||
{
|
||||
GString *gstr;
|
||||
|
||||
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);
|
||||
gstr = g_string_sized_new
|
||||
((opts & MU_MSG_OPTION_HEADERS_ONLY) ? 1024 : 8192);
|
||||
|
||||
if (docid == 0)
|
||||
g_string_append (gstr, "(\n");
|
||||
else
|
||||
g_string_append_printf (gstr, "(\n\t:docid %u\n", docid);
|
||||
|
||||
if (ti)
|
||||
append_sexp_thread_info (gstr, ti);
|
||||
|
||||
append_sexp_attr (gstr, "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)
|
||||
append_sexp_contacts (gstr, msg);
|
||||
|
||||
append_sexp_date_and_size (gstr, msg);
|
||||
|
||||
append_sexp_attr (gstr, "message-id", mu_msg_get_msgid (msg));
|
||||
append_sexp_attr (gstr, "mailing-list",
|
||||
mu_msg_get_mailing_list (msg));
|
||||
append_sexp_attr (gstr, "path", mu_msg_get_path (msg));
|
||||
append_sexp_attr (gstr, "maildir", mu_msg_get_maildir (msg));
|
||||
g_string_append_printf (gstr, "\t:priority %s\n",
|
||||
mu_msg_prio_name(mu_msg_get_prio(msg)));
|
||||
append_sexp_flags (gstr, msg);
|
||||
append_sexp_tags (gstr, msg);
|
||||
|
||||
append_sexp_attr_list (gstr, "references",
|
||||
mu_msg_get_references (msg));
|
||||
append_sexp_attr (gstr, "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))
|
||||
append_message_file_parts (gstr, msg, opts);
|
||||
|
||||
g_string_append (gstr, ")\n");
|
||||
return g_string_free (gstr, FALSE);
|
||||
}
|
||||
470
lib/mu-msg-sexp.cc
Normal file
470
lib/mu-msg-sexp.cc
Normal file
@ -0,0 +1,470 @@
|
||||
/*
|
||||
** Copyright (C) 2011-2020 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
**
|
||||
** 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 <string.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "utils/mu-str.h"
|
||||
#include "mu-msg.h"
|
||||
#include "mu-msg-iter.h"
|
||||
#include "mu-msg-part.h"
|
||||
#include "mu-maildir.h"
|
||||
|
||||
using namespace Mu;
|
||||
using namespace Sexp;
|
||||
|
||||
static void
|
||||
add_prop_nonempty (Node::Seq& items, const char* elm, const GSList *str_lst)
|
||||
{
|
||||
Node::Seq elms;
|
||||
while (str_lst) {
|
||||
elms.add((const char*)str_lst->data);
|
||||
str_lst = g_slist_next(str_lst);
|
||||
}
|
||||
|
||||
if (!elms.empty())
|
||||
items.add_prop(elm, elms);
|
||||
}
|
||||
|
||||
static void
|
||||
add_prop_nonempty (Node::Seq& items, const char* elm, Node::Seq&& seq)
|
||||
{
|
||||
if (!seq.empty())
|
||||
items.add_prop(elm, std::move(seq));
|
||||
}
|
||||
|
||||
static void
|
||||
add_prop_nonempty (Node::Seq& items, const char* name, const char *str)
|
||||
{
|
||||
if (str && str[0])
|
||||
items.add_prop(name, str);
|
||||
}
|
||||
|
||||
static void
|
||||
add_prop_symbol (Node::Seq& items, const char* name, const char *str)
|
||||
{
|
||||
items.add_prop(name, Node::make_symbol(str));
|
||||
}
|
||||
|
||||
static Node
|
||||
make_contact_node (MuMsgContact *c)
|
||||
{
|
||||
// a cons-pair...perhaps make this a plist too?
|
||||
|
||||
Node::Seq contact;
|
||||
if (mu_msg_contact_name(c))
|
||||
contact.add (mu_msg_contact_name(c));
|
||||
else
|
||||
contact.add (Node::make_symbol("nil"));
|
||||
|
||||
contact.add(Node::make_symbol("."));
|
||||
contact.add(mu_msg_contact_email(c));
|
||||
|
||||
return Node::make_list(std::move(contact));
|
||||
}
|
||||
|
||||
static void
|
||||
add_list_post (Node::Seq& items, 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 ("<?mailto:([a-z0-9%+@._-]+)>?", G_REGEX_CASELESS,
|
||||
(GRegexMatchFlags)0, NULL);
|
||||
g_return_if_fail(rx);
|
||||
|
||||
Node::Seq addrs;
|
||||
if (g_regex_match (rx, list_post, (GRegexMatchFlags)0, &minfo)) {
|
||||
auto address = (char*)g_match_info_fetch (minfo, 1);
|
||||
MuMsgContact contact{NULL, address};
|
||||
addrs.add(make_contact_node(&contact));
|
||||
items.add_prop(":list-post", std::move(addrs));
|
||||
g_free(address);
|
||||
}
|
||||
|
||||
g_match_info_free (minfo);
|
||||
g_regex_unref (rx);
|
||||
}
|
||||
|
||||
|
||||
struct _ContactData {
|
||||
Node::Seq from, to, cc, bcc, reply_to;
|
||||
};
|
||||
typedef struct _ContactData ContactData;
|
||||
|
||||
|
||||
static gboolean
|
||||
each_contact (MuMsgContact *c, ContactData *cdata)
|
||||
{
|
||||
switch (mu_msg_contact_type (c)) {
|
||||
|
||||
case MU_MSG_CONTACT_TYPE_FROM:
|
||||
cdata->from.add(make_contact_node(c));
|
||||
break;
|
||||
case MU_MSG_CONTACT_TYPE_TO:
|
||||
cdata->to.add(make_contact_node(c));
|
||||
break;
|
||||
case MU_MSG_CONTACT_TYPE_CC:
|
||||
cdata->cc.add(make_contact_node(c));
|
||||
break;
|
||||
case MU_MSG_CONTACT_TYPE_BCC:
|
||||
cdata->bcc.add(make_contact_node(c));
|
||||
break;
|
||||
case MU_MSG_CONTACT_TYPE_REPLY_TO:
|
||||
cdata->reply_to.add(make_contact_node(c));
|
||||
break;
|
||||
default:
|
||||
g_return_val_if_reached (FALSE);
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
add_contacts (Node::Seq& items, MuMsg *msg)
|
||||
{
|
||||
ContactData cdata{};
|
||||
mu_msg_contact_foreach (msg, (MuMsgContactForeachFunc)each_contact,
|
||||
&cdata);
|
||||
|
||||
add_prop_nonempty(items, ":from", std::move(cdata.from));
|
||||
add_prop_nonempty(items, ":to", std::move(cdata.to));
|
||||
add_prop_nonempty(items, ":cc", std::move(cdata.cc));
|
||||
add_prop_nonempty(items, ":bcc", std::move(cdata.bcc));
|
||||
add_prop_nonempty(items, ":reply-to", std::move(cdata.reply_to));
|
||||
|
||||
add_list_post (items, msg);
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
Node::Seq flagseq;
|
||||
MuFlags msgflags;
|
||||
} FlagData;
|
||||
|
||||
static void
|
||||
each_flag (MuFlags flag, FlagData *fdata)
|
||||
{
|
||||
if (flag & fdata->msgflags)
|
||||
fdata->flagseq.add(Node::make_symbol(mu_flag_name(flag)));
|
||||
}
|
||||
|
||||
static void
|
||||
add_flags (Node::Seq& items, MuMsg *msg)
|
||||
{
|
||||
FlagData fdata{};
|
||||
fdata.msgflags = mu_msg_get_flags (msg);
|
||||
|
||||
mu_flags_foreach ((MuFlagsForeachFunc)each_flag, &fdata);
|
||||
add_prop_nonempty(items, ":flags", std::move(fdata.flagseq));
|
||||
}
|
||||
|
||||
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 = (MuMsgOptions)((int)opts | (int)MU_MSG_OPTION_USE_EXISTING);
|
||||
if (!(opts & MU_MSG_OPTION_EXTRACT_IMAGES) ||
|
||||
g_ascii_strcasecmp (part->type, "image") != 0)
|
||||
return NULL;
|
||||
else
|
||||
return get_temp_file (msg, opts, part->index);
|
||||
}
|
||||
|
||||
struct PartInfo {
|
||||
Node::Seq parts;
|
||||
MuMsgOptions opts;
|
||||
};
|
||||
|
||||
static void
|
||||
sig_verdict (Node::Seq& partseq, MuMsgPart *mpart)
|
||||
{
|
||||
MuMsgPartSigStatusReport *report = mpart->sig_status_report;
|
||||
if (!report)
|
||||
return;
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wswitch-enum"
|
||||
switch (report->verdict) {
|
||||
case MU_MSG_PART_SIG_STATUS_GOOD:
|
||||
add_prop_symbol (partseq, ":signature", "verified");
|
||||
break;
|
||||
case MU_MSG_PART_SIG_STATUS_BAD:
|
||||
add_prop_symbol (partseq, ":signature", "bad");
|
||||
break;
|
||||
case MU_MSG_PART_SIG_STATUS_ERROR:
|
||||
add_prop_symbol (partseq, ":signature", "unverified");
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
add_prop_nonempty (partseq, ":signers", report->signers);
|
||||
}
|
||||
|
||||
static void
|
||||
dec_verdict (Node::Seq& partseq, MuMsgPart *mpart)
|
||||
{
|
||||
if (mpart->part_type & MU_MSG_PART_TYPE_DECRYPTED)
|
||||
partseq.add_prop(":decryption", Node::make_symbol("succeeded"));
|
||||
else if (mpart->part_type & MU_MSG_PART_TYPE_ENCRYPTED)
|
||||
partseq.add_prop(":decryption", Node::make_symbol("failed"));
|
||||
}
|
||||
|
||||
|
||||
static Node::Seq
|
||||
make_part_types (MuMsgPartType ptype)
|
||||
{
|
||||
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" }
|
||||
};
|
||||
|
||||
Node::Seq seq;
|
||||
for (auto u = 0U; u!= G_N_ELEMENTS(ptypes); ++u)
|
||||
if (ptype & ptypes[u].ptype)
|
||||
seq.add(Node::make_symbol(ptypes[u].name));
|
||||
|
||||
return seq;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
each_part (MuMsg *msg, MuMsgPart *part, PartInfo *pinfo)
|
||||
{
|
||||
auto mimetype = format("%s/%s",
|
||||
part->type ? part->type : "application",
|
||||
part->subtype ? part->subtype : "octet-stream");
|
||||
auto maybe_attach = Node::make_symbol(mu_msg_part_maybe_attachment (part) ?
|
||||
"t" : "nil");
|
||||
|
||||
Node::Seq partseq;
|
||||
|
||||
partseq.add_prop(":index", part->index);
|
||||
partseq.add_prop(":mime-type", mimetype);
|
||||
partseq.add_prop(":size", part->size);
|
||||
|
||||
dec_verdict (partseq, part);
|
||||
sig_verdict (partseq, part);
|
||||
|
||||
add_prop_nonempty(partseq, ":type", make_part_types(part->part_type));
|
||||
|
||||
char *fname = mu_msg_part_get_filename (part, TRUE);
|
||||
add_prop_nonempty(partseq, ":name", fname);
|
||||
g_free (fname);
|
||||
|
||||
if (mu_msg_part_maybe_attachment (part))
|
||||
add_prop_symbol (partseq, ":attachment", "t");
|
||||
|
||||
add_prop_nonempty (partseq, ":cid", mu_msg_part_get_content_id(part));
|
||||
|
||||
char *tempfile = get_temp_file_maybe (msg, part, pinfo->opts);
|
||||
add_prop_nonempty (partseq, ":temp", tempfile);
|
||||
g_free (tempfile);
|
||||
|
||||
pinfo->parts.add(Node::make_list(std::move(partseq)));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
add_parts (Node::Seq& items, MuMsg *msg, MuMsgOptions opts)
|
||||
{
|
||||
PartInfo pinfo;
|
||||
pinfo.opts = opts;
|
||||
|
||||
if (mu_msg_part_foreach (msg, opts, (MuMsgPartForeachFunc)each_part, &pinfo))
|
||||
add_prop_nonempty (items, ":parts", std::move(pinfo.parts));
|
||||
}
|
||||
|
||||
static void
|
||||
add_thread_info (Node::Seq& items, const MuMsgIterThreadInfo *ti)
|
||||
{
|
||||
Node::Seq info;
|
||||
|
||||
info.add_prop(":path", ti->threadpath);
|
||||
info.add_prop(":level", ti->level);
|
||||
|
||||
if (ti->prop & MU_MSG_ITER_THREAD_PROP_FIRST_CHILD)
|
||||
add_prop_symbol(info, ":first-child", "t");
|
||||
if (ti->prop & MU_MSG_ITER_THREAD_PROP_LAST_CHILD)
|
||||
add_prop_symbol(info, ":last-child", "t");
|
||||
if (ti->prop & MU_MSG_ITER_THREAD_PROP_EMPTY_PARENT)
|
||||
add_prop_symbol(info, ":empty-parent", "t");
|
||||
if (ti->prop & MU_MSG_ITER_THREAD_PROP_DUP)
|
||||
add_prop_symbol(info, ":duplicate", "t");
|
||||
if (ti->prop & MU_MSG_ITER_THREAD_PROP_HAS_CHILD)
|
||||
add_prop_symbol(info, ":has-child", "t");
|
||||
|
||||
items.add_prop(":thread", std::move(info));
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
add_message_file_parts (Node::Seq& items, MuMsg *msg, MuMsgOptions opts)
|
||||
{
|
||||
GError *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 (items, msg, opts);
|
||||
add_contacts (items, msg);
|
||||
|
||||
/* add the user-agent / x-mailer */
|
||||
auto str = mu_msg_get_header (msg, "User-Agent");
|
||||
if (!str)
|
||||
str = mu_msg_get_header (msg, "X-Mailer");
|
||||
|
||||
add_prop_nonempty (items, ":user-agent", str);
|
||||
add_prop_nonempty (items, ":body-txt-params",
|
||||
mu_msg_get_body_text_content_type_parameters (msg, opts));
|
||||
|
||||
add_prop_nonempty (items, ":body-txt", mu_msg_get_body_text(msg, opts));
|
||||
add_prop_nonempty (items, ":body-html", mu_msg_get_body_html(msg, opts));
|
||||
}
|
||||
|
||||
static void
|
||||
add_date_and_size (Node::Seq& items, MuMsg *msg)
|
||||
{
|
||||
auto t = mu_msg_get_date (msg);
|
||||
if (t == (time_t)-1) /* invalid date? */
|
||||
t = 0;
|
||||
|
||||
Node::Seq dseq;
|
||||
dseq.add((unsigned)(t >> 16));
|
||||
dseq.add((unsigned)(t && 0xffff));
|
||||
dseq.add(0);
|
||||
|
||||
items.add_prop(":date", std::move(dseq));
|
||||
|
||||
auto s = mu_msg_get_size (msg);
|
||||
if (s == (size_t)-1) /* invalid size? */
|
||||
s = 0;
|
||||
|
||||
items.add_prop(":size", s);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
add_tags (Node::Seq& items, MuMsg *msg)
|
||||
{
|
||||
Node::Seq tagseq;
|
||||
for (auto tags = mu_msg_get_tags(msg); tags; tags = g_slist_next(tags))
|
||||
tagseq.add((const char*)tags->data);
|
||||
|
||||
add_prop_nonempty (items, ":tags", std::move(tagseq));
|
||||
}
|
||||
|
||||
|
||||
Mu::Sexp::Node
|
||||
Mu::msg_to_sexp (MuMsg *msg, unsigned docid,
|
||||
const struct _MuMsgIterThreadInfo *ti,
|
||||
MuMsgOptions opts)
|
||||
{
|
||||
g_return_val_if_fail (msg, Sexp::Node::make("error"));
|
||||
g_return_val_if_fail (!((opts & MU_MSG_OPTION_HEADERS_ONLY) &&
|
||||
(opts & MU_MSG_OPTION_EXTRACT_IMAGES)),
|
||||
Sexp::Node::make("error"));
|
||||
Node::Seq items;
|
||||
|
||||
if (docid != 0)
|
||||
items.add_prop(":docid", docid);
|
||||
|
||||
if (ti)
|
||||
add_thread_info (items, ti);
|
||||
|
||||
add_prop_nonempty (items, ":subject", mu_msg_get_subject (msg));
|
||||
add_prop_nonempty (items, ":message-id", mu_msg_get_msgid (msg));
|
||||
add_prop_nonempty (items, ":mailing-list", mu_msg_get_mailing_list (msg));
|
||||
add_prop_nonempty (items, ":path", mu_msg_get_path (msg));
|
||||
add_prop_nonempty (items, ":maildir", mu_msg_get_maildir (msg));
|
||||
|
||||
items.add_prop(":priority", Node::make_symbol(mu_msg_prio_name(mu_msg_get_prio(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 (items, msg);
|
||||
|
||||
add_prop_nonempty (items, ":references", mu_msg_get_references (msg));
|
||||
add_prop_nonempty (items, ":in-reply-to", mu_msg_get_header (msg, "In-Reply-To"));
|
||||
|
||||
add_date_and_size (items, msg);
|
||||
add_flags (items, msg);
|
||||
add_tags (items, msg);
|
||||
|
||||
/* 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_message_file_parts (items, msg, opts);
|
||||
|
||||
return Node::make_list(std::move(items));
|
||||
}
|
||||
|
||||
|
||||
char*
|
||||
mu_msg_to_sexp (MuMsg *msg, unsigned docid, const MuMsgIterThreadInfo *ti,
|
||||
MuMsgOptions opts)
|
||||
{
|
||||
return g_strdup (Mu::msg_to_sexp (msg, docid, ti, opts)
|
||||
.to_string().c_str());
|
||||
}
|
||||
157
lib/mu-msg.h
157
lib/mu-msg.h
@ -1,3 +1,4 @@
|
||||
|
||||
/* -*- mode: c; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
||||
**
|
||||
** Copyright (C) 2010-2013 Dirk-Jan C. Binnema <djcb@djcbsoftware.nl>
|
||||
@ -32,7 +33,7 @@ struct _MuMsg;
|
||||
typedef struct _MuMsg MuMsg;
|
||||
|
||||
/* options for various functions */
|
||||
enum _MuMsgOptions {
|
||||
typedef enum {
|
||||
MU_MSG_OPTION_NONE = 0,
|
||||
/* 1 << 0 is still free! */
|
||||
|
||||
@ -59,10 +60,7 @@ enum _MuMsgOptions {
|
||||
/* recurse into submessages */
|
||||
MU_MSG_OPTION_RECURSE_RFC822 = 1 << 11
|
||||
|
||||
};
|
||||
typedef enum _MuMsgOptions MuMsgOptions;
|
||||
|
||||
|
||||
} MuMsgOptions;
|
||||
|
||||
/**
|
||||
* create a new MuMsg* instance which parses a message and provides
|
||||
@ -435,59 +433,6 @@ int mu_msg_cmp (MuMsg *m1, MuMsg *m2, MuMsgFieldId mfid);
|
||||
gboolean mu_msg_is_readable (MuMsg *self);
|
||||
|
||||
|
||||
struct _MuMsgIterThreadInfo;
|
||||
|
||||
|
||||
/**
|
||||
* convert the msg to a Lisp symbolic expression (for further processing in
|
||||
* e.g. emacs)
|
||||
*
|
||||
* @param msg a valid message
|
||||
* @param docid the docid for this message, or 0
|
||||
* @param ti thread info for the current message, or NULL
|
||||
* @param opts, bitwise OR'ed;
|
||||
* - MU_MSG_OPTION_HEADERS_ONLY: only include message fields which can be
|
||||
* obtained from the database (this is much faster if the MuMsg is
|
||||
* database-backed, so no file needs to be opened)
|
||||
* - MU_MSG_OPTION_EXTRACT_IMAGES: extract image attachments as temporary
|
||||
* files and include links to those in the sexp
|
||||
* and for message parts:
|
||||
* MU_MSG_OPTION_CHECK_SIGNATURES: check signatures
|
||||
* MU_MSG_OPTION_AUTO_RETRIEVE_KEY: attempt to retrieve keys online
|
||||
* MU_MSG_OPTION_USE_AGENT: attempt to use GPG-agent
|
||||
* MU_MSG_OPTION_USE_PKCS7: attempt to use PKCS (instead of gpg)
|
||||
*
|
||||
* @return a string with the sexp (free with g_free) or NULL in case of error
|
||||
*/
|
||||
char* mu_msg_to_sexp (MuMsg *msg, unsigned docid,
|
||||
const struct _MuMsgIterThreadInfo *ti,
|
||||
MuMsgOptions ops)
|
||||
G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
#ifdef HAVE_JSON_GLIB
|
||||
|
||||
struct _JsonNode; /* forward declaration */
|
||||
|
||||
/**
|
||||
* convert the msg to json
|
||||
*
|
||||
* @param msg a valid message
|
||||
* @param docid the docid for this message, or 0
|
||||
* @param ti thread info for the current message, or NULL
|
||||
* @param opts, bitwise OR'ed;
|
||||
* - MU_MSG_OPTION_HEADERS_ONLY: only include message fields which can be
|
||||
* obtained from the database (this is much faster if the MuMsg is
|
||||
* database-backed, so no file needs to be opened)
|
||||
* - MU_MSG_OPTION_EXTRACT_IMAGES: extract image attachments as temporary
|
||||
* files and include links to those in the sexp
|
||||
*
|
||||
* @return a string with the sexp (free with g_free) or NULL in case of error
|
||||
*/
|
||||
struct _JsonNode* mu_msg_to_json (MuMsg *msg, unsigned docid,
|
||||
const struct _MuMsgIterThreadInfo *ti,
|
||||
MuMsgOptions ops) G_GNUC_WARN_UNUSED_RESULT;
|
||||
#endif /*HAVE_JSON_GLIB*/
|
||||
|
||||
/**
|
||||
* move a message to another maildir; note that this does _not_ update
|
||||
* the database
|
||||
@ -544,14 +489,13 @@ typedef guint MuMsgContactType;
|
||||
#define mu_msg_contact_type_is_valid(MCT)\
|
||||
((MCT) < MU_MSG_CONTACT_TYPE_NUM)
|
||||
|
||||
struct _MuMsgContact {
|
||||
typedef struct {
|
||||
const char *name; /**< Foo Bar */
|
||||
const char *email; /**< foo@bar.cuux */
|
||||
const char *full_address; /**< Foo Bar <foo@bar.cuux> */
|
||||
MuMsgContactType type; /**< MU_MSG_CONTACT_TYPE_{ TO,
|
||||
CC, BCC, FROM, REPLY_TO} */
|
||||
};
|
||||
typedef struct _MuMsgContact MuMsgContact;
|
||||
} MuMsgContact;
|
||||
|
||||
|
||||
/**
|
||||
@ -649,6 +593,97 @@ char* mu_str_flags (MuFlags flags)
|
||||
G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
|
||||
struct _MuMsgIterThreadInfo;
|
||||
|
||||
#ifdef HAVE_JSON_GLIB
|
||||
|
||||
struct _JsonNode; /* forward declaration */
|
||||
|
||||
/**
|
||||
* convert the msg to json
|
||||
*
|
||||
* @param msg a valid message
|
||||
* @param docid the docid for this message, or 0
|
||||
* @param ti thread info for the current message, or NULL
|
||||
* @param opts, bitwise OR'ed;
|
||||
* - MU_MSG_OPTION_HEADERS_ONLY: only include message fields which can be
|
||||
* obtained from the database (this is much faster if the MuMsg is
|
||||
* database-backed, so no file needs to be opened)
|
||||
* - MU_MSG_OPTION_EXTRACT_IMAGES: extract image attachments as temporary
|
||||
* files and include links to those in the sexp
|
||||
*
|
||||
* @return a string with the sexp (free with g_free) or NULL in case of error
|
||||
*/
|
||||
struct _JsonNode* mu_msg_to_json (MuMsg *msg, unsigned docid,
|
||||
const struct _MuMsgIterThreadInfo *ti,
|
||||
MuMsgOptions ops) G_GNUC_WARN_UNUSED_RESULT;
|
||||
#endif /*HAVE_JSON_GLIB*/
|
||||
|
||||
|
||||
/**
|
||||
* convert the msg to a Lisp symbolic expression (for further processing in
|
||||
* e.g. emacs)
|
||||
*
|
||||
* @param msg a valid message
|
||||
* @param docid the docid for this message, or 0
|
||||
* @param ti thread info for the current message, or NULL
|
||||
* @param opts, bitwise OR'ed;
|
||||
* - MU_MSG_OPTION_HEADERS_ONLY: only include message fields which can be
|
||||
* obtained from the database (this is much faster if the MuMsg is
|
||||
* database-backed, so no file needs to be opened)
|
||||
* - MU_MSG_OPTION_EXTRACT_IMAGES: extract image attachments as temporary
|
||||
* files and include links to those in the sexp
|
||||
* and for message parts:
|
||||
* MU_MSG_OPTION_CHECK_SIGNATURES: check signatures
|
||||
* MU_MSG_OPTION_AUTO_RETRIEVE_KEY: attempt to retrieve keys online
|
||||
* MU_MSG_OPTION_USE_AGENT: attempt to use GPG-agent
|
||||
* MU_MSG_OPTION_USE_PKCS7: attempt to use PKCS (instead of gpg)
|
||||
*
|
||||
* @return a string with the sexp (free with g_free) or NULL in case of error
|
||||
*/
|
||||
char* mu_msg_to_sexp (MuMsg *msg, unsigned docid,
|
||||
const struct _MuMsgIterThreadInfo *ti,
|
||||
MuMsgOptions ops)
|
||||
G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
|
||||
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
#include <utils/mu-sexp.hh>
|
||||
|
||||
namespace Mu {
|
||||
|
||||
/**
|
||||
* convert the msg to a Lisp symbolic expression (for further processing in
|
||||
* e.g. emacs)
|
||||
*
|
||||
* @param msg a valid message
|
||||
* @param docid the docid for this message, or 0
|
||||
* @param ti thread info for the current message, or NULL
|
||||
* @param opts, bitwise OR'ed;
|
||||
* - MU_MSG_OPTION_HEADERS_ONLY: only include message fields which can be
|
||||
* obtained from the database (this is much faster if the MuMsg is
|
||||
* database-backed, so no file needs to be opened)
|
||||
* - MU_MSG_OPTION_EXTRACT_IMAGES: extract image attachments as temporary
|
||||
* files and include links to those in the sexp
|
||||
* and for message parts:
|
||||
* MU_MSG_OPTION_CHECK_SIGNATURES: check signatures
|
||||
* MU_MSG_OPTION_AUTO_RETRIEVE_KEY: attempt to retrieve keys online
|
||||
* MU_MSG_OPTION_USE_AGENT: attempt to use GPG-agent
|
||||
* MU_MSG_OPTION_USE_PKCS7: attempt to use PKCS (instead of gpg)
|
||||
*
|
||||
* @return a Sexp::Node representing the message
|
||||
*/
|
||||
Mu::Sexp::Node msg_to_sexp (MuMsg *msg, unsigned docid,
|
||||
const struct _MuMsgIterThreadInfo *ti,
|
||||
MuMsgOptions ops);
|
||||
}
|
||||
|
||||
#endif /*__cplusplus*/
|
||||
|
||||
|
||||
#endif /*__MU_MSG_H__*/
|
||||
|
||||
Reference in New Issue
Block a user