Re: [PATCH] add has: query prefix to search for specific properties
[notmuch-archives.git] / 48 / 1b79df44f4475ceb6087e50f7f1ab2699ff34c
1 Return-Path: <m.walters@qmul.ac.uk>\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 2A76C431FC7\r
6         for <notmuch@notmuchmail.org>; Thu, 30 Oct 2014 01:10:28 -0700 (PDT)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: -1.098\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=-1.098 tagged_above=-999 required=5\r
12         tests=[DKIM_ADSP_CUSTOM_MED=0.001, FREEMAIL_FROM=0.001,\r
13         NML_ADSP_CUSTOM_MED=1.2, RCVD_IN_DNSWL_MED=-2.3] autolearn=disabled\r
14 Received: from olra.theworths.org ([127.0.0.1])\r
15         by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
16         with ESMTP id q3uYmRTXMrNq for <notmuch@notmuchmail.org>;\r
17         Thu, 30 Oct 2014 01:10:20 -0700 (PDT)\r
18 Received: from mail2.qmul.ac.uk (mail2.qmul.ac.uk [138.37.6.6])\r
19         (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))\r
20         (No client certificate requested)\r
21         by olra.theworths.org (Postfix) with ESMTPS id 2E694431FB6\r
22         for <notmuch@notmuchmail.org>; Thu, 30 Oct 2014 01:10:20 -0700 (PDT)\r
23 Received: from smtp.qmul.ac.uk ([138.37.6.40])\r
24         by mail2.qmul.ac.uk with esmtp (Exim 4.71)\r
25         (envelope-from <m.walters@qmul.ac.uk>)\r
26         id 1Xjko9-0004Oe-9t; Thu, 30 Oct 2014 08:10:18 +0000\r
27 Received: from 5751dfa2.skybroadband.com ([87.81.223.162] helo=localhost)\r
28         by smtp.qmul.ac.uk with esmtpsa (TLSv1:AES128-SHA:128) (Exim 4.71)\r
29         (envelope-from <m.walters@qmul.ac.uk>)\r
30         id 1Xjko8-000887-AY; Thu, 30 Oct 2014 08:10:12 +0000\r
31 From: Mark Walters <markwalters1009@gmail.com>\r
32 To: Michal Sojka <sojkam1@fel.cvut.cz>, notmuch@notmuchmail.org\r
33 Subject: Re: [PATCH v4 4/6] cli: search: Add --output={sender,recipients}\r
34 In-Reply-To: <1414421455-3037-5-git-send-email-sojkam1@fel.cvut.cz>\r
35 References: <1414421455-3037-1-git-send-email-sojkam1@fel.cvut.cz>\r
36         <1414421455-3037-5-git-send-email-sojkam1@fel.cvut.cz>\r
37 User-Agent: Notmuch/0.18.1+86~gef5e66a (http://notmuchmail.org) Emacs/23.4.1\r
38         (x86_64-pc-linux-gnu)\r
39 Date: Thu, 30 Oct 2014 08:10:11 +0000\r
40 Message-ID: <87h9ymugf0.fsf@qmul.ac.uk>\r
41 MIME-Version: 1.0\r
42 Content-Type: text/plain; charset=us-ascii\r
43 X-Sender-Host-Address: 87.81.223.162\r
44 X-QM-Geographic: According to ripencc,\r
45         this message was delivered by a machine in Britain (UK) (GB).\r
46 X-QM-SPAM-Info: Sender has good ham record.  :)\r
47 X-QM-Body-MD5: dce63ec75da32219de800dd1bb597c76 (of first 20000 bytes)\r
48 X-SpamAssassin-Score: -0.1\r
49 X-SpamAssassin-SpamBar: /\r
50 X-SpamAssassin-Report: The QM spam filters have analysed this message to\r
51         determine if it is\r
52         spam. We require at least 5.0 points to mark a message as spam.\r
53         This message scored -0.1 points.\r
54         Summary of the scoring: \r
55         * 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail\r
56         provider *      (markwalters1009[at]gmail.com)\r
57         * -0.1 AWL AWL: From: address is in the auto white-list\r
58 X-QM-Scan-Virus: ClamAV says the message is clean\r
59 X-BeenThere: notmuch@notmuchmail.org\r
60 X-Mailman-Version: 2.1.13\r
61 Precedence: list\r
62 List-Id: "Use and development of the notmuch mail system."\r
63         <notmuch.notmuchmail.org>\r
64 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
65         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
66 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
67 List-Post: <mailto:notmuch@notmuchmail.org>\r
68 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
69 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
70         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
71 X-List-Received-Date: Thu, 30 Oct 2014 08:10:28 -0000\r
72 \r
73 On Mon, 27 Oct 2014, Michal Sojka <sojkam1@fel.cvut.cz> wrote:\r
74 > The new outputs allow printing senders, recipients or both of matching\r
75 > messages. To print both, the user can use --output=sender and\r
76 > --output=recipients simultaneously.\r
77 >\r
78 > Currently, the same address can appear multiple times in the output. The\r
79 > next patch will change this. For this reason, the test are introduced in\r
80 > the next patch as well.\r
81 >\r
82 > We use mailbox_t rather than InternetAddressMailbox because we will need\r
83 > to extend it in a following patch.\r
84 >\r
85 > This code based on a patch from Jani Nikula.\r
86 > ---\r
87 >  completion/notmuch-completion.bash |   2 +-\r
88 >  completion/notmuch-completion.zsh  |   3 +-\r
89 >  doc/man1/notmuch-search.rst        |  22 +++++++-\r
90 >  notmuch-search.c                   | 112 ++++++++++++++++++++++++++++++++++++-\r
91 >  4 files changed, 134 insertions(+), 5 deletions(-)\r
92 >\r
93 > diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash\r
94 > index 0571dc9..cfbd389 100644\r
95 > --- a/completion/notmuch-completion.bash\r
96 > +++ b/completion/notmuch-completion.bash\r
97 > @@ -294,7 +294,7 @@ _notmuch_search()\r
98 >           return\r
99 >           ;;\r
100 >       --output)\r
101 > -         COMPREPLY=( $( compgen -W "summary threads messages files tags" -- "${cur}" ) )\r
102 > +         COMPREPLY=( $( compgen -W "summary threads messages files tags sender recipients" -- "${cur}" ) )\r
103 >           return\r
104 >           ;;\r
105 >       --sort)\r
106 > diff --git a/completion/notmuch-completion.zsh b/completion/notmuch-completion.zsh\r
107 > index 67a9aba..3e52a00 100644\r
108 > --- a/completion/notmuch-completion.zsh\r
109 > +++ b/completion/notmuch-completion.zsh\r
110 > @@ -52,7 +52,8 @@ _notmuch_search()\r
111 >    _arguments -s : \\r
112 >      '--max-threads=[display only the first x threads from the search results]:number of threads to show: ' \\r
113 >      '--first=[omit the first x threads from the search results]:number of threads to omit: ' \\r
114 > -    '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))'\r
115 > +    '--sort=[sort results]:sorting:((newest-first\:"reverse chronological order" oldest-first\:"chronological order"))' \\r
116 > +    '--output=[select what to output]:output:((summary threads messages files tags sender recipients))'\r
117 >  }\r
118 >  \r
119 >  _notmuch()\r
120 > diff --git a/doc/man1/notmuch-search.rst b/doc/man1/notmuch-search.rst\r
121 > index 90160f2..b6607c9 100644\r
122 > --- a/doc/man1/notmuch-search.rst\r
123 > +++ b/doc/man1/notmuch-search.rst\r
124 > @@ -35,7 +35,7 @@ Supported options for **search** include\r
125 >          intended for programs that invoke **notmuch(1)** internally. If\r
126 >          omitted, the latest supported version will be used.\r
127 >  \r
128 > -    ``--output=(summary|threads|messages|files|tags)``\r
129 > +    ``--output=(summary|threads|messages|files|tags|sender|recipients)``\r
130 >  \r
131 >          **summary**\r
132 >              Output a summary of each thread with any message matching\r
133 > @@ -78,6 +78,26 @@ Supported options for **search** include\r
134 >              by null characters (--format=text0), as a JSON array\r
135 >              (--format=json), or as an S-Expression list (--format=sexp).\r
136 >  \r
137 > +     **sender**\r
138 > +            Output all addresses from the *From* header that appear on\r
139 > +            any message matching the search terms, either one per line\r
140 > +            (--format=text), separated by null characters\r
141 > +            (--format=text0), as a JSON array (--format=json), or as\r
142 > +            an S-Expression list (--format=sexp).\r
143 > +\r
144 > +         Note: Searching for **sender** should be much faster than\r
145 > +         searching for **recipients**, because sender addresses are\r
146 > +         cached directly in the database whereas other addresses\r
147 > +         need to be fetched from message files.\r
148 > +\r
149 > +     **recipients**\r
150 > +            Like **sender** but for addresses from *To*, *Cc* and\r
151 > +         *Bcc* headers.\r
152 > +\r
153 > +     This option can be given multiple times to combine different\r
154 > +     outputs. Currently, this is only supported for **sender** and\r
155 > +     **recipients** outputs.\r
156 > +\r
157 >      ``--sort=``\ (**newest-first**\ \|\ **oldest-first**)\r
158 >          This option can be used to present results in either\r
159 >          chronological order (**oldest-first**) or reverse chronological\r
160 > diff --git a/notmuch-search.c b/notmuch-search.c\r
161 > index ce46877..ce3bfb2 100644\r
162 > --- a/notmuch-search.c\r
163 > +++ b/notmuch-search.c\r
164 > @@ -28,8 +28,12 @@ typedef enum {\r
165 >      OUTPUT_MESSAGES  = 1 << 2,\r
166 >      OUTPUT_FILES     = 1 << 3,\r
167 >      OUTPUT_TAGS              = 1 << 4,\r
168 > +    OUTPUT_SENDER    = 1 << 5,\r
169 > +    OUTPUT_RECIPIENTS        = 1 << 6,\r
170 >  } output_t;\r
171 >  \r
172 > +#define OUTPUT_ADDRESS_FLAGS (OUTPUT_SENDER | OUTPUT_RECIPIENTS)\r
173 > +\r
174 >  typedef struct {\r
175 >      sprinter_t *format;\r
176 >      notmuch_query_t *query;\r
177 > @@ -40,6 +44,11 @@ typedef struct {\r
178 >      int dupe;\r
179 >  } search_options_t;\r
180 >  \r
181 > +typedef struct {\r
182 > +    const char *name;\r
183 > +    const char *addr;\r
184 > +} mailbox_t;\r
185 > +\r
186 >  /* Return two stable query strings that identify exactly the matched\r
187 >   * and unmatched messages currently in thread.  If there are no\r
188 >   * matched or unmatched messages, the returned buffers will be\r
189 > @@ -220,6 +229,84 @@ do_search_threads (search_options_t *opt)\r
190 >      return 0;\r
191 >  }\r
192 >  \r
193 > +static void\r
194 > +print_mailbox (const search_options_t *opt, const mailbox_t *mailbox)\r
195 > +{\r
196 > +    const char *name = mailbox->name;\r
197 > +    const char *addr = mailbox->addr;\r
198 > +\r
199 > +    if (opt->format->is_text_printer) {\r
200 > +     char *mailbox_str;\r
201 > +\r
202 > +     if (name && *name)\r
203 > +         mailbox_str = talloc_asprintf (opt->format, "%s <%s>", name, addr);\r
204 > +     else\r
205 > +         mailbox_str = talloc_strdup (opt->format, addr);\r
206 > +\r
207 > +     if (! mailbox_str) {\r
208 > +         fprintf (stderr, "Error: out of memory\n");\r
209 > +         return;\r
210 > +     }\r
211 > +     opt->format->string (opt->format, mailbox_str);\r
212 > +     opt->format->separator (opt->format);\r
213 > +\r
214 > +     talloc_free (mailbox_str);\r
215 > +    } else {\r
216 > +     opt->format->begin_map (opt->format);\r
217 > +     opt->format->map_key (opt->format, "name");\r
218 > +     opt->format->string (opt->format, name);\r
219 > +     opt->format->map_key (opt->format, "address");\r
220 > +     opt->format->string (opt->format, addr);\r
221 > +     opt->format->end (opt->format);\r
222 > +     opt->format->separator (opt->format);\r
223 > +    }\r
224 > +}\r
225 \r
226 How about letting format = opt->format in all the above. I think it\r
227 would improve readability and look more similar to the other cases.\r
228 \r
229 > +\r
230 > +static void\r
231 > +process_address_list (const search_options_t *opt, InternetAddressList *list)\r
232 > +{\r
233 \r
234 It might be worth documenting these functions: just something  brief\r
235 saying what arguments are expected and what is returned.\r
236 \r
237 Otherwise LGTM.\r
238 \r
239 Best wishes\r
240 \r
241 Mark\r
242 \r
243 \r
244 > +    InternetAddress *address;\r
245 > +    int i;\r
246 > +\r
247 > +    for (i = 0; i < internet_address_list_length (list); i++) {\r
248 > +     address = internet_address_list_get_address (list, i);\r
249 > +     if (INTERNET_ADDRESS_IS_GROUP (address)) {\r
250 > +         InternetAddressGroup *group;\r
251 > +         InternetAddressList *group_list;\r
252 > +\r
253 > +         group = INTERNET_ADDRESS_GROUP (address);\r
254 > +         group_list = internet_address_group_get_members (group);\r
255 > +         if (group_list == NULL)\r
256 > +             continue;\r
257 > +\r
258 > +         process_address_list (opt, group_list);\r
259 > +     } else {\r
260 > +         InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX (address);\r
261 > +         mailbox_t mbx = {\r
262 > +             .name = internet_address_get_name (address),\r
263 > +             .addr = internet_address_mailbox_get_addr (mailbox),\r
264 > +         };\r
265 > +\r
266 > +         print_mailbox (opt, &mbx);\r
267 > +     }\r
268 > +    }\r
269 > +}\r
270 > +\r
271 > +static void\r
272 > +process_address_header (const search_options_t *opt, const char *value)\r
273 > +{\r
274 > +    InternetAddressList *list;\r
275 > +\r
276 > +    if (value == NULL)\r
277 > +     return;\r
278 > +\r
279 > +    list = internet_address_list_parse_string (value);\r
280 > +    if (list == NULL)\r
281 > +     return;\r
282 > +\r
283 > +    process_address_list (opt, list);\r
284 > +}\r
285 > +\r
286 >  static int\r
287 >  do_search_messages (search_options_t *opt)\r
288 >  {\r
289 > @@ -266,11 +353,29 @@ do_search_messages (search_options_t *opt)\r
290 >           \r
291 >           notmuch_filenames_destroy( filenames );\r
292 >  \r
293 > -     } else { /* output == OUTPUT_MESSAGES */\r
294 > +     } else if (opt->output == OUTPUT_MESSAGES) {\r
295 >           format->set_prefix (format, "id");\r
296 >           format->string (format,\r
297 >                           notmuch_message_get_message_id (message));\r
298 >           format->separator (format);\r
299 > +     } else {\r
300 > +         if (opt->output & OUTPUT_SENDER) {\r
301 > +             const char *addrs;\r
302 > +\r
303 > +             addrs = notmuch_message_get_header (message, "from");\r
304 > +             process_address_header (opt, addrs);\r
305 > +         }\r
306 > +\r
307 > +         if (opt->output & OUTPUT_RECIPIENTS) {\r
308 > +             const char *hdrs[] = { "to", "cc", "bcc" };\r
309 > +             const char *addrs;\r
310 > +             size_t j;\r
311 > +\r
312 > +             for (j = 0; j < ARRAY_SIZE (hdrs); j++) {\r
313 > +                 addrs = notmuch_message_get_header (message, hdrs[j]);\r
314 > +                 process_address_header (opt, addrs);\r
315 > +             }\r
316 > +         }\r
317 >       }\r
318 >  \r
319 >       notmuch_message_destroy (message);\r
320 > @@ -371,6 +476,8 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])\r
321 >         (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },\r
322 >                                 { "threads", OUTPUT_THREADS },\r
323 >                                 { "messages", OUTPUT_MESSAGES },\r
324 > +                               { "sender", OUTPUT_SENDER },\r
325 > +                               { "recipients", OUTPUT_RECIPIENTS },\r
326 >                                 { "files", OUTPUT_FILES },\r
327 >                                 { "tags", OUTPUT_TAGS },\r
328 >                                 { 0, 0 } } },\r
329 > @@ -462,7 +569,8 @@ notmuch_search_command (notmuch_config_t *config, int argc, char *argv[])\r
330 >       opt.output == OUTPUT_THREADS)\r
331 >       ret = do_search_threads (&opt);\r
332 >      else if (opt.output == OUTPUT_MESSAGES ||\r
333 > -          opt.output == OUTPUT_FILES)\r
334 > +          opt.output == OUTPUT_FILES ||\r
335 > +          (opt.output & OUTPUT_ADDRESS_FLAGS && !(opt.output & ~OUTPUT_ADDRESS_FLAGS)))\r
336 >       ret = do_search_messages (&opt);\r
337 >      else if (opt.output == OUTPUT_TAGS)\r
338 >       ret = do_search_tags (notmuch, &opt);\r
339 > -- \r
340 > 2.1.1\r
341 >\r
342 > _______________________________________________\r
343 > notmuch mailing list\r
344 > notmuch@notmuchmail.org\r
345 > http://notmuchmail.org/mailman/listinfo/notmuch\r