1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 2003-2021 Free Software Foundation, Inc.
3 
4    GNU Mailutils is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    GNU Mailutils is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 /* MH pick command */
18 
19 #include <mh.h>
20 #include <regex.h>
21 #include <pick.h>
22 #include <pick-gram.h>
23 
24 static char prog_doc[] = N_("Search for messages by content");
25 static char args_doc[] = N_("[--COMPONENT PATTERN]... [MSGLIST]");
26 
27 static int public_option = 1;
28 static int zero_option = 0;
29 
30 static int list = 1;
31 static int seq_flags = 0; /* Create public sequences;
32 			     Do not zero the sequence before addition */
33 static mu_list_t seq_list;  /* List of sequence names to operate upon */
34 
35 static mu_list_t lexlist;   /* List of input tokens */
36 
37 static mu_msgset_t picked_message_uids;
38 
39 static void
add_component_name(struct mu_parseopt * po,struct mu_option * opt,char const * arg)40 add_component_name (struct mu_parseopt *po, struct mu_option *opt,
41 		    char const *arg)
42 {
43   pick_add_token (&lexlist, T_COMP, arg);
44 }
45 
46 static void
add_component_pattern(struct mu_parseopt * po,struct mu_option * opt,char const * arg)47 add_component_pattern (struct mu_parseopt *po, struct mu_option *opt,
48 		       char const *arg)
49 {
50   pick_add_token (&lexlist, T_STRING, arg);
51 }
52 
53 static void
add_cflags(struct mu_parseopt * po,struct mu_option * opt,char const * arg)54 add_cflags (struct mu_parseopt *po, struct mu_option *opt,
55 	    char const *arg)
56 {
57   pick_add_token (&lexlist, T_CFLAGS, arg);
58 }
59 
60 static void
add_comp_match(struct mu_parseopt * po,struct mu_option * opt,char const * arg)61 add_comp_match (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
62 {
63   pick_add_token (&lexlist, T_COMP, opt->opt_default);
64   pick_add_token (&lexlist, T_STRING, arg);
65 }
66 
67 static void
add_datefield(struct mu_parseopt * po,struct mu_option * opt,char const * arg)68 add_datefield (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
69 {
70   pick_add_token (&lexlist, T_DATEFIELD, arg);
71 }
72 
73 static void
add_after(struct mu_parseopt * po,struct mu_option * opt,char const * arg)74 add_after (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
75 {
76   pick_add_token (&lexlist, T_AFTER, NULL);
77   pick_add_token (&lexlist, T_STRING, arg);
78 }
79 
80 static void
add_before(struct mu_parseopt * po,struct mu_option * opt,char const * arg)81 add_before (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
82 {
83   pick_add_token (&lexlist, T_BEFORE, NULL);
84   pick_add_token (&lexlist, T_STRING, arg);
85 }
86 
87 static void
add_and(struct mu_parseopt * po,struct mu_option * opt,char const * arg)88 add_and (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
89 {
90   pick_add_token (&lexlist, T_AND, NULL);
91 }
92 
93 static void
add_or(struct mu_parseopt * po,struct mu_option * opt,char const * arg)94 add_or (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
95 {
96   pick_add_token (&lexlist, T_OR, NULL);
97 }
98 
99 static void
add_not(struct mu_parseopt * po,struct mu_option * opt,char const * arg)100 add_not (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
101 {
102   pick_add_token (&lexlist, T_NOT, NULL);
103 }
104 
105 static void
add_lbrace(struct mu_parseopt * po,struct mu_option * opt,char const * arg)106 add_lbrace (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
107 {
108   pick_add_token (&lexlist, T_LBRACE, NULL);
109 }
110 
111 static void
add_rbrace(struct mu_parseopt * po,struct mu_option * opt,char const * arg)112 add_rbrace (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
113 {
114   pick_add_token (&lexlist, T_RBRACE, NULL);
115 }
116 
117 static void
add_to_sequence(struct mu_parseopt * po,struct mu_option * opt,char const * arg)118 add_to_sequence (struct mu_parseopt *po, struct mu_option *opt,
119 		 char const *arg)
120 {
121   if (!seq_list && mu_list_create (&seq_list))
122     {
123       mu_error (_("cannot create sequence list"));
124       exit (1);
125     }
126   mu_list_append (seq_list, mu_strdup (arg));
127   list = 0;
128 }
129 
130 static struct mu_option options[] = {
131   MU_OPTION_GROUP (N_("Search patterns")),
132   { "component", 0, N_("FIELD"), MU_OPTION_DEFAULT,
133     N_("search the named header field"),
134     mu_c_string, NULL, add_component_name },
135   { "pattern",   0, N_("STRING"), MU_OPTION_DEFAULT,
136     N_("set pattern to look for"),
137     mu_c_string, NULL, add_component_pattern },
138   { "search",    0, NULL, MU_OPTION_ALIAS },
139   { "cflags",    0,  N_("STRING"), MU_OPTION_DEFAULT,
140     N_("flags controlling the type of regular expressions. "
141        "STRING must consist of one or more of the following letters: "
142        "B=basic, E=extended, I=ignore case, C=case sensitive. "
143        "Default is \"EI\". The flags remain in effect until the next "
144        "occurrence of --cflags option. The option must occur right "
145        "before --pattern or --component option (or its alias)."),
146     mu_c_string, NULL, add_cflags },
147   { "cc",        0,  N_("STRING"), MU_OPTION_DEFAULT,
148     N_("search for Cc matching STRING"),
149     mu_c_string, NULL, add_comp_match, "cc" },
150   { "date",      0,  N_("STRING"), MU_OPTION_DEFAULT,
151     N_("search for Date matching STRING"),
152     mu_c_string, NULL, add_comp_match, "date" },
153   { "from",      0,  N_("STRING"), MU_OPTION_DEFAULT,
154     N_("search for From matching STRING"),
155     mu_c_string, NULL, add_comp_match, "from" },
156   { "subject",   0,  N_("STRING"), MU_OPTION_DEFAULT,
157     N_("search for Subject matching STRING"),
158     mu_c_string, NULL, add_comp_match, "subject" },
159   { "to",        0,  N_("STRING"), MU_OPTION_DEFAULT,
160     N_("search for To matching STRING"),
161     mu_c_string, NULL, add_comp_match, "to" },
162 
163   MU_OPTION_GROUP (N_("Date constraint operations")),
164   { "datefield", 0, N_("STRING"), MU_OPTION_DEFAULT,
165     N_("search in the named date header field (default is `Date:')"),
166     mu_c_string, NULL, add_datefield },
167   { "after",     0, N_("DATE"), MU_OPTION_DEFAULT,
168     N_("match messages after the given date"),
169     mu_c_string, NULL, add_after },
170   { "before",    0, N_("DATE"), MU_OPTION_DEFAULT,
171     N_("match messages before the given date"),
172     mu_c_string, NULL, add_before },
173 
174   MU_OPTION_GROUP (N_("Logical operations and grouping")),
175   { "and",       0, NULL, MU_OPTION_DEFAULT,
176     N_("logical AND (default)"),
177     mu_c_string, NULL, add_and },
178   { "or",        0, NULL, MU_OPTION_DEFAULT,
179     N_("logical OR"),
180     mu_c_string, NULL, add_or },
181   { "not",       0, NULL, MU_OPTION_DEFAULT,
182     N_("logical NOT"),
183     mu_c_string, NULL, add_not },
184   { "lbrace",    0, NULL, MU_OPTION_DEFAULT,
185     N_("open group"),
186     mu_c_string, NULL, add_lbrace },
187   { "(",         0, NULL, MU_OPTION_ALIAS },
188   { "rbrace",    0, NULL, MU_OPTION_DEFAULT,
189     N_("close group"),
190     mu_c_string, NULL, add_rbrace },
191   { ")",         0, NULL, MU_OPTION_ALIAS },
192 
193   MU_OPTION_GROUP (N_("Operations over the selected messages")),
194   { "list",      0, NULL, MU_OPTION_DEFAULT,
195     N_("list the numbers of the selected messages (default)"),
196     mu_c_bool, &list },
197   { "sequence",  0, N_("NAME"), MU_OPTION_DEFAULT,
198     N_("add matching messages to the given sequence"),
199     mu_c_string, NULL, add_to_sequence },
200   { "public",    0, NULL,       MU_OPTION_DEFAULT,
201     N_("create public sequence"),
202     mu_c_bool, &public_option },
203   { "zero",      0, NULL,       MU_OPTION_DEFAULT,
204     N_("empty the sequence before adding messages"),
205     mu_c_bool, &zero_option },
206 
207   MU_OPTION_END
208 
209 };
210 
211 static int
pick_message(size_t num,mu_message_t msg,void * data)212 pick_message (size_t num, mu_message_t msg, void *data)
213 {
214   if (pick_eval (msg))
215     {
216       mh_message_number (msg, &num);
217       if (list)
218 	printf ("%s\n", mu_umaxtostr (0, num));
219       if (picked_message_uids)
220 	mu_msgset_add_range (picked_message_uids, num, num, MU_MSGSET_UID);
221     }
222   return 0;
223 }
224 
225 
226 static int
action_add(void * item,void * data)227 action_add (void *item, void *data)
228 {
229   mu_mailbox_t mbox = data;
230   mh_seq_add (mbox, (char *)item, picked_message_uids, seq_flags);
231   return 0;
232 }
233 
234 int
main(int argc,char ** argv)235 main (int argc, char **argv)
236 {
237   int i, status;
238   mu_mailbox_t mbox;
239   mu_msgset_t msgset;
240   int argv_alloc = 0;
241 
242   /* Expand eventual --COMPONENT NAME pairs */
243   for (i = 1; i < argc;)
244     {
245       if (strncmp (argv[i], "--", 2) == 0 && argv[i][3] && i + 1 < argc)
246 	{
247 	  size_t n = argc + 2;
248 	  if (argv_alloc)
249 	    argv = mu_realloc (argv, (n + 1) * sizeof (argv[0]));
250 	  else
251 	    {
252 	      char **p = mu_calloc (n + 1, sizeof p[0]);
253 	      memcpy (p, argv, (argc + 1) * sizeof argv[0]);
254 	      argv = p;
255 	      argv_alloc = 1;
256 	    }
257 
258 	  memmove (argv + i + 4, argv + i + 2,
259 		   (argc - i - 1) * sizeof argv[0]);
260 	  argv[i + 3] = argv[i + 1];
261 	  argv[i + 2] = mu_strdup ("-pattern");
262 	  argv[i + 1] = mu_strdup (argv[i] + 2);
263 	  argv[i] = mu_strdup ("-component");
264 	  argc = n;
265 	  i += 4;
266 	}
267       else
268 	i++;
269     }
270 
271   mh_getopt (&argc, &argv, options, MH_GETOPT_DEFAULT_FOLDER,
272 	     args_doc, prog_doc, NULL);
273 
274   if (pick_parse (lexlist))
275     return 1;
276 
277   seq_flags = (public_option ? 0 : SEQ_PRIVATE) | (zero_option ? SEQ_ZERO : 0);
278 
279   mbox = mh_open_folder (mh_current_folder (),
280 			 seq_list ? MU_STREAM_RDWR : MU_STREAM_READ);
281 
282   if (seq_list)
283     mu_msgset_create (&picked_message_uids, NULL, MU_MSGSET_UID);
284 
285   mh_msgset_parse (&msgset, mbox, argc, argv, "all");
286   status = mu_msgset_foreach_message (msgset, pick_message, NULL);
287 
288   if (picked_message_uids)
289     mu_list_foreach (seq_list, action_add, mbox);
290 
291   mh_global_save_state ();
292   mu_mailbox_close (mbox);
293   mu_mailbox_destroy (&mbox);
294   return status;
295 }
296 
297