Return-Path: X-Original-To: notmuch@notmuchmail.org Delivered-To: notmuch@notmuchmail.org Received: from localhost (localhost [127.0.0.1]) by olra.theworths.org (Postfix) with ESMTP id B6A51431FD6 for ; Tue, 21 Feb 2012 22:46:48 -0800 (PST) X-Virus-Scanned: Debian amavisd-new at olra.theworths.org X-Spam-Flag: NO X-Spam-Score: 0 X-Spam-Level: X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[RCVD_IN_DNSWL_NONE=-0.0001] autolearn=disabled Received: from olra.theworths.org ([127.0.0.1]) by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id k2iBzwk5M00Q for ; Tue, 21 Feb 2012 22:46:46 -0800 (PST) Received: from idcmail-mo2no.shaw.ca (idcmail-mo2no.shaw.ca [64.59.134.9]) by olra.theworths.org (Postfix) with ESMTP id D6F18431FC0 for ; Tue, 21 Feb 2012 22:46:45 -0800 (PST) Received: from lb7f8hsrpno-svcs.dcs.int.inet (HELO pd7ml2no-ssvc.prod.shaw.ca) ([10.0.144.222]) by pd7mo1no-svcs.prod.shaw.ca with ESMTP; 21 Feb 2012 23:46:45 -0700 X-Cloudmark-SP-Filtered: true X-Cloudmark-SP-Result: v=1.1 cv=GZn8e3lTBEeJrlGK3+GUWyR5aYe1SJcDn5uEERMe9yQ= c=1 sm=1 a=tXneHAB9HJoA:10 a=BLceEmwcHowA:10 a=yQp6g8lIsgqumF79BAsFDg==:17 a=bz-zy3QpAAAA:8 a=zj4l01KfgLfMms7IjnsA:9 a=tvKH82Il3pz4rfUQjx0A:7 a=NFWI-YhVVZsA:10 a=HpAAvcLHHh0Zw7uRqdWCyQ==:117 Received: from unknown (HELO lagos.xvx.ca) ([96.52.216.56]) by pd7ml2no-dmz.prod.shaw.ca with ESMTP; 21 Feb 2012 23:46:45 -0700 Received: by lagos.xvx.ca (Postfix, from userid 1000) id BF63B8095C4E; Tue, 21 Feb 2012 23:46:44 -0700 (MST) From: Adam Wolfe Gordon To: notmuch@notmuchmail.org Subject: [PATCH v6 05/10] reply: Add a JSON reply format. Date: Tue, 21 Feb 2012 23:46:34 -0700 Message-Id: <1329893199-21630-6-git-send-email-awg+notmuch@xvx.ca> X-Mailer: git-send-email 1.7.5.4 In-Reply-To: <1329893199-21630-1-git-send-email-awg+notmuch@xvx.ca> References: <1329893199-21630-1-git-send-email-awg+notmuch@xvx.ca> X-BeenThere: notmuch@notmuchmail.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: "Use and development of the notmuch mail system." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 22 Feb 2012 06:46:49 -0000 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 | 10 ++++++++-- notmuch-reply.c | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ notmuch-show.c | 30 ++++++++++++++++++++++-------- test/multipart | 1 - 4 files changed, 79 insertions(+), 11 deletions(-) diff --git a/notmuch-client.h b/notmuch-client.h index f4a62cc..ef4eaba 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); diff --git a/notmuch-reply.c b/notmuch-reply.c index 177e6ca..017c6ae 100644 --- a/notmuch-reply.c +++ b/notmuch-reply.c @@ -601,6 +601,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, @@ -663,6 +708,7 @@ notmuch_reply_format_headers_only(void *ctx, enum { FORMAT_DEFAULT, + FORMAT_JSON, FORMAT_HEADERS_ONLY, }; @@ -682,6 +728,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', @@ -700,6 +747,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 6a171a4..2126d78 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -290,8 +290,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; @@ -315,9 +315,23 @@ 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, "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"))); + } + + printf ("}"); talloc_free (local); } @@ -652,7 +666,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 @@ -663,7 +677,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); @@ -737,7 +751,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 f102294..e7abcc2 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@yoom.home.cworth.org' | notmuch_json_show_sanitize >OUTPUT cat <EXPECTED {"reply-headers": {"Subject": "Re: Multipart message", -- 1.7.5.4