1 Return-Path: <awg@lagos.xvx.ca>
\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 801CF40A3FF
\r
6 for <notmuch@notmuchmail.org>; Sat, 7 Jan 2012 23:53:12 -0800 (PST)
\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org
\r
11 X-Spam-Status: No, score=0 tagged_above=-999 required=5
\r
12 tests=[RCVD_IN_DNSWL_NONE=-0.0001] 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 40bDacPm2bWK for <notmuch@notmuchmail.org>;
\r
16 Sat, 7 Jan 2012 23:53:10 -0800 (PST)
\r
17 Received: from idcmail-mo2no.shaw.ca (idcmail-mo2no.shaw.ca [64.59.134.9])
\r
18 by olra.theworths.org (Postfix) with ESMTP id 385A340EF31
\r
19 for <notmuch@notmuchmail.org>; Sat, 7 Jan 2012 23:53:09 -0800 (PST)
\r
20 Received: from lb7f8hsrpno-svcs.dcs.int.inet (HELO pd5ml3no-ssvc.prod.shaw.ca)
\r
22 by pd7mo1no-svcs.prod.shaw.ca with ESMTP; 08 Jan 2012 00:53:08 -0700
\r
23 X-Cloudmark-SP-Filtered: true
\r
24 X-Cloudmark-SP-Result: v=1.1 cv=TH029eDfZHqIRue7hhxWRjZ7JL4S9vo/jJY5vkv2d5E=
\r
26 a=quntBF94qJUA:10 a=BLceEmwcHowA:10 a=yQp6g8lIsgqumF79BAsFDg==:17
\r
27 a=P2v0uuFMy3zdClWoV1EA:9 a=7r3cU_-jv2UgmVONYvoA:7
\r
28 a=HpAAvcLHHh0Zw7uRqdWCyQ==:117
\r
29 Received: from unknown (HELO lagos.xvx.ca) ([96.52.216.56])
\r
30 by pd5ml3no-dmz.prod.shaw.ca with ESMTP; 08 Jan 2012 00:53:08 -0700
\r
31 Received: by lagos.xvx.ca (Postfix, from userid 1000)
\r
32 id 72F0B8004202; Sun, 8 Jan 2012 00:53:08 -0700 (MST)
\r
33 From: Adam Wolfe Gordon <awg+notmuch@xvx.ca>
\r
34 To: notmuch@notmuchmail.org,
\r
36 Subject: [PATCH 2/4] reply: Add a JSON reply format.
\r
37 Date: Sun, 8 Jan 2012 00:52:40 -0700
\r
38 Message-Id: <1326009162-19524-3-git-send-email-awg+notmuch@xvx.ca>
\r
39 X-Mailer: git-send-email 1.7.5.4
\r
40 In-Reply-To: <1326009162-19524-1-git-send-email-awg+notmuch@xvx.ca>
\r
41 References: <1326009162-19524-1-git-send-email-awg+notmuch@xvx.ca>
\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: Sun, 08 Jan 2012 07:53:13 -0000
\r
56 From: Adam Wolfe Gordon <awg@xvx.ca>
\r
58 This new JSON format for replies includes headers generated for a reply
\r
59 message as well as the headers and all text parts of the original message.
\r
60 Using this data, a client can intelligently create a reply. For example,
\r
61 the emacs client will be able to create replies with quoted HTML parts by
\r
62 parsing the HTML parts using w3m.
\r
64 notmuch-reply.c | 269 +++++++++++++++++++++++++++++++++++++++++++++++--------
\r
65 1 files changed, 230 insertions(+), 39 deletions(-)
\r
67 diff --git a/notmuch-reply.c b/notmuch-reply.c
\r
68 index f8d5f64..82df396 100644
\r
69 --- a/notmuch-reply.c
\r
70 +++ b/notmuch-reply.c
\r
71 @@ -30,6 +30,15 @@ reply_headers_message_part (GMimeMessage *message);
\r
73 reply_part_content (GMimeObject *part);
\r
76 +reply_part_start_json (GMimeObject *part, int *part_count);
\r
79 +reply_part_content_json (GMimeObject *part);
\r
82 +reply_part_end_json (GMimeObject *part);
\r
84 static const notmuch_show_format_t format_reply = {
\r
87 @@ -46,6 +55,22 @@ static const notmuch_show_format_t format_reply = {
\r
91 +static const notmuch_show_format_t format_json = {
\r
94 + "", NULL, NULL, "",
\r
96 + reply_part_start_json,
\r
99 + reply_part_content_json,
\r
100 + reply_part_end_json,
\r
108 show_reply_headers (GMimeMessage *message)
\r
110 @@ -147,6 +172,78 @@ reply_part_content (GMimeObject *part)
\r
115 +reply_part_start_json (GMimeObject *part, unused(int *part_count))
\r
117 + GMimeContentType *content_type = g_mime_object_get_content_type (GMIME_OBJECT (part));
\r
118 + GMimeContentDisposition *disposition = g_mime_object_get_content_disposition (part);
\r
120 + if (g_mime_content_type_is_type (content_type, "text", "*") &&
\r
122 + strcmp (disposition->disposition, GMIME_DISPOSITION_INLINE) == 0))
\r
129 +reply_part_end_json (GMimeObject *part)
\r
131 + GMimeContentType *content_type = g_mime_object_get_content_type (GMIME_OBJECT (part));
\r
132 + GMimeContentDisposition *disposition = g_mime_object_get_content_disposition (part);
\r
134 + if (g_mime_content_type_is_type (content_type, "text", "*") &&
\r
136 + strcmp (disposition->disposition, GMIME_DISPOSITION_INLINE) == 0))
\r
141 +reply_part_content_json (GMimeObject *part)
\r
143 + GMimeContentType *content_type = g_mime_object_get_content_type (GMIME_OBJECT (part));
\r
144 + GMimeContentDisposition *disposition = g_mime_object_get_content_disposition (part);
\r
146 + void *ctx = talloc_new (NULL);
\r
148 + /* We only care about inline text parts for reply purposes */
\r
149 + if (g_mime_content_type_is_type (content_type, "text", "*") &&
\r
151 + strcmp (disposition->disposition, GMIME_DISPOSITION_INLINE) == 0))
\r
153 + GMimeStream *stream_memory = NULL, *stream_filter = NULL;
\r
154 + GMimeDataWrapper *wrapper;
\r
155 + GByteArray *part_content;
\r
156 + const char *charset;
\r
158 + printf("\"content-type\": %s, \"content\": ",
\r
159 + json_quote_str(ctx, g_mime_content_type_to_string(content_type)));
\r
161 + charset = g_mime_object_get_content_type_parameter (part, "charset");
\r
162 + stream_memory = g_mime_stream_mem_new ();
\r
163 + if (stream_memory) {
\r
164 + stream_filter = g_mime_stream_filter_new(stream_memory);
\r
166 + g_mime_stream_filter_add(GMIME_STREAM_FILTER(stream_filter),
\r
167 + g_mime_filter_charset_new(charset, "UTF-8"));
\r
170 + wrapper = g_mime_part_get_content_object (GMIME_PART (part));
\r
171 + if (wrapper && stream_filter)
\r
172 + g_mime_data_wrapper_write_to_stream (wrapper, stream_filter);
\r
173 + part_content = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (stream_memory));
\r
175 + printf("%s", json_quote_chararray(ctx, (char *) part_content->data, part_content->len));
\r
177 + if (stream_filter)
\r
178 + g_object_unref(stream_filter);
\r
179 + if (stream_memory)
\r
180 + g_object_unref(stream_memory);
\r
183 + talloc_free (ctx);
\r
186 /* Is the given address configured as one of the user's "personal" or
\r
187 * "other" addresses. */
\r
189 @@ -476,6 +573,59 @@ guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message
\r
193 +static GMimeMessage *
\r
194 +create_reply_message(void *ctx,
\r
195 + notmuch_config_t *config,
\r
196 + notmuch_message_t *message)
\r
198 + const char *subject, *from_addr = NULL;
\r
199 + const char *in_reply_to, *orig_references, *references;
\r
201 + /* The 1 means we want headers in a "pretty" order. */
\r
202 + GMimeMessage *reply = g_mime_message_new (1);
\r
203 + if (reply == NULL) {
\r
204 + fprintf (stderr, "Out of memory\n");
\r
208 + subject = notmuch_message_get_header (message, "subject");
\r
210 + if (strncasecmp (subject, "Re:", 3))
\r
211 + subject = talloc_asprintf (ctx, "Re: %s", subject);
\r
212 + g_mime_message_set_subject (reply, subject);
\r
215 + from_addr = add_recipients_from_message (reply, config, message);
\r
217 + if (from_addr == NULL)
\r
218 + from_addr = guess_from_received_header (config, message);
\r
220 + if (from_addr == NULL)
\r
221 + from_addr = notmuch_config_get_user_primary_email (config);
\r
223 + from_addr = talloc_asprintf (ctx, "%s <%s>",
\r
224 + notmuch_config_get_user_name (config),
\r
226 + g_mime_object_set_header (GMIME_OBJECT (reply),
\r
227 + "From", from_addr);
\r
229 + in_reply_to = talloc_asprintf (ctx, "<%s>",
\r
230 + notmuch_message_get_message_id (message));
\r
232 + g_mime_object_set_header (GMIME_OBJECT (reply),
\r
233 + "In-Reply-To", in_reply_to);
\r
235 + orig_references = notmuch_message_get_header (message, "references");
\r
236 + references = talloc_asprintf (ctx, "%s%s%s",
\r
237 + orig_references ? orig_references : "",
\r
238 + orig_references ? " " : "",
\r
240 + g_mime_object_set_header (GMIME_OBJECT (reply),
\r
241 + "References", references);
\r
247 notmuch_reply_format_default(void *ctx,
\r
248 notmuch_config_t *config,
\r
249 @@ -485,8 +635,6 @@ notmuch_reply_format_default(void *ctx,
\r
250 GMimeMessage *reply;
\r
251 notmuch_messages_t *messages;
\r
252 notmuch_message_t *message;
\r
253 - const char *subject, *from_addr = NULL;
\r
254 - const char *in_reply_to, *orig_references, *references;
\r
255 const notmuch_show_format_t *format = &format_reply;
\r
257 for (messages = notmuch_query_search_messages (query);
\r
258 @@ -495,61 +643,102 @@ notmuch_reply_format_default(void *ctx,
\r
260 message = notmuch_messages_get (messages);
\r
262 - /* The 1 means we want headers in a "pretty" order. */
\r
263 - reply = g_mime_message_new (1);
\r
264 - if (reply == NULL) {
\r
265 - fprintf (stderr, "Out of memory\n");
\r
268 + reply = create_reply_message(ctx, config, message);
\r
270 - subject = notmuch_message_get_header (message, "subject");
\r
272 - if (strncasecmp (subject, "Re:", 3))
\r
273 - subject = talloc_asprintf (ctx, "Re: %s", subject);
\r
274 - g_mime_message_set_subject (reply, subject);
\r
276 + show_reply_headers (reply);
\r
278 - from_addr = add_recipients_from_message (reply, config, message);
\r
279 + g_object_unref (G_OBJECT (reply));
\r
282 - if (from_addr == NULL)
\r
283 - from_addr = guess_from_received_header (config, message);
\r
284 + printf ("On %s, %s wrote:\n",
\r
285 + notmuch_message_get_header (message, "date"),
\r
286 + notmuch_message_get_header (message, "from"));
\r
288 - if (from_addr == NULL)
\r
289 - from_addr = notmuch_config_get_user_primary_email (config);
\r
290 + show_message_body (message, format, params);
\r
292 - from_addr = talloc_asprintf (ctx, "%s <%s>",
\r
293 - notmuch_config_get_user_name (config),
\r
295 - g_mime_object_set_header (GMIME_OBJECT (reply),
\r
296 - "From", from_addr);
\r
297 + notmuch_message_destroy (message);
\r
302 - in_reply_to = talloc_asprintf (ctx, "<%s>",
\r
303 - notmuch_message_get_message_id (message));
\r
305 +notmuch_reply_format_json(void *ctx,
\r
306 + notmuch_config_t *config,
\r
307 + notmuch_query_t *query,
\r
308 + unused (notmuch_show_params_t *params))
\r
310 + GMimeMessage *reply;
\r
311 + notmuch_messages_t *messages;
\r
312 + notmuch_message_t *message;
\r
313 + const notmuch_show_format_t *format = &format_json;
\r
315 - g_mime_object_set_header (GMIME_OBJECT (reply),
\r
316 - "In-Reply-To", in_reply_to);
\r
317 + const char *reply_headers[] = {"from", "to", "subject", "in-reply-to", "references"};
\r
318 + const int n_reply_headers = 5;
\r
319 + const char *orig_headers[] = {"from", "to", "cc", "subject", "date", "in-reply-to", "references"};
\r
320 + const int n_orig_headers = 7;
\r
323 - orig_references = notmuch_message_get_header (message, "references");
\r
324 - references = talloc_asprintf (ctx, "%s%s%s",
\r
325 - orig_references ? orig_references : "",
\r
326 - orig_references ? " " : "",
\r
328 - g_mime_object_set_header (GMIME_OBJECT (reply),
\r
329 - "References", references);
\r
330 + /* Start array of reply objects */
\r
333 - show_reply_headers (reply);
\r
334 + for (messages = notmuch_query_search_messages (query);
\r
335 + notmuch_messages_valid (messages);
\r
336 + notmuch_messages_move_to_next (messages))
\r
338 + /* Start a reply object */
\r
339 + printf ("{ \"reply\": { \"headers\": { ");
\r
341 + message = notmuch_messages_get (messages);
\r
343 + reply = create_reply_message(ctx, config, message);
\r
345 + for (hidx = 0; hidx < n_reply_headers; hidx += 1)
\r
351 + printf ("%s: %s", json_quote_str(ctx, reply_headers[hidx]),
\r
352 + json_quote_str (ctx, g_mime_object_get_header (GMIME_OBJECT (reply), reply_headers[hidx])));
\r
355 g_object_unref (G_OBJECT (reply));
\r
358 - printf ("On %s, %s wrote:\n",
\r
359 - notmuch_message_get_header (message, "date"),
\r
360 - notmuch_message_get_header (message, "from"));
\r
361 + /* Done the headers for the reply, which has no body parts */
\r
364 + /* Start the original */
\r
365 + printf (", \"original\": { \"headers\": { ");
\r
367 + for (hidx = 0; hidx < n_orig_headers; hidx += 1)
\r
373 + printf ("%s: %s", json_quote_str(ctx, orig_headers[hidx]),
\r
374 + json_quote_str (ctx, notmuch_message_get_header (message, orig_headers[hidx])));
\r
377 + /* End headers */
\r
378 + printf (" }, \"body\": [ ");
\r
380 + /* Show body parts */
\r
381 show_message_body (message, format, params);
\r
383 notmuch_message_destroy (message);
\r
385 + /* Done the original */
\r
386 + printf ("{} ] }");
\r
388 + /* End the reply object. */
\r
392 + /* End array of reply objects */
\r
393 + printf ("{} ]\n");
\r
398 @@ -640,6 +829,8 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])
\r
399 reply_format_func = notmuch_reply_format_default;
\r
400 } else if (strcmp (opt, "headers-only") == 0) {
\r
401 reply_format_func = notmuch_reply_format_headers_only;
\r
402 + } else if (strcmp (opt, "json") == 0) {
\r
403 + reply_format_func = notmuch_reply_format_json;
\r
405 fprintf (stderr, "Invalid value for --format: %s\n", opt);
\r