1 Return-Path: <jani@nikula.org>
\r
2 X-Original-To: notmuch@notmuchmail.org
\r
3 Delivered-To: notmuch@notmuchmail.org
\r
4 Received: from localhost (localhost [127.0.0.1])
\r
5 by olra.theworths.org (Postfix) with ESMTP id B19CA431FAF
\r
6 for <notmuch@notmuchmail.org>; Wed, 18 Jan 2012 15:07:09 -0800 (PST)
\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org
\r
11 X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5
\r
12 tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled
\r
13 Received: from olra.theworths.org ([127.0.0.1])
\r
14 by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)
\r
15 with ESMTP id LNvcW-t9nPoF for <notmuch@notmuchmail.org>;
\r
16 Wed, 18 Jan 2012 15:07:08 -0800 (PST)
\r
17 Received: from mail-ey0-f181.google.com (mail-ey0-f181.google.com
\r
18 [209.85.215.181]) (using TLSv1 with cipher RC4-SHA (128/128 bits))
\r
19 (No client certificate requested)
\r
20 by olra.theworths.org (Postfix) with ESMTPS id B5EC5431FAE
\r
21 for <notmuch@notmuchmail.org>; Wed, 18 Jan 2012 15:07:07 -0800 (PST)
\r
22 Received: by eaal1 with SMTP id l1so499610eaa.26
\r
23 for <notmuch@notmuchmail.org>; Wed, 18 Jan 2012 15:07:06 -0800 (PST)
\r
24 Received: by 10.213.16.132 with SMTP id o4mr1519710eba.88.1326928026594;
\r
25 Wed, 18 Jan 2012 15:07:06 -0800 (PST)
\r
26 Received: from localhost (dsl-hkibrasgw4-fe50f800-253.dhcp.inet.fi.
\r
28 by mx.google.com with ESMTPS id x4sm10718473eeb.4.2012.01.18.15.07.04
\r
29 (version=SSLv3 cipher=OTHER); Wed, 18 Jan 2012 15:07:05 -0800 (PST)
\r
30 From: Jani Nikula <jani@nikula.org>
\r
31 To: Adam Wolfe Gordon <awg+notmuch@xvx.ca>, notmuch@notmuchmail.org
\r
32 Subject: Re: [PATCH v2 2/4] reply: Add a JSON reply format.
\r
33 In-Reply-To: <1326737603-21166-3-git-send-email-awg+notmuch@xvx.ca>
\r
34 References: <1326737603-21166-1-git-send-email-awg+notmuch@xvx.ca>
\r
35 <1326737603-21166-3-git-send-email-awg+notmuch@xvx.ca>
\r
36 User-Agent: Notmuch/0.11+76~g1de742d (http://notmuchmail.org) Emacs/23.3.1
\r
38 Date: Thu, 19 Jan 2012 01:07:02 +0200
\r
39 Message-ID: <87sjjcbofd.fsf@nikula.org>
\r
41 Content-Type: text/plain; charset=us-ascii
\r
42 X-BeenThere: notmuch@notmuchmail.org
\r
43 X-Mailman-Version: 2.1.13
\r
45 List-Id: "Use and development of the notmuch mail system."
\r
46 <notmuch.notmuchmail.org>
\r
47 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
\r
48 <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
\r
49 List-Archive: <http://notmuchmail.org/pipermail/notmuch>
\r
50 List-Post: <mailto:notmuch@notmuchmail.org>
\r
51 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
\r
52 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
\r
53 <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
\r
54 X-List-Received-Date: Wed, 18 Jan 2012 23:07:09 -0000
\r
56 On Mon, 16 Jan 2012 11:13:21 -0700, Adam Wolfe Gordon <awg+notmuch@xvx.ca> wrote:
\r
57 > This new JSON format for replies includes headers generated for a reply
\r
58 > message as well as the headers and all text parts of the original message.
\r
59 > Using this data, a client can intelligently create a reply. For example,
\r
60 > the emacs client will be able to create replies with quoted HTML parts by
\r
61 > parsing the HTML parts using w3m.
\r
63 Hi, admittedly not a very thorough review, but please find a couple of
\r
71 > notmuch-reply.c | 313 ++++++++++++++++++++++++++++++++++++++++++++-----------
\r
72 > 1 files changed, 253 insertions(+), 60 deletions(-)
\r
74 > diff --git a/notmuch-reply.c b/notmuch-reply.c
\r
75 > index da3acce..f5a5dcf 100644
\r
76 > --- a/notmuch-reply.c
\r
77 > +++ b/notmuch-reply.c
\r
78 > @@ -30,6 +30,15 @@ reply_headers_message_part (GMimeMessage *message);
\r
80 > reply_part_content (GMimeObject *part);
\r
83 > +reply_part_start_json (GMimeObject *part, int *part_count);
\r
86 > +reply_part_content_json (GMimeObject *part);
\r
89 > +reply_part_end_json (GMimeObject *part);
\r
91 > static const notmuch_show_format_t format_reply = {
\r
94 > @@ -46,6 +55,22 @@ static const notmuch_show_format_t format_reply = {
\r
98 > +static const notmuch_show_format_t format_json = {
\r
101 > + "", NULL, NULL, "",
\r
103 > + reply_part_start_json,
\r
106 > + reply_part_content_json,
\r
107 > + reply_part_end_json,
\r
115 > show_reply_headers (GMimeMessage *message)
\r
117 > @@ -54,14 +79,14 @@ show_reply_headers (GMimeMessage *message)
\r
118 > stream_stdout = g_mime_stream_file_new (stdout);
\r
119 > if (stream_stdout) {
\r
120 > g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream_stdout), FALSE);
\r
121 > - stream_filter = g_mime_stream_filter_new(stream_stdout);
\r
122 > + stream_filter = g_mime_stream_filter_new (stream_stdout);
\r
123 > if (stream_filter) {
\r
124 > - g_mime_stream_filter_add(GMIME_STREAM_FILTER(stream_filter),
\r
125 > - g_mime_filter_headers_new());
\r
126 > - g_mime_object_write_to_stream(GMIME_OBJECT(message), stream_filter);
\r
127 > - g_object_unref(stream_filter);
\r
128 > + g_mime_stream_filter_add (GMIME_STREAM_FILTER(stream_filter),
\r
129 > + g_mime_filter_headers_new());
\r
130 > + g_mime_object_write_to_stream (GMIME_OBJECT(message), stream_filter);
\r
131 > + g_object_unref (stream_filter);
\r
133 > - g_object_unref(stream_stdout);
\r
134 > + g_object_unref (stream_stdout);
\r
136 I know I asked you to adhere to notmuch coding style like above, but I
\r
137 meant in the context of your patch, not elsewhere. Cleanups like this
\r
138 should really be separate patches. Sorry if I was ambiguous.
\r
143 > @@ -86,6 +111,17 @@ reply_headers_message_part (GMimeMessage *message)
\r
144 > printf ("> Date: %s\n", g_mime_message_get_date_as_string (message));
\r
147 > +static notmuch_bool_t
\r
148 > +reply_check_part_type (GMimeObject *part, const char *type, const char *subtype,
\r
149 > + const char *disposition)
\r
151 > + GMimeContentType *content_type = g_mime_object_get_content_type (GMIME_OBJECT (part));
\r
152 > + GMimeContentDisposition *part_disposition = g_mime_object_get_content_disposition (part);
\r
154 > + return (g_mime_content_type_is_type (content_type, type, subtype) &&
\r
155 > + (!part_disposition ||
\r
156 > + strcmp (part_disposition->disposition, disposition) == 0));
\r
160 > reply_part_content (GMimeObject *part)
\r
161 > @@ -108,32 +144,29 @@ reply_part_content (GMimeObject *part)
\r
163 > GMimeStream *stream_stdout = NULL, *stream_filter = NULL;
\r
164 > GMimeDataWrapper *wrapper;
\r
165 > - const char *charset;
\r
167 > - charset = g_mime_object_get_content_type_parameter (part, "charset");
\r
168 > stream_stdout = g_mime_stream_file_new (stdout);
\r
169 > if (stream_stdout) {
\r
170 > g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream_stdout), FALSE);
\r
171 > - stream_filter = g_mime_stream_filter_new(stream_stdout);
\r
173 > - g_mime_stream_filter_add(GMIME_STREAM_FILTER(stream_filter),
\r
174 > - g_mime_filter_charset_new(charset, "UTF-8"));
\r
176 > + stream_filter = g_mime_stream_filter_new (stream_stdout);
\r
178 > + const char *charset = g_mime_object_get_content_type_parameter (part, "charset");
\r
180 > + g_mime_stream_filter_add(GMIME_STREAM_FILTER (stream_filter),
\r
181 > + g_mime_filter_charset_new (charset, "UTF-8"));
\r
183 > - g_mime_stream_filter_add(GMIME_STREAM_FILTER(stream_filter),
\r
184 > - g_mime_filter_reply_new(TRUE));
\r
185 > + g_mime_stream_filter_add (GMIME_STREAM_FILTER (stream_filter),
\r
186 > + g_mime_filter_reply_new (TRUE));
\r
187 > wrapper = g_mime_part_get_content_object (GMIME_PART (part));
\r
188 > if (wrapper && stream_filter)
\r
189 > g_mime_data_wrapper_write_to_stream (wrapper, stream_filter);
\r
190 > if (stream_filter)
\r
191 > - g_object_unref(stream_filter);
\r
192 > + g_object_unref (stream_filter);
\r
193 > if (stream_stdout)
\r
194 > - g_object_unref(stream_stdout);
\r
195 > + g_object_unref (stream_stdout);
\r
199 > - if (disposition &&
\r
200 > - strcmp (disposition->disposition, GMIME_DISPOSITION_ATTACHMENT) == 0)
\r
201 > + if (disposition && strcmp (disposition->disposition, GMIME_DISPOSITION_ATTACHMENT) == 0)
\r
203 This is also a change not related to your patch.
\r
206 > const char *filename = g_mime_part_get_filename (GMIME_PART (part));
\r
207 > printf ("Attachment: %s (%s)\n", filename,
\r
208 > @@ -147,6 +180,67 @@ reply_part_content (GMimeObject *part)
\r
213 > +reply_part_start_json (GMimeObject *part, unused (int *part_count))
\r
215 > + if (reply_check_part_type (part, "text", "*", GMIME_DISPOSITION_INLINE))
\r
220 > +reply_part_end_json (GMimeObject *part)
\r
222 > + if (reply_check_part_type (part, "text", "*", GMIME_DISPOSITION_INLINE))
\r
223 > + printf ("}, ");
\r
227 > +reply_part_content_json (GMimeObject *part)
\r
229 > + GMimeContentType *content_type = g_mime_object_get_content_type (GMIME_OBJECT (part));
\r
230 > + void *ctx = talloc_new (NULL);
\r
232 > + /* We only care about inline text parts for reply purposes */
\r
233 > + if (reply_check_part_type (part, "text", "*", GMIME_DISPOSITION_INLINE))
\r
236 The style in notmuch is to put the opening brace in the end of the if
\r
237 line. Same applies below.
\r
239 > + GMimeDataWrapper *wrapper;
\r
240 > + GByteArray *part_content;
\r
242 > + printf ("\"content-type\": %s, \"content\": ",
\r
243 > + json_quote_str (ctx, g_mime_content_type_to_string (content_type)));
\r
245 > + wrapper = g_mime_part_get_content_object (GMIME_PART (part));
\r
248 > + const char *charset = g_mime_object_get_content_type_parameter (part, "charset");
\r
249 > + GMimeStream *stream_memory = g_mime_stream_mem_new ();
\r
250 > + if (stream_memory) {
\r
251 > + GMimeStream *stream_filter = NULL;
\r
252 > + stream_filter = g_mime_stream_filter_new (stream_memory);
\r
254 > + g_mime_stream_filter_add (GMIME_STREAM_FILTER (stream_filter),
\r
255 > + g_mime_filter_charset_new (charset, "UTF-8"));
\r
258 > + if (stream_filter)
\r
260 > + g_mime_data_wrapper_write_to_stream (wrapper, stream_filter);
\r
261 > + part_content = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (stream_memory));
\r
263 > + printf ("%s", json_quote_chararray (ctx, (char *) part_content->data, part_content->len));
\r
265 > + if (stream_filter)
\r
266 > + g_object_unref (stream_filter);
\r
269 > + if (stream_memory)
\r
270 > + g_object_unref (stream_memory);
\r
274 > + talloc_free (ctx);
\r
277 > /* Is the given address configured as one of the user's "personal" or
\r
278 > * "other" addresses. */
\r
280 > @@ -505,6 +599,61 @@ guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message
\r
284 > +static GMimeMessage *
\r
285 > +create_reply_message(void *ctx,
\r
286 > + notmuch_config_t *config,
\r
287 > + notmuch_message_t *message,
\r
288 > + notmuch_bool_t reply_all)
\r
290 > + const char *subject, *from_addr = NULL;
\r
291 > + const char *in_reply_to, *orig_references, *references;
\r
293 > + /* The 1 means we want headers in a "pretty" order. */
\r
294 > + GMimeMessage *reply = g_mime_message_new (1);
\r
295 > + if (reply == NULL) {
\r
296 > + fprintf (stderr, "Out of memory\n");
\r
300 > + subject = notmuch_message_get_header (message, "subject");
\r
302 > + if (strncasecmp (subject, "Re:", 3))
\r
303 > + subject = talloc_asprintf (ctx, "Re: %s", subject);
\r
304 > + g_mime_message_set_subject (reply, subject);
\r
307 > + from_addr = add_recipients_from_message (reply, config,
\r
308 > + message, reply_all);
\r
310 > + if (from_addr == NULL)
\r
311 > + from_addr = guess_from_received_header (config, message);
\r
313 > + if (from_addr == NULL)
\r
314 > + from_addr = notmuch_config_get_user_primary_email (config);
\r
316 > + from_addr = talloc_asprintf (ctx, "%s <%s>",
\r
317 > + notmuch_config_get_user_name (config),
\r
319 > + g_mime_object_set_header (GMIME_OBJECT (reply),
\r
320 > + "From", from_addr);
\r
322 > + in_reply_to = talloc_asprintf (ctx, "<%s>",
\r
323 > + notmuch_message_get_message_id (message));
\r
325 > + g_mime_object_set_header (GMIME_OBJECT (reply),
\r
326 > + "In-Reply-To", in_reply_to);
\r
328 > + orig_references = notmuch_message_get_header (message, "references");
\r
329 > + references = talloc_asprintf (ctx, "%s%s%s",
\r
330 > + orig_references ? orig_references : "",
\r
331 > + orig_references ? " " : "",
\r
333 > + g_mime_object_set_header (GMIME_OBJECT (reply),
\r
334 > + "References", references);
\r
340 > notmuch_reply_format_default(void *ctx,
\r
341 > notmuch_config_t *config,
\r
342 > @@ -515,8 +664,6 @@ notmuch_reply_format_default(void *ctx,
\r
343 > GMimeMessage *reply;
\r
344 > notmuch_messages_t *messages;
\r
345 > notmuch_message_t *message;
\r
346 > - const char *subject, *from_addr = NULL;
\r
347 > - const char *in_reply_to, *orig_references, *references;
\r
348 > const notmuch_show_format_t *format = &format_reply;
\r
350 > for (messages = notmuch_query_search_messages (query);
\r
351 > @@ -525,62 +672,104 @@ notmuch_reply_format_default(void *ctx,
\r
353 > message = notmuch_messages_get (messages);
\r
355 > - /* The 1 means we want headers in a "pretty" order. */
\r
356 > - reply = g_mime_message_new (1);
\r
357 > - if (reply == NULL) {
\r
358 > - fprintf (stderr, "Out of memory\n");
\r
361 > + reply = create_reply_message (ctx, config, message, reply_all);
\r
363 > - subject = notmuch_message_get_header (message, "subject");
\r
365 > - if (strncasecmp (subject, "Re:", 3))
\r
366 > - subject = talloc_asprintf (ctx, "Re: %s", subject);
\r
367 > - g_mime_message_set_subject (reply, subject);
\r
372 > - from_addr = add_recipients_from_message (reply, config, message,
\r
374 > + show_reply_headers (reply);
\r
376 > - if (from_addr == NULL)
\r
377 > - from_addr = guess_from_received_header (config, message);
\r
378 > + g_object_unref (G_OBJECT (reply));
\r
381 > - if (from_addr == NULL)
\r
382 > - from_addr = notmuch_config_get_user_primary_email (config);
\r
383 > + printf ("On %s, %s wrote:\n",
\r
384 > + notmuch_message_get_header (message, "date"),
\r
385 > + notmuch_message_get_header (message, "from"));
\r
387 > - from_addr = talloc_asprintf (ctx, "%s <%s>",
\r
388 > - notmuch_config_get_user_name (config),
\r
390 > - g_mime_object_set_header (GMIME_OBJECT (reply),
\r
391 > - "From", from_addr);
\r
392 > + show_message_body (message, format, params);
\r
394 > - in_reply_to = talloc_asprintf (ctx, "<%s>",
\r
395 > - notmuch_message_get_message_id (message));
\r
396 > + notmuch_message_destroy (message);
\r
401 > - g_mime_object_set_header (GMIME_OBJECT (reply),
\r
402 > - "In-Reply-To", in_reply_to);
\r
404 > +notmuch_reply_format_json(void *ctx,
\r
405 > + notmuch_config_t *config,
\r
406 > + notmuch_query_t *query,
\r
407 > + unused (notmuch_show_params_t *params),
\r
408 > + notmuch_bool_t reply_all)
\r
410 > + GMimeMessage *reply;
\r
411 > + notmuch_messages_t *messages;
\r
412 > + notmuch_message_t *message;
\r
413 > + const notmuch_show_format_t *format = &format_json;
\r
415 > - orig_references = notmuch_message_get_header (message, "references");
\r
416 > - references = talloc_asprintf (ctx, "%s%s%s",
\r
417 > - orig_references ? orig_references : "",
\r
418 > - orig_references ? " " : "",
\r
420 > - g_mime_object_set_header (GMIME_OBJECT (reply),
\r
421 > - "References", references);
\r
422 > + const char *reply_headers[] = {"from", "to", "subject", "in-reply-to", "references"};
\r
423 > + const char *orig_headers[] = {"from", "to", "cc", "subject", "date", "in-reply-to", "references"};
\r
424 > + unsigned int hidx;
\r
426 > - show_reply_headers (reply);
\r
427 > + /* Start array of reply objects */
\r
430 > + for (messages = notmuch_query_search_messages (query);
\r
431 > + notmuch_messages_valid (messages);
\r
432 > + notmuch_messages_move_to_next (messages))
\r
434 > + /* Start a reply object */
\r
435 > + printf ("{ \"reply\": { \"headers\": { ");
\r
437 > + message = notmuch_messages_get (messages);
\r
439 > + reply = create_reply_message (ctx, config, message, reply_all);
\r
443 If this occurs, it will create broken JSON.
\r
446 > + for (hidx = 0; hidx < ARRAY_SIZE (reply_headers); hidx++)
\r
451 > + printf ("%s: %s", json_quote_str (ctx, reply_headers[hidx]),
\r
452 > + json_quote_str (ctx, g_mime_object_get_header (GMIME_OBJECT (reply), reply_headers[hidx])));
\r
455 > g_object_unref (G_OBJECT (reply));
\r
458 > - printf ("On %s, %s wrote:\n",
\r
459 > - notmuch_message_get_header (message, "date"),
\r
460 > - notmuch_message_get_header (message, "from"));
\r
461 > + /* Done the headers for the reply, which has no body parts */
\r
462 > + printf ("} }");
\r
464 > + /* Start the original */
\r
465 > + printf (", \"original\": { \"headers\": { ");
\r
467 > + for (hidx = 0; hidx < ARRAY_SIZE (orig_headers); hidx++)
\r
472 > + printf ("%s: %s", json_quote_str (ctx, orig_headers[hidx]),
\r
473 > + json_quote_str (ctx, notmuch_message_get_header (message, orig_headers[hidx])));
\r
476 > + /* End headers */
\r
477 > + printf (" }, \"body\": [ ");
\r
479 > + /* Show body parts */
\r
480 > show_message_body (message, format, params);
\r
482 > notmuch_message_destroy (message);
\r
484 > + /* Done the original */
\r
485 > + printf ("{} ] }");
\r
487 > + /* End the reply object. */
\r
488 > + printf (" }, ");
\r
491 > + /* End array of reply objects */
\r
492 > + printf ("{} ]\n");
\r
497 > @@ -646,6 +835,7 @@ notmuch_reply_format_headers_only(void *ctx,
\r
502 > FORMAT_HEADERS_ONLY,
\r
505 > @@ -666,6 +856,7 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])
\r
506 > notmuch_opt_desc_t options[] = {
\r
507 > { NOTMUCH_OPT_KEYWORD, &format, "format", 'f',
\r
508 > (notmuch_keyword_t []){ { "default", FORMAT_DEFAULT },
\r
509 > + { "json", FORMAT_JSON },
\r
510 > { "headers-only", FORMAT_HEADERS_ONLY },
\r
512 > { NOTMUCH_OPT_KEYWORD, &reply_all, "reply-to", 'r',
\r
513 > @@ -684,6 +875,8 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])
\r
515 > if (format == FORMAT_HEADERS_ONLY)
\r
516 > reply_format_func = notmuch_reply_format_headers_only;
\r
517 > + else if (format == FORMAT_JSON)
\r
518 > + reply_format_func = notmuch_reply_format_json;
\r
520 > reply_format_func = notmuch_reply_format_default;
\r
525 > _______________________________________________
\r
526 > notmuch mailing list
\r
527 > notmuch@notmuchmail.org
\r
528 > http://notmuchmail.org/mailman/listinfo/notmuch
\r