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