1
/* Copyright (c) 2015 Dovecot authors, see the included COPYING file */
7
#include "utc-offset.h"
8
#include "mail-index.h"
10
#include "imap-util.h"
11
#include "imap-quote.h"
12
#include "mail-search.h"
17
mail_search_subargs_to_imap(string_t *dest, const struct mail_search_arg *args,
18
const char *prefix, const char **error_r)
20
const struct mail_search_arg *arg;
22
str_append_c(dest, '(');
23
for (arg = args; arg != NULL; arg = arg->next) {
24
if (arg->next != NULL)
25
str_append(dest, prefix);
26
if (!mail_search_arg_to_imap(dest, arg, error_r))
28
if (arg->next != NULL)
29
str_append_c(dest, ' ');
31
str_append_c(dest, ')');
36
mail_search_arg_to_imap_date(string_t *dest, const struct mail_search_arg *arg)
38
time_t timestamp = arg->value.time;
41
if ((arg->value.search_flags &
42
MAIL_SEARCH_ARG_FLAG_USE_TZ) == 0) {
43
struct tm *tm = localtime(×tamp);
44
int tz_offset = utc_offset(tm, timestamp);
45
timestamp -= tz_offset * 60;
47
if (!imap_to_date(timestamp, &str))
49
str_printfa(dest, " \"%s\"", str);
53
bool mail_search_arg_to_imap(string_t *dest, const struct mail_search_arg *arg,
56
unsigned int start_pos;
59
str_append(dest, "NOT ");
60
start_pos = str_len(dest);
63
if (!mail_search_subargs_to_imap(dest, arg->value.subargs,
68
if (!mail_search_subargs_to_imap(dest, arg->value.subargs,
73
str_append(dest, "ALL");
76
imap_write_seq_range(dest, &arg->value.seqset);
79
str_append(dest, "UID ");
80
imap_write_seq_range(dest, &arg->value.seqset);
83
i_assert((arg->value.flags & MAIL_FLAGS_MASK) != 0);
84
str_append_c(dest, '(');
85
if ((arg->value.flags & MAIL_ANSWERED) != 0)
86
str_append(dest, "ANSWERED ");
87
if ((arg->value.flags & MAIL_FLAGGED) != 0)
88
str_append(dest, "FLAGGED ");
89
if ((arg->value.flags & MAIL_DELETED) != 0)
90
str_append(dest, "DELETED ");
91
if ((arg->value.flags & MAIL_SEEN) != 0)
92
str_append(dest, "SEEN ");
93
if ((arg->value.flags & MAIL_DRAFT) != 0)
94
str_append(dest, "DRAFT ");
95
if ((arg->value.flags & MAIL_RECENT) != 0)
96
str_append(dest, "RECENT ");
97
str_truncate(dest, str_len(dest)-1);
98
str_append_c(dest, ')');
100
case SEARCH_KEYWORDS: {
101
const struct mail_keywords *kw = arg->value.keywords;
102
const ARRAY_TYPE(keywords) *names_arr;
103
const char *const *namep;
108
str_printfa(dest, "KEYWORD %s", arg->value.str);
112
names_arr = mail_index_get_keywords(kw->index);
114
str_append_c(dest, '(');
115
for (i = 0; i < kw->count; i++) {
116
namep = array_idx(names_arr, kw->idx[i]);
118
str_append_c(dest, ' ');
119
str_printfa(dest, "KEYWORD %s", *namep);
121
str_append_c(dest, ')');
126
switch (arg->value.date_type) {
127
case MAIL_SEARCH_DATE_TYPE_SENT:
128
str_append(dest, "SENTBEFORE");
130
case MAIL_SEARCH_DATE_TYPE_RECEIVED:
131
str_append(dest, "BEFORE");
133
case MAIL_SEARCH_DATE_TYPE_SAVED:
134
str_append(dest, "X-SAVEDBEFORE");
137
if (mail_search_arg_to_imap_date(dest, arg))
139
else if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_RECEIVED ||
140
arg->value.time > ioloop_time) {
141
*error_r = t_strdup_printf(
142
"SEARCH_BEFORE can't be written as IMAP for timestamp %ld (type=%d, use_tz=%d)",
143
(long)arg->value.time, arg->value.date_type,
144
(arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) != 0);
147
str_truncate(dest, start_pos);
148
str_printfa(dest, "OLDER %u",
149
(unsigned int)(ioloop_time - arg->value.time + 1));
153
switch (arg->value.date_type) {
154
case MAIL_SEARCH_DATE_TYPE_SENT:
155
str_append(dest, "SENTON");
157
case MAIL_SEARCH_DATE_TYPE_RECEIVED:
158
str_append(dest, "ON");
160
case MAIL_SEARCH_DATE_TYPE_SAVED:
161
str_append(dest, "X-SAVEDON");
164
if (!mail_search_arg_to_imap_date(dest, arg)) {
165
*error_r = t_strdup_printf(
166
"SEARCH_ON can't be written as IMAP for timestamp %ld (type=%d, use_tz=%d)",
167
(long)arg->value.time, arg->value.date_type,
168
(arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) != 0);
173
switch (arg->value.date_type) {
174
case MAIL_SEARCH_DATE_TYPE_SENT:
175
str_append(dest, "SENTSINCE");
177
case MAIL_SEARCH_DATE_TYPE_RECEIVED:
178
str_append(dest, "SINCE");
180
case MAIL_SEARCH_DATE_TYPE_SAVED:
181
str_append(dest, "X-SAVEDSINCE");
184
if (mail_search_arg_to_imap_date(dest, arg))
186
else if (arg->value.date_type != MAIL_SEARCH_DATE_TYPE_RECEIVED ||
187
arg->value.time >= ioloop_time) {
188
*error_r = t_strdup_printf(
189
"SEARCH_SINCE can't be written as IMAP for timestamp %ld (type=%d, use_tz=%d)",
190
(long)arg->value.time, arg->value.date_type,
191
(arg->value.search_flags & MAIL_SEARCH_ARG_FLAG_USE_TZ) != 0);
194
str_truncate(dest, start_pos);
195
str_printfa(dest, "YOUNGER %u",
196
(unsigned int)(ioloop_time - arg->value.time));
200
str_printfa(dest, "SMALLER %llu", (unsigned long long)arg->value.size);
203
str_printfa(dest, "LARGER %llu", (unsigned long long)arg->value.size);
206
case SEARCH_HEADER_ADDRESS:
207
case SEARCH_HEADER_COMPRESS_LWSP:
208
if (strcasecmp(arg->hdr_field_name, "From") == 0 ||
209
strcasecmp(arg->hdr_field_name, "To") == 0 ||
210
strcasecmp(arg->hdr_field_name, "Cc") == 0 ||
211
strcasecmp(arg->hdr_field_name, "Bcc") == 0 ||
212
strcasecmp(arg->hdr_field_name, "Subject") == 0)
213
str_append(dest, t_str_ucase(arg->hdr_field_name));
215
str_append(dest, "HEADER ");
216
imap_append_astring(dest, arg->hdr_field_name);
218
str_append_c(dest, ' ');
219
imap_append_astring(dest, arg->value.str);
223
str_append(dest, "BODY ");
224
imap_append_astring(dest, arg->value.str);
227
str_append(dest, "TEXT ");
228
imap_append_astring(dest, arg->value.str);
232
case SEARCH_MODSEQ: {
233
bool extended_output = FALSE;
235
str_append(dest, "MODSEQ ");
236
if (arg->value.str != NULL) {
237
str_printfa(dest, "/flags/%s", arg->value.str);
238
extended_output = TRUE;
239
} else if (arg->value.flags != 0) {
240
str_append(dest, "/flags/");
241
imap_write_flags(dest, arg->value.flags, NULL);
242
extended_output = TRUE;
244
if (extended_output) {
245
str_append_c(dest, ' ');
246
switch (arg->value.modseq->type) {
247
case MAIL_SEARCH_MODSEQ_TYPE_ANY:
248
str_append(dest, "all");
250
case MAIL_SEARCH_MODSEQ_TYPE_PRIVATE:
251
str_append(dest, "priv");
253
case MAIL_SEARCH_MODSEQ_TYPE_SHARED:
254
str_append(dest, "shared");
257
str_append_c(dest, ' ');
259
str_printfa(dest, "%llu", (unsigned long long)arg->value.modseq->modseq);
262
case SEARCH_INTHREAD:
263
str_append(dest, "INTHREAD ");
264
imap_append_astring(dest, mail_thread_type_to_str(arg->value.thread_type));
265
str_append_c(dest, ' ');
266
if (!mail_search_subargs_to_imap(dest, arg->value.subargs,
271
str_append(dest, "X-GUID ");
272
imap_append_astring(dest, arg->value.str);
275
*error_r = "SEARCH_MAILBOX can't be written as IMAP";
277
case SEARCH_MAILBOX_GUID:
278
*error_r = "SEARCH_MAILBOX_GUID can't be written as IMAP";
280
case SEARCH_MAILBOX_GLOB:
281
str_append(dest, "X-MAILBOX ");
282
imap_append_astring(dest, arg->value.str);
284
case SEARCH_REAL_UID:
285
str_append(dest, "X-REAL-UID ");
286
imap_write_seq_range(dest, &arg->value.seqset);
292
bool mail_search_args_to_imap(string_t *dest, const struct mail_search_arg *args,
293
const char **error_r)
295
const struct mail_search_arg *arg;
297
for (arg = args; arg != NULL; arg = arg->next) {
298
if (!mail_search_arg_to_imap(dest, arg, error_r))
300
if (arg->next != NULL)
301
str_append_c(dest, ' ');