1
/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
6
#include "imap-parser.h"
7
#include "imap-seqset.h"
8
#include "mail-search-build.h"
9
#include "mail-storage.h"
13
struct search_build_data {
18
static struct mail_search_arg *
19
search_arg_new(pool_t pool, enum mail_search_arg_type type)
21
struct mail_search_arg *arg;
23
arg = p_new(pool, struct mail_search_arg, 1);
30
arg_get_next(struct search_build_data *data, const struct imap_arg **args,
33
if ((*args)->type == IMAP_ARG_EOL) {
34
data->error = "Missing parameter for argument";
37
if ((*args)->type != IMAP_ARG_ATOM &&
38
(*args)->type != IMAP_ARG_STRING) {
39
data->error = "Invalid parameter for argument";
43
*value_r = IMAP_ARG_STR(*args);
48
#define ARG_NEW_SINGLE(type) \
49
arg_new_single(data, next_sarg, type)
51
arg_new_single(struct search_build_data *data,
52
struct mail_search_arg **next_sarg,
53
enum mail_search_arg_type type)
55
*next_sarg = search_arg_new(data->pool, type);
59
#define ARG_NEW_STR(type) \
60
arg_new_str(data, args, next_sarg, type)
62
arg_new_str(struct search_build_data *data,
63
const struct imap_arg **args, struct mail_search_arg **next_sarg,
64
enum mail_search_arg_type type)
66
struct mail_search_arg *sarg;
69
*next_sarg = sarg = search_arg_new(data->pool, type);
70
if (!arg_get_next(data, args, &value))
72
sarg->value.str = p_strdup(data->pool, value);
76
#define ARG_NEW_FLAGS(flags) \
77
arg_new_flags(data, next_sarg, flags)
79
arg_new_flags(struct search_build_data *data,
80
struct mail_search_arg **next_sarg, enum mail_flags flags)
82
struct mail_search_arg *sarg;
84
*next_sarg = sarg = search_arg_new(data->pool, SEARCH_FLAGS);
85
sarg->value.flags = flags;
89
#define ARG_NEW_SIZE(type) \
90
arg_new_size(data, args, next_sarg, type)
92
arg_new_size(struct search_build_data *data,
93
const struct imap_arg **args, struct mail_search_arg **next_sarg,
94
enum mail_search_arg_type type)
96
struct mail_search_arg *sarg;
100
*next_sarg = sarg = search_arg_new(data->pool, type);
101
if (!arg_get_next(data, args, &value))
104
sarg->value.size = strtoull(value, &p, 10);
106
data->error = "Invalid search size parameter";
112
#define ARG_NEW_DATE(type) \
113
arg_new_date(data, args, next_sarg, type)
115
arg_new_date(struct search_build_data *data,
116
const struct imap_arg **args, struct mail_search_arg **next_sarg,
117
enum mail_search_arg_type type)
119
struct mail_search_arg *sarg;
122
*next_sarg = sarg = search_arg_new(data->pool, type);
123
if (!arg_get_next(data, args, &value))
125
if (!imap_parse_date(value, &sarg->value.time)) {
126
data->error = "Invalid search date parameter";
132
#define ARG_NEW_INTERVAL(type) \
133
arg_new_interval(data, args, next_sarg, type)
135
arg_new_interval(struct search_build_data *data,
136
const struct imap_arg **args,
137
struct mail_search_arg **next_sarg,
138
enum mail_search_arg_type type)
140
struct mail_search_arg *sarg;
142
unsigned long interval;
145
*next_sarg = sarg = search_arg_new(data->pool, type);
146
if (!arg_get_next(data, args, &value))
149
interval = strtoul(value, &p, 10);
150
if (interval == 0 || *p != '\0') {
151
data->error = "Invalid search interval parameter";
154
sarg->value.search_flags = MAIL_SEARCH_ARG_FLAG_USE_TZ;
155
sarg->value.time = ioloop_time - interval;
159
#define ARG_NEW_HEADER(type, hdr_name) \
160
arg_new_header(data, args, next_sarg, type, hdr_name)
162
arg_new_header(struct search_build_data *data,
163
const struct imap_arg **args, struct mail_search_arg **next_sarg,
164
enum mail_search_arg_type type, const char *hdr_name)
166
struct mail_search_arg *sarg;
169
*next_sarg = sarg = search_arg_new(data->pool, type);
170
if (!arg_get_next(data, args, &value))
173
sarg->hdr_field_name = p_strdup(data->pool, hdr_name);
174
sarg->value.str = p_strdup(data->pool, value);
179
arg_modseq_set_name(struct search_build_data *data,
180
struct mail_search_arg *sarg, const char *name)
182
name = t_str_lcase(name);
183
if (strncmp(name, "/flags/", 7) != 0) {
184
data->error = "Invalid MODSEQ entry";
192
if (strcmp(name, "answered") == 0)
193
sarg->value.flags = MAIL_ANSWERED;
194
else if (strcmp(name, "flagged") == 0)
195
sarg->value.flags = MAIL_FLAGGED;
196
else if (strcmp(name, "deleted") == 0)
197
sarg->value.flags = MAIL_DELETED;
198
else if (strcmp(name, "seen") == 0)
199
sarg->value.flags = MAIL_SEEN;
200
else if (strcmp(name, "draft") == 0)
201
sarg->value.flags = MAIL_DRAFT;
203
data->error = "Invalid MODSEQ system flag";
208
sarg->value.str = p_strdup(data->pool, name);
213
arg_modseq_set_type(struct search_build_data *data,
214
struct mail_search_modseq *modseq, const char *name)
216
if (strcasecmp(name, "all") == 0)
217
modseq->type = MAIL_SEARCH_MODSEQ_TYPE_ANY;
218
else if (strcasecmp(name, "priv") == 0)
219
modseq->type = MAIL_SEARCH_MODSEQ_TYPE_PRIVATE;
220
else if (strcasecmp(name, "shared") == 0)
221
modseq->type = MAIL_SEARCH_MODSEQ_TYPE_SHARED;
223
data->error = "Invalid MODSEQ type";
229
#define ARG_NEW_MODSEQ() \
230
arg_new_modseq(data, args, next_sarg)
232
arg_new_modseq(struct search_build_data *data,
233
const struct imap_arg **args, struct mail_search_arg **next_sarg)
235
struct mail_search_arg *sarg;
238
*next_sarg = sarg = search_arg_new(data->pool, SEARCH_MODSEQ);
239
if (!arg_get_next(data, args, &value))
242
sarg->value.modseq = p_new(data->pool, struct mail_search_modseq, 1);
243
if ((*args)[-1].type == IMAP_ARG_STRING) {
245
if (!arg_modseq_set_name(data, sarg, value))
248
if (!arg_get_next(data, args, &value))
250
if (!arg_modseq_set_type(data, sarg->value.modseq, value))
253
if (!arg_get_next(data, args, &value))
256
if (!is_numeric(value, '\0')) {
257
data->error = "Invalid MODSEQ value";
260
sarg->value.modseq->modseq = strtoull(value, NULL, 10);
264
static bool search_arg_build(struct search_build_data *data,
265
const struct imap_arg **args,
266
struct mail_search_arg **next_sarg)
268
struct mail_search_arg **subargs, *sarg;
269
const struct imap_arg *arg;
272
if ((*args)->type == IMAP_ARG_EOL) {
273
data->error = "Missing argument";
279
if (arg->type == IMAP_ARG_NIL) {
280
/* NIL not allowed */
281
data->error = "NIL not allowed";
285
if (arg->type == IMAP_ARG_LIST) {
286
const struct imap_arg *listargs = IMAP_ARG_LIST_ARGS(arg);
288
if (listargs->type == IMAP_ARG_EOL) {
289
data->error = "Empty list not allowed";
293
*next_sarg = search_arg_new(data->pool, SEARCH_SUB);
294
subargs = &(*next_sarg)->value.subargs;
295
while (listargs->type != IMAP_ARG_EOL) {
296
if (!search_arg_build(data, &listargs, subargs))
298
subargs = &(*subargs)->next;
305
i_assert(arg->type == IMAP_ARG_ATOM ||
306
arg->type == IMAP_ARG_STRING);
308
/* string argument - get the name and jump to next */
309
str = IMAP_ARG_STR(arg);
311
str = t_str_ucase(str);
315
if (strcmp(str, "ANSWERED") == 0)
316
return ARG_NEW_FLAGS(MAIL_ANSWERED);
317
else if (strcmp(str, "ALL") == 0)
318
return ARG_NEW_SINGLE(SEARCH_ALL);
321
if (strcmp(str, "BODY") == 0) {
323
if (IMAP_ARG_TYPE_IS_STRING((*args)->type) &&
324
*IMAP_ARG_STR(*args) == '\0') {
326
return ARG_NEW_SINGLE(SEARCH_ALL);
328
return ARG_NEW_STR(SEARCH_BODY);
329
} else if (strcmp(str, "BEFORE") == 0) {
331
return ARG_NEW_DATE(SEARCH_BEFORE);
332
} else if (strcmp(str, "BCC") == 0) {
334
return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str);
338
if (strcmp(str, "CC") == 0) {
340
return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str);
344
if (strcmp(str, "DELETED") == 0)
345
return ARG_NEW_FLAGS(MAIL_DELETED);
346
else if (strcmp(str, "DRAFT") == 0)
347
return ARG_NEW_FLAGS(MAIL_DRAFT);
350
if (strcmp(str, "FLAGGED") == 0)
351
return ARG_NEW_FLAGS(MAIL_FLAGGED);
352
else if (strcmp(str, "FROM") == 0) {
354
return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str);
358
if (strcmp(str, "HEADER") == 0) {
359
/* <field-name> <string> */
362
if ((*args)->type == IMAP_ARG_EOL) {
363
data->error = "Missing parameter for HEADER";
366
if ((*args)->type != IMAP_ARG_ATOM &&
367
(*args)->type != IMAP_ARG_STRING) {
368
data->error = "Invalid parameter for HEADER";
372
key = t_str_ucase(IMAP_ARG_STR(*args));
374
return ARG_NEW_HEADER(SEARCH_HEADER, key);
378
if (strcmp(str, "INTHREAD") == 0) {
379
/* <algorithm> <search key> */
380
enum mail_thread_type thread_type;
383
if ((*args)->type != IMAP_ARG_ATOM) {
384
data->error = "Invalid parameter for INTHREAD";
388
str = IMAP_ARG_STR_NONULL(*args);
389
if (!mail_thread_type_parse(str, &thread_type)) {
390
data->error = "Unknown thread algorithm";
395
*next_sarg = search_arg_new(data->pool,
397
(*next_sarg)->value.thread_type = thread_type;
398
subargs = &(*next_sarg)->value.subargs;
399
return search_arg_build(data, args, subargs);
403
if (strcmp(str, "KEYWORD") == 0) {
404
return ARG_NEW_STR(SEARCH_KEYWORDS);
408
if (strcmp(str, "LARGER") == 0) {
410
return ARG_NEW_SIZE(SEARCH_LARGER);
414
if (strcmp(str, "MODSEQ") == 0) {
415
/* [<name> <type>] <n> */
416
return ARG_NEW_MODSEQ();
420
if (strcmp(str, "NOT") == 0) {
421
if (!search_arg_build(data, args, next_sarg))
423
(*next_sarg)->not = !(*next_sarg)->not;
425
} else if (strcmp(str, "NEW") == 0) {
426
/* NEW == (RECENT UNSEEN) */
427
*next_sarg = search_arg_new(data->pool, SEARCH_SUB);
429
subargs = &(*next_sarg)->value.subargs;
430
*subargs = search_arg_new(data->pool, SEARCH_FLAGS);
431
(*subargs)->value.flags = MAIL_RECENT;
432
(*subargs)->next = search_arg_new(data->pool,
434
(*subargs)->next->value.flags = MAIL_SEEN;
435
(*subargs)->next->not = TRUE;
440
if (strcmp(str, "OR") == 0) {
441
/* <search-key1> <search-key2> */
442
*next_sarg = search_arg_new(data->pool, SEARCH_OR);
444
subargs = &(*next_sarg)->value.subargs;
446
if (!search_arg_build(data, args, subargs))
449
subargs = &(*subargs)->next;
451
/* <key> OR <key> OR ... <key> - put them all
452
under one SEARCH_OR list. */
453
if ((*args)->type == IMAP_ARG_EOL)
456
if ((*args)->type != IMAP_ARG_ATOM ||
457
strcasecmp(IMAP_ARG_STR_NONULL(*args),
464
if (!search_arg_build(data, args, subargs))
467
} if (strcmp(str, "ON") == 0) {
469
return ARG_NEW_DATE(SEARCH_ON);
470
} if (strcmp(str, "OLD") == 0) {
471
/* OLD == NOT RECENT */
472
if (!ARG_NEW_FLAGS(MAIL_RECENT))
475
(*next_sarg)->not = TRUE;
477
} if (strcmp(str, "OLDER") == 0) {
478
/* <interval> - WITHIN extension */
479
if (!ARG_NEW_INTERVAL(SEARCH_BEFORE))
482
/* we need to match also equal, but standard BEFORE
484
(*next_sarg)->value.time++;
489
if (strcmp(str, "RECENT") == 0)
490
return ARG_NEW_FLAGS(MAIL_RECENT);
493
if (strcmp(str, "SEEN") == 0)
494
return ARG_NEW_FLAGS(MAIL_SEEN);
495
else if (strcmp(str, "SUBJECT") == 0) {
497
return ARG_NEW_HEADER(SEARCH_HEADER_COMPRESS_LWSP, str);
498
} else if (strcmp(str, "SENTBEFORE") == 0) {
500
return ARG_NEW_DATE(SEARCH_SENTBEFORE);
501
} else if (strcmp(str, "SENTON") == 0) {
503
return ARG_NEW_DATE(SEARCH_SENTON);
504
} else if (strcmp(str, "SENTSINCE") == 0) {
506
return ARG_NEW_DATE(SEARCH_SENTSINCE);
507
} else if (strcmp(str, "SINCE") == 0) {
509
return ARG_NEW_DATE(SEARCH_SINCE);
510
} else if (strcmp(str, "SMALLER") == 0) {
512
return ARG_NEW_SIZE(SEARCH_SMALLER);
516
if (strcmp(str, "TEXT") == 0) {
518
if (IMAP_ARG_TYPE_IS_STRING((*args)->type) &&
519
*IMAP_ARG_STR(*args) == '\0') {
521
return ARG_NEW_SINGLE(SEARCH_ALL);
523
return ARG_NEW_STR(SEARCH_TEXT);
524
} else if (strcmp(str, "TO") == 0) {
526
return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str);
530
if (strcmp(str, "UID") == 0) {
532
if (!ARG_NEW_STR(SEARCH_UIDSET))
536
p_array_init(&sarg->value.seqset, data->pool, 16);
537
if (strcmp(sarg->value.str, "$") == 0) {
538
/* SEARCHRES: delay initialization */
541
if (imap_seq_set_parse(sarg->value.str,
542
&sarg->value.seqset) < 0) {
543
data->error = "Invalid UID messageset";
547
} else if (strcmp(str, "UNANSWERED") == 0) {
548
if (!ARG_NEW_FLAGS(MAIL_ANSWERED))
550
(*next_sarg)->not = TRUE;
552
} else if (strcmp(str, "UNDELETED") == 0) {
553
if (!ARG_NEW_FLAGS(MAIL_DELETED))
555
(*next_sarg)->not = TRUE;
557
} else if (strcmp(str, "UNDRAFT") == 0) {
558
if (!ARG_NEW_FLAGS(MAIL_DRAFT))
560
(*next_sarg)->not = TRUE;
562
} else if (strcmp(str, "UNFLAGGED") == 0) {
563
if (!ARG_NEW_FLAGS(MAIL_FLAGGED))
565
(*next_sarg)->not = TRUE;
567
} else if (strcmp(str, "UNKEYWORD") == 0) {
568
if (!ARG_NEW_STR(SEARCH_KEYWORDS))
570
(*next_sarg)->not = TRUE;
572
} else if (strcmp(str, "UNSEEN") == 0) {
573
if (!ARG_NEW_FLAGS(MAIL_SEEN))
575
(*next_sarg)->not = TRUE;
580
if (strcmp(str, "YOUNGER") == 0) {
581
/* <interval> - WITHIN extension */
582
return ARG_NEW_INTERVAL(SEARCH_SINCE);
586
if (strcmp(str, "X-BODY-FAST") == 0) {
588
if (IMAP_ARG_TYPE_IS_STRING((*args)->type) &&
589
*IMAP_ARG_STR(*args) == '\0') {
591
return ARG_NEW_SINGLE(SEARCH_ALL);
593
return ARG_NEW_STR(SEARCH_BODY_FAST);
594
} else if (strcmp(str, "X-TEXT-FAST") == 0) {
596
if (IMAP_ARG_TYPE_IS_STRING((*args)->type) &&
597
*IMAP_ARG_STR(*args) == '\0') {
599
return ARG_NEW_SINGLE(SEARCH_ALL);
601
return ARG_NEW_STR(SEARCH_TEXT_FAST);
602
} else if (strcmp(str, "X-GUID") == 0) {
604
return ARG_NEW_STR(SEARCH_GUID);
605
} else if (strcmp(str, "X-MAILBOX") == 0) {
607
return ARG_NEW_STR(SEARCH_MAILBOX);
611
if (*str == '*' || (*str >= '0' && *str <= '9')) {
613
if (!ARG_NEW_SINGLE(SEARCH_SEQSET))
616
p_array_init(&(*next_sarg)->value.seqset,
618
if (imap_seq_set_parse(str, &(*next_sarg)->value.seqset) < 0) {
619
data->error = "Invalid messageset";
623
} else if (strcmp(str, "$") == 0) {
624
/* SEARCHRES: delay initialization */
625
if (!ARG_NEW_SINGLE(SEARCH_UIDSET))
628
(*next_sarg)->value.str = p_strdup(data->pool, "$");
629
p_array_init(&(*next_sarg)->value.seqset,
636
data->error = t_strconcat("Unknown argument ", str, NULL);
640
int mail_search_build_from_imap_args(const struct imap_arg *imap_args,
642
struct mail_search_args **args_r,
643
const char **error_r)
645
struct search_build_data data;
646
struct mail_search_args *args;
647
struct mail_search_arg **sargs;
652
args = mail_search_build_init();
653
args->charset = p_strdup(args->pool, charset);
655
data.pool = args->pool;
659
while (imap_args->type != IMAP_ARG_EOL) {
660
if (!search_arg_build(&data, &imap_args, sargs)) {
661
pool_unref(&args->pool);
662
*error_r = data.error;
665
sargs = &(*sargs)->next;
672
struct mail_search_args *mail_search_build_init(void)
674
struct mail_search_args *args;
677
pool = pool_alloconly_create("mail search args", 4096);
678
args = p_new(pool, struct mail_search_args, 1);
684
void mail_search_build_add_all(struct mail_search_args *args)
686
struct mail_search_arg *arg;
688
arg = p_new(args->pool, struct mail_search_arg, 1);
689
arg->type = SEARCH_ALL;
691
arg->next = args->args;
695
void mail_search_build_add_seqset(struct mail_search_args *args,
696
uint32_t seq1, uint32_t seq2)
698
struct mail_search_arg *arg;
700
arg = p_new(args->pool, struct mail_search_arg, 1);
701
arg->type = SEARCH_SEQSET;
702
p_array_init(&arg->value.seqset, args->pool, 1);
703
seq_range_array_add_range(&arg->value.seqset, seq1, seq2);
705
arg->next = args->args;