1 /* Copyright (c) 2006-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "array.h"
5 #include "str.h"
6 #include "unichar.h"
7 #include "message-address.h"
8 #include "message-header-decode.h"
9 #include "imap-base-subject.h"
10 #include "index-storage.h"
11 #include "index-sort-private.h"
12
13
14 struct mail_sort_node_date {
15 uint32_t seq;
16 time_t date;
17 };
18 ARRAY_DEFINE_TYPE(mail_sort_node_date, struct mail_sort_node_date);
19
20 struct mail_sort_node_size {
21 uint32_t seq;
22 uoff_t size;
23 };
24 ARRAY_DEFINE_TYPE(mail_sort_node_size, struct mail_sort_node_size);
25
26 struct mail_sort_node_float {
27 uint32_t seq;
28 float num;
29 };
30 ARRAY_DEFINE_TYPE(mail_sort_node_float, struct mail_sort_node_float);
31
32 struct sort_cmp_context {
33 struct mail_search_sort_program *program;
34 bool reverse;
35 };
36
37 static struct sort_cmp_context static_node_cmp_context;
38
39 static void
index_sort_program_set_mail_failed(struct mail_search_sort_program * program,struct mail * mail)40 index_sort_program_set_mail_failed(struct mail_search_sort_program *program,
41 struct mail *mail)
42 {
43 switch (mailbox_get_last_mail_error(mail->box)) {
44 case MAIL_ERROR_EXPUNGED:
45 break;
46 case MAIL_ERROR_LOOKUP_ABORTED:
47 /* just change the error message */
48 i_assert(program->slow_mails_left == 0);
49 mail_storage_set_error(program->t->box->storage, MAIL_ERROR_LIMIT,
50 "Requested sort would have taken too long.");
51 /* fall through */
52 default:
53 program->failed = TRUE;
54 break;
55 }
56 }
57
58 static time_t
index_sort_program_set_date_failed(struct mail_search_sort_program * program,struct mail * mail)59 index_sort_program_set_date_failed(struct mail_search_sort_program *program,
60 struct mail *mail)
61 {
62 index_sort_program_set_mail_failed(program, mail);
63
64 if (mailbox_get_last_mail_error(mail->box) == MAIL_ERROR_LIMIT) {
65 /* limit reached - sort the rest of the mails at the end of
66 the list by their UIDs */
67 return LONG_MAX;
68 } else {
69 /* expunged / some other error - sort in the beginning */
70 return 0;
71 }
72 }
73
74 static void
index_sort_list_add_arrival(struct mail_search_sort_program * program,struct mail * mail)75 index_sort_list_add_arrival(struct mail_search_sort_program *program,
76 struct mail *mail)
77 {
78 ARRAY_TYPE(mail_sort_node_date) *nodes = program->context;
79 struct mail_sort_node_date *node;
80
81 node = array_append_space(nodes);
82 node->seq = mail->seq;
83 if (mail_get_received_date(mail, &node->date) < 0)
84 node->date = index_sort_program_set_date_failed(program, mail);
85 }
86
87 static void
index_sort_list_add_date(struct mail_search_sort_program * program,struct mail * mail)88 index_sort_list_add_date(struct mail_search_sort_program *program,
89 struct mail *mail)
90 {
91 ARRAY_TYPE(mail_sort_node_date) *nodes = program->context;
92 struct mail_sort_node_date *node;
93 int tz;
94
95 node = array_append_space(nodes);
96 node->seq = mail->seq;
97 if (mail_get_date(mail, &node->date, &tz) < 0)
98 node->date = index_sort_program_set_date_failed(program, mail);
99 else if (node->date == 0) {
100 if (mail_get_received_date(mail, &node->date) < 0)
101 node->date = index_sort_program_set_date_failed(program, mail);
102 }
103 }
104
105 static void
index_sort_list_add_size(struct mail_search_sort_program * program,struct mail * mail)106 index_sort_list_add_size(struct mail_search_sort_program *program,
107 struct mail *mail)
108 {
109 ARRAY_TYPE(mail_sort_node_size) *nodes = program->context;
110 struct mail_sort_node_size *node;
111
112 node = array_append_space(nodes);
113 node->seq = mail->seq;
114 if (mail_get_virtual_size(mail, &node->size) < 0) {
115 index_sort_program_set_mail_failed(program, mail);
116 node->size = 0;
117 }
118 }
119
index_sort_get_pop3_order(struct mail * mail,uoff_t * size_r)120 static int index_sort_get_pop3_order(struct mail *mail, uoff_t *size_r)
121 {
122 const char *str;
123
124 if (mail_get_special(mail, MAIL_FETCH_POP3_ORDER, &str) < 0) {
125 *size_r = (uint32_t)-1;
126 return -1;
127 }
128
129 if (str_to_uoff(str, size_r) < 0)
130 *size_r = (uint32_t)-1;
131 return 0;
132 }
133
134 static void
index_sort_list_add_pop3_order(struct mail_search_sort_program * program,struct mail * mail)135 index_sort_list_add_pop3_order(struct mail_search_sort_program *program,
136 struct mail *mail)
137 {
138 ARRAY_TYPE(mail_sort_node_size) *nodes = program->context;
139 struct mail_sort_node_size *node;
140
141 node = array_append_space(nodes);
142 node->seq = mail->seq;
143 (void)index_sort_get_pop3_order(mail, &node->size);
144 }
145
index_sort_get_relevancy(struct mail * mail,float * result_r)146 static int index_sort_get_relevancy(struct mail *mail, float *result_r)
147 {
148 const char *str;
149
150 if (mail_get_special(mail, MAIL_FETCH_SEARCH_RELEVANCY, &str) < 0) {
151 *result_r = 0;
152 return -1;
153 }
154 *result_r = strtod(str, NULL);
155 return 0;
156 }
157
158 static void
index_sort_list_add_relevancy(struct mail_search_sort_program * program,struct mail * mail)159 index_sort_list_add_relevancy(struct mail_search_sort_program *program,
160 struct mail *mail)
161 {
162 ARRAY_TYPE(mail_sort_node_float) *nodes = program->context;
163 struct mail_sort_node_float *node;
164
165 node = array_append_space(nodes);
166 node->seq = mail->seq;
167 (void)index_sort_get_relevancy(mail, &node->num);
168 }
169
index_sort_list_add(struct mail_search_sort_program * program,struct mail * mail)170 void index_sort_list_add(struct mail_search_sort_program *program,
171 struct mail *mail)
172 {
173 enum mail_access_type orig_access_type = mail->access_type;
174 bool prev_slow = mail->mail_stream_opened ||
175 mail->mail_metadata_accessed;
176
177 i_assert(mail->transaction == program->t);
178 /* if lookup_abort isn't NEVER, mail_sort_max_read_count handling
179 doesn't work right. */
180 i_assert(mail->lookup_abort == MAIL_LOOKUP_ABORT_NEVER);
181
182 if (program->slow_mails_left == 0)
183 mail->lookup_abort = MAIL_LOOKUP_ABORT_NOT_IN_CACHE;
184
185 mail->access_type = MAIL_ACCESS_TYPE_SORT;
186 T_BEGIN {
187 program->sort_list_add(program, mail);
188 } T_END;
189 mail->access_type = orig_access_type;
190
191 if (!prev_slow && (mail->mail_stream_opened ||
192 mail->mail_metadata_accessed)) {
193 i_assert(program->slow_mails_left > 0);
194 program->slow_mails_left--;
195 }
196 mail->lookup_abort = MAIL_LOOKUP_ABORT_NEVER;
197 }
198
sort_node_date_cmp(const struct mail_sort_node_date * n1,const struct mail_sort_node_date * n2)199 static int sort_node_date_cmp(const struct mail_sort_node_date *n1,
200 const struct mail_sort_node_date *n2)
201 {
202 struct sort_cmp_context *ctx = &static_node_cmp_context;
203
204 if (n1->date < n2->date)
205 return !ctx->reverse ? -1 : 1;
206 if (n1->date > n2->date)
207 return !ctx->reverse ? 1 : -1;
208
209 return index_sort_node_cmp_type(ctx->program,
210 ctx->program->sort_program + 1,
211 n1->seq, n2->seq);
212 }
213
214 static void
index_sort_list_finish_date(struct mail_search_sort_program * program)215 index_sort_list_finish_date(struct mail_search_sort_program *program)
216 {
217 ARRAY_TYPE(mail_sort_node_date) *nodes = program->context;
218
219 array_sort(nodes, sort_node_date_cmp);
220 memcpy(&program->seqs, nodes, sizeof(program->seqs));
221 i_free(nodes);
222 program->context = NULL;
223 }
224
sort_node_size_cmp(const struct mail_sort_node_size * n1,const struct mail_sort_node_size * n2)225 static int sort_node_size_cmp(const struct mail_sort_node_size *n1,
226 const struct mail_sort_node_size *n2)
227 {
228 struct sort_cmp_context *ctx = &static_node_cmp_context;
229
230 if (n1->size < n2->size)
231 return !ctx->reverse ? -1 : 1;
232 if (n1->size > n2->size)
233 return !ctx->reverse ? 1 : -1;
234
235 return index_sort_node_cmp_type(ctx->program,
236 ctx->program->sort_program + 1,
237 n1->seq, n2->seq);
238 }
239
240 static void
index_sort_list_finish_size(struct mail_search_sort_program * program)241 index_sort_list_finish_size(struct mail_search_sort_program *program)
242 {
243 ARRAY_TYPE(mail_sort_node_size) *nodes = program->context;
244
245 array_sort(nodes, sort_node_size_cmp);
246 memcpy(&program->seqs, nodes, sizeof(program->seqs));
247 i_free(nodes);
248 program->context = NULL;
249 }
250
sort_node_float_cmp(const struct mail_sort_node_float * n1,const struct mail_sort_node_float * n2)251 static int sort_node_float_cmp(const struct mail_sort_node_float *n1,
252 const struct mail_sort_node_float *n2)
253 {
254 struct sort_cmp_context *ctx = &static_node_cmp_context;
255
256 if (n1->num < n2->num)
257 return !ctx->reverse ? -1 : 1;
258 if (n1->num > n2->num)
259 return !ctx->reverse ? 1 : -1;
260
261 return index_sort_node_cmp_type(ctx->program,
262 ctx->program->sort_program + 1,
263 n1->seq, n2->seq);
264 }
265
266 static void
index_sort_list_finish_float(struct mail_search_sort_program * program)267 index_sort_list_finish_float(struct mail_search_sort_program *program)
268 {
269 ARRAY_TYPE(mail_sort_node_float) *nodes = program->context;
270
271 /* NOTE: higher relevancy is returned first, unlike with all
272 other number based sort keys, so temporarily reverse the search */
273 static_node_cmp_context.reverse = !static_node_cmp_context.reverse;
274 array_sort(nodes, sort_node_float_cmp);
275 static_node_cmp_context.reverse = !static_node_cmp_context.reverse;
276
277 memcpy(&program->seqs, nodes, sizeof(program->seqs));
278 i_free(nodes);
279 program->context = NULL;
280 }
281
index_sort_list_finish(struct mail_search_sort_program * program)282 void index_sort_list_finish(struct mail_search_sort_program *program)
283 {
284 i_zero(&static_node_cmp_context);
285 static_node_cmp_context.program = program;
286 static_node_cmp_context.reverse =
287 (program->sort_program[0] & MAIL_SORT_FLAG_REVERSE) != 0;
288
289 program->sort_list_finish(program);
290 }
291
index_sort_list_next(struct mail_search_sort_program * program,uint32_t * seq_r)292 bool index_sort_list_next(struct mail_search_sort_program *program,
293 uint32_t *seq_r)
294 {
295 const uint32_t *seqp;
296
297 if (program->iter_idx == array_count(&program->seqs))
298 return FALSE;
299
300 seqp = array_idx(&program->seqs, program->iter_idx++);
301 *seq_r = *seqp;
302 return TRUE;
303 }
304
305 static void
get_wanted_fields(struct mailbox * box,const enum mail_sort_type * sort_program,enum mail_fetch_field * wanted_fields_r,struct mailbox_header_lookup_ctx ** wanted_headers_r)306 get_wanted_fields(struct mailbox *box, const enum mail_sort_type *sort_program,
307 enum mail_fetch_field *wanted_fields_r,
308 struct mailbox_header_lookup_ctx **wanted_headers_r)
309 {
310 enum mail_fetch_field fields = 0;
311 ARRAY_TYPE(const_string) headers;
312 const char *hdr_name;
313
314 t_array_init(&headers, 4);
315 for (unsigned int i = 0; sort_program[i] != MAIL_SORT_END; i++) {
316 enum mail_sort_type sort_type =
317 sort_program[i] & MAIL_SORT_MASK;
318 switch (sort_type) {
319 case MAIL_SORT_ARRIVAL:
320 fields |= MAIL_FETCH_RECEIVED_DATE;
321 break;
322 case MAIL_SORT_CC:
323 hdr_name = "Cc";
324 array_push_back(&headers, &hdr_name);
325 break;
326 case MAIL_SORT_DATE:
327 fields |= MAIL_FETCH_DATE;
328 break;
329 case MAIL_SORT_FROM:
330 case MAIL_SORT_DISPLAYFROM:
331 hdr_name = "From";
332 array_push_back(&headers, &hdr_name);
333 break;
334 case MAIL_SORT_SIZE:
335 fields |= MAIL_FETCH_VIRTUAL_SIZE;
336 break;
337 case MAIL_SORT_SUBJECT:
338 hdr_name = "Subject";
339 array_push_back(&headers, &hdr_name);
340 break;
341 case MAIL_SORT_TO:
342 case MAIL_SORT_DISPLAYTO:
343 hdr_name = "To";
344 array_push_back(&headers, &hdr_name);
345 break;
346 case MAIL_SORT_RELEVANCY:
347 fields |= MAIL_FETCH_SEARCH_RELEVANCY;
348 break;
349 case MAIL_SORT_POP3_ORDER:
350 fields |= MAIL_FETCH_POP3_ORDER;
351 break;
352
353 case MAIL_SORT_MASK:
354 case MAIL_SORT_FLAG_REVERSE:
355 case MAIL_SORT_END:
356 i_unreached();
357 }
358 }
359 *wanted_fields_r = fields;
360 if (array_count(&headers) == 0)
361 *wanted_headers_r = NULL;
362 else {
363 array_append_zero(&headers);
364 *wanted_headers_r =
365 mailbox_header_lookup_init(box, array_idx(&headers, 0));
366 }
367 }
368
369 struct mail_search_sort_program *
index_sort_program_init(struct mailbox_transaction_context * t,const enum mail_sort_type * sort_program)370 index_sort_program_init(struct mailbox_transaction_context *t,
371 const enum mail_sort_type *sort_program)
372 {
373 struct mail_search_sort_program *program;
374 enum mail_fetch_field wanted_fields;
375 struct mailbox_header_lookup_ctx *wanted_headers;
376 unsigned int i;
377
378 if (sort_program == NULL || sort_program[0] == MAIL_SORT_END)
379 return NULL;
380
381 get_wanted_fields(t->box, sort_program, &wanted_fields, &wanted_headers);
382
383 /* we support internal sorting by the primary condition */
384 program = i_new(struct mail_search_sort_program, 1);
385 program->t = t;
386 program->temp_mail = mail_alloc(t, wanted_fields, wanted_headers);
387 program->temp_mail->access_type = MAIL_ACCESS_TYPE_SORT;
388 if (wanted_headers != NULL)
389 mailbox_header_lookup_unref(&wanted_headers);
390
391 program->slow_mails_left =
392 program->t->box->storage->set->mail_sort_max_read_count;
393 if (program->slow_mails_left == 0)
394 program->slow_mails_left = UINT_MAX;
395
396 for (i = 0; i < MAX_SORT_PROGRAM_SIZE; i++) {
397 program->sort_program[i] = sort_program[i];
398 if (sort_program[i] == MAIL_SORT_END)
399 break;
400 }
401 if (i == MAX_SORT_PROGRAM_SIZE)
402 i_panic("index_sort_program_init(): Invalid sort program");
403
404 switch (program->sort_program[0] & MAIL_SORT_MASK) {
405 case MAIL_SORT_ARRIVAL:
406 case MAIL_SORT_DATE: {
407 ARRAY_TYPE(mail_sort_node_date) *nodes;
408
409 nodes = i_malloc(sizeof(*nodes));
410 i_array_init(nodes, 128);
411
412 if ((program->sort_program[0] &
413 MAIL_SORT_MASK) == MAIL_SORT_ARRIVAL)
414 program->sort_list_add = index_sort_list_add_arrival;
415 else
416 program->sort_list_add = index_sort_list_add_date;
417 program->sort_list_finish = index_sort_list_finish_date;
418 program->context = nodes;
419 break;
420 }
421 case MAIL_SORT_SIZE: {
422 ARRAY_TYPE(mail_sort_node_size) *nodes;
423
424 nodes = i_malloc(sizeof(*nodes));
425 i_array_init(nodes, 128);
426 program->sort_list_add = index_sort_list_add_size;
427 program->sort_list_finish = index_sort_list_finish_size;
428 program->context = nodes;
429 break;
430 }
431 case MAIL_SORT_CC:
432 case MAIL_SORT_FROM:
433 case MAIL_SORT_SUBJECT:
434 case MAIL_SORT_TO:
435 case MAIL_SORT_DISPLAYFROM:
436 case MAIL_SORT_DISPLAYTO:
437 program->sort_list_add = index_sort_list_add_string;
438 program->sort_list_finish = index_sort_list_finish_string;
439 index_sort_list_init_string(program);
440 break;
441 case MAIL_SORT_RELEVANCY: {
442 ARRAY_TYPE(mail_sort_node_float) *nodes;
443
444 nodes = i_malloc(sizeof(*nodes));
445 i_array_init(nodes, 128);
446 program->sort_list_add = index_sort_list_add_relevancy;
447 program->sort_list_finish = index_sort_list_finish_float;
448 program->context = nodes;
449 break;
450 }
451 case MAIL_SORT_POP3_ORDER: {
452 ARRAY_TYPE(mail_sort_node_size) *nodes;
453
454 nodes = i_malloc(sizeof(*nodes));
455 i_array_init(nodes, 128);
456 program->sort_list_add = index_sort_list_add_pop3_order;
457 program->sort_list_finish = index_sort_list_finish_size;
458 program->context = nodes;
459 break;
460 }
461 default:
462 i_unreached();
463 }
464 return program;
465 }
466
index_sort_program_deinit(struct mail_search_sort_program ** _program)467 int index_sort_program_deinit(struct mail_search_sort_program **_program)
468 {
469 struct mail_search_sort_program *program = *_program;
470
471 *_program = NULL;
472
473 if (program->context != NULL)
474 index_sort_list_finish(program);
475 mail_free(&program->temp_mail);
476 array_free(&program->seqs);
477
478 int ret = program->failed ? -1 : 0;
479 i_free(program);
480 return ret;
481 }
482
483 static int
get_first_addr(struct mail * mail,const char * header,struct message_address ** addr_r)484 get_first_addr(struct mail *mail, const char *header,
485 struct message_address **addr_r)
486 {
487 const char *str;
488 int ret;
489
490 if ((ret = mail_get_first_header(mail, header, &str)) <= 0) {
491 *addr_r = NULL;
492 return ret;
493 }
494
495 *addr_r = message_address_parse(pool_datastack_create(),
496 (const unsigned char *)str,
497 strlen(str), 1,
498 MESSAGE_ADDRESS_PARSE_FLAG_FILL_MISSING);
499 return 0;
500 }
501
502 static int
get_first_mailbox(struct mail * mail,const char * header,const char ** mailbox_r)503 get_first_mailbox(struct mail *mail, const char *header, const char **mailbox_r)
504 {
505 struct message_address *addr;
506
507 if (get_first_addr(mail, header, &addr) < 0) {
508 *mailbox_r = "";
509 return -1;
510 }
511 *mailbox_r = addr != NULL && addr->mailbox != NULL ? addr->mailbox : "";
512 return 0;
513 }
514
515 static int
get_display_name(struct mail * mail,const char * header,const char ** name_r)516 get_display_name(struct mail *mail, const char *header, const char **name_r)
517 {
518 struct message_address *addr;
519
520 *name_r = "";
521
522 if (get_first_addr(mail, header, &addr) < 0)
523 return -1;
524 if (addr == NULL)
525 return 0;
526
527 if (addr->name != NULL) {
528 string_t *str;
529 size_t len = strlen(addr->name);
530
531 str = t_str_new(len*2);
532 (void)message_header_decode_utf8(
533 (const unsigned char *)addr->name, len, str, NULL);
534 if (str_len(str) > 0) {
535 *name_r = str_c(str);
536 return 0;
537 }
538 }
539 if (addr->mailbox != NULL && addr->domain != NULL)
540 *name_r = t_strconcat(addr->mailbox, "@", addr->domain, NULL);
541 else if (addr->mailbox != NULL)
542 *name_r = addr->mailbox;
543 return 0;
544 }
545
546 static void
index_sort_set_seq(struct mail_search_sort_program * program,struct mail * mail,uint32_t seq)547 index_sort_set_seq(struct mail_search_sort_program *program,
548 struct mail *mail, uint32_t seq)
549 {
550 if ((mail->mail_stream_opened || mail->mail_metadata_accessed) &&
551 program->slow_mails_left > 0)
552 program->slow_mails_left--;
553 mail_set_seq(mail, seq);
554 if (program->slow_mails_left == 0) {
555 /* too many slow lookups - just return the rest of the results
556 in whatever order. */
557 mail->lookup_abort = MAIL_LOOKUP_ABORT_NOT_IN_CACHE;
558 }
559 }
560
index_sort_header_get(struct mail_search_sort_program * program,uint32_t seq,enum mail_sort_type sort_type,string_t * dest)561 int index_sort_header_get(struct mail_search_sort_program *program, uint32_t seq,
562 enum mail_sort_type sort_type, string_t *dest)
563 {
564 struct mail *mail = program->temp_mail;
565 const char *str;
566 int ret;
567 bool reply_or_fw;
568
569 index_sort_set_seq(program, mail, seq);
570 str_truncate(dest, 0);
571
572 switch (sort_type & MAIL_SORT_MASK) {
573 case MAIL_SORT_SUBJECT:
574 ret = mail_get_first_header(mail, "Subject", &str);
575 if (ret < 0)
576 break;
577 if (ret == 0) {
578 /* nonexistent header */
579 return 1;
580 }
581 str = imap_get_base_subject_cased(pool_datastack_create(),
582 str, &reply_or_fw);
583 str_append(dest, str);
584 return 1;
585 case MAIL_SORT_CC:
586 ret = get_first_mailbox(mail, "Cc", &str);
587 break;
588 case MAIL_SORT_FROM:
589 ret = get_first_mailbox(mail, "From", &str);
590 break;
591 case MAIL_SORT_TO:
592 ret = get_first_mailbox(mail, "To", &str);
593 break;
594 case MAIL_SORT_DISPLAYFROM:
595 ret = get_display_name(mail, "From", &str);
596 break;
597 case MAIL_SORT_DISPLAYTO:
598 ret = get_display_name(mail, "To", &str);
599 break;
600 default:
601 i_unreached();
602 }
603 if (ret < 0) {
604 index_sort_program_set_mail_failed(program, mail);
605 if (!program->failed)
606 return 0;
607 return -1;
608 }
609
610 (void)uni_utf8_to_decomposed_titlecase(str, strlen(str), dest);
611 return 1;
612 }
613
index_sort_node_cmp_type(struct mail_search_sort_program * program,const enum mail_sort_type * sort_program,uint32_t seq1,uint32_t seq2)614 int index_sort_node_cmp_type(struct mail_search_sort_program *program,
615 const enum mail_sort_type *sort_program,
616 uint32_t seq1, uint32_t seq2)
617 {
618 struct mail *mail = program->temp_mail;
619 enum mail_sort_type sort_type;
620 time_t time1, time2;
621 uoff_t size1, size2;
622 float float1, float2;
623 int tz, ret = 0;
624
625 sort_type = *sort_program & MAIL_SORT_MASK;
626 switch (sort_type) {
627 case MAIL_SORT_CC:
628 case MAIL_SORT_FROM:
629 case MAIL_SORT_TO:
630 case MAIL_SORT_SUBJECT:
631 case MAIL_SORT_DISPLAYFROM:
632 case MAIL_SORT_DISPLAYTO:
633 T_BEGIN {
634 string_t *str1, *str2;
635
636 str1 = t_str_new(256);
637 str2 = t_str_new(256);
638 if (index_sort_header_get(program, seq1, sort_type, str1) < 0)
639 index_sort_program_set_mail_failed(program, mail);
640 if (index_sort_header_get(program, seq2, sort_type, str2) < 0)
641 index_sort_program_set_mail_failed(program, mail);
642
643 ret = strcmp(str_c(str1), str_c(str2));
644 } T_END;
645 break;
646 case MAIL_SORT_ARRIVAL:
647 index_sort_set_seq(program, mail, seq1);
648 if (mail_get_received_date(mail, &time1) < 0)
649 time1 = index_sort_program_set_date_failed(program, mail);
650
651 index_sort_set_seq(program, mail, seq2);
652 if (mail_get_received_date(mail, &time2) < 0)
653 time2 = index_sort_program_set_date_failed(program, mail);
654
655 ret = time1 < time2 ? -1 :
656 (time1 > time2 ? 1 : 0);
657 break;
658 case MAIL_SORT_DATE:
659 index_sort_set_seq(program, mail, seq1);
660 if (mail_get_date(mail, &time1, &tz) < 0)
661 time1 = index_sort_program_set_date_failed(program, mail);
662 else if (time1 == 0) {
663 if (mail_get_received_date(mail, &time1) < 0)
664 time1 = index_sort_program_set_date_failed(program, mail);
665 }
666
667 index_sort_set_seq(program, mail, seq2);
668 if (mail_get_date(mail, &time2, &tz) < 0)
669 time2 = index_sort_program_set_date_failed(program, mail);
670 else if (time2 == 0) {
671 if (mail_get_received_date(mail, &time2) < 0)
672 time2 = index_sort_program_set_date_failed(program, mail);
673 }
674
675 ret = time1 < time2 ? -1 :
676 (time1 > time2 ? 1 : 0);
677 break;
678 case MAIL_SORT_SIZE:
679 index_sort_set_seq(program, mail, seq1);
680 if (mail_get_virtual_size(mail, &size1) < 0) {
681 index_sort_program_set_mail_failed(program, mail);
682 size1 = 0;
683 }
684
685 index_sort_set_seq(program, mail, seq2);
686 if (mail_get_virtual_size(mail, &size2) < 0) {
687 index_sort_program_set_mail_failed(program, mail);
688 size2 = 0;
689 }
690
691 ret = size1 < size2 ? -1 :
692 (size1 > size2 ? 1 : 0);
693 break;
694 case MAIL_SORT_RELEVANCY:
695 index_sort_set_seq(program, mail, seq1);
696 if (index_sort_get_relevancy(mail, &float1) < 0)
697 index_sort_program_set_mail_failed(program, mail);
698 index_sort_set_seq(program, mail, seq2);
699 if (index_sort_get_relevancy(mail, &float2) < 0)
700 index_sort_program_set_mail_failed(program, mail);
701
702 /* NOTE: higher relevancy is returned first, unlike with all
703 other number based sort keys */
704 ret = float1 < float2 ? 1 :
705 (float1 > float2 ? -1 : 0);
706 break;
707 case MAIL_SORT_POP3_ORDER:
708 /* 32bit numbers would be enough, but since there is already
709 existing code for uoff_t in sizes, just use them. */
710 index_sort_set_seq(program, mail, seq1);
711 if (index_sort_get_pop3_order(mail, &size1) < 0)
712 index_sort_program_set_mail_failed(program, mail);
713 index_sort_set_seq(program, mail, seq2);
714 if (index_sort_get_pop3_order(mail, &size2) < 0)
715 index_sort_program_set_mail_failed(program, mail);
716
717 ret = size1 < size2 ? -1 :
718 (size1 > size2 ? 1 : 0);
719 break;
720 case MAIL_SORT_END:
721 return seq1 < seq2 ? -1 :
722 (seq1 > seq2 ? 1 : 0);
723 case MAIL_SORT_MASK:
724 case MAIL_SORT_FLAG_REVERSE:
725 i_unreached();
726 }
727
728 if (ret == 0) {
729 return index_sort_node_cmp_type(program, sort_program+1,
730 seq1, seq2);
731 }
732
733 if ((*sort_program & MAIL_SORT_FLAG_REVERSE) != 0)
734 ret = ret < 0 ? 1 : -1;
735 return ret;
736 }
737