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