~ubuntu-branches/ubuntu/wily/dovecot/wily

« back to all changes in this revision

Viewing changes to src/lib-storage/mail-search-build.c

  • Committer: Bazaar Package Importer
  • Author(s): CHuck Short, Chuck Short
  • Date: 2009-11-06 00:47:29 UTC
  • mfrom: (4.1.9 squeeze)
  • Revision ID: james.westby@ubuntu.com-20091106004729-i39n7v9e7d4h51f6
Tags: 1:1.2.6-1ubuntu1
* Merge from debian testing, remaining changes:
  Add new binary pkg dovecot-postfix that integrates postfix and dovecot
  automatically: (LP: #164837)
  + debian/control:
    - add new binary with short description
    - set Architecture all for dovecot-postfix (LP: #329878)
  + debian/dovecot-postfix.postinst:
    - create initial certificate symlinks to snakeoil.
    - set up postfix with postconf to:
      - use Maildir/ as the default mailbox.
      - use dovecot as the sasl authentication server.
      - use dovecot LDA (deliver).
      - use tls for smtp{d} services.
    - fix certificates paths in postfix' main.cf
    - add reject_unauth_destination to postfix' recipient restrictions
    - add reject_unknown_sender_domain to postfix' sender restrictions
    - rename configuration name on remove, delete on purge
    - restart dovecot after linking certificates
    - handle use case when postfix is unconfigurated
   + debian/dovecot-postfix.dirs: create backup directory for postfix's configuration
   + restart postfix and dovecot.
   + debian/dovecot-postfix.postrm:
     - remove all dovecot related configuration from postfix.
     - restart postfix and dovecot.
   + debian/dovecot-common.init:
     - check if /etc/dovecot/dovecot-postfix.conf exists and use it
       as the configuration file if so.
   + debian/patches/warning-ubuntu-postfix.dpatch
     - add warning about dovecot-postfix.conf in dovecot default 
       configuration file
   + debian/patches/dovecot-postfix.conf.diff:
     - Ubuntu server custom changes to the default dovecot configuration for
       better interfation with postfix
     - enable sieve plugin
   + debian/patches/dovecot-postfix.conf.diff:
     + Ubuntu server custom changes to the default dovecot configuration for
       better integration with postfix:
       - enable imap, pop3, imaps, pop3s and managesieve by default.
       - enable dovecot LDA (deliver).
       - enable SASL auth socket in postfix private directory.
   + debian/rules:
     - copy, patch and install dovecot-postfix.conf in /etc/dovecot/.
     - build architecure independent packages too
   + Use Snakeoil SSL certificates by default.
     - debian/control: Depend on ssl-cert.
     - debian/patches/ssl-cert-snakeoil.dpatch: Change default SSL cert
       paths to snakeoil.
     - debian/dovecot-common.postinst: Relax grep for SSL_* a bit.
   + Add autopkgtest to debian/tests/*.
   + Fast TearDown: Update the lsb init header to not stop in level 6.
   + Add ufw integration:
     - Created debian/dovecot-common.ufw.profile.
     - debian/rules:
       + install profile
     - debian/control:
       + Suggest ufw
   + debian/{control,rules}: enable PIE hardening.
   + dovecot-imapd, dovecot-pop3: Replaces dovecot-common (<< 1:1.1). LP: #254721
   + debian/control:
     - Update Vcs-* headers.
   + debian/rules:
     - Create emtpy stamp.h.in files in dovecot-sieve/ and dovecot-managesieve/
       if they're not there since empty files are not included in the diff.gz 
       file.
   + Add SMTP-AUTH support for Outlook (login auth mechanism)
   + Dropped:
     - debian/patches/security-CVE-2009-3235: Applied upstream.
     - debian/patches/fix-pop3-assertion.dpatch: Applied upstream.
     - dovecot-sieve and dovecot-managesieve: Use the debian patches instead.

  [Chuck Short]
  - Updated dovecot-sieve to 0.1.13.
  - Updated dovecot-managesieve to 0.11.9.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2002-2009 Dovecot authors, see the included COPYING file */
 
2
 
 
3
#include "lib.h"
 
4
#include "ioloop.h"
 
5
#include "imap-date.h"
 
6
#include "imap-parser.h"
 
7
#include "imap-seqset.h"
 
8
#include "mail-search-build.h"
 
9
#include "mail-storage.h"
 
10
 
 
11
#include <stdlib.h>
 
12
 
 
13
struct search_build_data {
 
14
        pool_t pool;
 
15
        const char *error;
 
16
};
 
17
 
 
18
static struct mail_search_arg *
 
19
search_arg_new(pool_t pool, enum mail_search_arg_type type)
 
20
{
 
21
        struct mail_search_arg *arg;
 
22
 
 
23
        arg = p_new(pool, struct mail_search_arg, 1);
 
24
        arg->type = type;
 
25
 
 
26
        return arg;
 
27
}
 
28
 
 
29
static bool
 
30
arg_get_next(struct search_build_data *data, const struct imap_arg **args,
 
31
             const char **value_r)
 
32
{
 
33
        if ((*args)->type == IMAP_ARG_EOL) {
 
34
                data->error = "Missing parameter for argument";
 
35
                return FALSE;
 
36
        }
 
37
        if ((*args)->type != IMAP_ARG_ATOM &&
 
38
            (*args)->type != IMAP_ARG_STRING) {
 
39
                data->error = "Invalid parameter for argument";
 
40
                return FALSE;
 
41
        }
 
42
 
 
43
        *value_r = IMAP_ARG_STR(*args);
 
44
        *args += 1;
 
45
        return TRUE;
 
46
}
 
47
 
 
48
#define ARG_NEW_SINGLE(type) \
 
49
        arg_new_single(data, next_sarg, type)
 
50
static bool
 
51
arg_new_single(struct search_build_data *data,
 
52
               struct mail_search_arg **next_sarg,
 
53
               enum mail_search_arg_type type)
 
54
{
 
55
        *next_sarg = search_arg_new(data->pool, type);
 
56
        return TRUE;
 
57
}
 
58
 
 
59
#define ARG_NEW_STR(type) \
 
60
        arg_new_str(data, args, next_sarg, type)
 
61
static bool
 
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)
 
65
{
 
66
        struct mail_search_arg *sarg;
 
67
        const char *value;
 
68
 
 
69
        *next_sarg = sarg = search_arg_new(data->pool, type);
 
70
        if (!arg_get_next(data, args, &value))
 
71
                return FALSE;
 
72
        sarg->value.str = p_strdup(data->pool, value);
 
73
        return TRUE;
 
74
}
 
75
 
 
76
#define ARG_NEW_FLAGS(flags) \
 
77
        arg_new_flags(data, next_sarg, flags)
 
78
static bool
 
79
arg_new_flags(struct search_build_data *data,
 
80
              struct mail_search_arg **next_sarg, enum mail_flags flags)
 
81
{
 
82
        struct mail_search_arg *sarg;
 
83
 
 
84
        *next_sarg = sarg = search_arg_new(data->pool, SEARCH_FLAGS);
 
85
        sarg->value.flags = flags;
 
86
        return TRUE;
 
87
}
 
88
 
 
89
#define ARG_NEW_SIZE(type) \
 
90
        arg_new_size(data, args, next_sarg, type)
 
91
static bool
 
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)
 
95
{
 
96
        struct mail_search_arg *sarg;
 
97
        const char *value;
 
98
        char *p;
 
99
 
 
100
        *next_sarg = sarg = search_arg_new(data->pool, type);
 
101
        if (!arg_get_next(data, args, &value))
 
102
                return FALSE;
 
103
 
 
104
        sarg->value.size = strtoull(value, &p, 10);
 
105
        if (*p != '\0') {
 
106
                data->error = "Invalid search size parameter";
 
107
                return FALSE;
 
108
        }
 
109
        return TRUE;
 
110
}
 
111
 
 
112
#define ARG_NEW_DATE(type) \
 
113
        arg_new_date(data, args, next_sarg, type)
 
114
static bool
 
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)
 
118
{
 
119
        struct mail_search_arg *sarg;
 
120
        const char *value;
 
121
 
 
122
        *next_sarg = sarg = search_arg_new(data->pool, type);
 
123
        if (!arg_get_next(data, args, &value))
 
124
                return FALSE;
 
125
        if (!imap_parse_date(value, &sarg->value.time)) {
 
126
                data->error = "Invalid search date parameter";
 
127
                return FALSE;
 
128
        }
 
129
        return TRUE;
 
130
}
 
131
 
 
132
#define ARG_NEW_INTERVAL(type) \
 
133
        arg_new_interval(data, args, next_sarg, type)
 
134
static bool
 
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)
 
139
{
 
140
        struct mail_search_arg *sarg;
 
141
        const char *value;
 
142
        unsigned long interval;
 
143
        char *p;
 
144
 
 
145
        *next_sarg = sarg = search_arg_new(data->pool, type);
 
146
        if (!arg_get_next(data, args, &value))
 
147
                return FALSE;
 
148
 
 
149
        interval = strtoul(value, &p, 10);
 
150
        if (interval == 0 || *p != '\0') {
 
151
                data->error = "Invalid search interval parameter";
 
152
                return FALSE;
 
153
        }
 
154
        sarg->value.search_flags = MAIL_SEARCH_ARG_FLAG_USE_TZ;
 
155
        sarg->value.time = ioloop_time - interval;
 
156
        return TRUE;
 
157
}
 
158
 
 
159
#define ARG_NEW_HEADER(type, hdr_name) \
 
160
        arg_new_header(data, args, next_sarg, type, hdr_name)
 
161
static bool
 
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)
 
165
{
 
166
        struct mail_search_arg *sarg;
 
167
        const char *value;
 
168
 
 
169
        *next_sarg = sarg = search_arg_new(data->pool, type);
 
170
        if (!arg_get_next(data, args, &value))
 
171
                return FALSE;
 
172
 
 
173
        sarg->hdr_field_name = p_strdup(data->pool, hdr_name);
 
174
        sarg->value.str = p_strdup(data->pool, value);
 
175
        return TRUE;
 
176
}
 
177
 
 
178
static bool
 
179
arg_modseq_set_name(struct search_build_data *data,
 
180
                    struct mail_search_arg *sarg, const char *name)
 
181
{
 
182
        name = t_str_lcase(name);
 
183
        if (strncmp(name, "/flags/", 7) != 0) {
 
184
                data->error = "Invalid MODSEQ entry";
 
185
                return FALSE;
 
186
        }
 
187
        name += 7;
 
188
 
 
189
        if (*name == '\\') {
 
190
                /* system flag */
 
191
                name++;
 
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;
 
202
                else {
 
203
                        data->error = "Invalid MODSEQ system flag";
 
204
                        return FALSE;
 
205
                }
 
206
                return TRUE;
 
207
        }
 
208
        sarg->value.str = p_strdup(data->pool, name);
 
209
        return TRUE;
 
210
}
 
211
 
 
212
static bool
 
213
arg_modseq_set_type(struct search_build_data *data,
 
214
                    struct mail_search_modseq *modseq, const char *name)
 
215
{
 
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;
 
222
        else {
 
223
                data->error = "Invalid MODSEQ type";
 
224
                return FALSE;
 
225
        }
 
226
        return TRUE;
 
227
}
 
228
 
 
229
#define ARG_NEW_MODSEQ() \
 
230
        arg_new_modseq(data, args, next_sarg)
 
231
static bool
 
232
arg_new_modseq(struct search_build_data *data,
 
233
               const struct imap_arg **args, struct mail_search_arg **next_sarg)
 
234
{
 
235
        struct mail_search_arg *sarg;
 
236
        const char *value;
 
237
 
 
238
        *next_sarg = sarg = search_arg_new(data->pool, SEARCH_MODSEQ);
 
239
        if (!arg_get_next(data, args, &value))
 
240
                return FALSE;
 
241
 
 
242
        sarg->value.modseq = p_new(data->pool, struct mail_search_modseq, 1);
 
243
        if ((*args)[-1].type == IMAP_ARG_STRING) {
 
244
                /* <name> <type> */
 
245
                if (!arg_modseq_set_name(data, sarg, value))
 
246
                        return FALSE;
 
247
 
 
248
                if (!arg_get_next(data, args, &value))
 
249
                        return FALSE;
 
250
                if (!arg_modseq_set_type(data, sarg->value.modseq, value))
 
251
                        return FALSE;
 
252
 
 
253
                if (!arg_get_next(data, args, &value))
 
254
                        return FALSE;
 
255
        }
 
256
        if (!is_numeric(value, '\0')) {
 
257
                data->error = "Invalid MODSEQ value";
 
258
                return FALSE;
 
259
        }
 
260
        sarg->value.modseq->modseq = strtoull(value, NULL, 10);
 
261
        return TRUE;
 
262
}
 
263
 
 
264
static bool search_arg_build(struct search_build_data *data,
 
265
                             const struct imap_arg **args,
 
266
                             struct mail_search_arg **next_sarg)
 
267
{
 
268
        struct mail_search_arg **subargs, *sarg;
 
269
        const struct imap_arg *arg;
 
270
        const char *str;
 
271
 
 
272
        if ((*args)->type == IMAP_ARG_EOL) {
 
273
                data->error = "Missing argument";
 
274
                return FALSE;
 
275
        }
 
276
 
 
277
        arg = *args;
 
278
 
 
279
        if (arg->type == IMAP_ARG_NIL) {
 
280
                /* NIL not allowed */
 
281
                data->error = "NIL not allowed";
 
282
                return FALSE;
 
283
        }
 
284
 
 
285
        if (arg->type == IMAP_ARG_LIST) {
 
286
                const struct imap_arg *listargs = IMAP_ARG_LIST_ARGS(arg);
 
287
 
 
288
                if (listargs->type == IMAP_ARG_EOL) {
 
289
                        data->error = "Empty list not allowed";
 
290
                        return FALSE;
 
291
                }
 
292
 
 
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))
 
297
                                return FALSE;
 
298
                        subargs = &(*subargs)->next;
 
299
                }
 
300
 
 
301
                *args += 1;
 
302
                return TRUE;
 
303
        }
 
304
 
 
305
        i_assert(arg->type == IMAP_ARG_ATOM ||
 
306
                 arg->type == IMAP_ARG_STRING);
 
307
 
 
308
        /* string argument - get the name and jump to next */
 
309
        str = IMAP_ARG_STR(arg);
 
310
        *args += 1;
 
311
        str = t_str_ucase(str);
 
312
 
 
313
        switch (*str) {
 
314
        case 'A':
 
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);
 
319
                break;
 
320
        case 'B':
 
321
                if (strcmp(str, "BODY") == 0) {
 
322
                        /* <string> */
 
323
                        if (IMAP_ARG_TYPE_IS_STRING((*args)->type) &&
 
324
                            *IMAP_ARG_STR(*args) == '\0') {
 
325
                                *args += 1;
 
326
                                return ARG_NEW_SINGLE(SEARCH_ALL);
 
327
                        }
 
328
                        return ARG_NEW_STR(SEARCH_BODY);
 
329
                } else if (strcmp(str, "BEFORE") == 0) {
 
330
                        /* <date> */
 
331
                        return ARG_NEW_DATE(SEARCH_BEFORE);
 
332
                } else if (strcmp(str, "BCC") == 0) {
 
333
                        /* <string> */
 
334
                        return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str);
 
335
                }
 
336
                break;
 
337
        case 'C':
 
338
                if (strcmp(str, "CC") == 0) {
 
339
                        /* <string> */
 
340
                        return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str);
 
341
                }
 
342
                break;
 
343
        case 'D':
 
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);
 
348
                break;
 
349
        case 'F':
 
350
                if (strcmp(str, "FLAGGED") == 0)
 
351
                        return ARG_NEW_FLAGS(MAIL_FLAGGED);
 
352
                else if (strcmp(str, "FROM") == 0) {
 
353
                        /* <string> */
 
354
                        return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str);
 
355
                }
 
356
                break;
 
357
        case 'H':
 
358
                if (strcmp(str, "HEADER") == 0) {
 
359
                        /* <field-name> <string> */
 
360
                        const char *key;
 
361
 
 
362
                        if ((*args)->type == IMAP_ARG_EOL) {
 
363
                                data->error = "Missing parameter for HEADER";
 
364
                                return FALSE;
 
365
                        }
 
366
                        if ((*args)->type != IMAP_ARG_ATOM &&
 
367
                            (*args)->type != IMAP_ARG_STRING) {
 
368
                                data->error = "Invalid parameter for HEADER";
 
369
                                return FALSE;
 
370
                        }
 
371
 
 
372
                        key = t_str_ucase(IMAP_ARG_STR(*args));
 
373
                        *args += 1;
 
374
                        return ARG_NEW_HEADER(SEARCH_HEADER, key);
 
375
                }
 
376
                break;
 
377
        case 'I':
 
378
                if (strcmp(str, "INTHREAD") == 0) {
 
379
                        /* <algorithm> <search key> */
 
380
                        enum mail_thread_type thread_type;
 
381
                        const char *str;
 
382
 
 
383
                        if ((*args)->type != IMAP_ARG_ATOM) {
 
384
                                data->error = "Invalid parameter for INTHREAD";
 
385
                                return FALSE;
 
386
                        }
 
387
 
 
388
                        str = IMAP_ARG_STR_NONULL(*args);
 
389
                        if (!mail_thread_type_parse(str, &thread_type)) {
 
390
                                data->error = "Unknown thread algorithm";
 
391
                                return FALSE;
 
392
                        }
 
393
                        *args += 1;
 
394
 
 
395
                        *next_sarg = search_arg_new(data->pool,
 
396
                                                    SEARCH_INTHREAD);
 
397
                        (*next_sarg)->value.thread_type = thread_type;
 
398
                        subargs = &(*next_sarg)->value.subargs;
 
399
                        return search_arg_build(data, args, subargs);
 
400
                }
 
401
                break;
 
402
        case 'K':
 
403
                if (strcmp(str, "KEYWORD") == 0) {
 
404
                        return ARG_NEW_STR(SEARCH_KEYWORDS);
 
405
                }
 
406
                break;
 
407
        case 'L':
 
408
                if (strcmp(str, "LARGER") == 0) {
 
409
                        /* <n> */
 
410
                        return ARG_NEW_SIZE(SEARCH_LARGER);
 
411
                }
 
412
                break;
 
413
        case 'M':
 
414
                if (strcmp(str, "MODSEQ") == 0) {
 
415
                        /* [<name> <type>] <n> */
 
416
                        return ARG_NEW_MODSEQ();
 
417
                }
 
418
                break;
 
419
        case 'N':
 
420
                if (strcmp(str, "NOT") == 0) {
 
421
                        if (!search_arg_build(data, args, next_sarg))
 
422
                                return FALSE;
 
423
                        (*next_sarg)->not = !(*next_sarg)->not;
 
424
                        return TRUE;
 
425
                } else if (strcmp(str, "NEW") == 0) {
 
426
                        /* NEW == (RECENT UNSEEN) */
 
427
                        *next_sarg = search_arg_new(data->pool, SEARCH_SUB);
 
428
 
 
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,
 
433
                                                          SEARCH_FLAGS);
 
434
                        (*subargs)->next->value.flags = MAIL_SEEN;
 
435
                        (*subargs)->next->not = TRUE;
 
436
                        return TRUE;
 
437
                }
 
438
                break;
 
439
        case 'O':
 
440
                if (strcmp(str, "OR") == 0) {
 
441
                        /* <search-key1> <search-key2> */
 
442
                        *next_sarg = search_arg_new(data->pool, SEARCH_OR);
 
443
 
 
444
                        subargs = &(*next_sarg)->value.subargs;
 
445
                        for (;;) {
 
446
                                if (!search_arg_build(data, args, subargs))
 
447
                                        return FALSE;
 
448
 
 
449
                                subargs = &(*subargs)->next;
 
450
 
 
451
                                /* <key> OR <key> OR ... <key> - put them all
 
452
                                   under one SEARCH_OR list. */
 
453
                                if ((*args)->type == IMAP_ARG_EOL)
 
454
                                        break;
 
455
 
 
456
                                if ((*args)->type != IMAP_ARG_ATOM ||
 
457
                                    strcasecmp(IMAP_ARG_STR_NONULL(*args),
 
458
                                               "OR") != 0)
 
459
                                        break;
 
460
 
 
461
                                *args += 1;
 
462
                        }
 
463
 
 
464
                        if (!search_arg_build(data, args, subargs))
 
465
                                return FALSE;
 
466
                        return TRUE;
 
467
                } if (strcmp(str, "ON") == 0) {
 
468
                        /* <date> */
 
469
                        return ARG_NEW_DATE(SEARCH_ON);
 
470
                } if (strcmp(str, "OLD") == 0) {
 
471
                        /* OLD == NOT RECENT */
 
472
                        if (!ARG_NEW_FLAGS(MAIL_RECENT))
 
473
                                return FALSE;
 
474
 
 
475
                        (*next_sarg)->not = TRUE;
 
476
                        return TRUE;
 
477
                } if (strcmp(str, "OLDER") == 0) {
 
478
                        /* <interval> - WITHIN extension */
 
479
                        if (!ARG_NEW_INTERVAL(SEARCH_BEFORE))
 
480
                                return FALSE;
 
481
 
 
482
                        /* we need to match also equal, but standard BEFORE
 
483
                           compares with "<" */
 
484
                        (*next_sarg)->value.time++;
 
485
                        return TRUE;
 
486
                }
 
487
                break;
 
488
        case 'R':
 
489
                if (strcmp(str, "RECENT") == 0)
 
490
                        return ARG_NEW_FLAGS(MAIL_RECENT);
 
491
                break;
 
492
        case 'S':
 
493
                if (strcmp(str, "SEEN") == 0)
 
494
                        return ARG_NEW_FLAGS(MAIL_SEEN);
 
495
                else if (strcmp(str, "SUBJECT") == 0) {
 
496
                        /* <string> */
 
497
                        return ARG_NEW_HEADER(SEARCH_HEADER_COMPRESS_LWSP, str);
 
498
                } else if (strcmp(str, "SENTBEFORE") == 0) {
 
499
                        /* <date> */
 
500
                        return ARG_NEW_DATE(SEARCH_SENTBEFORE);
 
501
                } else if (strcmp(str, "SENTON") == 0) {
 
502
                        /* <date> */
 
503
                        return ARG_NEW_DATE(SEARCH_SENTON);
 
504
                } else if (strcmp(str, "SENTSINCE") == 0) {
 
505
                        /* <date> */
 
506
                        return ARG_NEW_DATE(SEARCH_SENTSINCE);
 
507
                } else if (strcmp(str, "SINCE") == 0) {
 
508
                        /* <date> */
 
509
                        return ARG_NEW_DATE(SEARCH_SINCE);
 
510
                } else if (strcmp(str, "SMALLER") == 0) {
 
511
                        /* <n> */
 
512
                        return ARG_NEW_SIZE(SEARCH_SMALLER);
 
513
                }
 
514
                break;
 
515
        case 'T':
 
516
                if (strcmp(str, "TEXT") == 0) {
 
517
                        /* <string> */
 
518
                        if (IMAP_ARG_TYPE_IS_STRING((*args)->type) &&
 
519
                            *IMAP_ARG_STR(*args) == '\0') {
 
520
                                *args += 1;
 
521
                                return ARG_NEW_SINGLE(SEARCH_ALL);
 
522
                        }
 
523
                        return ARG_NEW_STR(SEARCH_TEXT);
 
524
                } else if (strcmp(str, "TO") == 0) {
 
525
                        /* <string> */
 
526
                        return ARG_NEW_HEADER(SEARCH_HEADER_ADDRESS, str);
 
527
                }
 
528
                break;
 
529
        case 'U':
 
530
                if (strcmp(str, "UID") == 0) {
 
531
                        /* <message set> */
 
532
                        if (!ARG_NEW_STR(SEARCH_UIDSET))
 
533
                                return FALSE;
 
534
 
 
535
                        sarg = *next_sarg;
 
536
                        p_array_init(&sarg->value.seqset, data->pool, 16);
 
537
                        if (strcmp(sarg->value.str, "$") == 0) {
 
538
                                /* SEARCHRES: delay initialization */
 
539
                                return TRUE;
 
540
                        }
 
541
                        if (imap_seq_set_parse(sarg->value.str,
 
542
                                               &sarg->value.seqset) < 0) {
 
543
                                data->error = "Invalid UID messageset";
 
544
                                return FALSE;
 
545
                        }
 
546
                        return TRUE;
 
547
                } else if (strcmp(str, "UNANSWERED") == 0) {
 
548
                        if (!ARG_NEW_FLAGS(MAIL_ANSWERED))
 
549
                                return FALSE;
 
550
                        (*next_sarg)->not = TRUE;
 
551
                        return TRUE;
 
552
                } else if (strcmp(str, "UNDELETED") == 0) {
 
553
                        if (!ARG_NEW_FLAGS(MAIL_DELETED))
 
554
                                return FALSE;
 
555
                        (*next_sarg)->not = TRUE;
 
556
                        return TRUE;
 
557
                } else if (strcmp(str, "UNDRAFT") == 0) {
 
558
                        if (!ARG_NEW_FLAGS(MAIL_DRAFT))
 
559
                                return FALSE;
 
560
                        (*next_sarg)->not = TRUE;
 
561
                        return TRUE;
 
562
                } else if (strcmp(str, "UNFLAGGED") == 0) {
 
563
                        if (!ARG_NEW_FLAGS(MAIL_FLAGGED))
 
564
                                return FALSE;
 
565
                        (*next_sarg)->not = TRUE;
 
566
                        return TRUE;
 
567
                } else if (strcmp(str, "UNKEYWORD") == 0) {
 
568
                        if (!ARG_NEW_STR(SEARCH_KEYWORDS))
 
569
                                return FALSE;
 
570
                        (*next_sarg)->not = TRUE;
 
571
                        return TRUE;
 
572
                } else if (strcmp(str, "UNSEEN") == 0) {
 
573
                        if (!ARG_NEW_FLAGS(MAIL_SEEN))
 
574
                                return FALSE;
 
575
                        (*next_sarg)->not = TRUE;
 
576
                        return TRUE;
 
577
                }
 
578
                break;
 
579
        case 'Y':
 
580
                if (strcmp(str, "YOUNGER") == 0) {
 
581
                        /* <interval> - WITHIN extension */
 
582
                        return ARG_NEW_INTERVAL(SEARCH_SINCE);
 
583
                }
 
584
                break;
 
585
        case 'X':
 
586
                if (strcmp(str, "X-BODY-FAST") == 0) {
 
587
                        /* <string> */
 
588
                        if (IMAP_ARG_TYPE_IS_STRING((*args)->type) &&
 
589
                            *IMAP_ARG_STR(*args) == '\0') {
 
590
                                *args += 1;
 
591
                                return ARG_NEW_SINGLE(SEARCH_ALL);
 
592
                        }
 
593
                        return ARG_NEW_STR(SEARCH_BODY_FAST);
 
594
                } else if (strcmp(str, "X-TEXT-FAST") == 0) {
 
595
                        /* <string> */
 
596
                        if (IMAP_ARG_TYPE_IS_STRING((*args)->type) &&
 
597
                            *IMAP_ARG_STR(*args) == '\0') {
 
598
                                *args += 1;
 
599
                                return ARG_NEW_SINGLE(SEARCH_ALL);
 
600
                        }
 
601
                        return ARG_NEW_STR(SEARCH_TEXT_FAST);
 
602
                } else if (strcmp(str, "X-GUID") == 0) {
 
603
                        /* <string> */
 
604
                        return ARG_NEW_STR(SEARCH_GUID);
 
605
                } else if (strcmp(str, "X-MAILBOX") == 0) {
 
606
                        /* <string> */
 
607
                        return ARG_NEW_STR(SEARCH_MAILBOX);
 
608
                }
 
609
                break;
 
610
        default:
 
611
                if (*str == '*' || (*str >= '0' && *str <= '9')) {
 
612
                        /* <message-set> */
 
613
                        if (!ARG_NEW_SINGLE(SEARCH_SEQSET))
 
614
                                return FALSE;
 
615
 
 
616
                        p_array_init(&(*next_sarg)->value.seqset,
 
617
                                     data->pool, 16);
 
618
                        if (imap_seq_set_parse(str, &(*next_sarg)->value.seqset) < 0) {
 
619
                                data->error = "Invalid messageset";
 
620
                                return FALSE;
 
621
                        }
 
622
                        return TRUE;
 
623
                } else if (strcmp(str, "$") == 0) {
 
624
                        /* SEARCHRES: delay initialization */
 
625
                        if (!ARG_NEW_SINGLE(SEARCH_UIDSET))
 
626
                                return FALSE;
 
627
 
 
628
                        (*next_sarg)->value.str = p_strdup(data->pool, "$");
 
629
                        p_array_init(&(*next_sarg)->value.seqset,
 
630
                                     data->pool, 16);
 
631
                        return TRUE;
 
632
                }
 
633
                break;
 
634
        }
 
635
 
 
636
        data->error = t_strconcat("Unknown argument ", str, NULL);
 
637
        return FALSE;
 
638
}
 
639
 
 
640
int mail_search_build_from_imap_args(const struct imap_arg *imap_args,
 
641
                                     const char *charset,
 
642
                                     struct mail_search_args **args_r,
 
643
                                     const char **error_r)
 
644
{
 
645
        struct search_build_data data;
 
646
        struct mail_search_args *args;
 
647
        struct mail_search_arg **sargs;
 
648
 
 
649
        *args_r = NULL;
 
650
        *error_r = NULL;
 
651
 
 
652
        args = mail_search_build_init();
 
653
        args->charset = p_strdup(args->pool, charset);
 
654
 
 
655
        data.pool = args->pool;
 
656
        data.error = NULL;
 
657
 
 
658
        sargs = &args->args;
 
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;
 
663
                        return -1;
 
664
                }
 
665
                sargs = &(*sargs)->next;
 
666
        }
 
667
 
 
668
        *args_r = args;
 
669
        return 0;
 
670
}
 
671
 
 
672
struct mail_search_args *mail_search_build_init(void)
 
673
{
 
674
        struct mail_search_args *args;
 
675
        pool_t pool;
 
676
 
 
677
        pool = pool_alloconly_create("mail search args", 4096);
 
678
        args = p_new(pool, struct mail_search_args, 1);
 
679
        args->pool = pool;
 
680
        args->refcount = 1;
 
681
        return args;
 
682
}
 
683
 
 
684
void mail_search_build_add_all(struct mail_search_args *args)
 
685
{
 
686
        struct mail_search_arg *arg;
 
687
 
 
688
        arg = p_new(args->pool, struct mail_search_arg, 1);
 
689
        arg->type = SEARCH_ALL;
 
690
 
 
691
        arg->next = args->args;
 
692
        args->args = arg;
 
693
}
 
694
 
 
695
void mail_search_build_add_seqset(struct mail_search_args *args,
 
696
                                  uint32_t seq1, uint32_t seq2)
 
697
{
 
698
        struct mail_search_arg *arg;
 
699
 
 
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);
 
704
 
 
705
        arg->next = args->args;
 
706
        args->args = arg;
 
707
}