[PATCH v7 04/10] reply: Add a JSON reply format.

2012-03-11 Thread Adam Wolfe Gordon
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-s

[PATCH v7 04/10] reply: Add a JSON reply format.

2012-03-11 Thread Adam Wolfe Gordon
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