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