1 /*
2  Copyright (c) 2004-2012 NFG Net Facilities Group BV support@nfg.nl
3 
4   This program is free software; you can redistribute it and/or
5   modify it under the terms of the GNU General Public License
6   as published by the Free Software Foundation; either
7   version 2 of the License, or (at your option) any later
8   version.
9 
10   This program is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   GNU General Public License for more details.
14 
15   You should have received a copy of the GNU General Public License
16   along with this program; if not, write to the Free Software
17   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 /**
21  *
22  * implements DbmailMailbox object
23  */
24 
25 #include "dbmail.h"
26 #define THIS_MODULE "mailbox"
27 
28 extern DBParam_T db_params;
29 extern Mempool_T small_pool;
30 
31 #define DBPFX db_params.pfx
32 
33 /* internal utilities */
34 
35 /* class methods */
36 
dbmail_mailbox_new(Mempool_T pool,uint64_t id)37 DbmailMailbox * dbmail_mailbox_new(Mempool_T pool, uint64_t id) {
38 	gboolean freepool = FALSE;
39 	if (!pool) {
40 		pool = mempool_open();
41 		freepool = TRUE;
42 	}
43 
44 	DbmailMailbox *self = mempool_pop(pool, sizeof (DbmailMailbox));
45 	self->pool = pool;
46 	self->freepool = freepool;
47 
48 	assert(id);
49 	assert(self);
50 
51 	self->id = id;
52 	dbmail_mailbox_set_uid(self, FALSE);
53 
54 	return self;
55 }
56 
_node_free(GNode * node,gpointer data)57 static gboolean _node_free(GNode *node, gpointer data) {
58 	DbmailMailbox *self = (DbmailMailbox *) data;
59 	search_key *s = (search_key *) node->data;
60 	if (s->found)
61 		g_tree_destroy(s->found);
62 	mempool_push(self->pool, s, sizeof (search_key));
63 	return FALSE;
64 }
65 
dbmail_mailbox_free(DbmailMailbox * self)66 void dbmail_mailbox_free(DbmailMailbox *self) {
67 	Mempool_T pool = self->pool;
68 	gboolean freepool = self->freepool;
69 	if (self->found) g_tree_destroy(self->found);
70 	if (self->sorted) g_list_destroy(self->sorted);
71 	if (self->search) {
72 		g_node_traverse(g_node_get_root(self->search), G_POST_ORDER, G_TRAVERSE_ALL, -1, (GNodeTraverseFunc) _node_free, self);
73 		g_node_destroy(self->search);
74 	}
75 
76 	mempool_push(pool, self, sizeof (DbmailMailbox));
77 	if (freepool)
78 		mempool_close(&pool);
79 }
80 
dbmail_mailbox_get_id(DbmailMailbox * self)81 uint64_t dbmail_mailbox_get_id(DbmailMailbox *self) {
82 	assert(self->id > 0);
83 	return self->id;
84 }
85 
dbmail_mailbox_set_uid(DbmailMailbox * self,gboolean uid)86 void dbmail_mailbox_set_uid(DbmailMailbox *self, gboolean uid) {
87 	self->uid = uid;
88 }
89 
dbmail_mailbox_get_uid(DbmailMailbox * self)90 gboolean dbmail_mailbox_get_uid(DbmailMailbox *self) {
91 	return self->uid;
92 }
93 
dbmail_mailbox_open(DbmailMailbox * self)94 int dbmail_mailbox_open(DbmailMailbox *self) {
95 	if ((self->mbstate = MailboxState_new(self->pool, self->id)) == NULL)
96 		return DM_EQUERY;
97 	return DM_SUCCESS;
98 }
99 
100 #define FROM_STANDARD_DATE "Tue Oct 11 13:06:24 2005"
101 
_message_get_envelope_date(DbmailMailbox * mailbox,const DbmailMessage * message)102 static String_T _message_get_envelope_date(DbmailMailbox *mailbox, const DbmailMessage *message) {
103 	struct tm gmt;
104 	String_T date;
105 
106 	assert(message->internal_date);
107 
108 	memset(&gmt, 0, sizeof (struct tm));
109 	if (gmtime_r(&message->internal_date, &gmt)) {
110 		char res[TIMESTRING_SIZE + 1];
111 		memset(res, 0, sizeof (res));
112 		strftime(res, TIMESTRING_SIZE, "%a %b %d %H:%M:%S %Y", &gmt);
113 		date = p_string_new(mailbox->pool, res);
114 	} else {
115 		date = p_string_new(mailbox->pool, FROM_STANDARD_DATE);
116 	}
117 
118 	return date;
119 }
120 
dump_message_to_stream(DbmailMailbox * self,DbmailMessage * message,GMimeStream * ostream)121 static size_t dump_message_to_stream(DbmailMailbox *self, DbmailMessage *message, GMimeStream *ostream) {
122 	size_t r = 0;
123 	gchar *s;
124 	String_T sender;
125 	String_T t;
126 	String_T date;
127 	InternetAddressList *ialist;
128 	InternetAddress *ia;
129 
130 
131 	g_return_val_if_fail(GMIME_IS_MESSAGE(message->content), 0);
132 
133 	s = dbmail_message_to_string(message);
134 
135 	if (!strncmp(s, "From ", 5) == 0) {
136 		ialist = internet_address_list_parse_string(g_mime_message_get_sender(GMIME_MESSAGE(message->content)));
137 		sender = p_string_new(self->pool, "nobody@foo");
138 		if (ialist) {
139 			ia = internet_address_list_get_address(ialist, 0);
140 			if (ia) {
141 				char *addr = (char *) internet_address_mailbox_get_addr((InternetAddressMailbox *) ia);
142 				g_strstrip(g_strdelimit(addr, "\"", ' '));
143 				p_string_printf(sender, "%s", addr);
144 			}
145 		}
146 		g_object_unref(ialist);
147 
148 		date = _message_get_envelope_date(self, message);
149 		t = p_string_new(self->pool, "From ");
150 		p_string_append_printf(t, "%s %s\n",
151 			p_string_str(sender), p_string_str(date));
152 
153 		r = g_mime_stream_write_string(ostream, p_string_str(t));
154 
155 		p_string_free(t, TRUE);
156 		p_string_free(sender, TRUE);
157 		p_string_free(date, TRUE);
158 
159 	}
160 
161 	r += g_mime_stream_write_string(ostream, s);
162 	r += g_mime_stream_write_string(ostream, "\n");
163 
164 	g_free(s);
165 	return r;
166 }
167 
_mimeparts_dump(DbmailMailbox * self,GMimeStream * ostream)168 static int _mimeparts_dump(DbmailMailbox *self, GMimeStream *ostream) {
169 	List_T ids = NULL;
170 	uint64_t msgid, physid, *id;
171 	DbmailMessage *m;
172 	GTree *uids;
173 	volatile int count = 0;
174 	PreparedStatement_T stmt;
175 	Connection_T c;
176 	ResultSet_T r;
177 	volatile int t = FALSE;
178 
179 	uids = self->found;
180 
181 	c = db_con_get();
182 	TRY
183 	stmt = db_stmt_prepare(c,
184 		"SELECT id,message_idnr FROM %sphysmessage p "
185 		"LEFT JOIN %smessages m ON p.id=m.physmessage_id "
186 		"LEFT JOIN %smailboxes b ON b.mailbox_idnr=m.mailbox_idnr "
187 		"WHERE b.mailbox_idnr=? ORDER BY message_idnr",
188 		DBPFX, DBPFX, DBPFX);
189 	db_stmt_set_u64(stmt, 1, self->id);
190 	r = db_stmt_query(stmt);
191 
192 	ids = p_list_new(self->pool);
193 	while (db_result_next(r)) {
194 		physid = db_result_get_u64(r, 0);
195 		msgid = db_result_get_u64(r, 1);
196 		if (g_tree_lookup(uids, &msgid)) {
197 			id = mempool_pop(self->pool, sizeof (uint64_t));
198 			*id = physid;
199 			ids = p_list_append(ids, id);
200 		}
201 	}
202 	CATCH(SQLException)
203 	LOG_SQLERROR;
204 	t = DM_EQUERY;
205 	FINALLY
206 	db_con_close(c);
207 	END_TRY;
208 
209 	if (t == DM_EQUERY) return t;
210 
211 	ids = p_list_first(ids);
212 
213 	while (ids) {
214 		physid = *(uint64_t *) p_list_data(ids);
215 		mempool_push(self->pool, p_list_data(ids), sizeof (uint64_t));
216 		m = dbmail_message_new(self->pool);
217 		m = dbmail_message_retrieve(m, physid);
218 		if (dump_message_to_stream(self, m, ostream) > 0)
219 			count++;
220 		dbmail_message_free(m);
221 
222 		if (!p_list_next(ids)) break;
223 		ids = p_list_next(ids);
224 	}
225 
226 	ids = p_list_first(ids);
227 	p_list_free(&ids);
228 
229 	return count;
230 }
231 
232 /* Caller must fclose the file pointer itself. */
dbmail_mailbox_dump(DbmailMailbox * self,FILE * file)233 int dbmail_mailbox_dump(DbmailMailbox *self, FILE *file) {
234 	int count = 0;
235 	GMimeStream *ostream;
236 
237 	dbmail_mailbox_open(self);
238 
239 	GTree *ids = self->found;
240 
241 	if (ids == NULL || g_tree_nnodes(ids) == 0) {
242 		TRACE(TRACE_DEBUG, "cannot dump empty mailbox");
243 		return 0;
244 	}
245 
246 	assert(ids);
247 
248 	ostream = g_mime_stream_file_new(file);
249 	g_mime_stream_file_set_owner((GMimeStreamFile *) ostream, FALSE);
250 
251 	count += _mimeparts_dump(self, ostream);
252 
253 	g_object_unref(ostream);
254 
255 	return count;
256 }
257 
_tree_foreach(gpointer key UNUSED,gpointer value,GString * data)258 static gboolean _tree_foreach(gpointer key UNUSED, gpointer value, GString * data) {
259 	gboolean res = FALSE;
260 	uint64_t *id;
261 	GList *sublist = g_list_first((GList *) value);
262 	GString *t = g_string_new("");
263 	int m = g_list_length(sublist);
264 
265 	sublist = g_list_first(sublist);
266 	while (sublist) {
267 		id = sublist->data;
268 		g_string_append_printf(t, "(%" PRIu64 ")", *id);
269 
270 		if (!g_list_next(sublist))
271 			break;
272 		sublist = g_list_next(sublist);
273 	}
274 	if (m > 1)
275 		g_string_append_printf(data, "(%s)", t->str);
276 	else
277 		g_string_append_printf(data, "%s", t->str);
278 
279 	g_string_free(t, TRUE);
280 
281 	return res;
282 }
283 
dbmail_mailbox_orderedsubject(DbmailMailbox * self)284 char * dbmail_mailbox_orderedsubject(DbmailMailbox *self) {
285 	GList *sublist = NULL;
286 	volatile uint64_t i = 0, idnr = 0;
287 	char *subj;
288 	char *res = NULL;
289 	uint64_t *id, *msn;
290 	GTree *tree;
291 	GString *threads;
292 	PreparedStatement_T stmt;
293 	Connection_T c;
294 	ResultSet_T r;
295 	volatile int t = FALSE;
296 
297 	tree = g_tree_new_full((GCompareDataFunc) dm_strcmpdata, NULL, (GDestroyNotify) g_free, NULL);
298 
299 	t = FALSE;
300 	c = db_con_get();
301 	TRY
302 	/* thread-roots (ordered) */
303 	stmt = db_stmt_prepare(c,
304 		"SELECT min(m.message_idnr),v.sortfield "
305 		"FROM %smessages m "
306 		"LEFT JOIN %sheader h USING (physmessage_id) "
307 		"LEFT JOIN %sheadername n ON h.headername_id = n.id "
308 		"LEFT JOIN %sheadervalue v ON h.headervalue_id = v.id "
309 		"WHERE m.mailbox_idnr=? "
310 		"AND n.headername = 'subject' AND m.status < %d "
311 		"GROUP BY v.sortfield",
312 		DBPFX, DBPFX, DBPFX, DBPFX,
313 		MESSAGE_STATUS_DELETE);
314 	db_stmt_set_u64(stmt, 1, self->id);
315 	r = db_stmt_query(stmt);
316 
317 	i = 0;
318 	while (db_result_next(r)) {
319 		i++;
320 		idnr = db_result_get_u64(r, 0);
321 		if (!g_tree_lookup(self->found, (gconstpointer) & idnr))
322 			continue;
323 		subj = (char *) db_result_get(r, 1);
324 		g_tree_insert(tree, g_strdup(subj), NULL);
325 	}
326 	CATCH(SQLException)
327 	LOG_SQLERROR;
328 	t = DM_EQUERY;
329 	END_TRY;
330 
331 	if ((t == DM_EQUERY) || (!i)) {
332 		g_tree_destroy(tree);
333 		db_con_close(c);
334 		return res;
335 	}
336 
337 	db_con_clear(c);
338 
339 	TRY
340 	/* full threads (unordered) */
341 	stmt = db_stmt_prepare(c,
342 		"SELECT m.message_idnr,v.sortfield "
343 		"FROM %smessages m "
344 		"LEFT JOIN %sheader h USING (physmessage_id) "
345 		"LEFT JOIN %sheadername n ON h.headername_id = n.id "
346 		"LEFT JOIN %sheadervalue v ON h.headervalue_id = v.id "
347 		"WHERE m.mailbox_idnr = ? "
348 		"AND n.headername = 'subject' AND m.status < %d "
349 		"ORDER BY v.sortfield, v.datefield",
350 		DBPFX, DBPFX, DBPFX, DBPFX,
351 		MESSAGE_STATUS_DELETE);
352 	db_stmt_set_u64(stmt, 1, self->id);
353 	r = db_stmt_query(stmt);
354 
355 	i = 0;
356 	while (db_result_next(r)) {
357 		i++;
358 		idnr = db_result_get_u64(r, 0);
359 		if (!(msn = g_tree_lookup(self->found, (gconstpointer) & idnr)))
360 			continue;
361 		subj = (char *) db_result_get(r, 1);
362 
363 		id = g_new0(uint64_t, 1);
364 		if (dbmail_mailbox_get_uid(self))
365 			*id = idnr;
366 		else
367 			*id = *msn;
368 
369 		sublist = g_tree_lookup(tree, (gconstpointer) subj);
370 		sublist = g_list_append(sublist, id);
371 		g_tree_insert(tree, g_strdup(subj), sublist);
372 	}
373 	CATCH(SQLException)
374 	LOG_SQLERROR;
375 	t = DM_EQUERY;
376 	FINALLY
377 	db_con_close(c);
378 	END_TRY;
379 
380 	if ((t == DM_EQUERY) || (!i)) {
381 		g_tree_destroy(tree);
382 		return res;
383 	}
384 
385 	threads = g_string_new("");
386 	g_tree_foreach(tree, (GTraverseFunc) _tree_foreach, threads);
387 	res = threads->str;
388 
389 	g_string_free(threads, FALSE);
390 	g_tree_destroy(tree);
391 
392 	return res;
393 }
394 
395 /*
396  * return self->ids as a string
397  */
dbmail_mailbox_ids_as_string(DbmailMailbox * self,gboolean uid,const char * sep)398 char * dbmail_mailbox_ids_as_string(DbmailMailbox *self, gboolean uid, const char *sep) {
399 	TRACE(TRACE_DEBUG, "Call: dbmail_mailbox_ids_as_string");
400 	GString *t;
401 	gchar *s = NULL;
402 	GList *l = NULL, *h = NULL;
403 	GTree *msginfo;
404 	GTree *msn;
405 	uint64_t maxseq = 0;
406 
407 	if ((self->found == NULL) || g_tree_nnodes(self->found) <= 0) {
408 		TRACE(TRACE_DEBUG, "no ids found");
409 		return s;
410 	}
411 
412 	t = g_string_new("");
413 	if (uid || dbmail_mailbox_get_uid(self)) {
414 		l = g_tree_keys(self->found);
415 	} else {
416 		l = g_tree_values(self->found);
417 	}
418 
419 	h = l;
420 
421 	msginfo = MailboxState_getMsginfo(self->mbstate);
422 	msn = MailboxState_getMsn(self->mbstate);
423 
424 	while (l->data) {
425 		uint64_t *key = (uint64_t *) l->data;
426 		if (self->modseq) {
427 			uint64_t *id;
428 			if (uid || dbmail_mailbox_get_uid(self)) {
429 				id = key;
430 			} else {
431 				id = g_tree_lookup(msn, key);
432 			}
433 
434 			MessageInfo *info = g_tree_lookup(msginfo, id);
435 			maxseq = max(maxseq, info->seq);
436 		}
437 		g_string_append_printf(t, "%" PRIu64 "", *key);
438 		if (!g_list_next(l))
439 			break;
440 		g_string_append_printf(t, "%s", sep);
441 		l = g_list_next(l);
442 	}
443 
444 	g_list_free(h);
445 
446 	if (self->modseq)
447 		g_string_append_printf(t, " (MODSEQ %" PRIu64 ")", maxseq);
448 
449 	s = t->str;
450 	g_string_free(t, FALSE);
451 
452 	return g_strchomp(s);
453 
454 }
455 
dbmail_mailbox_sorted_as_string(DbmailMailbox * self)456 char * dbmail_mailbox_sorted_as_string(DbmailMailbox *self) {
457 	GString *t;
458 	gchar *s = NULL;
459 	GList *l = NULL;
460 	gboolean uid;
461 	uint64_t *msn;
462 
463 	l = g_list_first(self->sorted);
464 	if (!(g_list_length(l) > 0))
465 		return s;
466 
467 	t = g_string_new("");
468 	uid = dbmail_mailbox_get_uid(self);
469 
470 	while (l->data) {
471 		msn = g_tree_lookup(self->found, l->data);
472 		if (msn) {
473 			if (uid)
474 				g_string_append_printf(t, "%" PRIu64 " ", *(uint64_t *) l->data);
475 			else
476 				g_string_append_printf(t, "%" PRIu64 " ", *(uint64_t *) msn);
477 		}
478 		if (!g_list_next(l))
479 			break;
480 		l = g_list_next(l);
481 	}
482 
483 	s = t->str;
484 	g_string_free(t, FALSE);
485 
486 	return g_strchomp(s);
487 }
488 
489 /* imap sorted search */
append_search(DbmailMailbox * self,search_key * value,gboolean descend)490 static int append_search(DbmailMailbox *self, search_key *value, gboolean descend) {
491 	GNode *n;
492 
493 	if (self->search) {
494 		n = g_node_append_data(self->search, value);
495 	} else {
496 		descend = TRUE;
497 		n = g_node_new(value);
498 	}
499 
500 	if (descend)
501 		self->search = n;
502 
503 	TRACE(TRACE_DEBUG, "[%p] leaf [%d] type [%d] field [%s] search [%s] at depth [%u]\n", value, G_NODE_IS_LEAF(n),
504 		value->type, value->hdrfld, value->search,
505 		g_node_depth(self->search));
506 	return 0;
507 }
508 
509 #define BUFSIZE 255
510 
_append_join(char * join,char * table)511 static void _append_join(char *join, char *table) {
512 	char tmp[BUFSIZE + 1];
513 	memset(tmp, 0, sizeof (tmp));
514 	g_snprintf(tmp, BUFSIZE, "LEFT JOIN %s%s ON m.physmessage_id=%s%s.physmessage_id ", DBPFX, table, DBPFX, table);
515 	g_strlcat(join, tmp, MAX_SEARCH_LEN);
516 }
517 
_append_sort(char * order,char * field,gboolean reverse)518 static void _append_sort(char *order, char *field, gboolean reverse) {
519 	char tmp[BUFSIZE + 1];
520 	memset(tmp, 0, sizeof (tmp));
521 	g_snprintf(tmp, BUFSIZE, "%s%s,", field, reverse ? " DESC" : "");
522 	g_strlcat(order, tmp, MAX_SEARCH_LEN);
523 }
524 
_handle_sort_args(DbmailMailbox * self,String_T * search_keys,search_key * value,uint64_t * idx)525 static int _handle_sort_args(DbmailMailbox *self, String_T *search_keys, search_key *value, uint64_t *idx) {
526 	value->type = IST_SORT;
527 
528 	gboolean reverse = FALSE;
529 
530 	if (!(search_keys && search_keys[*idx]))
531 		return -1;
532 
533 	const char *key = p_string_str(search_keys[*idx]);
534 
535 	if (MATCH(key, "reverse")) {
536 		reverse = TRUE;
537 		(*idx)++;
538 		key = p_string_str(search_keys[*idx]);
539 	}
540 
541 	if (MATCH(key, "arrival")) {
542 		_append_sort(value->order, "internal_date", reverse);
543 		(*idx)++;
544 	} else if (MATCH(key, "size")) {
545 		_append_sort(value->order, "messagesize", reverse);
546 		(*idx)++;
547 	} else if (MATCH(key, "from")) {
548 		_append_join(value->table, "fromfield");
549 		_append_sort(value->order, "fromfield", reverse);
550 		(*idx)++;
551 	} else if (MATCH(key, "subject")) {
552 		_append_join(value->table, "subjectfield");
553 		_append_sort(value->order, "sortfield", reverse);
554 		(*idx)++;
555 	} else if (MATCH(key, "cc")) {
556 		_append_join(value->table, "ccfield");
557 		_append_sort(value->order, "ccfield", reverse);
558 		(*idx)++;
559 	} else if (MATCH(key, "to")) {
560 		_append_join(value->table, "tofield");
561 		_append_sort(value->order, "tofield", reverse);
562 		(*idx)++;
563 	} else if (MATCH(key, "date")) {
564 		_append_join(value->table, "datefield");
565 		_append_sort(value->order, "sortfield", reverse);
566 		(*idx)++;
567 	} else if (MATCH(key, "("))
568 		(*idx)++;
569 
570 	else if (MATCH(key, ")"))
571 		(*idx)++;
572 
573 	else if (MATCH(key, "utf-8")) {
574 		(*idx)++;
575 		append_search(self, value, 0);
576 		return 1;
577 	} else if (MATCH(key, "us-ascii")) {
578 		(*idx)++;
579 		append_search(self, value, 0);
580 		return 1;
581 	} else if (MATCH(key, "iso-8859-1")) {
582 		(*idx)++;
583 		append_search(self, value, 0);
584 		return 1;
585 	} else
586 		return -1; /* done */
587 
588 	return 0;
589 }
590 
pop_search(DbmailMailbox * self)591 static void pop_search(DbmailMailbox *self) {
592 	// switch back to parent
593 	if (self->search && self->search->parent)
594 		self->search = self->search->parent;
595 }
596 
_handle_search_args(DbmailMailbox * self,String_T * search_keys,uint64_t * idx)597 static int _handle_search_args(DbmailMailbox *self, String_T *search_keys, uint64_t *idx) {
598 	int result = 0;
599 
600 	if (!(search_keys && search_keys[*idx]))
601 		return 1;
602 
603 	char *p = NULL, *t = NULL;
604 	const char *key = p_string_str(search_keys[*idx]);
605 
606 	search_key *value = mempool_pop(self->pool, sizeof (search_key));
607 
608 	/* SEARCH */
609 
610 	TRACE(TRACE_DEBUG, "key [%s]", key);
611 
612 	if (MATCH(key, "all")) {
613 		value->type = IST_UIDSET;
614 		strcpy(value->search, "1:*");
615 		(*idx)++;
616 
617 	}
618 #define RETURN_IF_FAIL(x, y) \
619 	if (! (x)) { \
620 		mempool_push(self->pool, value, sizeof(search_key)); \
621 		return y; \
622 	}
623 
624 	else if (MATCH(key, "uid")) {
625 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
626 		RETURN_IF_FAIL(check_msg_set(p_string_str(search_keys[*idx + 1])), -1);
627 		value->type = IST_UIDSET;
628 		(*idx)++;
629 		strncpy(value->search, p_string_str(search_keys[(*idx)]), MAX_SEARCH_LEN - 1);
630 		(*idx)++;
631 	}/*
632 	 * FLAG search keys
633 	 */
634 
635 	else if (MATCH(key, "answered")) {
636 		value->type = IST_FLAG;
637 		strncpy(value->search, "answered_flag=1", MAX_SEARCH_LEN - 1);
638 		(*idx)++;
639 
640 	} else if (MATCH(key, "deleted")) {
641 		value->type = IST_FLAG;
642 		strncpy(value->search, "deleted_flag=1", MAX_SEARCH_LEN - 1);
643 		(*idx)++;
644 
645 	} else if (MATCH(key, "flagged")) {
646 		value->type = IST_FLAG;
647 		strncpy(value->search, "flagged_flag=1", MAX_SEARCH_LEN - 1);
648 		(*idx)++;
649 
650 	} else if (MATCH(key, "recent")) {
651 		value->type = IST_FLAG;
652 		strncpy(value->search, "recent_flag=1", MAX_SEARCH_LEN - 1);
653 		(*idx)++;
654 
655 	} else if (MATCH(key, "seen")) {
656 		value->type = IST_FLAG;
657 		strncpy(value->search, "seen_flag=1", MAX_SEARCH_LEN - 1);
658 		(*idx)++;
659 
660 	} else if (MATCH(key, "draft")) {
661 		value->type = IST_FLAG;
662 		strncpy(value->search, "draft_flag=1", MAX_SEARCH_LEN - 1);
663 		(*idx)++;
664 
665 	} else if (MATCH(key, "new")) {
666 		value->type = IST_FLAG;
667 		strncpy(value->search, "(seen_flag=0 AND recent_flag=1)", MAX_SEARCH_LEN - 1);
668 		(*idx)++;
669 
670 	} else if (MATCH(key, "old")) {
671 		value->type = IST_FLAG;
672 		strncpy(value->search, "recent_flag=0", MAX_SEARCH_LEN - 1);
673 		(*idx)++;
674 
675 	} else if (MATCH(key, "unanswered")) {
676 		value->type = IST_FLAG;
677 		strncpy(value->search, "answered_flag=0", MAX_SEARCH_LEN - 1);
678 		(*idx)++;
679 
680 	} else if (MATCH(key, "undeleted")) {
681 		value->type = IST_FLAG;
682 		strncpy(value->search, "deleted_flag=0", MAX_SEARCH_LEN - 1);
683 		(*idx)++;
684 
685 	} else if (MATCH(key, "unflagged")) {
686 		value->type = IST_FLAG;
687 		strncpy(value->search, "flagged_flag=0", MAX_SEARCH_LEN - 1);
688 		(*idx)++;
689 
690 	} else if (MATCH(key, "unseen")) {
691 		value->type = IST_FLAG;
692 		strncpy(value->search, "seen_flag=0", MAX_SEARCH_LEN - 1);
693 		(*idx)++;
694 
695 	} else if (MATCH(key, "undraft")) {
696 		value->type = IST_FLAG;
697 		strncpy(value->search, "draft_flag=0", MAX_SEARCH_LEN - 1);
698 		(*idx)++;
699 
700 	}
701 #define IMAP_SET_SEARCH  (*idx)++; \
702 		if ((p = dbmail_iconv_str_to_db(p_string_str(search_keys[*idx]), self->charset)) == NULL) {  \
703 			TRACE(TRACE_WARNING, "search_key [%s] is not charset [%s]", p_string_str(search_keys[*idx]), self->charset); \
704 		} else { \
705 			strncpy(value->search, p, MAX_SEARCH_LEN-1); \
706 			g_free(p); \
707 		} \
708 		g_free(t); \
709 		(*idx)++
710 
711 		/*
712 		 * keyword search
713 		 */
714 
715 	else if (MATCH(key, "keyword")) {
716 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
717 		value->type = IST_KEYWORD;
718 		IMAP_SET_SEARCH;
719 	} else if (MATCH(key, "unkeyword")) {
720 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
721 		value->type = IST_UNKEYWORD;
722 		IMAP_SET_SEARCH;
723 	}/*
724 	 * HEADER search keys
725 	 */
726 	else if (MATCH(key, "bcc")) {
727 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
728 		value->type = IST_HDR;
729 		strncpy(value->hdrfld, "bcc", MIME_FIELD_MAX - 1);
730 		IMAP_SET_SEARCH;
731 
732 	} else if (MATCH(key, "cc")) {
733 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
734 		value->type = IST_HDR;
735 		strncpy(value->hdrfld, "cc", MIME_FIELD_MAX - 1);
736 		IMAP_SET_SEARCH;
737 
738 	} else if (MATCH(key, "from")) {
739 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
740 		value->type = IST_HDR;
741 		strncpy(value->hdrfld, "from", MIME_FIELD_MAX - 1);
742 		IMAP_SET_SEARCH;
743 
744 	} else if (MATCH(key, "to")) {
745 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
746 		value->type = IST_HDR;
747 		strncpy(value->hdrfld, "to", MIME_FIELD_MAX - 1);
748 		IMAP_SET_SEARCH;
749 
750 	} else if (MATCH(key, "subject")) {
751 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
752 		value->type = IST_HDR;
753 		strncpy(value->hdrfld, "subject", MIME_FIELD_MAX - 1);
754 		IMAP_SET_SEARCH;
755 
756 	} else if (MATCH(key, "header")) {
757 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
758 		RETURN_IF_FAIL(search_keys[*idx + 2], -1);
759 		value->type = IST_HDR;
760 
761 		const char *hdr = p_string_str(search_keys[*idx + 1]);
762 		char *hdrfld = g_ascii_strdown(hdr, strlen(hdr));
763 		strncpy(value->hdrfld, hdrfld, MIME_FIELD_MAX - 1);
764 		g_free(hdrfld);
765 
766 		strncpy(value->search, p_string_str(search_keys[*idx + 2]), MAX_SEARCH_LEN - 1);
767 		(*idx) += 3;
768 
769 	} else if (MATCH(key, "sentbefore")) {
770 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
771 		value->type = IST_HDRDATE_BEFORE;
772 		strncpy(value->hdrfld, "datefield", MIME_FIELD_MAX - 1);
773 		IMAP_SET_SEARCH;
774 
775 	} else if (MATCH(key, "senton")) {
776 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
777 		value->type = IST_HDRDATE_ON;
778 		strncpy(value->hdrfld, "datefield", MIME_FIELD_MAX - 1);
779 		IMAP_SET_SEARCH;
780 
781 	} else if (MATCH(key, "sentsince")) {
782 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
783 		value->type = IST_HDRDATE_SINCE;
784 		strncpy(value->hdrfld, "datefield", MIME_FIELD_MAX - 1);
785 		IMAP_SET_SEARCH;
786 	}/*
787 	 * INTERNALDATE keys
788 	 */
789 
790 	else if (MATCH(key, "before")) {
791 		char s[SQL_INTERNALDATE_LEN];
792 		memset(s, 0, sizeof (s));
793 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
794 		RETURN_IF_FAIL(check_date(p_string_str(search_keys[*idx + 1])), -1);
795 		value->type = IST_IDATE;
796 		(*idx)++;
797 		date_imap2sql(p_string_str(search_keys[*idx]), s);
798 		g_snprintf(value->search, MAX_SEARCH_LEN - 1, "p.internal_date < '%s'", s);
799 		(*idx)++;
800 
801 	} else if (MATCH(key, "on")) {
802 		char s[SQL_INTERNALDATE_LEN], d[MIME_FIELD_MAX];
803 		memset(s, 0, sizeof (s));
804 		memset(d, 0, sizeof (d));
805 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
806 		RETURN_IF_FAIL(check_date(p_string_str(search_keys[*idx + 1])), -1);
807 		value->type = IST_IDATE;
808 		(*idx)++;
809 		date_imap2sql(p_string_str(search_keys[*idx]), s);
810 		g_snprintf(d, MIME_FIELD_MAX - 1, db_get_sql(SQL_TO_DATE), "p.internal_date");
811 		g_snprintf(value->search, MAX_SEARCH_LEN - 1, "%s = '%s'", d, s);
812 		(*idx)++;
813 
814 	} else if (MATCH(key, "since")) {
815 		char s[SQL_INTERNALDATE_LEN];
816 		memset(s, 0, sizeof (s));
817 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
818 		RETURN_IF_FAIL(check_date(p_string_str(search_keys[*idx + 1])), -1);
819 		value->type = IST_IDATE;
820 		(*idx)++;
821 		date_imap2sql(p_string_str(search_keys[*idx]), s);
822 		g_snprintf(value->search, MAX_SEARCH_LEN - 1, "p.internal_date > '%s'", s);
823 		(*idx)++;
824 
825 	} else if (MATCH(key, "older")) {
826 		uint64_t seconds;
827 		char partial[DEF_FRAGSIZE];
828 		memset(partial, 0, sizeof (partial));
829 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
830 		errno = 0;
831 		seconds = dm_strtoull(p_string_str(search_keys[*idx + 1]), NULL, 10);
832 		int ser = errno;
833 		if (ser) {
834 			TRACE(TRACE_DEBUG, "%s", strerror(ser));
835 			return -1;
836 		}
837 		value->type = IST_IDATE;
838 		(*idx)++;
839 		g_snprintf(partial, DEF_FRAGSIZE - 1, db_get_sql(SQL_WITHIN), seconds);
840 		g_snprintf(value->search, MAX_SEARCH_LEN - 1, "p.internal_date < %s", partial);
841 		(*idx)++;
842 
843 	} else if (MATCH(key, "younger")) {
844 		uint64_t seconds;
845 		char partial[DEF_FRAGSIZE];
846 		memset(partial, 0, sizeof (partial));
847 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
848 		errno = 0;
849 		seconds = dm_strtoull(p_string_str(search_keys[*idx + 1]), NULL, 10);
850 		int ser = errno;
851 		if (ser) {
852 			TRACE(TRACE_DEBUG, "%s", strerror(ser));
853 			return -1;
854 		}
855 		value->type = IST_IDATE;
856 		(*idx)++;
857 		g_snprintf(partial, DEF_FRAGSIZE - 1, db_get_sql(SQL_WITHIN), seconds);
858 		g_snprintf(value->search, MAX_SEARCH_LEN - 1, "p.internal_date > %s", partial);
859 		(*idx)++;
860 
861 	} else if (MATCH(key, "modseq")) {
862 		const char *token;
863 		gboolean valid = false;
864 		uint64_t seq = 0;
865 		int i = 0;
866 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
867 		(*idx)++;
868 		for (i = 0; i <= 2; i++) {
869 			char *rest;
870 			token = p_string_str(search_keys[*idx + i]);
871 			TRACE(TRACE_DEBUG, "check token [%d:%s]", i, token);
872 			if (!token)
873 				break;
874 			if (i == 1)
875 				continue;
876 			seq = dm_strtoull(token, &rest, 10);
877 			if (rest != token) {
878 				valid = true;
879 				break;
880 			}
881 		}
882 		(*idx) += i + 1;
883 		RETURN_IF_FAIL(valid, -1);
884 		self->modseq = seq;
885 		self->condstore = true;
886 	}		/*
887 	 * DATA-keys
888 	 */
889 
890 	else if (MATCH(key, "body")) {
891 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
892 		value->type = IST_DATA_BODY;
893 		IMAP_SET_SEARCH;
894 
895 	} else if (MATCH(key, "text")) {
896 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
897 		value->type = IST_DATA_TEXT;
898 		IMAP_SET_SEARCH;
899 	}/*
900 	 * SIZE keys
901 	 */
902 
903 	else if (MATCH(key, "larger")) {
904 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
905 		value->type = IST_SIZE_LARGER;
906 		(*idx)++;
907 		value->size = strtoull(p_string_str(search_keys[(*idx)]), NULL, 10);
908 		(*idx)++;
909 
910 	} else if (MATCH(key, "smaller")) {
911 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
912 		value->type = IST_SIZE_SMALLER;
913 		(*idx)++;
914 		value->size = strtoull(p_string_str(search_keys[(*idx)]), NULL, 10);
915 		(*idx)++;
916 
917 	}/*
918 	 * NOT, OR, ()
919 	 */
920 
921 	else if (MATCH(key, "not")) {
922 		const char *nextkey;
923 
924 		RETURN_IF_FAIL(search_keys[*idx + 1], -1);
925 
926 		nextkey = p_string_str(search_keys[*idx + 1]);
927 
928 		if (MATCH(nextkey, "answered")) {
929 			value->type = IST_FLAG;
930 			strncpy(value->search, "answered_flag=0", MAX_SEARCH_LEN - 1);
931 			(*idx) += 2;
932 
933 		} else if (MATCH(nextkey, "deleted")) {
934 			value->type = IST_FLAG;
935 			strncpy(value->search, "deleted_flag=0", MAX_SEARCH_LEN - 1);
936 			(*idx) += 2;
937 
938 		} else if (MATCH(nextkey, "flagged")) {
939 			value->type = IST_FLAG;
940 			strncpy(value->search, "flagged_flag=0", MAX_SEARCH_LEN - 1);
941 			(*idx) += 2;
942 
943 		} else if (MATCH(nextkey, "recent")) {
944 			value->type = IST_FLAG;
945 			strncpy(value->search, "recent_flag=0", MAX_SEARCH_LEN - 1);
946 			(*idx) += 2;
947 
948 		} else if (MATCH(nextkey, "seen")) {
949 			value->type = IST_FLAG;
950 			strncpy(value->search, "seen_flag=0", MAX_SEARCH_LEN - 1);
951 			(*idx) += 2;
952 
953 		} else if (MATCH(nextkey, "draft")) {
954 			value->type = IST_FLAG;
955 			strncpy(value->search, "draft_flag=0", MAX_SEARCH_LEN - 1);
956 			(*idx) += 2;
957 
958 		} else if (MATCH(nextkey, "new")) {
959 			value->type = IST_FLAG;
960 			strncpy(value->search, "(seen_flag=1 AND recent_flag=0)", MAX_SEARCH_LEN - 1);
961 			(*idx) += 2;
962 
963 		} else if (MATCH(nextkey, "old")) {
964 			value->type = IST_FLAG;
965 			strncpy(value->search, "recent_flag=1", MAX_SEARCH_LEN - 1);
966 			(*idx) += 2;
967 
968 		} else {
969 			value->type = IST_SUBSEARCH_NOT;
970 			(*idx)++;
971 
972 			append_search(self, value, 1);
973 			if ((result = _handle_search_args(self, search_keys, idx)) < 0)
974 				return result;
975 			pop_search(self);
976 
977 			return 0;
978 		}
979 
980 	} else if (MATCH(key, "or")) {
981 		value->type = IST_SUBSEARCH_OR;
982 		(*idx)++;
983 
984 		append_search(self, value, 1);
985 		if ((result = _handle_search_args(self, search_keys, idx)) < 0)
986 			return result;
987 		if ((result = _handle_search_args(self, search_keys, idx)) < 0)
988 			return result;
989 		pop_search(self);
990 
991 		return 0;
992 
993 	} else if (MATCH(key, "(")) {
994 		value->type = IST_SUBSEARCH_AND;
995 		(*idx)++;
996 
997 		append_search(self, value, 1);
998 		while ((result = dbmail_mailbox_build_imap_search(self, search_keys, idx, 0)) == 0);
999 		pop_search(self);
1000 
1001 		return 0;
1002 
1003 	} else if (MATCH(key, ")")) {
1004 		(*idx)++;
1005 		mempool_push(self->pool, value, sizeof (search_key));
1006 		return 1;
1007 
1008 	} else if (check_msg_set(key)) {
1009 		value->type = IST_SET;
1010 		strncpy(value->search, key, MAX_SEARCH_LEN - 1);
1011 		(*idx)++;
1012 
1013 		/* ignore the charset. Let the database handle this */
1014 	} else if (MATCH(key, "charset")) {
1015 		(*idx)++; // FIXME: check for valid charset here
1016 		self->charset = p_string_str(search_keys[*idx]);
1017 		TRACE(TRACE_DEBUG, "using charset [%s] for searching", self->charset);
1018 		(*idx)++;
1019 	} else {
1020 		/* unknown search key */
1021 		TRACE(TRACE_DEBUG, "unknown search key [%s]", key);
1022 		mempool_push(self->pool, value, sizeof (search_key));
1023 		return -1;
1024 	}
1025 
1026 	if (value->type)
1027 		append_search(self, value, 0);
1028 	else
1029 		mempool_push(self->pool, value, sizeof (search_key));
1030 
1031 	return 0;
1032 }
1033 
1034 /*
1035  * build_imap_search()
1036  *
1037  * builds a linked list of search items from a set of IMAP search keys
1038  * sl should be initialized; new search items are simply added to the list
1039  *
1040  * returns -1 on syntax error, -2 on memory error; 0 on success, 1 if ')' has been encountered
1041  */
dbmail_mailbox_build_imap_search(DbmailMailbox * self,String_T * search_keys,uint64_t * idx,search_order order)1042 int dbmail_mailbox_build_imap_search(DbmailMailbox *self, String_T *search_keys, uint64_t *idx, search_order order) {
1043 	int result = 0;
1044 	search_key * value, * s;
1045 
1046 	if (!(search_keys && search_keys[*idx]))
1047 		return 1;
1048 
1049 	/* default initial key for ANDing */
1050 	value = mempool_pop(self->pool, sizeof (search_key));
1051 	value->type = IST_SET;
1052 
1053 	if (check_msg_set(p_string_str(search_keys[*idx]))) {
1054 		strncpy(value->search, p_string_str(search_keys[*idx]), MAX_SEARCH_LEN - 1);
1055 		(*idx)++;
1056 	} else {
1057 		/* match all messages if no initial sequence set is defined */
1058 		strncpy(value->search, "1:*", MAX_SEARCH_LEN - 1);
1059 	}
1060 	append_search(self, value, 0);
1061 
1062 	/* SORT */
1063 	switch (order) {
1064 		case SEARCH_SORTED:
1065 			value = mempool_pop(self->pool, sizeof (search_key));
1066 			value->type = IST_SORT;
1067 			s = value;
1068 			while (((result = _handle_sort_args(self, search_keys, value, idx)) == 0) && search_keys[*idx]);
1069 			if (result < 0)
1070 				mempool_push(self->pool, s, sizeof (search_key));
1071 			break;
1072 		case SEARCH_THREAD_ORDEREDSUBJECT:
1073 		case SEARCH_THREAD_REFERENCES:
1074 			(*idx)++;
1075 			TRACE(TRACE_DEBUG, "search_key: [%s]", p_string_str(search_keys[*idx]));
1076 			// eat the charset arg
1077 			if (MATCH(p_string_str(search_keys[*idx]), "utf-8"))
1078 				(*idx)++;
1079 			else if (MATCH(p_string_str(search_keys[*idx]), "us-ascii"))
1080 				(*idx)++;
1081 			else if (MATCH(p_string_str(search_keys[*idx]), "iso-8859-1"))
1082 				(*idx)++;
1083 			else
1084 				return -1;
1085 
1086 			break;
1087 		case SEARCH_UNORDERED:
1088 		default:
1089 			// ignore
1090 			break;
1091 
1092 	}
1093 
1094 	/* SEARCH */
1095 	while (search_keys[*idx] && ((result = _handle_search_args(self, search_keys, idx)) == 0));
1096 
1097 	TRACE(TRACE_DEBUG, "done [%d] at idx [%" PRIu64 "]", result, *idx);
1098 	return result;
1099 }
1100 
_do_sort(GNode * node,DbmailMailbox * self)1101 static gboolean _do_sort(GNode *node, DbmailMailbox *self) {
1102 	TRACE(TRACE_DEBUG, "Call: _do_sort");
1103 	GString *q;
1104 	uint64_t tid, *id;
1105 	Connection_T c;
1106 	ResultSet_T r;
1107 	volatile int t = FALSE;
1108 	search_key *s = (search_key *) node->data;
1109 	GTree *z;
1110 
1111 	TRACE(TRACE_DEBUG, "type [%d]", s->type);
1112 
1113 	if (s->type != IST_SORT) return FALSE;
1114 
1115 	if (s->searched) return FALSE;
1116 
1117 	q = g_string_new("");
1118 	g_string_printf(q, "SELECT m.message_idnr FROM %smessages m "
1119 		"LEFT JOIN %sphysmessage p ON m.physmessage_id=p.id "
1120 		"%s"
1121 		"WHERE m.mailbox_idnr = %" PRIu64 " AND m.status < %d "
1122 		"ORDER BY %smessage_idnr", DBPFX, DBPFX, s->table,
1123 		dbmail_mailbox_get_id(self), MESSAGE_STATUS_DELETE, s->order);
1124 
1125 	if (self->sorted) {
1126 		g_list_destroy(self->sorted);
1127 		self->sorted = NULL;
1128 	}
1129 
1130 	z = g_tree_new((GCompareFunc) ucmp);
1131 	c = db_con_get();
1132 	TRY
1133 	r = db_query(c, q->str);
1134 	while (db_result_next(r)) {
1135 		tid = db_result_get_u64(r, 0);
1136 		if (g_tree_lookup(self->found, &tid) && (!g_tree_lookup(z, &tid))) {
1137 			id = g_new0(uint64_t, 1);
1138 			*id = tid;
1139 			g_tree_insert(z, id, id);
1140 			self->sorted = g_list_prepend(self->sorted, id);
1141 		}
1142 	}
1143 	CATCH(SQLException)
1144 	LOG_SQLERROR;
1145 	t = DM_EQUERY;
1146 	FINALLY
1147 	db_con_close(c);
1148 	g_tree_destroy(z);
1149 	END_TRY;
1150 
1151 	if (t == DM_EQUERY) return TRUE;
1152 
1153 	self->sorted = g_list_reverse(self->sorted);
1154 
1155 	g_string_free(q, TRUE);
1156 
1157 	s->searched = TRUE;
1158 
1159 	return FALSE;
1160 }
1161 
mailbox_search(DbmailMailbox * self,search_key * s)1162 static GTree * mailbox_search(DbmailMailbox *self, search_key *s) {
1163 	TRACE(TRACE_DEBUG, "Call: mailbox_search");
1164 	uint64_t *k, *v, *w;
1165 	uint64_t id;
1166 	char gt_lt = 0;
1167 	const char *op;
1168 	char partial[DEF_FRAGSIZE];
1169 	Connection_T c;
1170 	ResultSet_T r;
1171 	PreparedStatement_T st = NULL;
1172 	GTree *ids;
1173 	volatile char *inset = NULL;
1174 	/* helper for some operations in TREE mode search */
1175 	char *cond = NULL;
1176 	cond = malloc(30);
1177 	memset(cond, 0, 30);
1178 	int searchPerformed = 0;
1179 	/* if the query will be performed in sql mode */
1180 	int sql;
1181 	sql = 1;
1182 
1183 	GString *t;
1184 	String_T q;
1185 
1186 	int mailbox_search_strategy = config_get_value_default_int("mailbox_search_strategy", "IMAP", 1);
1187 	TRACE(TRACE_DEBUG, "mailbox_search_strategy %d", mailbox_search_strategy);
1188 
1189 	if (self->found && g_tree_nnodes(self->found) <= 200) {
1190 		char *setlist = dbmail_mailbox_ids_as_string(self, TRUE, ",");
1191 		if (setlist) {
1192 			inset = g_strdup_printf("AND m.message_idnr IN (%s)", setlist);
1193 			g_free(setlist);
1194 		}
1195 	}
1196 
1197 	c = db_con_get();
1198 	t = g_string_new("");
1199 	q = p_string_new(self->pool, "");
1200 	s->found = g_tree_new_full((GCompareDataFunc) ucmpdata, NULL, (GDestroyNotify) uint64_free, (GDestroyNotify) uint64_free);
1201 
1202 
1203 
1204 	TRY
1205 		/* this searches cannot be made via state */
1206 	switch (s->type) {
1207 		case IST_HDRDATE_ON:
1208 		case IST_HDRDATE_SINCE:
1209 		case IST_HDRDATE_BEFORE:
1210 		{
1211 			searchPerformed = 1;
1212 			char qs[DEF_FRAGSIZE];
1213 			char field[DEF_FRAGSIZE];
1214 			char d[SQL_INTERNALDATE_LEN];
1215 			char date[DEF_FRAGSIZE];
1216 			memset(d, 0, sizeof (d));
1217 			memset(qs, 0, sizeof (qs));
1218 			memset(date, 0, sizeof (date));
1219 			memset(field, 0, sizeof (field));
1220 
1221 			g_snprintf(field, DEF_FRAGSIZE - 1, db_get_sql(SQL_TO_DATE), s->hdrfld);
1222 			date_imap2sql(s->search, d);
1223 			g_snprintf(qs, DEF_FRAGSIZE - 1, "'%s'", d);
1224 			g_snprintf(date, DEF_FRAGSIZE - 1, db_get_sql(SQL_TO_DATE), qs);
1225 
1226 			if (s->type == IST_HDRDATE_SINCE)
1227 				op = ">=";
1228 			else if (s->type == IST_HDRDATE_BEFORE)
1229 				op = "<";
1230 			else
1231 				op = "=";
1232 			TRACE(TRACE_DEBUG, "IST_HDRDATE_ON/IST_HDRDATE_SINCE/IST_HDRDATE_BEFORE sql (%s) %s", op, field);
1233 			p_string_printf(q, "SELECT message_idnr FROM %smessages m "
1234 				"LEFT JOIN %sheader h USING (physmessage_id) "
1235 				"LEFT JOIN %sheadername n ON h.headername_id = n.id "
1236 				"LEFT JOIN %sheadervalue v ON h.headervalue_id = v.id "
1237 				"WHERE m.mailbox_idnr=? AND m.status < ? "
1238 				"%s "
1239 				"AND n.headername = 'date' "
1240 				"AND %s %s %s ORDER BY message_idnr",
1241 				DBPFX, DBPFX, DBPFX, DBPFX,
1242 				inset ? inset : "",
1243 				field, op, date);
1244 
1245 			st = db_stmt_prepare(c, p_string_str(q));
1246 			db_stmt_set_u64(st, 1, dbmail_mailbox_get_id(self));
1247 			db_stmt_set_int(st, 2, MESSAGE_STATUS_DELETE);
1248 		}
1249 
1250 			break;
1251 
1252 		case IST_HDR:
1253 			searchPerformed = 1;
1254 			TRACE(TRACE_DEBUG, "IST_HDR sql");
1255 			p_string_printf(q, "SELECT message_idnr FROM %smessages m "
1256 				"LEFT JOIN %sheader h USING (physmessage_id) "
1257 				"LEFT JOIN %sheadername n ON h.headername_id = n.id "
1258 				"LEFT JOIN %sheadervalue v ON h.headervalue_id = v.id "
1259 				"WHERE mailbox_idnr=? AND status < ? "
1260 				"%s "
1261 				"AND n.headername = '%s' AND v.headervalue %s ? "
1262 				"ORDER BY message_idnr",
1263 				DBPFX, DBPFX, DBPFX, DBPFX,
1264 				inset ? inset : "",
1265 				s->hdrfld, db_get_sql(SQL_INSENSITIVE_LIKE));
1266 
1267 			st = db_stmt_prepare(c, p_string_str(q));
1268 			db_stmt_set_u64(st, 1, dbmail_mailbox_get_id(self));
1269 			db_stmt_set_int(st, 2, MESSAGE_STATUS_DELETE);
1270 			memset(partial, 0, sizeof (partial));
1271 			if (snprintf(partial, DEF_FRAGSIZE - 1, "%%%s%%", s->search) < 0)
1272 				abort();
1273 			db_stmt_set_str(st, 3, partial);
1274 
1275 			break;
1276 
1277 		case IST_DATA_TEXT:
1278 			searchPerformed = 1;
1279 			TRACE(TRACE_DEBUG, "IST_DATA_TEXT sql");
1280 			p_string_printf(q, "SELECT DISTINCT m.message_idnr "
1281 				"FROM %smimeparts k "
1282 				"LEFT JOIN %spartlists l ON k.id=l.part_id "
1283 				"LEFT JOIN %sphysmessage p ON l.physmessage_id=p.id "
1284 				"LEFT JOIN %sheader h ON h.physmessage_id=p.id "
1285 				"LEFT JOIN %sheadervalue v ON h.headervalue_id=v.id "
1286 				"LEFT JOIN %smessages m ON m.physmessage_id=p.id "
1287 				"WHERE m.mailbox_idnr = ? AND m.status < ? "
1288 				"%s "
1289 				"AND (v.headervalue %s ? OR k.data %s ?) "
1290 				"ORDER BY m.message_idnr",
1291 				DBPFX, DBPFX, DBPFX, DBPFX, DBPFX, DBPFX,
1292 				inset ? inset : "",
1293 				db_get_sql(SQL_INSENSITIVE_LIKE),
1294 				db_get_sql(SQL_SENSITIVE_LIKE)); // pgsql will trip over ilike against bytea
1295 
1296 			st = db_stmt_prepare(c, p_string_str(q));
1297 			db_stmt_set_u64(st, 1, dbmail_mailbox_get_id(self));
1298 			db_stmt_set_int(st, 2, MESSAGE_STATUS_DELETE);
1299 
1300 			memset(partial, 0, sizeof (partial));
1301 			if (snprintf(partial, DEF_FRAGSIZE - 1, "%%%s%%", s->search) < 0)
1302 				abort();
1303 			db_stmt_set_str(st, 3, partial);
1304 			db_stmt_set_str(st, 4, partial);
1305 
1306 			break;
1307 
1308 		case IST_DATA_BODY:
1309 			searchPerformed = 1;
1310 			TRACE(TRACE_DEBUG, "IST_DATA_BODY sql %s", t->str);
1311 			g_string_printf(t, db_get_sql(SQL_ENCODE_ESCAPE), "p.data");
1312 			p_string_printf(q, "SELECT DISTINCT m.message_idnr FROM %smimeparts p "
1313 				"LEFT JOIN %spartlists l ON p.id=l.part_id "
1314 				"LEFT JOIN %sphysmessage s ON l.physmessage_id=s.id "
1315 				"LEFT JOIN %smessages m ON m.physmessage_id=s.id "
1316 				"LEFT JOIN %smailboxes b ON m.mailbox_idnr = b.mailbox_idnr "
1317 				"WHERE b.mailbox_idnr=? AND m.status < ? "
1318 				"%s "
1319 				"AND (l.part_key > 1 OR l.is_header=0) "
1320 				"AND %s %s ? "
1321 				"ORDER BY m.message_idnr",
1322 				DBPFX, DBPFX, DBPFX, DBPFX, DBPFX,
1323 				inset ? inset : "",
1324 				t->str, db_get_sql(SQL_SENSITIVE_LIKE)); // pgsql will trip over ilike against bytea
1325 
1326 			st = db_stmt_prepare(c, p_string_str(q));
1327 			db_stmt_set_u64(st, 1, dbmail_mailbox_get_id(self));
1328 			db_stmt_set_int(st, 2, MESSAGE_STATUS_DELETE);
1329 
1330 			memset(partial, 0, sizeof (partial));
1331 			if (snprintf(partial, DEF_FRAGSIZE - 1, "%%%s%%", s->search) < 0)
1332 				abort();
1333 
1334 			db_stmt_set_str(st, 3, partial);
1335 
1336 			break;
1337 
1338 		case IST_KEYWORD:
1339 		case IST_UNKEYWORD:
1340 			searchPerformed = 1;
1341 			TRACE(TRACE_DEBUG, "IST_KEYWORD/IST_UNKEYWORD sql %s", s->search);
1342 			p_string_printf(q, "SELECT m.message_idnr FROM %smessages m "
1343 				"JOIN %skeywords k ON m.message_idnr=k.message_idnr "
1344 				"WHERE mailbox_idnr=? AND status < ? "
1345 				"%s "
1346 				"AND k.keyword = ? ORDER BY message_idnr",
1347 				DBPFX, DBPFX,
1348 				inset ? inset : "");
1349 
1350 			st = db_stmt_prepare(c, p_string_str(q));
1351 			db_stmt_set_u64(st, 1, dbmail_mailbox_get_id(self));
1352 			db_stmt_set_int(st, 2, MESSAGE_STATUS_DELETE);
1353 			db_stmt_set_str(st, 3, s->search);
1354 			break;
1355 	}
1356 
1357 
1358 	if (searchPerformed == 0 && mailbox_search_strategy == 1) {
1359 		/* general queries have not been executed and strategy is sql */
1360 		switch (s->type) {
1361 			case IST_IDATE:
1362 				TRACE(TRACE_DEBUG, "IST_IDATE sql %s", s->search);
1363 				/* implemented search by state, avoiding queries in DB */
1364 				p_string_printf(q, "SELECT message_idnr FROM %smessages m "
1365 					"LEFT JOIN %sphysmessage p ON m.physmessage_id=p.id "
1366 					"WHERE mailbox_idnr = ? AND status < ? "
1367 					"%s "
1368 					"AND %s "
1369 					"ORDER BY message_idnr",
1370 					DBPFX, DBPFX,
1371 					inset ? inset : "",
1372 					s->search);
1373 
1374 				st = db_stmt_prepare(c, p_string_str(q));
1375 				db_stmt_set_u64(st, 1, dbmail_mailbox_get_id(self));
1376 				db_stmt_set_int(st, 2, MESSAGE_STATUS_DELETE);
1377 
1378 				break;
1379 			case IST_SIZE_LARGER:
1380 				searchPerformed = 1;
1381 				gt_lt = '>';
1382 				// fallthrough
1383 
1384 			case IST_SIZE_SMALLER:
1385 				if (!gt_lt) gt_lt = '<';
1386 				TRACE(TRACE_DEBUG, "IST_SIZE_SMALLER/IST_SIZE_LARGER sql %ld", s->size);
1387 				p_string_printf(q, "SELECT m.message_idnr FROM %smessages m "
1388 					"LEFT JOIN %sphysmessage p ON m.physmessage_id = p.id "
1389 					"WHERE m.mailbox_idnr = ? AND m.status < ? "
1390 					"%s "
1391 					"AND p.rfcsize %c ? "
1392 					"ORDER BY message_idnr",
1393 					DBPFX, DBPFX,
1394 					inset ? inset : "",
1395 					gt_lt);
1396 
1397 				st = db_stmt_prepare(c, p_string_str(q));
1398 				db_stmt_set_u64(st, 1, dbmail_mailbox_get_id(self));
1399 				db_stmt_set_int(st, 2, MESSAGE_STATUS_DELETE);
1400 				db_stmt_set_u64(st, 3, s->size);
1401 
1402 				break;
1403 
1404 
1405 			default:
1406 				TRACE(TRACE_DEBUG, "IST* sql %s", s->search);
1407 				/* other searches */
1408 				p_string_printf(q, "SELECT message_idnr FROM %smessages "
1409 					"WHERE mailbox_idnr = ? AND status < ? AND %s "
1410 					"ORDER BY message_idnr", DBPFX,
1411 					s->search); // FIXME: Sometimes s->search is ""
1412 
1413 				st = db_stmt_prepare(c, p_string_str(q));
1414 				db_stmt_set_u64(st, 1, dbmail_mailbox_get_id(self));
1415 				db_stmt_set_int(st, 2, MESSAGE_STATUS_DELETE);
1416 		}
1417 	}
1418 	if (searchPerformed == 0 && mailbox_search_strategy == 2) {
1419 		int IST_MBS_COND = 0;
1420 		/* general queries have not been executed and strategy is tree/state */
1421 		/* handle old default case, we need to test it the hard way */
1422 		if (strcmp(s->search, "answered_flag=1") == 0) {
1423 			TRACE(TRACE_DEBUG, "IST* answered_flag=1");
1424 			sql = 0;
1425 			IST_MBS_COND = 1;
1426 		}
1427 		if (strcmp(s->search, "deleted_flag=1") == 0) {
1428 			TRACE(TRACE_DEBUG, "IST* deleted_flag=1");
1429 			sql = 0;
1430 			IST_MBS_COND = 2;
1431 		}
1432 		if (strcmp(s->search, "flagged_flag=1") == 0) {
1433 			TRACE(TRACE_DEBUG, "IST* flagged_flag=1");
1434 			sql = 0;
1435 			IST_MBS_COND = 3;
1436 		}
1437 		if (strcmp(s->search, "recent_flag=1") == 0) {
1438 			TRACE(TRACE_DEBUG, "IST* recent_flag=1");
1439 			sql = 0;
1440 			IST_MBS_COND = 4;
1441 		}
1442 		if (strcmp(s->search, "seen_flag=1") == 0) {
1443 			TRACE(TRACE_DEBUG, "IST* seen_flag=1");
1444 			sql = 0;
1445 			IST_MBS_COND = 5;
1446 		}
1447 		if (strcmp(s->search, "draft_flag=1") == 0) {
1448 			TRACE(TRACE_DEBUG, "IST* draft_flag=1");
1449 			sql = 0;
1450 			IST_MBS_COND = 6;
1451 		}
1452 		if (strcmp(s->search, "(seen_flag=0 AND recent_flag=1)") == 0) {
1453 			TRACE(TRACE_DEBUG, "IST* (seen_flag=0 AND recent_flag=1)");
1454 			sql = 0;
1455 			IST_MBS_COND = 7;
1456 		}
1457 		if (strcmp(s->search, "recent_flag=0") == 0) {
1458 			TRACE(TRACE_DEBUG, "IST* recent_flag=1");
1459 			sql = 0;
1460 			IST_MBS_COND = 8;
1461 		}
1462 		if (strcmp(s->search, "answered_flag=0") == 0) {
1463 			TRACE(TRACE_DEBUG, "IST* answered_flag=1");
1464 			sql = 0;
1465 			IST_MBS_COND = 9;
1466 		}
1467 		if (strcmp(s->search, "deleted_flag=0") == 0) {
1468 			TRACE(TRACE_DEBUG, "IST* deleted_flag=1");
1469 			sql = 0;
1470 			IST_MBS_COND = 10;
1471 		}
1472 		if (strcmp(s->search, "flagged_flag=0") == 0) {
1473 			TRACE(TRACE_DEBUG, "IST* flagged_flag=1");
1474 			sql = 0;
1475 			IST_MBS_COND = 11;
1476 		}
1477 		if (strcmp(s->search, "seen_flag=0") == 0) {
1478 			TRACE(TRACE_DEBUG, "IST* seen_flag=1");
1479 			sql = 0;
1480 			IST_MBS_COND = 12;
1481 		}
1482 		if (strcmp(s->search, "draft_flag=0") == 0) {
1483 			TRACE(TRACE_DEBUG, "IST* draft_flag=1");
1484 			sql = 0;
1485 			IST_MBS_COND = 13;
1486 		}
1487 		/* handle IST_DATE */
1488 		if (strstr(s->search, "p.internal_date")) {
1489 			/* the equivalent in query is at IST_IDATE, see above */
1490 			if (strstr(s->search, ">")) {
1491 				sql = 0;
1492 				IST_MBS_COND = 14;
1493 				strcpy(cond, strstr(s->search, ">") + 1);
1494 				TRACE(TRACE_DEBUG, "IST_IDATE [>]");
1495 			}
1496 			if (strstr(s->search, "=")) {
1497 				sql = 0;
1498 				IST_MBS_COND = 15;
1499 				strcpy(cond, strstr(s->search, "=") + 1);
1500 				TRACE(TRACE_DEBUG, "IST_IDATE [=]");
1501 			}
1502 			if (strstr(s->search, "<")) {
1503 				sql = 0;
1504 				IST_MBS_COND = 16;
1505 				strcpy(cond, strstr(s->search, "<") + 1);
1506 				TRACE(TRACE_DEBUG, "IST_IDATE [<]");
1507 			}
1508 			if (IST_MBS_COND != 0) {
1509 				p_trim(cond, " '><="); //trimming, just a safety precaution
1510 				TRACE(TRACE_DEBUG, "IST_IDATE %s -> %s ", s->search, cond);
1511 			}
1512 
1513 		}
1514 		if (s->type == IST_SIZE_LARGER) {
1515 			TRACE(TRACE_DEBUG, "IST_SIZE_LARGER");
1516 			sql = 0;
1517 			IST_MBS_COND = 17;
1518 
1519 		}
1520 		if (s->type == IST_SIZE_SMALLER) {
1521 			TRACE(TRACE_DEBUG, "IST_SIZE_SMALLER");
1522 			sql = 0;
1523 			IST_MBS_COND = 18;
1524 		}
1525 		//TRACE(TRACE_DEBUG,"IST_IDATE %s -> %d %d", s->search, sql,IST_MBS_COND);
1526 		if (sql == 0) {
1527 			int foundItems = 0;
1528 			ids = MailboxState_getIds(self->mbstate);
1529 			GList *uids = g_tree_keys(ids);
1530 			uids = g_list_first(uids);
1531 			/* creating another tree by rebuilding it against a condition */
1532 			/* we shoud have used g_tree_foreach?! may have been faster and more memory efficient */
1533 			/* @todo  g_tree_foreach */
1534 			while (uids) {
1535 				uint64_t id = *(uint64_t *) uids->data;
1536 				/* no need to check it, is the same list*/
1537 				/*if (! (w = g_tree_lookup(ids, &id))) {
1538 					TRACE(TRACE_ERR, "key missing in ids: [%" PRIu64 "]", id);
1539 					if (! g_list_next(uids)) break;
1540 					uids = g_list_next(uids);
1541 					continue;
1542 				}*/
1543 				MessageInfo *msginfo = g_tree_lookup(MailboxState_getMsginfo(self->mbstate), &id);
1544 
1545 				int found = 0;
1546 				switch (IST_MBS_COND) {
1547 					case 1: found = (int) (msginfo->flags[IMAP_FLAG_ANSWERED] == 1);
1548 						break;
1549 					case 2: found = (int) (msginfo->flags[IMAP_FLAG_DELETED] == 1);
1550 						break;
1551 					case 3: found = (int) (msginfo->flags[IMAP_FLAG_FLAGGED] == 1);
1552 						break;
1553 					case 4: found = (int) (msginfo->flags[IMAP_FLAG_RECENT] == 1);
1554 						break;
1555 					case 5: found = (int) (msginfo->flags[IMAP_FLAG_SEEN] == 1);
1556 						break;
1557 					case 6: found = (int) (msginfo->flags[IMAP_FLAG_DRAFT] == 1);
1558 						break;
1559 					case 7: found = (int) (msginfo->flags[IMAP_FLAG_SEEN] == 0 && msginfo->flags[IMAP_FLAG_RECENT] == 1);
1560 						break;
1561 					case 8: found = (int) (msginfo->flags[IMAP_FLAG_RECENT] == 0);
1562 						break;
1563 					case 9: found = (int) (msginfo->flags[IMAP_FLAG_ANSWERED] == 0);
1564 						break;
1565 					case 10: found = (int) (msginfo->flags[IMAP_FLAG_DELETED] == 0);
1566 						break;
1567 					case 11: found = (int) (msginfo->flags[IMAP_FLAG_FLAGGED] == 0);
1568 						break;
1569 					case 12: found = (int) (msginfo->flags[IMAP_FLAG_SEEN] == 0);
1570 						break;
1571 					case 13: found = (int) (msginfo->flags[IMAP_FLAG_DRAFT] == 0);
1572 						break;
1573 					case 14: found = (int) (strcmp(msginfo->internaldate, cond) > 0);
1574 						break;
1575 					case 15: found = (int) (strcmp(msginfo->internaldate, cond) == 0);
1576 						break;
1577 					case 16: found = (int) (strcmp(msginfo->internaldate, cond) < 0);
1578 						break;
1579 						//IST_SIZE_LARGER
1580 					case 17: found = (int) (msginfo->rfcsize > s->size);
1581 						break;
1582 						//IST_SIZE_SMALLER
1583 					case 18: found = (int) (msginfo->rfcsize < s->size);
1584 						break;
1585 
1586 				}
1587 				if (found == 1) {
1588 					//TRACE(TRACE_DEBUG, "Found (%d) %ld vs %ld ",IST_MBS_COND,id,msginfo->uid);
1589 					k = mempool_pop(small_pool, sizeof (uint64_t));
1590 					v = mempool_pop(small_pool, sizeof (uint64_t));
1591 					*k = id;
1592 					*v = id;
1593 
1594 					g_tree_insert(s->found, k, v);
1595 					foundItems++;
1596 				}
1597 				if (!g_list_next(uids)) break;
1598 				uids = g_list_next(uids);
1599 			}
1600 			g_list_free(g_list_first(uids));
1601 			if (cond) {
1602 				TRACE(TRACE_DEBUG, "IST RESULT TREE (message_search_strategy=2) found %s (%s), found  %d",
1603 					s->search,
1604 					cond,
1605 					foundItems);
1606 			} else {
1607 				TRACE(TRACE_DEBUG, "IST RESULT TREE (message_search_strategy=2) found %s, found  %d", s->search, foundItems);
1608 			}
1609 		} else {
1610 			TRACE(TRACE_WARNING, "IST unhandled search for message_search_strategy=2 so fall back (should be handled)");
1611 			p_string_printf(q, "SELECT message_idnr FROM %smessages "
1612 				"WHERE mailbox_idnr = ? AND status < ? AND %s "
1613 				"ORDER BY message_idnr", DBPFX,
1614 				s->search); // FIXME: Sometimes s->search is ""
1615 
1616 			st = db_stmt_prepare(c, p_string_str(q));
1617 			db_stmt_set_u64(st, 1, dbmail_mailbox_get_id(self));
1618 			db_stmt_set_int(st, 2, MESSAGE_STATUS_DELETE);
1619 		}
1620 
1621 
1622 	}
1623 	/* due to the fact that may be some issues related to the redirecting of queries for strategy = 2*/
1624 	/* we may need to test for st not being null */
1625 	if (st && sql == 1) {
1626 		int foundItems = 0;
1627 		r = db_stmt_query(st);
1628 
1629 		ids = MailboxState_getIds(self->mbstate);
1630 		while (db_result_next(r)) {
1631 			id = db_result_get_u64(r, 0);
1632 			if (!(w = g_tree_lookup(ids, &id))) {
1633 				TRACE(TRACE_ERR, "key missing in ids: [%" PRIu64 "]\n", id);
1634 				continue;
1635 			}
1636 			assert(w);
1637 
1638 			k = mempool_pop(small_pool, sizeof (uint64_t));
1639 			v = mempool_pop(small_pool, sizeof (uint64_t));
1640 			*k = id;
1641 			*v = *w;
1642 			g_tree_insert(s->found, k, v);
1643 			foundItems++;
1644 		}
1645 		TRACE(TRACE_DEBUG, "IST RESULT SQL found %s, found  %d", s->search, foundItems);
1646 	}
1647 	if (s->type == IST_UNKEYWORD) {
1648 		GTree *old = NULL;
1649 		GTree *invert = g_tree_new_full((GCompareDataFunc) ucmpdata, NULL, (GDestroyNotify) uint64_free, (GDestroyNotify) uint64_free);
1650 		GList *uids = g_tree_keys(ids);
1651 		uids = g_list_first(uids);
1652 		while (uids) {
1653 			uint64_t id = *(uint64_t *) uids->data;
1654 			if (!(w = g_tree_lookup(ids, &id))) {
1655 				TRACE(TRACE_ERR, "key missing in ids: [%" PRIu64 "]", id);
1656 
1657 				if (!g_list_next(uids)) break;
1658 				uids = g_list_next(uids);
1659 				continue;
1660 			}
1661 
1662 			assert(w);
1663 
1664 			if (g_tree_lookup(s->found, &id)) {
1665 				TRACE(TRACE_DEBUG, "skip key [%" PRIu64 "]", id);
1666 
1667 				if (!g_list_next(uids)) break;
1668 				uids = g_list_next(uids);
1669 				continue;
1670 			}
1671 
1672 			k = mempool_pop(small_pool, sizeof (uint64_t));
1673 			v = mempool_pop(small_pool, sizeof (uint64_t));
1674 			*k = id;
1675 			*v = *w;
1676 
1677 			g_tree_insert(invert, k, v);
1678 
1679 
1680 			if (!g_list_next(uids)) break;
1681 			uids = g_list_next(uids);
1682 
1683 		}
1684 
1685 		old = s->found;
1686 		s->found = invert;
1687 		g_tree_destroy(old);
1688 		g_list_free(g_list_first(uids));
1689 	}
1690 
1691 	CATCH(SQLException)
1692 	LOG_SQLERROR;
1693 	FINALLY
1694 	db_con_close(c);
1695 	END_TRY;
1696 
1697 	if (inset)
1698 		g_free((char *) inset);
1699 
1700 	p_string_free(q, TRUE);
1701 	g_string_free(t, TRUE);
1702 	free(cond);
1703 
1704 	return s->found;
1705 }
1706 
checkset(const char * s)1707 static int checkset(const char *s) {
1708 	int i;
1709 	for (i = 0; s[i]; i++) {
1710 		if (!strchr("0123456789:,*", s[i]))
1711 			return 0;
1712 	}
1713 	return 1;
1714 }
1715 
1716 struct filter_modseq_helper {
1717 	GTree *msginfo;
1718 	uint64_t modseq;
1719 	GList *remove;
1720 };
1721 
filter_modseq(gpointer key,gpointer UNUSED value,gpointer data)1722 static int filter_modseq(gpointer key, gpointer UNUSED value, gpointer data) {
1723 	struct filter_modseq_helper *d = (struct filter_modseq_helper *) data;
1724 	uint64_t *id;
1725 
1726 	id = (uint64_t *) key;
1727 	MessageInfo *info = g_tree_lookup(d->msginfo, id);
1728 	if (!info)
1729 		return TRUE;
1730 
1731 	if (info->seq <= d->modseq)
1732 		d->remove = g_list_prepend(d->remove, key);
1733 	return FALSE;
1734 }
1735 
find_modseq(DbmailMailbox * self,GTree * in)1736 GTree * find_modseq(DbmailMailbox *self, GTree *in) {
1737 	if (!self->modseq)
1738 		return in;
1739 	GList *remove;
1740 	struct filter_modseq_helper data;
1741 	data.msginfo = MailboxState_getMsginfo(self->mbstate);
1742 	data.modseq = self->modseq;
1743 	data.remove = NULL;
1744 
1745 	g_tree_foreach(in, (GTraverseFunc) filter_modseq, &data);
1746 	remove = data.remove;
1747 	while (remove) {
1748 		uint64_t *key = (uint64_t *) remove->data;
1749 		g_tree_remove(in, key);
1750 		if (!g_list_next(remove))
1751 			break;
1752 		remove = g_list_next(remove);
1753 	}
1754 
1755 	return in;
1756 }
1757 
dbmail_mailbox_get_set(DbmailMailbox * self,const char * set,gboolean uid)1758 GTree * dbmail_mailbox_get_set(DbmailMailbox *self, const char *set, gboolean uid) {
1759 	GTree *uids;
1760 	GTree *b;
1761 
1762 	TRACE(TRACE_DEBUG, "[%s] uid [%d]", set, uid);
1763 
1764 	if (!self->mbstate)
1765 		return NULL;
1766 
1767 	assert(self && self->mbstate && set);
1768 
1769 	uids = MailboxState_getIds(self->mbstate);
1770 	if ((!uid) && (g_tree_nnodes(uids) == 0))
1771 		return NULL;
1772 
1773 	if (!checkset(set)) // invalid chars
1774 		return NULL;
1775 
1776 
1777 	b = MailboxState_get_set(self->mbstate, set, uid);
1778 	return find_modseq(self, b);
1779 }
1780 
_found_tree_copy(uint64_t * key,uint64_t * val,GTree * tree)1781 static gboolean _found_tree_copy(uint64_t *key, uint64_t *val, GTree *tree) {
1782 	uint64_t *a, *b;
1783 	a = g_new0(uint64_t, 1);
1784 	b = g_new0(uint64_t, 1);
1785 	*a = *key;
1786 	*b = *val;
1787 	g_tree_insert(tree, a, b);
1788 	return FALSE;
1789 }
1790 
_shallow_tree_copy(uint64_t * key,uint64_t * val,GTree * tree)1791 static gboolean _shallow_tree_copy(uint64_t *key, uint64_t *val, GTree *tree) {
1792 	g_tree_insert(tree, key, val);
1793 	return FALSE;
1794 }
1795 
_prescan_search(GNode * node,DbmailMailbox * self)1796 static gboolean _prescan_search(GNode *node, DbmailMailbox *self) {
1797 	search_key *s = (search_key *) node->data;
1798 
1799 	if (s->searched) return FALSE;
1800 
1801 	switch (s->type) {
1802 		case IST_SET:
1803 			if (!(s->found = dbmail_mailbox_get_set(self, (const char *) s->search, 0)))
1804 				return TRUE;
1805 			break;
1806 		case IST_UIDSET:
1807 			if (!(s->found = dbmail_mailbox_get_set(self, (const char *) s->search, 1)))
1808 				return TRUE;
1809 			break;
1810 		default:
1811 			return FALSE;
1812 
1813 	}
1814 	s->searched = TRUE;
1815 
1816 	g_tree_merge(self->found, s->found, IST_SUBSEARCH_AND);
1817 	s->merged = TRUE;
1818 
1819 	TRACE(TRACE_DEBUG, "[%p] depth [%d] type [%d] rows [%d]\n",
1820 		s, g_node_depth(node), s->type, s->found ? g_tree_nnodes(s->found) : 0);
1821 
1822 	g_tree_destroy(s->found);
1823 	s->found = NULL;
1824 
1825 	return FALSE;
1826 }
1827 
_do_search(GNode * node,DbmailMailbox * self)1828 static gboolean _do_search(GNode *node, DbmailMailbox *self) {
1829 	search_key *s = (search_key *) node->data;
1830 
1831 	if (s->searched) return FALSE;
1832 
1833 	switch (s->type) {
1834 		case IST_SORT:
1835 			return FALSE;
1836 			break;
1837 
1838 		case IST_SET:
1839 			if (!(s->found = dbmail_mailbox_get_set(self, (const char *) s->search, 0)))
1840 				return TRUE;
1841 			break;
1842 		case IST_UIDSET:
1843 			if (!(s->found = dbmail_mailbox_get_set(self, (const char *) s->search, 1)))
1844 				return TRUE;
1845 			break;
1846 
1847 		case IST_KEYWORD:
1848 		case IST_UNKEYWORD:
1849 		case IST_SIZE_LARGER:
1850 		case IST_SIZE_SMALLER:
1851 		case IST_HDRDATE_BEFORE:
1852 		case IST_HDRDATE_SINCE:
1853 		case IST_HDRDATE_ON:
1854 		case IST_IDATE:
1855 		case IST_FLAG:
1856 		case IST_HDR:
1857 		case IST_DATA_TEXT:
1858 		case IST_DATA_BODY:
1859 			mailbox_search(self, s);
1860 			break;
1861 
1862 		case IST_SUBSEARCH_NOT:
1863 		case IST_SUBSEARCH_AND:
1864 		case IST_SUBSEARCH_OR:
1865 			g_node_children_foreach(node, G_TRAVERSE_ALL, (GNodeForeachFunc) _do_search, (gpointer) self);
1866 			s->found = g_tree_new_full((GCompareDataFunc) ucmpdata, NULL, (GDestroyNotify) g_free, (GDestroyNotify) g_free);
1867 			break;
1868 
1869 
1870 		default:
1871 			return TRUE;
1872 	}
1873 
1874 	s->searched = TRUE;
1875 
1876 	TRACE(TRACE_DEBUG, "[%p] depth [%d] type [%d] rows [%d]\n",
1877 		s, g_node_depth(node), s->type, s->found ? g_tree_nnodes(s->found) : 0);
1878 
1879 	return FALSE;
1880 }
1881 
_merge_search(GNode * node,GTree * found)1882 static gboolean _merge_search(GNode *node, GTree *found) {
1883 	search_key *s = (search_key *) node->data;
1884 	search_key *a, *b;
1885 	GNode *x, *y;
1886 
1887 	if (s->type == IST_SORT)
1888 		return FALSE;
1889 
1890 	if (s->merged == TRUE)
1891 		return FALSE;
1892 
1893 
1894 	switch (s->type) {
1895 		case IST_SUBSEARCH_AND:
1896 			g_node_children_foreach(node, G_TRAVERSE_ALL, (GNodeForeachFunc) _merge_search, (gpointer) found);
1897 			break;
1898 
1899 		case IST_SUBSEARCH_NOT:
1900 			g_tree_foreach(found, (GTraverseFunc) _found_tree_copy, s->found);
1901 			g_node_children_foreach(node, G_TRAVERSE_ALL, (GNodeForeachFunc) _merge_search, (gpointer) s->found);
1902 			g_tree_merge(found, s->found, IST_SUBSEARCH_NOT);
1903 			s->merged = TRUE;
1904 			g_tree_destroy(s->found);
1905 			s->found = NULL;
1906 
1907 			break;
1908 
1909 		case IST_SUBSEARCH_OR:
1910 			x = g_node_nth_child(node, 0);
1911 			y = g_node_nth_child(node, 1);
1912 			a = (search_key *) x->data;
1913 			b = (search_key *) y->data;
1914 
1915 			if (a->type == IST_SUBSEARCH_AND) {
1916 				g_tree_foreach(found, (GTraverseFunc) _found_tree_copy, a->found);
1917 				g_node_children_foreach(x, G_TRAVERSE_ALL, (GNodeForeachFunc) _merge_search, (gpointer) a->found);
1918 			}
1919 
1920 			if (b->type == IST_SUBSEARCH_AND) {
1921 				g_tree_foreach(found, (GTraverseFunc) _found_tree_copy, b->found);
1922 				g_node_children_foreach(y, G_TRAVERSE_ALL, (GNodeForeachFunc) _merge_search, (gpointer) b->found);
1923 			}
1924 
1925 			g_tree_merge(a->found, b->found, IST_SUBSEARCH_OR);
1926 			b->merged = TRUE;
1927 			g_tree_destroy(b->found);
1928 			b->found = NULL;
1929 
1930 			g_tree_merge(s->found, a->found, IST_SUBSEARCH_OR);
1931 			a->merged = TRUE;
1932 			g_tree_destroy(a->found);
1933 			a->found = NULL;
1934 
1935 			g_tree_merge(found, s->found, IST_SUBSEARCH_AND);
1936 			s->merged = TRUE;
1937 			g_tree_destroy(s->found);
1938 			s->found = NULL;
1939 
1940 			break;
1941 
1942 		default:
1943 			g_tree_merge(found, s->found, IST_SUBSEARCH_AND);
1944 			s->merged = TRUE;
1945 			g_tree_destroy(s->found);
1946 			s->found = NULL;
1947 
1948 			break;
1949 	}
1950 
1951 	TRACE(TRACE_DEBUG, "[%p] leaf [%d] depth [%d] type [%d] found [%d]",
1952 		s, G_NODE_IS_LEAF(node), g_node_depth(node), s->type, found ? g_tree_nnodes(found) : 0);
1953 
1954 	return FALSE;
1955 }
1956 
dbmail_mailbox_sort(DbmailMailbox * self)1957 int dbmail_mailbox_sort(DbmailMailbox *self) {
1958 	if (!self->search) return 0;
1959 
1960 	g_node_traverse(g_node_get_root(self->search), G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1961 		(GNodeTraverseFunc) _do_sort, (gpointer) self);
1962 
1963 	return 0;
1964 }
1965 
dbmail_mailbox_search(DbmailMailbox * self)1966 int dbmail_mailbox_search(DbmailMailbox *self) {
1967 	TRACE(TRACE_DEBUG, "Call: dbmail_mailbox_search");
1968 	GTree *ids;
1969 	if (!self->search) return 0;
1970 
1971 	if (!self->mbstate)
1972 		dbmail_mailbox_open(self);
1973 
1974 	if (self->found) g_tree_destroy(self->found);
1975 	self->found = g_tree_new_full((GCompareDataFunc) ucmpdata, NULL, NULL, NULL);
1976 
1977 	ids = MailboxState_getIds(self->mbstate);
1978 
1979 	g_tree_foreach(ids, (GTraverseFunc) _shallow_tree_copy, self->found);
1980 
1981 	g_node_traverse(g_node_get_root(self->search), G_LEVEL_ORDER, G_TRAVERSE_ALL, 2,
1982 		(GNodeTraverseFunc) _prescan_search, (gpointer) self);
1983 
1984 	g_node_traverse(g_node_get_root(self->search), G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1985 		(GNodeTraverseFunc) _do_search, (gpointer) self);
1986 
1987 	g_node_traverse(g_node_get_root(self->search), G_PRE_ORDER, G_TRAVERSE_ALL, -1,
1988 		(GNodeTraverseFunc) _merge_search, (gpointer) self->found);
1989 
1990 	if (self->found == NULL)
1991 		TRACE(TRACE_DEBUG, "found no ids\n");
1992 	else
1993 		TRACE(TRACE_DEBUG, "found [%d] ids\n", self->found ? g_tree_nnodes(self->found) : 0);
1994 
1995 	return 0;
1996 }
1997 
1998