Re: Hi all
[notmuch-archives.git] / c7 / 7b6f6f79cd72ecf4b735dd4844174504c058f9
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 656C1431FBD\r
6         for <notmuch@notmuchmail.org>; Tue, 14 Feb 2012 21:42:19 -0800 (PST)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: 0\r
10 X-Spam-Level: \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 DvAkOkkaYraS for <notmuch@notmuchmail.org>;\r
16         Tue, 14 Feb 2012 21:42:18 -0800 (PST)\r
17 Received: from idcmail-mo1so.shaw.ca (idcmail-mo1so.shaw.ca [24.71.223.10])\r
18         by olra.theworths.org (Postfix) with ESMTP id 59F56431FBC\r
19         for <notmuch@notmuchmail.org>; Tue, 14 Feb 2012 21:42:18 -0800 (PST)\r
20 Received: from pd3ml1so-ssvc.prod.shaw.ca ([10.0.141.140])\r
21         by pd3mo1so-svcs.prod.shaw.ca with ESMTP; 14 Feb 2012 22:42:17 -0700\r
22 X-Cloudmark-SP-Filtered: true\r
23 X-Cloudmark-SP-Result: v=1.1 cv=MPNiKFfsidoaPqBs0kThsodqbsbgvPHp5CGEg9DOvhI=\r
24         c=1 sm=1\r
25         a=WZU82kzV3_QA:10 a=BLceEmwcHowA:10 a=yQp6g8lIsgqumF79BAsFDg==:17\r
26         a=3-z4jJ9MB81Z5CkkUWIA:9 a=7RHqQ3Bl_oONjem5DEkA:7 a=4tDw8iMZ7BjuzbIB:21\r
27         a=nVwLb1l9P6mUoZsG:21 a=HpAAvcLHHh0Zw7uRqdWCyQ==:117\r
28 Received: from unknown (HELO lagos.xvx.ca) ([96.52.216.56])\r
29         by pd3ml1so-dmz.prod.shaw.ca with ESMTP; 14 Feb 2012 22:42:17 -0700\r
30 Received: by lagos.xvx.ca (Postfix, from userid 1000)\r
31         id 5448B8004EBA; Tue, 14 Feb 2012 22:42:17 -0700 (MST)\r
32 From: Adam Wolfe Gordon <awg+notmuch@xvx.ca>\r
33 To: notmuch@notmuchmail.org\r
34 Subject: [PATCH v5.1 2/4] reply: Add a JSON reply format.\r
35 Date: Tue, 14 Feb 2012 22:42:15 -0700\r
36 Message-Id: <1329284535-2140-1-git-send-email-awg+notmuch@xvx.ca>\r
37 X-Mailer: git-send-email 1.7.5.4\r
38 In-Reply-To: <1329282027-29457-3-git-send-email-awg+notmuch@xvx.ca>\r
39 References: <1329282027-29457-3-git-send-email-awg+notmuch@xvx.ca>\r
40 X-BeenThere: notmuch@notmuchmail.org\r
41 X-Mailman-Version: 2.1.13\r
42 Precedence: list\r
43 List-Id: "Use and development of the notmuch mail system."\r
44         <notmuch.notmuchmail.org>\r
45 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
46         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
47 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
48 List-Post: <mailto:notmuch@notmuchmail.org>\r
49 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
50 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
51         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
52 X-List-Received-Date: Wed, 15 Feb 2012 05:42:19 -0000\r
53 \r
54 This new JSON format for replies includes headers generated for a\r
55 reply message as well as the headers of the original message.  Using\r
56 this data, a client can intelligently create a reply. For example, the\r
57 emacs client will be able to create replies with quoted HTML parts by\r
58 parsing the HTML parts.\r
59 \r
60 Reply now enforces that only one message is returned, as the semantics\r
61 of replying to multiple messages are not well-defined.\r
62 ---\r
63 \r
64 Adjusted the header display to be consistent with the other JSON formats.\r
65 \r
66  notmuch-client.h |    3 +\r
67  notmuch-reply.c  |  188 +++++++++++++++++++++++++++++++++++++++++------------\r
68  notmuch-show.c   |    2 +-\r
69  3 files changed, 149 insertions(+), 44 deletions(-)\r
70 \r
71 diff --git a/notmuch-client.h b/notmuch-client.h\r
72 index 60828aa..d28ea07 100644\r
73 --- a/notmuch-client.h\r
74 +++ b/notmuch-client.h\r
75 @@ -344,6 +344,9 @@ typedef struct mime_node {\r
76      int next_part_num;\r
77  } mime_node_t;\r
78  \r
79 +void\r
80 +format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first);\r
81 +\r
82  /* Construct a new MIME node pointing to the root message part of\r
83   * message.  If cryptoctx is non-NULL, it will be used to verify\r
84   * signatures on any child parts.  If decrypt is true, then cryptoctx\r
85 diff --git a/notmuch-reply.c b/notmuch-reply.c\r
86 index 6b244e6..76995b1 100644\r
87 --- a/notmuch-reply.c\r
88 +++ b/notmuch-reply.c\r
89 @@ -505,6 +505,61 @@ guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message\r
90      return NULL;\r
91  }\r
92  \r
93 +static GMimeMessage *\r
94 +create_reply_message(void *ctx,\r
95 +                    notmuch_config_t *config,\r
96 +                    notmuch_message_t *message,\r
97 +                    notmuch_bool_t reply_all)\r
98 +{\r
99 +    const char *subject, *from_addr = NULL;\r
100 +    const char *in_reply_to, *orig_references, *references;\r
101 +\r
102 +    /* The 1 means we want headers in a "pretty" order. */\r
103 +    GMimeMessage *reply = g_mime_message_new (1);\r
104 +    if (reply == NULL) {\r
105 +       fprintf (stderr, "Out of memory\n");\r
106 +       return NULL;\r
107 +    }\r
108 +\r
109 +    subject = notmuch_message_get_header (message, "subject");\r
110 +    if (subject) {\r
111 +       if (strncasecmp (subject, "Re:", 3))\r
112 +           subject = talloc_asprintf (ctx, "Re: %s", subject);\r
113 +       g_mime_message_set_subject (reply, subject);\r
114 +    }\r
115 +\r
116 +    from_addr = add_recipients_from_message (reply, config,\r
117 +                                            message, reply_all);\r
118 +\r
119 +    if (from_addr == NULL)\r
120 +       from_addr = guess_from_received_header (config, message);\r
121 +\r
122 +    if (from_addr == NULL)\r
123 +       from_addr = notmuch_config_get_user_primary_email (config);\r
124 +\r
125 +    from_addr = talloc_asprintf (ctx, "%s <%s>",\r
126 +                                notmuch_config_get_user_name (config),\r
127 +                                from_addr);\r
128 +    g_mime_object_set_header (GMIME_OBJECT (reply),\r
129 +                             "From", from_addr);\r
130 +\r
131 +    in_reply_to = talloc_asprintf (ctx, "<%s>",\r
132 +                                  notmuch_message_get_message_id (message));\r
133 +\r
134 +    g_mime_object_set_header (GMIME_OBJECT (reply),\r
135 +                             "In-Reply-To", in_reply_to);\r
136 +\r
137 +    orig_references = notmuch_message_get_header (message, "references");\r
138 +    references = talloc_asprintf (ctx, "%s%s%s",\r
139 +                                 orig_references ? orig_references : "",\r
140 +                                 orig_references ? " " : "",\r
141 +                                 in_reply_to);\r
142 +    g_mime_object_set_header (GMIME_OBJECT (reply),\r
143 +                             "References", references);\r
144 +\r
145 +    return reply;\r
146 +}\r
147 +\r
148  static int\r
149  notmuch_reply_format_default(void *ctx,\r
150                              notmuch_config_t *config,\r
151 @@ -515,8 +570,6 @@ notmuch_reply_format_default(void *ctx,\r
152      GMimeMessage *reply;\r
153      notmuch_messages_t *messages;\r
154      notmuch_message_t *message;\r
155 -    const char *subject, *from_addr = NULL;\r
156 -    const char *in_reply_to, *orig_references, *references;\r
157      const notmuch_show_format_t *format = &format_reply;\r
158  \r
159      for (messages = notmuch_query_search_messages (query);\r
160 @@ -525,48 +578,10 @@ notmuch_reply_format_default(void *ctx,\r
161      {\r
162         message = notmuch_messages_get (messages);\r
163  \r
164 -       /* The 1 means we want headers in a "pretty" order. */\r
165 -       reply = g_mime_message_new (1);\r
166 -       if (reply == NULL) {\r
167 -           fprintf (stderr, "Out of memory\n");\r
168 -           return 1;\r
169 -       }\r
170 -\r
171 -       subject = notmuch_message_get_header (message, "subject");\r
172 -       if (subject) {\r
173 -           if (strncasecmp (subject, "Re:", 3))\r
174 -               subject = talloc_asprintf (ctx, "Re: %s", subject);\r
175 -           g_mime_message_set_subject (reply, subject);\r
176 -       }\r
177 -\r
178 -       from_addr = add_recipients_from_message (reply, config, message,\r
179 -                                                reply_all);\r
180 -\r
181 -       if (from_addr == NULL)\r
182 -           from_addr = guess_from_received_header (config, message);\r
183 -\r
184 -       if (from_addr == NULL)\r
185 -           from_addr = notmuch_config_get_user_primary_email (config);\r
186 +       reply = create_reply_message (ctx, config, message, reply_all);\r
187  \r
188 -       from_addr = talloc_asprintf (ctx, "%s <%s>",\r
189 -                                    notmuch_config_get_user_name (config),\r
190 -                                    from_addr);\r
191 -       g_mime_object_set_header (GMIME_OBJECT (reply),\r
192 -                                 "From", from_addr);\r
193 -\r
194 -       in_reply_to = talloc_asprintf (ctx, "<%s>",\r
195 -                            notmuch_message_get_message_id (message));\r
196 -\r
197 -       g_mime_object_set_header (GMIME_OBJECT (reply),\r
198 -                                 "In-Reply-To", in_reply_to);\r
199 -\r
200 -       orig_references = notmuch_message_get_header (message, "references");\r
201 -       references = talloc_asprintf (ctx, "%s%s%s",\r
202 -                                     orig_references ? orig_references : "",\r
203 -                                     orig_references ? " " : "",\r
204 -                                     in_reply_to);\r
205 -       g_mime_object_set_header (GMIME_OBJECT (reply),\r
206 -                                 "References", references);\r
207 +       if (!reply)\r
208 +           continue;\r
209  \r
210         show_reply_headers (reply);\r
211  \r
212 @@ -584,6 +599,89 @@ notmuch_reply_format_default(void *ctx,\r
213      return 0;\r
214  }\r
215  \r
216 +static void\r
217 +format_reply_headers_json (const void *ctx, GMimeMessage *message)\r
218 +{\r
219 +    void *local = talloc_new (ctx);\r
220 +    InternetAddressList *recipients;\r
221 +    const char *recipients_string;\r
222 +\r
223 +    printf ("{%s: %s",\r
224 +           json_quote_str (local, "Subject"),\r
225 +           json_quote_str (local, g_mime_message_get_subject (message)));\r
226 +    printf (", %s: %s",\r
227 +           json_quote_str (local, "From"),\r
228 +           json_quote_str (local, g_mime_message_get_sender (message)));\r
229 +    recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_TO);\r
230 +    recipients_string = internet_address_list_to_string (recipients, 0);\r
231 +    if (recipients_string)\r
232 +       printf (", %s: %s",\r
233 +               json_quote_str (local, "To"),\r
234 +               json_quote_str (local, recipients_string));\r
235 +    recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_CC);\r
236 +    recipients_string = internet_address_list_to_string (recipients, 0);\r
237 +    if (recipients_string)\r
238 +       printf (", %s: %s",\r
239 +               json_quote_str (local, "Cc"),\r
240 +               json_quote_str (local, recipients_string));\r
241 +\r
242 +    printf (", %s: %s",\r
243 +           json_quote_str (local, "In-reply-to"),\r
244 +           json_quote_str (local, g_mime_object_get_header (GMIME_OBJECT (message), "In-reply-to")));\r
245 +\r
246 +    printf (", %s: %s",\r
247 +           json_quote_str (local, "References"),\r
248 +           json_quote_str (local, g_mime_object_get_header (GMIME_OBJECT (message), "References")));\r
249 +\r
250 +    talloc_free (local);\r
251 +}\r
252 +\r
253 +static int\r
254 +notmuch_reply_format_json(void *ctx,\r
255 +                         notmuch_config_t *config,\r
256 +                         notmuch_query_t *query,\r
257 +                         notmuch_show_params_t *params,\r
258 +                         notmuch_bool_t reply_all)\r
259 +{\r
260 +    GMimeMessage *reply;\r
261 +    notmuch_messages_t *messages;\r
262 +    notmuch_message_t *message;\r
263 +    mime_node_t *node;\r
264 +\r
265 +    if (notmuch_query_count_messages (query) != 1) {\r
266 +       fprintf (stderr, "Error: search term did not match precisely one message.\n");\r
267 +       return 1;\r
268 +    }\r
269 +\r
270 +    messages = notmuch_query_search_messages (query);\r
271 +    message = notmuch_messages_get (messages);\r
272 +    if (mime_node_open (ctx, message, params->cryptoctx, params->decrypt,\r
273 +                       &node) != NOTMUCH_STATUS_SUCCESS)\r
274 +       return 1;\r
275 +\r
276 +    reply = create_reply_message (ctx, config, message, reply_all);\r
277 +    if (!reply)\r
278 +       return 1;\r
279 +\r
280 +    /* The headers of the reply message we've created */\r
281 +    printf ("{\"reply-headers\": ");\r
282 +    format_reply_headers_json (ctx, reply);\r
283 +    printf ("}");\r
284 +    g_object_unref (G_OBJECT (reply));\r
285 +    reply = NULL;\r
286 +\r
287 +    /* Start the original */\r
288 +    printf (", \"original\": ");\r
289 +\r
290 +    format_part_json (ctx, node, TRUE);\r
291 +\r
292 +    /* End */\r
293 +    printf ("}\n");\r
294 +    notmuch_message_destroy (message);\r
295 +\r
296 +    return 0;\r
297 +}\r
298 +\r
299  /* This format is currently tuned for a git send-email --notmuch hook */\r
300  static int\r
301  notmuch_reply_format_headers_only(void *ctx,\r
302 @@ -646,6 +744,7 @@ notmuch_reply_format_headers_only(void *ctx,\r
303  \r
304  enum {\r
305      FORMAT_DEFAULT,\r
306 +    FORMAT_JSON,\r
307      FORMAT_HEADERS_ONLY,\r
308  };\r
309  \r
310 @@ -665,6 +764,7 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])\r
311      notmuch_opt_desc_t options[] = {\r
312         { NOTMUCH_OPT_KEYWORD, &format, "format", 'f',\r
313           (notmuch_keyword_t []){ { "default", FORMAT_DEFAULT },\r
314 +                                 { "json", FORMAT_JSON },\r
315                                   { "headers-only", FORMAT_HEADERS_ONLY },\r
316                                   { 0, 0 } } },\r
317         { NOTMUCH_OPT_KEYWORD, &reply_all, "reply-to", 'r',\r
318 @@ -683,6 +783,8 @@ notmuch_reply_command (void *ctx, int argc, char *argv[])\r
319  \r
320      if (format == FORMAT_HEADERS_ONLY)\r
321         reply_format_func = notmuch_reply_format_headers_only;\r
322 +    else if (format == FORMAT_JSON)\r
323 +       reply_format_func = notmuch_reply_format_json;\r
324      else\r
325         reply_format_func = notmuch_reply_format_default;\r
326  \r
327 diff --git a/notmuch-show.c b/notmuch-show.c\r
328 index 6a171a4..c570a16 100644\r
329 --- a/notmuch-show.c\r
330 +++ b/notmuch-show.c\r
331 @@ -652,7 +652,7 @@ format_part_text (const void *ctx, mime_node_t *node,\r
332      printf ("\f%s}\n", part_type);\r
333  }\r
334  \r
335 -static void\r
336 +void\r
337  format_part_json (const void *ctx, mime_node_t *node, notmuch_bool_t first)\r
338  {\r
339      /* Any changes to the JSON format should be reflected in the file\r
340 -- \r
341 1.7.5.4\r
342 \r