Re: [PATCH v5.2 3/7] reply: Add a JSON reply format.
authorAustin Clements <amdragon@MIT.EDU>
Fri, 17 Feb 2012 17:04:57 +0000 (12:04 +1900)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:44:38 +0000 (09:44 -0800)
0f/6a7ce9f2bc00e01f7eec90b27711ab7b2c3471 [new file with mode: 0644]

diff --git a/0f/6a7ce9f2bc00e01f7eec90b27711ab7b2c3471 b/0f/6a7ce9f2bc00e01f7eec90b27711ab7b2c3471
new file mode 100644 (file)
index 0000000..eca27eb
--- /dev/null
@@ -0,0 +1,308 @@
+Return-Path: <amdragon@mit.edu>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+       by olra.theworths.org (Postfix) with ESMTP id 23EC1429E42\r
+       for <notmuch@notmuchmail.org>; Fri, 17 Feb 2012 09:06:48 -0800 (PST)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -0.7\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
+       tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+       with ESMTP id R+5ECyW2woE9 for <notmuch@notmuchmail.org>;\r
+       Fri, 17 Feb 2012 09:06:47 -0800 (PST)\r
+Received: from dmz-mailsec-scanner-1.mit.edu (DMZ-MAILSEC-SCANNER-1.MIT.EDU\r
+       [18.9.25.12])\r
+       by olra.theworths.org (Postfix) with ESMTP id 290E6431FB6\r
+       for <notmuch@notmuchmail.org>; Fri, 17 Feb 2012 09:06:47 -0800 (PST)\r
+X-AuditID: 1209190c-b7fad6d000000920-07-4f3e8924764b\r
+Received: from mailhub-auth-3.mit.edu ( [18.9.21.43])\r
+       by dmz-mailsec-scanner-1.mit.edu (Symantec Messaging Gateway) with SMTP\r
+       id 08.F0.02336.4298E3F4; Fri, 17 Feb 2012 12:06:44 -0500 (EST)\r
+Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])\r
+       by mailhub-auth-3.mit.edu (8.13.8/8.9.2) with ESMTP id q1HH6hJ4005424; \r
+       Fri, 17 Feb 2012 12:06:44 -0500\r
+Received: from awakening.csail.mit.edu (awakening.csail.mit.edu [18.26.4.91])\r
+       (authenticated bits=0)\r
+       (User authenticated as amdragon@ATHENA.MIT.EDU)\r
+       by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id q1HH6fmh016474\r
+       (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
+       Fri, 17 Feb 2012 12:06:43 -0500 (EST)\r
+Received: from amthrax by awakening.csail.mit.edu with local (Exim 4.77)\r
+       (envelope-from <amdragon@mit.edu>)\r
+       id 1RyREv-0003Ys-5N; Fri, 17 Feb 2012 12:04:57 -0500\r
+Date: Fri, 17 Feb 2012 12:04:57 -0500\r
+From: Austin Clements <amdragon@MIT.EDU>\r
+To: Adam Wolfe Gordon <awg+notmuch@xvx.ca>\r
+Subject: Re: [PATCH v5.2 3/7] reply: Add a JSON reply format.\r
+Message-ID: <20120217170457.GE5991@mit.edu>\r
+References: <1329361957-28493-1-git-send-email-awg+notmuch@xvx.ca>\r
+       <1329361957-28493-4-git-send-email-awg+notmuch@xvx.ca>\r
+MIME-Version: 1.0\r
+Content-Type: text/plain; charset=us-ascii\r
+Content-Disposition: inline\r
+In-Reply-To: <1329361957-28493-4-git-send-email-awg+notmuch@xvx.ca>\r
+User-Agent: Mutt/1.5.21 (2010-09-15)\r
+X-Brightmail-Tracker:\r
+ H4sIAAAAAAAAA+NgFupnleLIzCtJLcpLzFFi42IR4hTV1lXptPM3eHqV2+LInlnsFtdvzmR2\r
+       YPJ4tuoWs0fTj8WsAUxRXDYpqTmZZalF+nYJXBl7P/UxF7y1r3g79w5bA+Nfwy5GTg4JAROJ\r
+       yxN2sUPYYhIX7q1n62Lk4hAS2Mco0dF3gw0kISSwgVHi5R9uiMRJJonTu44xQThLGCWOPpsD\r
+       VsUioCqxu3MtmM0moCGxbf9yRhBbREBL4sf6r6wgNrOAtMS3381MILawgK3Es0+XmEFsXgFt\r
+       id1PjrBAbKuWOD53KRNEXFDi5MwnLBC9WhI3/r0EinOAzVn+jwMkzCngLPFyQSfYGFEBFYkp\r
+       J7exTWAUmoWkexaS7lkI3QsYmVcxyqbkVunmJmbmFKcm6xYnJ+blpRbpGurlZpbopaaUbmIE\r
+       h7Ukzw7GNweVDjEKcDAq8fAmGdr6C7EmlhVX5h5ilORgUhLl3dpu5y/El5SfUpmRWJwRX1Sa\r
+       k1p8iFGCg1lJhPdbLlCONyWxsiq1KB8mJc3BoiTOq6L1zk9IID2xJDU7NbUgtQgmK8PBoSTB\r
+       W9MB1ChYlJqeWpGWmVOCkGbi4AQZzgM0vAqkhre4IDG3ODMdIn+KUZfj44EnFxiFWPLy81Kl\r
+       xHmTQYoEQIoySvPg5sDS0StGcaC3hHmzQKp4gKkMbtIroCVMQEt4hcCWlCQipKQaGHlZeX+t\r
+       4Zq03/Kl8AqH2wUH/vqYHpdYeD60XjnDeE/B1Gqr6VNNZni6Jwi2PP8d8OzT27z5dsZGqyoP\r
+       fJ/97jnfotrEo5edThkHvOG4t7DqjGqFzpfIjkOCKa88QwOSup4uWR6W4xTnu4lTyjrp4i6W\r
+       6XsVhN5GJH+a4djr+eqbvu9tyZc5r5RYijMSDbWYi4oTAQZdxeQiAwAA\r
+Cc: notmuch@notmuchmail.org\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+       <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Fri, 17 Feb 2012 17:06:48 -0000\r
+\r
+The first two patches LGTM.  A few nits in this one.\r
+\r
+Quoth Adam Wolfe Gordon on Feb 15 at  8:12 pm:\r
+> This new JSON format for replies includes headers generated for a\r
+> reply message as well as the headers of the original message.  Using\r
+> this data, a client can intelligently create a reply. For example, the\r
+> emacs client will be able to create replies with quoted HTML parts by\r
+> parsing the HTML parts.\r
+> \r
+> Reply now enforces that only one message is returned, as the semantics\r
+> of replying to multiple messages are not well-defined.\r
+> ---\r
+>  notmuch-client.h |    3 +\r
+>  notmuch-reply.c  |  121 ++++++++++++++++++++++++++++++++++++++++++++++-------\r
+>  notmuch-show.c   |    2 +-\r
+>  3 files changed, 109 insertions(+), 17 deletions(-)\r
+> \r
+> diff --git a/notmuch-client.h b/notmuch-client.h\r
+> index 60828aa..d28ea07 100644\r
+> --- a/notmuch-client.h\r
+> +++ b/notmuch-client.h\r
+> @@ -344,6 +344,9 @@ typedef struct mime_node {\r
+>      int next_part_num;\r
+>  } mime_node_t;\r
+>  \r
+> +void\r
+> +format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first);\r
+> +\r
+\r
+This is the wrong place for this declaration, since it is not part of\r
+the MIME node abstraction.  It should go somewhere above the /*\r
+notmuch-config.c */ comment.  Above that, it's a bit of a jumble.  I'd\r
+probably put it right after notmuch_show_command.\r
+\r
+>  /* Construct a new MIME node pointing to the root message part of\r
+>   * message.  If cryptoctx is non-NULL, it will be used to verify\r
+>   * signatures on any child parts.  If decrypt is true, then cryptoctx\r
+> diff --git a/notmuch-reply.c b/notmuch-reply.c\r
+> index 8e56245..6670288 100644\r
+> --- a/notmuch-reply.c\r
+> +++ b/notmuch-reply.c\r
+> @@ -572,30 +572,115 @@ notmuch_reply_format_default(void *ctx,\r
+>      notmuch_message_t *message;\r
+>      const notmuch_show_format_t *format = &format_reply;\r
+>  \r
+> -    for (messages = notmuch_query_search_messages (query);\r
+> -     notmuch_messages_valid (messages);\r
+> -     notmuch_messages_move_to_next (messages))\r
+> -    {\r
+> -    message = notmuch_messages_get (messages);\r
+> +    if (notmuch_query_count_messages (query) != 1) {\r
+> +    fprintf (stderr, "Error: search term did not match precisely one message.\n");\r
+> +    return 1;\r
+> +    }\r
+\r
+Technically count_messages does not have to be accurate, but since\r
+this is the same thing notmuch-show does, it's probably fine for now.\r
+\r
+Perhaps we should add proper handling of multi-message replies to\r
+devel/TODO?\r
+\r
+>  \r
+> -    reply = create_reply_message (ctx, config, message, reply_all);\r
+> +    messages = notmuch_query_search_messages (query);\r
+> +    message = notmuch_messages_get (messages);\r
+>  \r
+> -    if (!reply)\r
+> -        continue;\r
+> +    reply = create_reply_message (ctx, config, message, reply_all);\r
+>  \r
+> -    show_reply_headers (reply);\r
+> +    if (!reply)\r
+> +    return 1;\r
+>  \r
+> -    g_object_unref (G_OBJECT (reply));\r
+> -    reply = NULL;\r
+> +    show_reply_headers (reply);\r
+>  \r
+> -    printf ("On %s, %s wrote:\n",\r
+> -            notmuch_message_get_header (message, "date"),\r
+> -            notmuch_message_get_header (message, "from"));\r
+> +    g_object_unref (G_OBJECT (reply));\r
+> +    reply = NULL;\r
+>  \r
+> -    show_message_body (message, format, params);\r
+> +    printf ("On %s, %s wrote:\n",\r
+> +        notmuch_message_get_header (message, "date"),\r
+> +        notmuch_message_get_header (message, "from"));\r
+>  \r
+> -    notmuch_message_destroy (message);\r
+> +    show_message_body (message, format, params);\r
+> +\r
+> +    notmuch_message_destroy (message);\r
+> +\r
+> +    return 0;\r
+> +}\r
+\r
+The above change could be separated out in to a separate patch, since\r
+it has nothing to do with JSON.\r
+\r
+> +\r
+> +static void\r
+> +format_reply_headers_json (const void *ctx, GMimeMessage *message)\r
+> +{\r
+> +    void *local = talloc_new (ctx);\r
+> +    InternetAddressList *recipients;\r
+> +    const char *recipients_string;\r
+> +\r
+> +    printf ("{%s: %s",\r
+> +        json_quote_str (local, "Subject"),\r
+> +        json_quote_str (local, g_mime_message_get_subject (message)));\r
+> +    printf (", %s: %s",\r
+> +        json_quote_str (local, "From"),\r
+> +        json_quote_str (local, g_mime_message_get_sender (message)));\r
+> +    recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_TO);\r
+> +    recipients_string = internet_address_list_to_string (recipients, 0);\r
+> +    if (recipients_string)\r
+> +    printf (", %s: %s",\r
+> +            json_quote_str (local, "To"),\r
+> +            json_quote_str (local, recipients_string));\r
+> +    recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_CC);\r
+> +    recipients_string = internet_address_list_to_string (recipients, 0);\r
+> +    if (recipients_string)\r
+> +    printf (", %s: %s",\r
+> +            json_quote_str (local, "Cc"),\r
+> +            json_quote_str (local, recipients_string));\r
+> +\r
+> +    printf (", %s: %s",\r
+> +        json_quote_str (local, "In-reply-to"),\r
+> +        json_quote_str (local, g_mime_object_get_header (GMIME_OBJECT (message), "In-reply-to")));\r
+> +\r
+> +    printf (", %s: %s",\r
+\r
+There should be a close brace in this string, rather than opening the\r
+object in this function and closing it in the caller.\r
+\r
+> +        json_quote_str (local, "References"),\r
+> +        json_quote_str (local, g_mime_object_get_header (GMIME_OBJECT (message), "References")));\r
+> +\r
+> +    talloc_free (local);\r
+> +}\r
+\r
+I would much rather see format_headers_json in notmuch-show.c gain a\r
+parameter that tells it whether or not to include in-reply-to and\r
+references.\r
+\r
+> +\r
+> +static int\r
+> +notmuch_reply_format_json(void *ctx,\r
+> +                      notmuch_config_t *config,\r
+> +                      notmuch_query_t *query,\r
+> +                      notmuch_show_params_t *params,\r
+> +                      notmuch_bool_t reply_all)\r
+> +{\r
+> +    GMimeMessage *reply;\r
+> +    notmuch_messages_t *messages;\r
+> +    notmuch_message_t *message;\r
+> +    mime_node_t *node;\r
+> +\r
+> +    if (notmuch_query_count_messages (query) != 1) {\r
+> +    fprintf (stderr, "Error: search term did not match precisely one message.\n");\r
+> +    return 1;\r
+>      }\r
+> +\r
+> +    messages = notmuch_query_search_messages (query);\r
+> +    message = notmuch_messages_get (messages);\r
+> +    if (mime_node_open (ctx, message, params->cryptoctx, params->decrypt,\r
+> +                    &node) != NOTMUCH_STATUS_SUCCESS)\r
+> +    return 1;\r
+> +\r
+> +    reply = create_reply_message (ctx, config, message, reply_all);\r
+> +    if (!reply)\r
+> +    return 1;\r
+> +\r
+> +    /* The headers of the reply message we've created */\r
+> +    printf ("{\"reply-headers\": ");\r
+> +    format_reply_headers_json (ctx, reply);\r
+> +    printf ("}");\r
+> +    g_object_unref (G_OBJECT (reply));\r
+> +    reply = NULL;\r
+> +\r
+> +    /* Start the original */\r
+> +    printf (", \"original\": ");\r
+> +\r
+> +    format_part_json (ctx, node, TRUE);\r
+> +\r
+> +    /* End */\r
+> +    printf ("}\n");\r
+> +    notmuch_message_destroy (message);\r
+> +\r
+>      return 0;\r
+>  }\r
+>  \r
+> @@ -661,6 +746,7 @@ notmuch_reply_format_headers_only(void *ctx,\r
+>  \r
+>  enum {\r
+>      FORMAT_DEFAULT,\r
+> +    FORMAT_JSON,\r
+>      FORMAT_HEADERS_ONLY,\r
+>  };\r
+>  \r
+> @@ -680,6 +766,7 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])\r
+>      notmuch_opt_desc_t options[] = {\r
+>      { NOTMUCH_OPT_KEYWORD, &format, "format", 'f',\r
+>        (notmuch_keyword_t []){ { "default", FORMAT_DEFAULT },\r
+> +                              { "json", FORMAT_JSON },\r
+>                                { "headers-only", FORMAT_HEADERS_ONLY },\r
+>                                { 0, 0 } } },\r
+>      { NOTMUCH_OPT_KEYWORD, &reply_all, "reply-to", 'r',\r
+> @@ -698,6 +785,8 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])\r
+>  \r
+>      if (format == FORMAT_HEADERS_ONLY)\r
+>      reply_format_func = notmuch_reply_format_headers_only;\r
+> +    else if (format == FORMAT_JSON)\r
+> +    reply_format_func = notmuch_reply_format_json;\r
+>      else\r
+>      reply_format_func = notmuch_reply_format_default;\r
+>  \r
+> diff --git a/notmuch-show.c b/notmuch-show.c\r
+> index 6a171a4..c570a16 100644\r
+> --- a/notmuch-show.c\r
+> +++ b/notmuch-show.c\r
+> @@ -652,7 +652,7 @@ format_part_text (const void *ctx, mime_node_t *node,\r
+>      printf ("\f%s}\n", part_type);\r
+>  }\r
+>  \r
+> -static void\r
+> +void\r
+>  format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first)\r
+>  {\r
+>      /* Any changes to the JSON format should be reflected in the file\r