1 /* Copyright (c) 2016-2018 Dovecot authors, see the included COPYING file */
2 
3 #include "lib.h"
4 #include "ioloop.h"
5 #include "array.h"
6 #include "str.h"
7 #include "imap-date.h"
8 #include "imap-seqset.h"
9 #include "imap-utf7.h"
10 #include "imap-util.h"
11 #include "mail-search-parser.h"
12 #include "mail-search-mime-register.h"
13 #include "mail-search-mime-build.h"
14 #include "mail-search-mime.h"
15 
16 struct mail_search_mime_register {
17 	ARRAY(struct mail_search_mime_register_arg) args;
18 
19 	bool args_sorted:1;
20 };
21 
22 struct mail_search_mime_register *mail_search_mime_register = NULL;
23 
24 static void
25 mail_search_register_add_default(void);
26 
27 /*
28  * Register
29  */
30 
31 static struct mail_search_mime_register *
mail_search_mime_register_init(void)32 mail_search_mime_register_init(void)
33 {
34 	struct mail_search_mime_register *reg =
35 		mail_search_mime_register;
36 	if (reg == NULL) {
37 		reg = i_new(struct mail_search_mime_register, 1);
38 		i_array_init(&reg->args, 64);
39 
40 		mail_search_mime_register = reg;
41 		mail_search_register_add_default();
42 	}
43 
44 	return reg;
45 }
46 
mail_search_mime_register_deinit(void)47 void mail_search_mime_register_deinit(void)
48 {
49 	struct mail_search_mime_register *reg =
50 		mail_search_mime_register;
51 
52 	mail_search_mime_register = NULL;
53 	if (reg == NULL)
54 		return;
55 
56 	array_free(&reg->args);
57 	i_free(reg);
58 }
59 
mail_search_mime_register_add(const struct mail_search_mime_register_arg * arg,unsigned int count)60 void mail_search_mime_register_add(
61 			      const struct mail_search_mime_register_arg *arg,
62 			      unsigned int count)
63 {
64 	struct mail_search_mime_register *reg =
65 		mail_search_mime_register_init();
66 
67 	array_append(&reg->args, arg, count);
68 	reg->args_sorted = FALSE;
69 }
70 
71 static int
mail_search_mime_register_arg_cmp(const struct mail_search_mime_register_arg * arg1,const struct mail_search_mime_register_arg * arg2)72 mail_search_mime_register_arg_cmp(
73 	const struct mail_search_mime_register_arg *arg1,
74 	const struct mail_search_mime_register_arg *arg2)
75 {
76 	return strcmp(arg1->key, arg2->key);
77 }
78 
79 const struct mail_search_mime_register_arg *
mail_search_mime_register_get(unsigned int * count_r)80 mail_search_mime_register_get(unsigned int *count_r)
81 {
82 	struct mail_search_mime_register *reg =
83 		mail_search_mime_register_init();
84 
85 	if (!reg->args_sorted) {
86 		array_sort(&reg->args, mail_search_mime_register_arg_cmp);
87 		reg->args_sorted = TRUE;
88 	}
89 
90 	return array_get(&reg->args, count_r);
91 }
92 
93 const struct mail_search_mime_register_arg *
mail_search_mime_register_find(const char * key)94 mail_search_mime_register_find(const char *key)
95 {
96 	struct mail_search_mime_register_arg arg;
97 	struct mail_search_mime_register *reg =
98 		mail_search_mime_register_init();
99 
100 	if (!reg->args_sorted) {
101 		array_sort(&reg->args, mail_search_mime_register_arg_cmp);
102 		reg->args_sorted = TRUE;
103 	}
104 
105 	arg.key = key;
106 	return array_bsearch(&reg->args, &arg, mail_search_mime_register_arg_cmp);
107 }
108 
109 /*
110  * Default MIMEPART args
111  */
112 
113 static struct mail_search_mime_arg *
mail_search_mime_not(struct mail_search_mime_build_context * ctx)114 mail_search_mime_not(struct mail_search_mime_build_context *ctx)
115 {
116 	struct mail_search_mime_arg *smarg;
117 
118 	if (mail_search_mime_build_key(ctx, ctx->parent, &smarg) < 0)
119 		return NULL;
120 
121 	smarg->match_not = !smarg->match_not;
122 	return smarg;
123 }
124 
125 static struct mail_search_mime_arg *
mail_search_mime_or(struct mail_search_mime_build_context * ctx)126 mail_search_mime_or(struct mail_search_mime_build_context *ctx)
127 {
128 	struct mail_search_mime_arg *smarg, **subargs;
129 
130 	/* <search-key1> <search-key2> */
131 	smarg = mail_search_mime_build_new(ctx, SEARCH_MIME_OR);
132 
133 	subargs = &smarg->value.subargs;
134 	do {
135 		if (mail_search_mime_build_key(ctx, smarg, subargs) < 0)
136 			return NULL;
137 		subargs = &(*subargs)->next;
138 
139 		/* <key> OR <key> OR ... <key> - put them all
140 		   under one SEARCH_MIME_OR list. */
141 	} while (mail_search_parse_skip_next(ctx->ctx->parser, "OR"));
142 
143 	if (mail_search_mime_build_key(ctx, smarg, subargs) < 0)
144 		return NULL;
145 	return smarg;
146 }
147 
148 #define CALLBACK_STR(_func, _type) \
149 static struct mail_search_mime_arg *\
150 mail_search_mime_##_func(struct mail_search_mime_build_context *ctx) \
151 { \
152 	return mail_search_mime_build_str(ctx, _type); \
153 }
154 
155 static struct mail_search_mime_arg *
arg_new_date(struct mail_search_mime_build_context * ctx,enum mail_search_mime_arg_type type)156 arg_new_date(struct mail_search_mime_build_context *ctx,
157 	     enum mail_search_mime_arg_type type)
158 {
159 	struct mail_search_mime_arg *smarg;
160 	const char *value;
161 
162 	smarg = mail_search_mime_build_new(ctx, type);
163 	if (mail_search_parse_string(ctx->ctx->parser, &value) < 0)
164 		return NULL;
165 	if (!imap_parse_date(value, &smarg->value.time)) {
166 		ctx->ctx->_error = "Invalid search date parameter";
167 		return NULL;
168 	}
169 	return smarg;
170 }
171 
172 #define CALLBACK_DATE(_func, _type) \
173 static struct mail_search_mime_arg *\
174 mail_search_mime_##_func(struct mail_search_mime_build_context *ctx) \
175 { \
176 	return arg_new_date(ctx, _type); \
177 }
CALLBACK_DATE(sentbefore,SEARCH_MIME_SENTBEFORE)178 CALLBACK_DATE(sentbefore, SEARCH_MIME_SENTBEFORE)
179 CALLBACK_DATE(senton, SEARCH_MIME_SENTON)
180 CALLBACK_DATE(sentsince, SEARCH_MIME_SENTSINCE)
181 
182 static struct mail_search_mime_arg *
183 mail_search_mime_size(struct mail_search_mime_build_context *ctx)
184 {
185 	struct mail_search_mime_arg *smarg;
186 	enum mail_search_mime_arg_type type;
187 	const char *key, *value;
188 	uoff_t size;
189 
190 	if (mail_search_parse_key(ctx->ctx->parser, &key) <= 0) {
191 		ctx->ctx->_error = "Invalid MIMEPART SIZE key type";
192 		return NULL;
193 	}
194 
195 	key = t_str_ucase(key);
196 	if (strcmp(key, "LARGER") == 0)
197 		type = SEARCH_MIME_SIZE_LARGER;
198 	else 	if (strcmp(key, "SMALLER") == 0)
199 		type = SEARCH_MIME_SIZE_SMALLER;
200 	else {
201 		type = SEARCH_MIME_SIZE_EQUAL;
202 		value = key;
203 	}
204 
205 	if (type != SEARCH_MIME_SIZE_EQUAL &&
206 		mail_search_parse_string(ctx->ctx->parser, &value) < 0) {
207 		ctx->ctx->_error = "Invalid MIMEPART SIZE value";
208 		return NULL;
209 	}
210 
211 	if (str_to_uoff(value, &size) < 0) {
212 		ctx->ctx->_error = "Invalid MIMEPART SIZE value";
213 		return NULL;
214 	}
215 
216 	smarg = mail_search_mime_build_new(ctx, type);
217 	smarg->value.size = size;
218 	return smarg;
219 }
220 
CALLBACK_STR(description,SEARCH_MIME_DESCRIPTION)221 CALLBACK_STR(description, SEARCH_MIME_DESCRIPTION)
222 CALLBACK_STR(encoding, SEARCH_MIME_ENCODING)
223 CALLBACK_STR(id, SEARCH_MIME_ID)
224 CALLBACK_STR(language, SEARCH_MIME_LANGUAGE)
225 CALLBACK_STR(location, SEARCH_MIME_LOCATION)
226 CALLBACK_STR(md5, SEARCH_MIME_MD5)
227 
228 CALLBACK_STR(type, SEARCH_MIME_TYPE)
229 CALLBACK_STR(subtype, SEARCH_MIME_SUBTYPE)
230 
231 CALLBACK_STR(bcc, SEARCH_MIME_BCC)
232 CALLBACK_STR(cc, SEARCH_MIME_CC)
233 CALLBACK_STR(from, SEARCH_MIME_FROM)
234 CALLBACK_STR(in_reply_to, SEARCH_MIME_IN_REPLY_TO)
235 CALLBACK_STR(message_id, SEARCH_MIME_MESSAGE_ID)
236 CALLBACK_STR(reply_to, SEARCH_MIME_REPLY_TO)
237 CALLBACK_STR(sender, SEARCH_MIME_SENDER)
238 CALLBACK_STR(subject, SEARCH_MIME_SUBJECT)
239 CALLBACK_STR(to, SEARCH_MIME_TO)
240 
241 static struct mail_search_mime_arg *
242 arg_new_field(struct mail_search_mime_build_context *ctx,
243 	enum mail_search_mime_arg_type type)
244 {
245 	struct mail_search_mime_arg *smarg;
246 	const char *field_name, *value;
247 
248 	/* <field-name> <string> */
249 	if (mail_search_parse_string(ctx->ctx->parser, &field_name) < 0)
250 		return NULL;
251 	if (mail_search_build_get_utf8(ctx->ctx, field_name, &field_name) < 0)
252 		return NULL;
253 	if (mail_search_parse_string(ctx->ctx->parser, &value) < 0)
254 		return NULL;
255 	if (mail_search_build_get_utf8(ctx->ctx, value, &value) < 0)
256 		return NULL;
257 
258 	smarg = mail_search_mime_build_new(ctx, type);
259 	smarg->field_name = str_ucase(p_strdup(ctx->ctx->pool, field_name));
260 	smarg->value.str = value;
261 
262 	return smarg;
263 }
264 
265 static struct mail_search_mime_arg *
mail_search_mime_param(struct mail_search_mime_build_context * ctx)266 mail_search_mime_param(struct mail_search_mime_build_context *ctx)
267 {
268 	return arg_new_field
269 		(ctx, SEARCH_MIME_PARAM);
270 }
271 
272 static struct mail_search_mime_arg *
mail_search_mime_header(struct mail_search_mime_build_context * ctx)273 mail_search_mime_header(struct mail_search_mime_build_context *ctx)
274 {
275 	return arg_new_field
276 		(ctx, SEARCH_MIME_HEADER);
277 }
278 
279 static struct mail_search_mime_arg *
arg_new_body(struct mail_search_mime_build_context * ctx,enum mail_search_mime_arg_type type)280 arg_new_body(struct mail_search_mime_build_context *ctx,
281 	     enum mail_search_mime_arg_type type)
282 {
283 	struct mail_search_mime_arg *smarg;
284 
285 	smarg = mail_search_mime_build_str(ctx, type);
286 	if (smarg == NULL)
287 		return NULL;
288 
289 	if (mail_search_build_get_utf8(ctx->ctx, smarg->value.str,
290 				       &smarg->value.str) < 0)
291 		return NULL;
292 	return smarg;
293 }
294 
295 #define CALLBACK_BODY(_func, _type) \
296 static struct mail_search_mime_arg *\
297 mail_search_mime_##_func(struct mail_search_mime_build_context *ctx) \
298 { \
299 	return arg_new_body(ctx, _type); \
300 }
CALLBACK_BODY(body,SEARCH_MIME_BODY)301 CALLBACK_BODY(body, SEARCH_MIME_BODY)
302 CALLBACK_BODY(text, SEARCH_MIME_TEXT)
303 
304 static struct mail_search_mime_arg *
305 mail_search_mime_disposition(struct mail_search_mime_build_context *ctx)
306 {
307 	struct mail_search_mime_arg *smarg;
308 	const char *key, *value;
309 
310 	if (mail_search_parse_key(ctx->ctx->parser, &key) <= 0) {
311 		ctx->ctx->_error = "Invalid MIMEPART DISPOSITION key type";
312 		return NULL;
313 	}
314 
315 	key = t_str_ucase(key);
316 	if (strcmp(key, "TYPE") == 0) {
317 		if (mail_search_parse_string(ctx->ctx->parser, &value) < 0) {
318 			ctx->ctx->_error = "Invalid MIMEPART DISPOSITION TYPE value";
319 			return NULL;
320 		}
321 		smarg = mail_search_mime_build_new
322 			(ctx, SEARCH_MIME_DISPOSITION_TYPE);
323 		smarg->value.str = p_strdup(ctx->ctx->pool, value);
324 		return smarg;
325 	} else 	if (strcmp(key, "PARAM") == 0) {
326 		return arg_new_field
327 			(ctx, SEARCH_MIME_DISPOSITION_PARAM);
328 	}
329 
330 	ctx->ctx->_error = "Invalid MIMEPART DISPOSITION key type";
331 	return NULL;
332 }
333 
334 static struct mail_search_mime_arg *
mail_search_mime_depth(struct mail_search_mime_build_context * ctx)335 mail_search_mime_depth(struct mail_search_mime_build_context *ctx)
336 {
337 	struct mail_search_mime_arg *smarg;
338 	enum mail_search_mime_arg_type type;
339 	const char *key, *value;
340 	unsigned int depth;
341 
342 	if (mail_search_parse_key(ctx->ctx->parser, &key) <= 0) {
343 		ctx->ctx->_error = "Invalid MIMEPART DEPTH key";
344 		return NULL;
345 	}
346 
347 	key = t_str_ucase(key);
348 	if (strcmp(key, "MIN") == 0)
349 		type = SEARCH_MIME_DEPTH_MIN;
350 	else 	if (strcmp(key, "MAX") == 0)
351 		type = SEARCH_MIME_DEPTH_MAX;
352 	else {
353 		type = SEARCH_MIME_DEPTH_EQUAL;
354 		value = key;
355 	}
356 
357 	if (type != SEARCH_MIME_DEPTH_EQUAL &&
358 		mail_search_parse_string(ctx->ctx->parser, &value) < 0) {
359 		ctx->ctx->_error = "Invalid MIMEPART DEPTH value";
360 		return NULL;
361 	}
362 
363 	if (str_to_uint(value, &depth) < 0) {
364 		ctx->ctx->_error = "Invalid MIMEPART DEPTH level";
365 		return NULL;
366 	}
367 
368 	smarg = mail_search_mime_build_new(ctx, type);
369 	smarg->value.number = depth;
370 	return smarg;
371 }
372 
373 static struct mail_search_mime_arg *
mail_search_mime_index(struct mail_search_mime_build_context * ctx)374 mail_search_mime_index(struct mail_search_mime_build_context *ctx)
375 {
376 	struct mail_search_mime_arg *smarg;
377 	const char *value;
378 	unsigned int index;
379 
380 	if (mail_search_parse_string(ctx->ctx->parser, &value) < 0) {
381 		ctx->ctx->_error = "Invalid MIMEPART INDEX value";
382 		return NULL;
383 	}
384 
385 	if (str_to_uint(value, &index) < 0) {
386 		ctx->ctx->_error = "Invalid MIMEPART INDEX number";
387 		return NULL;
388 	}
389 
390 	smarg = mail_search_mime_build_new
391 		(ctx, SEARCH_MIME_INDEX);
392 	smarg->value.number = index;
393 	return smarg;
394 }
395 
396 static struct mail_search_mime_arg *
mail_search_mime_filename(struct mail_search_mime_build_context * ctx)397 mail_search_mime_filename(struct mail_search_mime_build_context *ctx)
398 {
399 	struct mail_search_mime_arg *smarg;
400 	enum mail_search_mime_arg_type type;
401 	const char *key, *value;
402 
403 	if (mail_search_parse_key(ctx->ctx->parser, &key) <= 0) {
404 		ctx->ctx->_error = "Invalid MIMEPART FILENAME match type";
405 		return NULL;
406 	}
407 
408 	key = t_str_ucase(key);
409 	if (strcmp(key, "IS") == 0)
410 		type = SEARCH_MIME_FILENAME_IS;
411 	else 	if (strcmp(key, "CONTAINS") == 0)
412 		type = SEARCH_MIME_FILENAME_CONTAINS;
413 	else 	if (strcmp(key, "BEGINS") == 0)
414 		type = SEARCH_MIME_FILENAME_BEGINS;
415 	else 	if (strcmp(key, "ENDS") == 0)
416 		type = SEARCH_MIME_FILENAME_ENDS;
417 	else {
418 		ctx->ctx->_error = "Invalid MIMEPART FILENAME match type";
419 		return NULL;
420 	}
421 
422 	if (mail_search_parse_string(ctx->ctx->parser, &value) < 0) {
423 		ctx->ctx->_error = "Invalid MIMEPART FILENAME string value";
424 		return NULL;
425 	}
426 
427 	if (mail_search_build_get_utf8(ctx->ctx, value, &value) < 0) {
428 		ctx->ctx->_error = "Invalid MIMEPART FILENAME stromg value";
429 		return NULL;
430 	}
431 
432 	smarg = mail_search_mime_build_new(ctx, type);
433 	smarg->value.str = value;
434 	return smarg;
435 }
436 
437 static struct mail_search_mime_arg *
mail_search_mime_parent(struct mail_search_mime_build_context * ctx)438 mail_search_mime_parent(struct mail_search_mime_build_context *ctx)
439 {
440 	struct mail_search_mime_arg *smarg, *subargs;
441 
442 	smarg = mail_search_mime_build_new(ctx, SEARCH_MIME_PARENT);
443 	if (mail_search_mime_build_key(ctx, smarg, &subargs) < 0)
444 		return NULL;
445 	if (subargs == smarg)
446 		smarg->value.subargs = NULL;
447 	else if (subargs->type == SEARCH_MIME_SUB)
448 		smarg->value.subargs = subargs->value.subargs;
449 	else
450 		smarg->value.subargs = subargs;
451 	return smarg;
452 }
453 
454 static struct mail_search_mime_arg *
mail_search_mime_child(struct mail_search_mime_build_context * ctx)455 mail_search_mime_child(struct mail_search_mime_build_context *ctx)
456 {
457 	struct mail_search_mime_arg *smarg, *subargs;
458 
459 	smarg = mail_search_mime_build_new(ctx, SEARCH_MIME_CHILD);
460 	if (mail_search_mime_build_key(ctx, smarg, &subargs) < 0)
461 		return NULL;
462 	if (subargs == smarg)
463 		smarg->value.subargs = NULL;
464 	else if (subargs->type == SEARCH_MIME_SUB)
465 		smarg->value.subargs = subargs->value.subargs;
466 	else
467 		smarg->value.subargs = subargs;
468 	return smarg;
469 }
470 
471 static struct mail_search_mime_arg *
mail_search_mime_exists(struct mail_search_mime_build_context * ctx)472 mail_search_mime_exists(struct mail_search_mime_build_context *ctx)
473 {
474 	if (ctx->parent == NULL ||
475 		(ctx->parent->type != SEARCH_MIME_PARENT &&
476 			ctx->parent->type != SEARCH_MIME_CHILD)) {
477 		ctx->ctx->_error = "EXISTS key can only be used with PARENT or CHILD";
478 		return NULL;
479 	}
480 	return ctx->parent;
481 }
482 
483 static const struct mail_search_mime_register_arg
484 mime_register_args[] = {
485 	/* argument set operations */
486 	{ "NOT", mail_search_mime_not },
487 	{ "OR", mail_search_mime_or },
488 
489 	/* dates */
490 	{ "SENTBEFORE", mail_search_mime_sentbefore },
491 	{ "SENTON", mail_search_mime_senton },
492 	{ "SENTSINCE", mail_search_mime_sentsince },
493 
494 	/* size */
495 	{ "SIZE", mail_search_mime_size },
496 
497 	/* part properties */
498 	{ "DESCRIPTION", mail_search_mime_description },
499 	{ "DISPOSITION", mail_search_mime_disposition },
500 	{ "ENCODING", mail_search_mime_encoding },
501 	{ "ID", mail_search_mime_id },
502 	{ "LANGUAGE", mail_search_mime_language },
503 	{ "LOCATION", mail_search_mime_location },
504 	{ "MD5", mail_search_mime_md5 },
505 
506 	/* content-type */
507 	{ "TYPE", mail_search_mime_type },
508 	{ "SUBTYPE", mail_search_mime_subtype },
509 	{ "PARAM", mail_search_mime_param },
510 
511 	/* headers */
512 	{ "HEADER", mail_search_mime_header },
513 
514 	/* message */
515 	{ "BCC", mail_search_mime_bcc },
516 	{ "CC", mail_search_mime_cc },
517 	{ "FROM", mail_search_mime_from },
518 	{ "IN-REPLY-TO", mail_search_mime_in_reply_to },
519 	{ "MESSAGE-ID", mail_search_mime_message_id },
520 	{ "REPLY-TO", mail_search_mime_reply_to },
521 	{ "SENDER", mail_search_mime_sender },
522 	{ "SUBJECT", mail_search_mime_subject },
523 	{ "TO", mail_search_mime_to },
524 
525 	/* body */
526 	{ "BODY", mail_search_mime_body },
527 	{ "TEXT", mail_search_mime_text },
528 
529 	/* position */
530 	{ "DEPTH", mail_search_mime_depth },
531 	{ "INDEX", mail_search_mime_index },
532 
533 	/* relations */
534 	{ "PARENT", mail_search_mime_parent },
535 	{ "CHILD", mail_search_mime_child },
536 	{ "EXISTS", mail_search_mime_exists },
537 
538 	/* filename */
539 	{ "FILENAME", mail_search_mime_filename },
540 };
541 
542 static void
mail_search_register_add_default(void)543 mail_search_register_add_default(void)
544 {
545 	mail_search_mime_register_add(mime_register_args,
546 			 N_ELEMENTS(mime_register_args));
547 }
548