[PATCH v7.1 04/11] reply: Add a JSON reply format.
Adam Wolfe Gordon
awg+notmuch at xvx.ca
Tue Mar 13 21:30:09 PDT 2012
This new JSON format for replies includes headers generated for a
reply message as well as the headers of the original message. Using
this data, a client can intelligently create a reply. For example, the
emacs client will be able to create replies with quoted HTML parts by
parsing the HTML parts.
---
notmuch-client.h | 14 ++++++++++----
notmuch-reply.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
notmuch-show.c | 29 +++++++++++++++++++++--------
test/multipart | 1 -
4 files changed, 80 insertions(+), 13 deletions(-)
diff --git a/notmuch-client.h b/notmuch-client.h
index f4a62cc..270daff 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -62,13 +62,13 @@
#define STRINGIFY(s) STRINGIFY_(s)
#define STRINGIFY_(s) #s
-struct mime_node;
+typedef struct mime_node mime_node_t;
struct notmuch_show_params;
typedef struct notmuch_show_format {
const char *message_set_start;
void (*part) (const void *ctx,
- struct mime_node *node, int indent,
+ mime_node_t *node, int indent,
const struct notmuch_show_params *params);
const char *message_start;
void (*message) (const void *ctx,
@@ -191,6 +191,12 @@ show_message_body (notmuch_message_t *message,
notmuch_status_t
show_one_part (const char *filename, int part);
+void
+format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first);
+
+void
+format_headers_json (const void *ctx, GMimeMessage *message, notmuch_bool_t reply);
+
char *
json_quote_chararray (const void *ctx, const char *str, const size_t len);
@@ -288,7 +294,7 @@ debugger_is_active (void);
* parts. Message-type parts have one child, multipart-type parts
* have multiple children, and leaf parts have zero children.
*/
-typedef struct mime_node {
+struct mime_node {
/* The MIME object of this part. This will be a GMimeMessage,
* GMimePart, GMimeMultipart, or a subclass of one of these.
*
@@ -351,7 +357,7 @@ typedef struct mime_node {
* number to assign it (or -1 if unknown). */
int next_child;
int next_part_num;
-} mime_node_t;
+};
/* Construct a new MIME node pointing to the root message part of
* message. If cryptoctx is non-NULL, it will be used to verify
diff --git a/notmuch-reply.c b/notmuch-reply.c
index f1478cc..e2b6c25 100644
--- a/notmuch-reply.c
+++ b/notmuch-reply.c
@@ -604,6 +604,51 @@ notmuch_reply_format_default(void *ctx,
return 0;
}
+static int
+notmuch_reply_format_json(void *ctx,
+ notmuch_config_t *config,
+ notmuch_query_t *query,
+ notmuch_show_params_t *params,
+ notmuch_bool_t reply_all)
+{
+ GMimeMessage *reply;
+ notmuch_messages_t *messages;
+ notmuch_message_t *message;
+ mime_node_t *node;
+
+ if (notmuch_query_count_messages (query) != 1) {
+ fprintf (stderr, "Error: search term did not match precisely one message.\n");
+ return 1;
+ }
+
+ messages = notmuch_query_search_messages (query);
+ message = notmuch_messages_get (messages);
+ if (mime_node_open (ctx, message, params->cryptoctx, params->decrypt,
+ &node) != NOTMUCH_STATUS_SUCCESS)
+ return 1;
+
+ reply = create_reply_message (ctx, config, message, reply_all);
+ if (!reply)
+ return 1;
+
+ /* The headers of the reply message we've created */
+ printf ("{\"reply-headers\": ");
+ format_headers_json (ctx, reply, TRUE);
+ g_object_unref (G_OBJECT (reply));
+ reply = NULL;
+
+ /* Start the original */
+ printf (", \"original\": ");
+
+ format_part_json (ctx, node, TRUE);
+
+ /* End */
+ printf ("}\n");
+ notmuch_message_destroy (message);
+
+ return 0;
+}
+
/* This format is currently tuned for a git send-email --notmuch hook */
static int
notmuch_reply_format_headers_only(void *ctx,
@@ -666,6 +711,7 @@ notmuch_reply_format_headers_only(void *ctx,
enum {
FORMAT_DEFAULT,
+ FORMAT_JSON,
FORMAT_HEADERS_ONLY,
};
@@ -685,6 +731,7 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])
notmuch_opt_desc_t options[] = {
{ NOTMUCH_OPT_KEYWORD, &format, "format", 'f',
(notmuch_keyword_t []){ { "default", FORMAT_DEFAULT },
+ { "json", FORMAT_JSON },
{ "headers-only", FORMAT_HEADERS_ONLY },
{ 0, 0 } } },
{ NOTMUCH_OPT_KEYWORD, &reply_all, "reply-to", 'r',
@@ -703,6 +750,8 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])
if (format == FORMAT_HEADERS_ONLY)
reply_format_func = notmuch_reply_format_headers_only;
+ else if (format == FORMAT_JSON)
+ reply_format_func = notmuch_reply_format_json;
else
reply_format_func = notmuch_reply_format_default;
diff --git a/notmuch-show.c b/notmuch-show.c
index 05d51b2..cb3a427 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -291,8 +291,8 @@ format_headers_message_part_text (GMimeMessage *message)
printf ("Date: %s\n", g_mime_message_get_date_as_string (message));
}
-static void
-format_headers_json (const void *ctx, GMimeMessage *message)
+void
+format_headers_json (const void *ctx, GMimeMessage *message, notmuch_bool_t reply)
{
void *local = talloc_new (ctx);
InternetAddressList *recipients;
@@ -316,9 +316,22 @@ format_headers_json (const void *ctx, GMimeMessage *message)
printf (", %s: %s",
json_quote_str (local, "Cc"),
json_quote_str (local, recipients_string));
- printf (", %s: %s}",
- json_quote_str (local, "Date"),
- json_quote_str (local, g_mime_message_get_date_as_string (message)));
+
+ if (reply) {
+ printf (", %s: %s",
+ json_quote_str (local, "In-reply-to"),
+ json_quote_str (local, g_mime_object_get_header (GMIME_OBJECT (message), "In-reply-to")));
+
+ printf (", %s: %s",
+ json_quote_str (local, "References"),
+ json_quote_str (local, g_mime_object_get_header (GMIME_OBJECT (message), "References")));
+ } else {
+ printf (", %s: %s",
+ json_quote_str (local, "Date"),
+ json_quote_str (local, g_mime_message_get_date_as_string (message)));
+ }
+
+ printf ("}");
talloc_free (local);
}
@@ -654,7 +667,7 @@ format_part_text (const void *ctx, mime_node_t *node,
printf ("\f%s}\n", part_type);
}
-static void
+void
format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first)
{
/* Any changes to the JSON format should be reflected in the file
@@ -665,7 +678,7 @@ format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first)
format_message_json (ctx, node->envelope_file);
printf ("\"headers\": ");
- format_headers_json (ctx, GMIME_MESSAGE (node->part));
+ format_headers_json (ctx, GMIME_MESSAGE (node->part), FALSE);
printf (", \"body\": [");
format_part_json (ctx, mime_node_child (node, 0), first);
@@ -739,7 +752,7 @@ format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first)
} else if (GMIME_IS_MESSAGE (node->part)) {
printf (", \"content\": [{");
printf ("\"headers\": ");
- format_headers_json (local, GMIME_MESSAGE (node->part));
+ format_headers_json (local, GMIME_MESSAGE (node->part), FALSE);
printf (", \"body\": [");
terminator = "]}]";
diff --git a/test/multipart b/test/multipart
index 80d6e88..2383b9c 100755
--- a/test/multipart
+++ b/test/multipart
@@ -590,7 +590,6 @@ EOF
test_expect_equal_file OUTPUT EXPECTED
test_begin_subtest "'notmuch reply' to a multipart message with json format"
-test_subtest_known_broken
notmuch reply --format=json 'id:87liy5ap00.fsf at yoom.home.cworth.org' | notmuch_json_show_sanitize >OUTPUT
cat <<EOF >EXPECTED
{"reply-headers": {"Subject": "Re: Multipart message",
--
1.7.5.4
More information about the notmuch
mailing list