Mercurial > dovecot > core-2.2
annotate src/lib-storage/index/index-search.c @ 10:82b7de533f98 HEAD
s/user_data/context/ and some s/Data/Context/
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Thu, 22 Aug 2002 15:48:38 +0300 |
parents | 1b34ec11fff8 |
children | b2bc8d2e56ff |
rev | line source |
---|---|
0 | 1 /* Copyright (C) 2002 Timo Sirainen */ |
2 | |
3 #include "lib.h" | |
4 #include "iobuffer.h" | |
5 #include "mmap-util.h" | |
6 #include "rfc822-date.h" | |
7 #include "rfc822-tokenize.h" | |
8 #include "index-storage.h" | |
9 #include "mail-search.h" | |
10 | |
11 #include <stdlib.h> | |
12 #include <ctype.h> | |
13 | |
14 #define ARG_SET_RESULT(arg, res) \ | |
15 STMT_START { \ | |
16 (arg)->result = !(arg)->not ? (res) : -(res); \ | |
17 } STMT_END | |
18 | |
19 typedef struct { | |
20 IndexMailbox *ibox; | |
21 MailIndexRecord *rec; | |
22 unsigned int seq; | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
23 } SearchIndexContext; |
0 | 24 |
25 typedef struct { | |
26 MailSearchArg *args; | |
27 int custom_header; | |
28 | |
29 const char *name, *value; | |
30 unsigned int name_len, value_len; | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
31 } SearchHeaderContext; |
0 | 32 |
33 typedef struct { | |
34 MailSearchArg *args; | |
35 const char *msg; | |
36 size_t size; | |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
37 |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
38 unsigned int max_searchword_len; |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
39 } SearchTextContext; |
0 | 40 |
41 /* truncate timestamp to day */ | |
42 static time_t timestamp_trunc(time_t t) | |
43 { | |
44 struct tm *tm; | |
45 | |
46 tm = localtime(&t); | |
47 tm->tm_hour = tm->tm_min = tm->tm_sec = 0; | |
48 | |
49 return mktime(tm); | |
50 } | |
51 | |
52 static int msgset_contains(const char *set, unsigned int match_num, | |
53 unsigned int max_num) | |
54 { | |
55 unsigned int num, num2; | |
56 | |
57 while (*set != '\0') { | |
58 if (*set == '*') { | |
59 set++; | |
60 num = max_num; | |
61 } else { | |
62 num = 0; | |
63 while (*set >= '0' && *set <= '9') { | |
64 num = num*10 + (*set-'0'); | |
65 set++; | |
66 } | |
67 } | |
68 | |
69 if (*set == ',' || *set == '\0') { | |
70 if (num == match_num) | |
71 return TRUE; | |
72 if (*set == '\0') | |
73 return FALSE; | |
74 } else if (*set == ':') { | |
75 if (*set == '*') { | |
76 set++; | |
77 | |
78 if (match_num >= num && num <= max_num) | |
79 return TRUE; | |
80 } else { | |
81 num2 = 0; | |
82 while (*set >= '0' && *set <= '9') { | |
83 num2 = num2*10 + (*set-'0'); | |
84 set++; | |
85 } | |
86 | |
87 if (match_num >= num && match_num <= num2) | |
88 return TRUE; | |
89 } | |
90 | |
91 if (*set != ',') | |
92 return FALSE; | |
93 } | |
94 | |
95 set++; | |
96 } | |
97 | |
98 return FALSE; | |
99 } | |
100 | |
101 /* Returns >0 = matched, 0 = not matched, -1 = unknown */ | |
102 static int search_arg_match_index(IndexMailbox *ibox, MailIndexRecord *rec, | |
103 unsigned int seq, MailSearchArgType type, | |
104 const char *value) | |
105 { | |
106 time_t t; | |
107 | |
108 switch (type) { | |
109 case SEARCH_ALL: | |
110 return TRUE; | |
111 case SEARCH_SET: | |
112 return msgset_contains(value, seq, ibox->synced_messages_count); | |
113 case SEARCH_UID: | |
114 return msgset_contains(value, rec->uid, | |
115 ibox->synced_messages_count); | |
116 | |
117 /* flags */ | |
118 case SEARCH_ANSWERED: | |
119 return rec->msg_flags & MAIL_ANSWERED; | |
120 case SEARCH_DELETED: | |
121 return rec->msg_flags & MAIL_DELETED; | |
122 case SEARCH_DRAFT: | |
123 return rec->msg_flags & MAIL_DRAFT; | |
124 case SEARCH_FLAGGED: | |
125 return rec->msg_flags & MAIL_FLAGGED; | |
126 case SEARCH_SEEN: | |
127 return rec->msg_flags & MAIL_SEEN; | |
128 case SEARCH_RECENT: | |
129 return rec->uid >= ibox->index->first_recent_uid; | |
130 case SEARCH_KEYWORD: | |
131 return FALSE; | |
132 | |
133 /* dates */ | |
134 case SEARCH_BEFORE: | |
135 if (!rfc822_parse_date(value, &t)) | |
136 return FALSE; | |
137 return rec->internal_date < timestamp_trunc(t); | |
138 case SEARCH_ON: | |
139 if (!rfc822_parse_date(value, &t)) | |
140 return FALSE; | |
141 t = timestamp_trunc(t); | |
142 return rec->internal_date >= t && | |
143 rec->internal_date < t + 3600*24; | |
144 case SEARCH_SINCE: | |
145 if (!rfc822_parse_date(value, &t)) | |
146 return FALSE; | |
147 return rec->internal_date >= timestamp_trunc(t); | |
148 | |
149 case SEARCH_SENTBEFORE: | |
150 if (!rfc822_parse_date(value, &t)) | |
151 return FALSE; | |
152 return rec->sent_date < timestamp_trunc(t); | |
153 case SEARCH_SENTON: | |
154 if (!rfc822_parse_date(value, &t)) | |
155 return FALSE; | |
156 t = timestamp_trunc(t); | |
157 return rec->sent_date >= t && | |
158 rec->sent_date < t + 3600*24; | |
159 case SEARCH_SENTSINCE: | |
160 if (!rfc822_parse_date(value, &t)) | |
161 return FALSE; | |
162 return rec->sent_date >= timestamp_trunc(t); | |
163 | |
164 /* sizes */ | |
165 case SEARCH_SMALLER: | |
166 return rec->full_virtual_size < strtoul(value, NULL, 10); | |
167 case SEARCH_LARGER: | |
168 return rec->full_virtual_size > strtoul(value, NULL, 10); | |
169 | |
170 default: | |
171 return -1; | |
172 } | |
173 } | |
174 | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
175 static void search_index_arg(MailSearchArg *arg, void *context) |
0 | 176 { |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
177 SearchIndexContext *ctx = context; |
0 | 178 |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
179 switch (search_arg_match_index(ctx->ibox, ctx->rec, ctx->seq, |
0 | 180 arg->type, arg->value.str)) { |
181 case -1: | |
182 /* unknown */ | |
183 break; | |
184 case 0: | |
185 ARG_SET_RESULT(arg, -1); | |
186 break; | |
187 default: | |
188 ARG_SET_RESULT(arg, 1); | |
189 break; | |
190 } | |
191 } | |
192 | |
193 static int match_field(MailIndex *index, MailIndexRecord *rec, | |
194 MailField field, const char *value) | |
195 { | |
196 const char *field_value; | |
197 unsigned int i, value_len; | |
198 | |
199 field_value = index->lookup_field(index, rec, field); | |
200 if (field_value == NULL) | |
201 return -1; | |
202 | |
203 /* note: value is already uppercased */ | |
204 value_len = strlen(value); | |
205 for (i = 0; field_value[i] != '\0'; i++) { | |
206 if (value[0] == i_toupper(field_value[i]) && | |
207 strncasecmp(value, field_value+i, value_len) == 0) | |
208 return 1; | |
209 } | |
210 | |
211 return 0; | |
212 } | |
213 | |
214 /* Returns >0 = matched, 0 = not matched, -1 = unknown */ | |
215 static int search_arg_match_cached(MailIndex *index, MailIndexRecord *rec, | |
216 MailSearchArgType type, const char *value) | |
217 { | |
218 switch (type) { | |
219 case SEARCH_FROM: | |
220 return match_field(index, rec, FIELD_TYPE_FROM, value); | |
221 case SEARCH_TO: | |
222 return match_field(index, rec, FIELD_TYPE_TO, value); | |
223 case SEARCH_CC: | |
224 return match_field(index, rec, FIELD_TYPE_CC, value); | |
225 case SEARCH_BCC: | |
226 return match_field(index, rec, FIELD_TYPE_BCC, value); | |
227 case SEARCH_SUBJECT: | |
228 return match_field(index, rec, FIELD_TYPE_SUBJECT, value); | |
229 default: | |
230 return -1; | |
231 } | |
232 } | |
233 | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
234 static void search_cached_arg(MailSearchArg *arg, void *context) |
0 | 235 { |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
236 SearchIndexContext *ctx = context; |
0 | 237 |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
238 switch (search_arg_match_cached(ctx->ibox->index, ctx->rec, |
0 | 239 arg->type, arg->value.str)) { |
240 case -1: | |
241 /* unknown */ | |
242 break; | |
243 case 0: | |
244 ARG_SET_RESULT(arg, -1); | |
245 break; | |
246 default: | |
247 ARG_SET_RESULT(arg, 1); | |
248 break; | |
249 } | |
250 } | |
251 | |
252 /* needle must be uppercased */ | |
253 static int header_value_match(const char *haystack, unsigned int haystack_len, | |
254 const char *needle) | |
255 { | |
256 const char *n; | |
257 unsigned int i, j, needle_len, max; | |
258 | |
259 if (*needle == '\0') | |
260 return TRUE; | |
261 | |
262 needle_len = strlen(needle); | |
263 if (haystack_len < needle_len) | |
264 return FALSE; | |
265 | |
266 max = haystack_len - needle_len; | |
267 for (i = 0; i <= max; i++) { | |
268 if (needle[0] != i_toupper(haystack[i])) | |
269 continue; | |
270 | |
271 for (j = i, n = needle; j < haystack_len; j++) { | |
272 if (haystack[j] == '\r') { | |
273 if (j+1 != haystack_len) | |
274 j++; | |
275 } | |
276 | |
277 if (haystack[j] == '\n' && j+1 < haystack_len && | |
278 IS_LWSP(haystack[j+1])) { | |
279 /* long header continuation */ | |
280 j++; | |
281 } | |
282 | |
283 if (*n++ != i_toupper(haystack[j])) | |
284 break; | |
285 | |
286 if (*n == '\0') | |
287 return 1; | |
288 } | |
289 } | |
290 | |
291 return -1; | |
292 } | |
293 | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
294 static void search_header_arg(MailSearchArg *arg, void *context) |
0 | 295 { |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
296 SearchHeaderContext *ctx = context; |
0 | 297 const char *value; |
298 unsigned int len; | |
299 int ret; | |
300 | |
301 /* first check that the field name matches to argument. */ | |
302 switch (arg->type) { | |
303 case SEARCH_FROM: | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
304 if (ctx->name_len != 4 || |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
305 strncasecmp(ctx->name, "From", 4) != 0) |
0 | 306 return; |
307 value = arg->value.str; | |
308 break; | |
309 case SEARCH_TO: | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
310 if (ctx->name_len != 2 || |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
311 strncasecmp(ctx->name, "To", 2) != 0) |
0 | 312 return; |
313 value = arg->value.str; | |
314 break; | |
315 case SEARCH_CC: | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
316 if (ctx->name_len != 2 || |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
317 strncasecmp(ctx->name, "Cc", 2) != 0) |
0 | 318 return; |
319 value = arg->value.str; | |
320 break; | |
321 case SEARCH_BCC: | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
322 if (ctx->name_len != 3 || |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
323 strncasecmp(ctx->name, "Bcc", 3) != 0) |
0 | 324 return; |
325 value = arg->value.str; | |
326 break; | |
327 case SEARCH_SUBJECT: | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
328 if (ctx->name_len != 7 || |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
329 strncasecmp(ctx->name, "Subject", 7) != 0) |
0 | 330 return; |
331 value = arg->value.str; | |
332 break; | |
333 case SEARCH_HEADER: | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
334 ctx->custom_header = TRUE; |
0 | 335 |
336 len = strlen(arg->value.str); | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
337 if (ctx->name_len != len || |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
338 strncasecmp(ctx->name, arg->value.str, len) != 0) |
0 | 339 return; |
340 | |
341 value = arg->hdr_value; | |
342 default: | |
343 return; | |
344 } | |
345 | |
346 /* then check if the value matches */ | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
347 ret = header_value_match(ctx->value, ctx->value_len, value); |
0 | 348 ARG_SET_RESULT(arg, ret); |
349 } | |
350 | |
351 static void search_header(MessagePart *part __attr_unused__, | |
352 const char *name, unsigned int name_len, | |
353 const char *value, unsigned int value_len, | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
354 void *context) |
0 | 355 { |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
356 SearchHeaderContext *ctx = context; |
0 | 357 |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
358 if (ctx->custom_header || |
0 | 359 (name_len == 4 && strncasecmp(name, "From", 4) == 0) || |
360 (name_len == 2 && strncasecmp(name, "To", 2) == 0) || | |
361 (name_len == 2 && strncasecmp(name, "Cc", 2) == 0) || | |
362 (name_len == 3 && strncasecmp(name, "Bcc", 3) == 0) || | |
363 (name_len == 7 && strncasecmp(name, "Subject", 7) == 0)) { | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
364 ctx->name = name; |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
365 ctx->value = value; |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
366 ctx->name_len = name_len; |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
367 ctx->value_len = value_len; |
0 | 368 |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
369 ctx->custom_header = FALSE; |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
370 mail_search_args_foreach(ctx->args, search_header_arg, ctx); |
0 | 371 } |
372 } | |
373 | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
374 static void search_text(MailSearchArg *arg, SearchTextContext *ctx) |
0 | 375 { |
376 const char *p; | |
377 unsigned int i, len, max; | |
378 | |
379 if (arg->result != 0) | |
380 return; | |
381 | |
382 len = strlen(arg->value.str); | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
383 if (len > ctx->max_searchword_len) |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
384 ctx->max_searchword_len = len; |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
385 |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
386 if (ctx->size >= len) { |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
387 max = ctx->size-len; |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
388 for (i = 0, p = ctx->msg; i <= max; i++, p++) { |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
389 if (i_toupper(*p) == arg->value.str[0] && |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
390 strncasecmp(p, arg->value.str, len) == 0) { |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
391 /* match */ |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
392 ARG_SET_RESULT(arg, 1); |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
393 return; |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
394 } |
0 | 395 } |
396 } | |
397 } | |
398 | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
399 static void search_text_header(MailSearchArg *arg, void *context) |
0 | 400 { |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
401 SearchTextContext *ctx = context; |
0 | 402 |
403 if (arg->type == SEARCH_TEXT) | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
404 search_text(arg, ctx); |
0 | 405 } |
406 | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
407 static void search_text_body(MailSearchArg *arg, void *context) |
0 | 408 { |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
409 SearchTextContext *ctx = context; |
0 | 410 |
411 if (arg->type == SEARCH_TEXT || arg->type == SEARCH_BODY) | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
412 search_text(arg, ctx); |
0 | 413 } |
414 | |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
415 static void search_text_set_unmatched(MailSearchArg *arg, |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
416 void *context __attr_unused__) |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
417 { |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
418 if (arg->type == SEARCH_TEXT || arg->type == SEARCH_BODY) |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
419 ARG_SET_RESULT(arg, -1); |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
420 } |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
421 |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
422 static void search_arg_match_data(IOBuffer *inbuf, unsigned int max_size, |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
423 MailSearchArg *args, |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
424 MailSearchForeachFunc search_func) |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
425 { |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
426 SearchTextContext ctx; |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
427 unsigned int size; |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
428 int ret; |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
429 |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
430 memset(&ctx, 0, sizeof(ctx)); |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
431 ctx.args = args; |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
432 |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
433 /* do this in blocks: read data, compare it for all search words, skip |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
434 for block size - (strlen(largest_searchword)-1) and continue. */ |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
435 while (max_size > 0 && |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
436 (ret = io_buffer_read_max(inbuf, max_size)) > 0) { |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
437 ctx.msg = io_buffer_get_data(inbuf, &size); |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
438 if (size > 0) { |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
439 ctx.size = max_size < size ? max_size : size; |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
440 max_size -= ctx.size; |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
441 |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
442 mail_search_args_foreach(args, search_func, &ctx); |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
443 |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
444 if (ctx.max_searchword_len < size) |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
445 size -= ctx.max_searchword_len-1; |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
446 io_buffer_skip(inbuf, size); |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
447 } |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
448 } |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
449 } |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
450 |
0 | 451 static int search_arg_match_text(IndexMailbox *ibox, MailIndexRecord *rec, |
452 MailSearchArg *args) | |
453 { | |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
454 IOBuffer *inbuf; |
0 | 455 int have_headers, have_body, have_text; |
456 | |
457 /* first check what we need to use */ | |
458 mail_search_args_analyze(args, &have_headers, &have_body, &have_text); | |
459 if (!have_headers && !have_body && !have_text) | |
460 return TRUE; | |
461 | |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
462 inbuf = ibox->index->open_mail(ibox->index, rec); |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
463 if (inbuf == NULL) |
0 | 464 return FALSE; |
465 | |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
466 if (have_headers) { |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
467 SearchHeaderContext ctx; |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
468 |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
469 memset(&ctx, 0, sizeof(ctx)); |
0 | 470 |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
471 /* header checks */ |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
472 ctx.custom_header = TRUE; |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
473 ctx.args = args; |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
474 message_parse_header(NULL, inbuf, NULL, search_header, &ctx); |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
475 } |
0 | 476 |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
477 if (have_text) { |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
478 if (inbuf->offset != 0) { |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
479 /* need to rewind back to beginning of headers */ |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
480 if (!io_buffer_seek(inbuf, 0)) { |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
481 i_error("io_buffer_seek() failed: %m"); |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
482 return FALSE; |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
483 } |
0 | 484 } |
485 | |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
486 search_arg_match_data(inbuf, rec->header_size, |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
487 args, search_text_header); |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
488 } |
0 | 489 |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
490 if (have_text || have_body) { |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
491 if (inbuf->offset != (off_t)rec->header_size) { |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
492 /* skip over headers */ |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
493 i_assert(inbuf->offset == 0); |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
494 io_buffer_skip(inbuf, rec->header_size); |
0 | 495 } |
496 | |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
497 search_arg_match_data(inbuf, UINT_MAX, args, search_text_body); |
0 | 498 |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
499 /* set the rest as unmatched */ |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
500 mail_search_args_foreach(args, search_text_set_unmatched, NULL); |
0 | 501 } |
502 | |
5
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
503 (void)close(inbuf->fd); |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
504 io_buffer_destroy(inbuf); |
1b34ec11fff8
Message data is parsed in blocks (no longer entirely mmap()ed). Several
Timo Sirainen <tss@iki.fi>
parents:
0
diff
changeset
|
505 return TRUE; |
0 | 506 } |
507 | |
508 static void seq_update(const char *set, unsigned int *first_seq, | |
509 unsigned int *last_seq, unsigned int max_value) | |
510 { | |
511 unsigned int seq; | |
512 | |
513 while (*set != '\0') { | |
514 if (*set == '*') { | |
515 seq = max_value; | |
516 set++; | |
517 } else { | |
518 seq = 0; | |
519 while (*set >= '0' && *set <= '9') { | |
520 seq = seq*10 + (*set-'0'); | |
521 set++; | |
522 } | |
523 } | |
524 | |
525 if (seq != 0) { | |
526 if (*first_seq == 0 || seq < *first_seq) | |
527 *first_seq = seq; | |
528 if (*last_seq == 0 || seq > *last_seq) | |
529 *last_seq = seq; | |
530 } | |
531 | |
532 seq++; | |
533 } | |
534 } | |
535 | |
536 static void search_get_sequid(IndexMailbox *ibox, MailSearchArg *args, | |
537 unsigned int *first_seq, unsigned int *last_seq, | |
538 unsigned int *first_uid, unsigned int *last_uid) | |
539 { | |
540 for (; args != NULL; args = args->next) { | |
541 if (args->type == SEARCH_OR || args->type == SEARCH_SUB) { | |
542 search_get_sequid(ibox, args->value.subargs, | |
543 first_seq, last_seq, | |
544 first_uid, last_uid); | |
545 } if (args->type == SEARCH_SET) { | |
546 seq_update(args->value.str, first_seq, last_seq, | |
547 ibox->synced_messages_count); | |
548 } else if (args->type == SEARCH_UID) { | |
549 seq_update(args->value.str, first_uid, last_uid, | |
550 ibox->index->header->next_uid-1); | |
551 } else if (args->type == SEARCH_ALL) { | |
552 /* go through everything */ | |
553 *first_seq = 1; | |
554 *last_seq = ibox->synced_messages_count; | |
555 return; | |
556 } | |
557 } | |
558 } | |
559 | |
560 static void search_get_sequences(IndexMailbox *ibox, MailSearchArg *args, | |
561 unsigned int *first_seq, | |
562 unsigned int *last_seq) | |
563 { | |
564 MailIndexRecord *rec; | |
565 unsigned int seq, first_uid, last_uid; | |
566 | |
567 *first_seq = *last_seq = 0; | |
568 first_uid = last_uid = 0; | |
569 | |
570 search_get_sequid(ibox, args, first_seq, last_seq, | |
571 &first_uid, &last_uid); | |
572 | |
573 if (first_uid != 0 && (*first_seq != 1 || | |
574 *last_seq != ibox->synced_messages_count)) { | |
575 /* UIDs were used - see if they affect the sequences */ | |
576 rec = ibox->index->lookup_uid_range(ibox->index, | |
577 first_uid, last_uid); | |
578 if (rec != NULL) { | |
579 /* update lower UID */ | |
580 seq = ibox->index->get_sequence(ibox->index, rec); | |
581 if (seq < *first_seq) | |
582 *first_seq = seq; | |
583 | |
584 /* update higher UID .. except we don't really | |
585 know it and it'd be uselessly slow to find it. | |
586 use a kludgy method which might limit the | |
587 sequences. */ | |
588 seq += last_uid-first_uid; | |
589 if (seq >= ibox->synced_messages_count) | |
590 seq = ibox->synced_messages_count; | |
591 | |
592 if (seq > *last_seq) | |
593 *last_seq = seq; | |
594 } | |
595 } | |
596 | |
597 if (*first_seq == 0) | |
598 *first_seq = 1; | |
599 if (*last_seq == 0) | |
600 *last_seq = ibox->synced_messages_count; | |
601 } | |
602 | |
603 static void search_messages(IndexMailbox *ibox, MailSearchArg *args, | |
604 IOBuffer *outbuf, int uid_result) | |
605 { | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
606 SearchIndexContext ctx; |
0 | 607 MailIndexRecord *rec; |
608 unsigned int first_seq, last_seq, seq; | |
609 char num[MAX_INT_STRLEN+10]; | |
610 | |
611 /* see if we can limit the records we look at */ | |
612 search_get_sequences(ibox, args, &first_seq, &last_seq); | |
613 | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
614 ctx.ibox = ibox; |
0 | 615 |
616 rec = ibox->index->lookup(ibox->index, first_seq); | |
617 for (seq = first_seq; rec != NULL && seq <= last_seq; seq++) { | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
618 ctx.rec = rec; |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
619 ctx.seq = seq; |
0 | 620 |
621 mail_search_args_reset(args); | |
622 | |
10
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
623 mail_search_args_foreach(args, search_index_arg, &ctx); |
82b7de533f98
s/user_data/context/ and some s/Data/Context/
Timo Sirainen <tss@iki.fi>
parents:
5
diff
changeset
|
624 mail_search_args_foreach(args, search_cached_arg, &ctx); |
0 | 625 |
626 if (search_arg_match_text(ibox, rec, args) && | |
627 args->result == 1) { | |
628 i_snprintf(num, sizeof(num), " %u", | |
629 uid_result ? rec->uid : seq); | |
630 io_buffer_send(outbuf, num, strlen(num)); | |
631 } | |
632 rec = ibox->index->next(ibox->index, rec); | |
633 } | |
634 } | |
635 | |
636 int index_storage_search(Mailbox *box, MailSearchArg *args, | |
637 IOBuffer *outbuf, int uid_result) | |
638 { | |
639 IndexMailbox *ibox = (IndexMailbox *) box; | |
640 int failed; | |
641 | |
642 if (!ibox->index->set_lock(ibox->index, MAIL_LOCK_SHARED)) | |
643 failed = TRUE; | |
644 else { | |
645 io_buffer_send(outbuf, "* SEARCH", 8); | |
646 | |
647 search_messages(ibox, args, outbuf, uid_result); | |
648 failed = !ibox->index->set_lock(ibox->index, | |
649 MAIL_LOCK_UNLOCK); | |
650 io_buffer_send(outbuf, "\r\n", 2); | |
651 } | |
652 | |
653 if (failed) | |
654 (void)mail_storage_set_index_error(ibox); | |
655 | |
656 return !failed; | |
657 } |