1 /*
2
3 Copyright (c) 2004-2012 NFG Net Facilities Group BV support@nfg.nl
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later
9 version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20
21 #include <string.h>
22
23 #include "dbmail.h"
24 #include "dm_mailboxstate.h"
25 #include "dm_db.h"
26
27 #define THIS_MODULE "MailboxState"
28
29 /*
30 */
31 extern DBParam_T db_params;
32 extern Mempool_T small_pool;
33 extern const char *imap_flag_desc_escaped[];
34 #define DBPFX db_params.pfx
35
36 #define T MailboxState_T
37
38
39 static void db_getmailbox_seq(T M, Connection_T c);
40 static void db_getmailbox_permission(T M, Connection_T c);
41 static void state_load_metadata(T M, Connection_T c);
42 static void MailboxState_setMsginfo(T M, GTree *msginfo);
43 /* */
44
MailboxState_uid_msn_new(T M)45 static void MailboxState_uid_msn_new(T M)
46 {
47 if (M->msn) g_tree_destroy(M->msn);
48 M->msn = g_tree_new_full((GCompareDataFunc)ucmpdata,NULL,NULL,NULL);
49
50 if (M->ids) g_tree_destroy(M->ids);
51 M->ids = g_tree_new_full((GCompareDataFunc)ucmpdata,NULL,NULL,(GDestroyNotify)g_free);
52 }
53
MessageInfo_free(MessageInfo * m)54 static void MessageInfo_free(MessageInfo *m)
55 {
56 g_list_destroy(m->keywords);
57 g_free(m);
58 }
59
60
state_load_messages(T M,Connection_T c,gboolean coldLoad)61 static T state_load_messages(T M, Connection_T c, gboolean coldLoad)
62 {
63 unsigned nrows = 0, i = 0, j;
64 struct timeval before, after;
65 const char *query_result;
66 uint64_t tempId;
67 MessageInfo *result;
68 GTree *msginfo;
69 uint64_t *uid, id = 0;
70 ResultSet_T r;
71 PreparedStatement_T stmt;
72 Field_T frag;
73 INIT_QUERY;
74 char filterCondition[64]; memset(filterCondition,0,64);
75 /* the initialization should be done elsewhere, see ols MailboxState_new and MailboxState_update */
76 msginfo=MailboxState_getMsginfo(M);
77
78 if (coldLoad){
79 //msginfo = g_tree_new_full((GCompareDataFunc)ucmpdata, NULL,(GDestroyNotify)g_free,(GDestroyNotify)MessageInfo_free);
80 TRACE(TRACE_DEBUG, "SEQ New");
81 snprintf(filterCondition,64-1,"/*SEQ New*/ AND m.status < %d ", MESSAGE_STATUS_DELETE);
82 }else{
83 uint64_t seq=MailboxState_getSeq(M);
84 //msginfo=MailboxState_getMsginfo(M);
85
86 TRACE(TRACE_DEBUG, "SEQ RENew");
87 //MailboxState_uid_msn_new(M);
88 /* use seq to select only changed elements, event this which are deleted*/
89 snprintf(filterCondition,64-1,"/*SEQ ReNew*/ AND m.seq >= %" PRIu64 "-1 AND m.status <= %d ", seq, MESSAGE_STATUS_DELETE );
90 }
91
92
93 date2char_str("internal_date", &frag);
94 snprintf(query, DEF_QUERYSIZE-1,
95 "SELECT seen_flag, answered_flag, deleted_flag, flagged_flag, "
96 "draft_flag, recent_flag, %s, rfcsize, seq, m.message_idnr, status, m.physmessage_id "
97 "FROM %smessages m "
98 "LEFT JOIN %sphysmessage p ON p.id = m.physmessage_id "
99 "WHERE m.mailbox_idnr = ? %s "
100 "ORDER BY m.seq DESC",
101 frag,
102 DBPFX, DBPFX,
103 filterCondition);
104
105
106
107 stmt = db_stmt_prepare(c, query);
108 db_stmt_set_u64(stmt, 1, M->id);
109 gettimeofday(&before, NULL);
110 r = db_stmt_query(stmt);
111 gettimeofday(&after, NULL);
112 log_query_time(query,before,after);
113 i = 0;
114 gettimeofday(&before, NULL);
115 int shouldAdd = 0;
116 while (db_result_next(r)) {
117 i++;
118
119 id = db_result_get_u64(r, IMAP_NFLAGS + 3);
120
121 /* reset */
122 shouldAdd=0;
123 if (coldLoad){
124 /* new element*/
125 result = g_new0(MessageInfo,1);
126 uid = g_new0(uint64_t,1);
127 *uid = id;
128 shouldAdd=1;
129 result->expunge=0;
130 result->expunged=0;
131 //TRACE(TRACE_DEBUG, "SEQ CREATED %ld",id);
132 }else{
133 /* soft renew, so search */
134 result = g_tree_lookup(msginfo, &id);
135 if (result == NULL){
136 /* not found so create*/
137 result = g_new0(MessageInfo,1);
138 uid = g_new0(uint64_t,1);
139 *uid = id;
140 shouldAdd=1;
141 result->expunge=0;
142 result->expunged=0;
143 }else{
144 //TRACE(TRACE_DEBUG, "SEQ FOUND %ld",id);
145 }
146 }
147
148 /* id */
149 result->uid = id;
150
151 /* mailbox_id */
152 result->mailbox_id = M->id;
153
154 /* flags */
155 for (j = 0; j < IMAP_NFLAGS; j++)
156 result->flags[j] = db_result_get_bool(r,j);
157
158 /* internal date */
159 query_result = db_result_get(r,IMAP_NFLAGS);
160 strncpy(result->internaldate,
161 (query_result) ? query_result :
162 "01-Jan-1970 00:00:01 +0100",
163 IMAP_INTERNALDATE_LEN-1);
164
165 /* rfcsize */
166 result->rfcsize = db_result_get_u64(r,IMAP_NFLAGS + 1);
167 /* modseq */
168 result->seq = db_result_get_u64(r,IMAP_NFLAGS + 2);
169 /* status */
170 result->status = db_result_get_int(r, IMAP_NFLAGS + 4);
171 /* physmessage_id */
172 result->phys_id = db_result_get_int(r, IMAP_NFLAGS + 5);
173
174 if (result->status>=MESSAGE_STATUS_DELETE /*|| result->flags[IMAP_FLAG_DELETED]==1*/ ){
175 /* message is delete so mark as to be expunged */
176 result->expunge++;
177 if (result->expunged==1){
178 if (shouldAdd==0){
179 /* remove the node if exists from message info, should be removed, but we will not due to some references present*/
180 //g_tree_remove(msginfo, &id);
181 continue;
182 }
183 }else{
184 if (shouldAdd==1){
185 /* message is in state of state=2 or already deleted but not in our state */
186 g_tree_remove(msginfo,uid);
187 g_free(result);
188 g_free(uid);
189 continue;
190 }
191
192 }
193 }
194
195 if (shouldAdd==1){
196 //TRACE(TRACE_DEBUG, "SEQ ADDED %ld",id);
197 /* it's new */
198 g_tree_insert(msginfo, uid, result);
199 }else{
200 /* no need, result was updated */
201 }
202
203 }
204 gettimeofday(&after, NULL);
205 log_query_time("Parsing State ",before,after);
206 if (! i) { // empty mailbox
207 MailboxState_setMsginfo(M, msginfo);
208 return M;
209 }
210
211 db_con_clear(c);
212
213 memset(query, 0, sizeof(query));
214 snprintf(query, DEF_QUERYSIZE-1,
215 "SELECT k.message_idnr, k.keyword FROM %skeywords k "
216 "LEFT JOIN %smessages m ON k.message_idnr=m.message_idnr "
217 "WHERE m.mailbox_idnr = ? %s "
218 "order by m.message_idnr "
219 ,
220 DBPFX, DBPFX,
221 filterCondition);
222
223 nrows = 0;
224 stmt = db_stmt_prepare(c, query);
225 db_stmt_set_u64(stmt, 1, M->id);
226 r = db_stmt_query(stmt);
227 gettimeofday(&before, NULL);
228 tempId=0;
229
230 while (db_result_next(r)) {
231 nrows++;
232 id = db_result_get_u64(r,0);
233
234 const char * keyword = db_result_get(r,1);
235 if (strlen(keyword)>0){
236 TRACE(TRACE_INFO, "Keyword line [%d %s]", nrows, keyword);
237 /* id is presented via query in ordered fashion so, we use tempId as a cached last tree lookup */
238 if ( tempId!=id || tempId==0 ){
239 result = g_tree_lookup(msginfo, &id);
240 tempId=id;
241 }
242 if ( result != NULL ){
243 result->keywords = g_list_append(result->keywords, g_strdup(keyword));
244 }
245 }
246 }
247 if (! nrows) TRACE(TRACE_DEBUG, "no keywords");
248
249 gettimeofday(&after, NULL);
250 log_query_time("Parsing Keywords ",before,after);
251 /* on both cases can use the same function due to some checks at MailboxState_setMsgInfo*/
252 MailboxState_setMsginfo(M, msginfo);
253 return M;
254 }
255
_compare_data(gconstpointer a,gconstpointer b,gpointer UNUSED data)256 gboolean _compare_data(gconstpointer a, gconstpointer b, gpointer UNUSED data)
257 {
258 return strcmp((const char *)a,(const char *)b);
259 }
260
MailboxState_new(Mempool_T pool,uint64_t id)261 T MailboxState_new(Mempool_T pool, uint64_t id)
262 {
263 T M; Connection_T c;
264 volatile int t = DM_SUCCESS;
265 gboolean freepool = FALSE;
266
267 if (! pool) {
268 pool = mempool_open();
269 freepool = TRUE;
270 }
271
272 M = mempool_pop(pool, sizeof(*M));
273 M->pool = pool;
274 M->freepool = freepool;
275
276 if (! id) return M;
277
278 M->id = id;
279 M->recent_queue = g_tree_new((GCompareFunc)ucmp);
280 M->keywords = g_tree_new_full((GCompareDataFunc)_compare_data,NULL,g_free,NULL);
281 M->msginfo = g_tree_new_full((GCompareDataFunc)ucmpdata, NULL,(GDestroyNotify)g_free,(GDestroyNotify)MessageInfo_free);
282 M->differential_iterations = 0;
283 c = db_con_get();
284 TRY
285 db_begin_transaction(c); // we need read-committed isolation
286 state_load_metadata(M, c);
287 state_load_messages(M, c, true);
288 db_commit_transaction(c);
289 CATCH(SQLException)
290 LOG_SQLERROR;
291 db_rollback_transaction(c);
292 t = DM_EQUERY;
293 FINALLY
294 db_con_close(c);
295 END_TRY;
296
297 if (t == DM_EQUERY) {
298 TRACE(TRACE_ERR, "Error opening mailbox");
299 MailboxState_free(&M);
300 }
301
302 return M;
303 }
304
305 /**
306 * Update only the mailbox.
307 * @param M
308 * @return
309 */
310
MailboxState_update(Mempool_T pool,T OldM)311 T MailboxState_update(Mempool_T pool, T OldM)
312 {
313
314 T M; Connection_T c;
315 volatile int t = DM_SUCCESS;
316 gboolean freepool = FALSE;
317 uint64_t id;
318
319 /* differential mode, evaluate max iterations */
320 int mailbox_diffential_max_iterations = config_get_value_default_int("mailbox_update_strategy_2_max_iterations", "IMAP", 300);
321 if (mailbox_diffential_max_iterations > 0 && OldM->differential_iterations >= mailbox_diffential_max_iterations-1 ){
322 TRACE(TRACE_DEBUG, "Strategy differential mode override due to max iterations, see config [IMAP] mailbox_update_strategy_2_max_iterations");
323 return MailboxState_new(pool, OldM->id);
324 }
325
326
327 if (! pool) {
328 pool = mempool_open();
329 freepool = TRUE;
330 }
331 id = OldM->id;
332 M = mempool_pop(pool, sizeof(*M));
333 M->pool = pool;
334 M->freepool = freepool;
335
336 if (! id) return M;
337
338 M->id = id;
339 M->recent_queue = g_tree_new((GCompareFunc)ucmp);
340
341 M->keywords = g_tree_new_full((GCompareDataFunc)_compare_data,NULL,g_free,NULL);
342 M->msginfo = g_tree_new_full((GCompareDataFunc)ucmpdata, NULL,(GDestroyNotify)g_free,(GDestroyNotify)MessageInfo_free);
343 // increase differential iterations in order to apply mailbox_update_strategy_2_max_iterations
344 M->differential_iterations = OldM->differential_iterations + 1;
345 TRACE(TRACE_DEBUG, "Strategy SEQ UPDATE, iterations %d", M->differential_iterations);
346 //M->ids = g_tree_new_full((GCompareDataFunc)_compare_data,NULL,g_free,NULL);
347 //M->msn = g_tree_new_full((GCompareDataFunc)_compare_data,NULL,g_free,NULL);
348
349 //g_tree_merge(M->recent, OldM->recent, IST_SUBSEARCH_OR);
350
351
352 //g_tree_merge(M->msginfo, OldM->msginfo, IST_SUBSEARCH_OR);
353 g_tree_copy_MessageInfo(M->msginfo,OldM->msginfo);
354
355 //no need, those keywords will be populated at metadata
356 //@todo change this behaviour in state_load_metadata
357 //g_tree_copy_String(M->keywords,OldM->keywords);
358
359
360 //MailboxState_remap(M);
361 /* reset the sequence */
362 MailboxState_resetSeq(OldM);
363 //uint64_t seq = MailboxState_getSeq(OldM);
364
365 c = db_con_get();
366 TRY
367 db_begin_transaction(c); // we need read-committed isolation
368 state_load_metadata(M, c);
369 state_load_messages(M, c, false); // do a soft refresh
370 db_commit_transaction(c);
371 CATCH(SQLException)
372 LOG_SQLERROR;
373 db_rollback_transaction(c);
374 t = DM_EQUERY;
375 FINALLY
376 db_con_close(c);
377 END_TRY;
378
379 if (t == DM_EQUERY) {
380 TRACE(TRACE_ERR, "SEQ Error opening mailbox");
381 MailboxState_free(&M);
382 }
383
384 return M;
385 }
386
MailboxState_remap(T M)387 void MailboxState_remap(T M)
388 {
389 GList *ids = NULL;
390 uint64_t *uid, *msn = NULL, rows = 1;
391 MessageInfo *msginfo;
392
393 MailboxState_uid_msn_new(M);
394
395 ids = g_tree_keys(M->msginfo);
396 ids = g_list_first(ids);
397 while (ids) {
398 uid = (uint64_t *)ids->data;
399
400 msginfo = g_tree_lookup(M->msginfo, uid);
401 if (msginfo->status < MESSAGE_STATUS_DELETE) {
402 msn = g_new0(uint64_t,1);
403 *msn = msginfo->msn = rows++;
404
405 g_tree_insert(M->ids, uid, msn);
406 g_tree_insert(M->msn, msn, uid);
407 }
408
409 if (! g_list_next(ids)) break;
410 ids = g_list_next(ids);
411 }
412
413 g_list_free(g_list_first(ids));
414 }
415
MailboxState_getMsginfo(T M)416 GTree * MailboxState_getMsginfo(T M)
417 {
418 return M->msginfo;
419 }
420
MailboxState_setMsginfo(T M,GTree * msginfo)421 static void MailboxState_setMsginfo(T M, GTree *msginfo)
422 {
423 GTree *oldmsginfo = M->msginfo;
424 M->msginfo = msginfo;
425 MailboxState_remap(M);
426 if (oldmsginfo){
427 /* might be situations when old msginfo is the same as the new */
428 if (msginfo != oldmsginfo)
429 g_tree_destroy(oldmsginfo);
430 }
431 }
432
MailboxState_addMsginfo(T M,uint64_t uid,MessageInfo * msginfo)433 void MailboxState_addMsginfo(T M, uint64_t uid, MessageInfo *msginfo)
434 {
435 uint64_t *id = g_new0(uint64_t,1);
436 *id = uid;
437 g_tree_insert(M->msginfo, id, msginfo);
438 if (msginfo->flags[IMAP_FLAG_RECENT] == 1) {
439 M->seq--; // force resync
440 M->recent++;
441 }
442 MailboxState_build_recent(M);
443 MailboxState_remap(M);
444 }
445
MailboxState_removeUid(T M,uint64_t uid)446 int MailboxState_removeUid(T M, uint64_t uid)
447 {
448 MessageInfo *msginfo = g_tree_lookup(M->msginfo, &uid);
449 if (! msginfo) {
450 TRACE(TRACE_WARNING,"trying to remove unknown UID [%" PRIu64 "]", uid);
451 return DM_EGENERAL;
452 }
453 msginfo->status = MESSAGE_STATUS_DELETE;
454 M->exists--;
455
456 MailboxState_remap(M);
457
458 return DM_SUCCESS;
459 }
460
MailboxState_getIds(T M)461 GTree * MailboxState_getIds(T M)
462 {
463 return M->ids;
464 }
465
MailboxState_getMsn(T M)466 GTree * MailboxState_getMsn(T M)
467 {
468 return M->msn;
469 }
470
MailboxState_setId(T M,uint64_t id)471 void MailboxState_setId(T M, uint64_t id)
472 {
473 M->id = id;
474 }
MailboxState_getId(T M)475 uint64_t MailboxState_getId(T M)
476 {
477 return M->id;
478 }
479
MailboxState_getSeq(T M)480 uint64_t MailboxState_getSeq(T M)
481 {
482 if (! M->seq) {
483 Connection_T c = db_con_get();
484 TRY
485 db_getmailbox_seq(M, c);
486 CATCH(SQLException)
487 LOG_SQLERROR;
488 FINALLY
489 db_con_close(c);
490 END_TRY;
491 }
492
493 return M->seq;
494 }
495
496 /**
497 * Reset the sequence stored at structure level.
498 * Subsequent call for MailboxState_getSeq will be required to retrieve the the sequence
499 * @param M
500 * @return void
501 */
MailboxState_resetSeq(T M)502 void MailboxState_resetSeq(T M)
503 {
504 M->seq=0;
505
506 }
507
508
509 /**
510 * Reset the sequence stored at structure level and store the new seq from db
511 * Internally it will use MailboxState_getSeq
512 * @param M
513 * @return void
514 */
MailboxState_resyncSeq(T M)515 uint64_t MailboxState_resyncSeq(T M)
516 {
517 M->seq=0;
518 return MailboxState_getSeq(M);
519 }
520
521
MailboxState_getExists(T M)522 unsigned MailboxState_getExists(T M)
523 {
524 int real = g_tree_nnodes(M->ids);
525 if (real > (int)M->exists) {
526 TRACE(TRACE_DEBUG, "[%" PRIu64 "] exists [%u] -> [%d]",
527 M->id, M->exists, real);
528 M->exists = (unsigned)real;
529 }
530 return M->exists;
531 }
532
MailboxState_setExists(T M,unsigned exists)533 void MailboxState_setExists(T M, unsigned exists)
534 {
535 TRACE(TRACE_DEBUG, "[%" PRIu64 "] exists [%u] -> [%u]",
536 M->id, M->exists, exists);
537 M->exists = exists;
538 }
539
MailboxState_getRecent(T M)540 unsigned MailboxState_getRecent(T M)
541 {
542 return M->recent;
543 }
544
MailboxState_getUidnext(T M)545 uint64_t MailboxState_getUidnext(T M)
546 {
547 return M->uidnext;
548 }
MailboxState_setOwner(T M,uint64_t owner_id)549 void MailboxState_setOwner(T M, uint64_t owner_id)
550 {
551 M->owner_id = owner_id;
552 }
MailboxState_getOwner(T M)553 uint64_t MailboxState_getOwner(T M)
554 {
555 return M->owner_id;
556 }
557
MailboxState_setPermission(T M,int permission)558 void MailboxState_setPermission(T M, int permission)
559 {
560 M->permission = permission;
561 }
562
MailboxState_getPermission(T M)563 unsigned MailboxState_getPermission(T M)
564 {
565 if (! M->permission) {
566 Connection_T c = db_con_get();
567 TRY
568 db_getmailbox_permission(M, c);
569 CATCH(SQLException)
570 LOG_SQLERROR;
571 FINALLY
572 db_con_close(c);
573 END_TRY;
574 }
575 return M->permission;
576 }
577
MailboxState_setName(T M,const char * name)578 void MailboxState_setName(T M, const char *name)
579 {
580 String_T old = M->name;
581 M->name = p_string_new(M->pool, name);
582 if (old)
583 p_string_free(old, TRUE);
584 }
585
MailboxState_getName(T M)586 const char * MailboxState_getName(T M)
587 {
588 return p_string_str(M->name);
589 }
590
MailboxState_setIsUsers(T M,gboolean t)591 void MailboxState_setIsUsers(T M, gboolean t)
592 {
593 M->is_users = t;
594 }
595
MailboxState_isUsers(T M)596 gboolean MailboxState_isUsers(T M)
597 {
598 return M->is_users;
599 }
600
MailboxState_setIsPublic(T M,gboolean t)601 void MailboxState_setIsPublic(T M, gboolean t)
602 {
603 M->is_public = t;
604 }
MailboxState_isPublic(T M)605 gboolean MailboxState_isPublic(T M)
606 {
607 return M->is_public;
608 }
609
MailboxState_hasKeyword(T M,const char * keyword)610 gboolean MailboxState_hasKeyword(T M, const char *keyword)
611 {
612 if (g_tree_lookup(M->keywords, (gpointer)keyword))
613 return TRUE;
614 return FALSE;
615 }
MailboxState_addKeyword(T M,const char * keyword)616 void MailboxState_addKeyword(T M, const char *keyword)
617 {
618 char *kw = g_strdup(keyword);
619 g_tree_insert(M->keywords, kw, kw);
620 }
621
MailboxState_setNoSelect(T M,gboolean no_select)622 void MailboxState_setNoSelect(T M, gboolean no_select)
623 {
624 M->no_select = no_select;
625 }
626
MailboxState_noSelect(T M)627 gboolean MailboxState_noSelect(T M)
628 {
629 return M->no_select;
630 }
MailboxState_setNoChildren(T M,gboolean no_children)631 void MailboxState_setNoChildren(T M, gboolean no_children)
632 {
633 M->no_children = no_children;
634 }
635
MailboxState_noChildren(T M)636 gboolean MailboxState_noChildren(T M)
637 {
638 return M->no_children;
639 }
640
MailboxState_noInferiors(T M)641 gboolean MailboxState_noInferiors(T M)
642 {
643 return M->no_inferiors;
644 }
645
MailboxState_getUnseen(T M)646 unsigned MailboxState_getUnseen(T M)
647 {
648 return M->unseen;
649 }
650
651 struct filter_range_helper {
652 gboolean uid;
653 uint64_t min;
654 uint64_t max;
655 GTree *a;
656 };
657
filter_range(gpointer key,gpointer value,gpointer data)658 static int filter_range(gpointer key, gpointer value, gpointer data)
659 {
660 uint64_t *k, *v;
661 struct filter_range_helper *d = (struct filter_range_helper *)data;
662
663 if (*(uint64_t *)key < d->min) return FALSE; // skip
664 if (*(uint64_t *)key > d->max) return TRUE; // done
665
666 k = mempool_pop(small_pool, sizeof(uint64_t));
667 v = mempool_pop(small_pool, sizeof(uint64_t));
668
669 *k = *(uint64_t *)key;
670 *v = *(uint64_t *)value;
671
672 if (d->uid)
673 g_tree_insert(d->a, k, v);
674 else
675 g_tree_insert(d->a, v, k);
676
677 return FALSE;
678 }
679
find_range(GTree * c,uint64_t l,uint64_t r,GTree * a,gboolean uid)680 static void find_range(GTree *c, uint64_t l, uint64_t r, GTree *a, gboolean uid)
681 {
682 struct filter_range_helper data;
683
684 data.uid = uid;
685 data.min = l;
686 data.max = r;
687 data.a = a;
688
689 g_tree_foreach(c, (GTraverseFunc)filter_range, &data);
690 }
691
MailboxState_get_set(MailboxState_T M,const char * set,gboolean uid)692 GTree * MailboxState_get_set(MailboxState_T M, const char *set, gboolean uid)
693 {
694 GTree *inset, *a, *b;
695 GList *sets = NULL;
696 GString *t;
697 uint64_t lo = 0, hi = 0;
698 gboolean error = FALSE;
699
700 if (uid)
701 inset = MailboxState_getIds(M);
702 else
703 inset = MailboxState_getMsn(M);
704
705 a = g_tree_new_full((GCompareDataFunc)ucmpdata,NULL, (GDestroyNotify)uint64_free, (GDestroyNotify)uint64_free);
706 b = g_tree_new_full((GCompareDataFunc)ucmpdata,NULL, (GDestroyNotify)uint64_free, (GDestroyNotify)uint64_free);
707
708 if (! uid) {
709 lo = 1;
710 hi = MailboxState_getExists(M);
711 } else {
712 GList *ids = g_tree_keys(inset);
713 if (ids) {
714 ids = g_list_last(ids);
715 hi = *((uint64_t *)ids->data);
716 ids = g_list_first(ids);
717 lo = *((uint64_t *)ids->data);
718 g_list_free(g_list_first(ids));
719 }
720 }
721
722 t = g_string_new(set);
723 sets = g_string_split(t, ",");
724 g_string_free(t,TRUE);
725
726 sets = g_list_first(sets);
727 while(sets) {
728 uint64_t l = 0, r = 0;
729
730 char *rest = (char *)sets->data;
731
732 if (strlen(rest) < 1) break;
733
734 if (g_tree_nnodes(inset) == 0) { // empty box
735 if (rest[0] == '*') {
736 uint64_t *k = mempool_pop(small_pool, sizeof(uint64_t));
737 uint64_t *v = mempool_pop(small_pool, sizeof(uint64_t));
738
739 *k = 1;
740 *v = MailboxState_getUidnext(M);
741
742 g_tree_insert(b, k, v);
743 } else {
744 if (! (l = dm_strtoull(sets->data, &rest, 10))) {
745 error = TRUE;
746 break;
747 }
748 if (rest[0]) {
749 if (rest[0] != ':') {
750 error = TRUE;
751 break;
752 }
753 rest++;
754 if ((rest[0] != '*') && (! dm_strtoull(rest, NULL, 10))) {
755 error = TRUE;
756 break;
757 }
758 }
759 uint64_t *k = mempool_pop(small_pool, sizeof(uint64_t));
760 uint64_t *v = mempool_pop(small_pool, sizeof(uint64_t));
761
762 *k = 1;
763 *v = MailboxState_getUidnext(M);
764
765 g_tree_insert(b, k, v);
766 }
767 } else {
768 if (rest[0] == '*') {
769 l = hi;
770 r = l;
771 if (strlen(rest) > 1)
772 rest++;
773 } else {
774 if (! (l = dm_strtoull(sets->data,&rest,10))) {
775 error = TRUE;
776 break;
777 }
778
779 if (l == 0xffffffff) l = hi; // outlook
780
781 l = max(l,lo);
782 r = l;
783 }
784
785 if (rest[0]==':') {
786 if (strlen(rest)>1) rest++;
787 if (rest[0] == '*') r = hi;
788 else {
789 if (! (r = dm_strtoull(rest,NULL,10))) {
790 error = TRUE;
791 break;
792 }
793
794 if (r == 0xffffffff) r = hi; // outlook
795 }
796
797 if (!r) break;
798 }
799
800 if (! (l && r)) break;
801
802 find_range(inset, min(l,r), max(l,r), a, uid);
803
804 if (g_tree_merge(b,a,IST_SUBSEARCH_OR)) {
805 error = TRUE;
806 TRACE(TRACE_ERR, "cannot compare null trees");
807 break;
808 }
809 }
810
811 if (! g_list_next(sets)) break;
812 sets = g_list_next(sets);
813 }
814
815 g_list_destroy(sets);
816
817 if (a) g_tree_destroy(a);
818
819 if (error) {
820 g_tree_destroy(b);
821 b = NULL;
822 TRACE(TRACE_DEBUG, "return NULL");
823 }
824
825 return b;
826 }
827
_free_recent_queue(gpointer key,gpointer UNUSED value,gpointer data)828 static gboolean _free_recent_queue(gpointer key, gpointer UNUSED value, gpointer data)
829 {
830 T M = (T)data;
831 mempool_push(M->pool, key, sizeof(uint64_t));
832 return FALSE;
833 }
834
835
MailboxState_free(T * M)836 void MailboxState_free(T *M)
837 {
838
839 T s = *M;
840 if (s->name)
841 p_string_free(s->name, TRUE);
842
843 g_tree_destroy(s->keywords);
844 s->keywords = NULL;
845
846 if (s->msn) g_tree_destroy(s->msn);
847 s->msn = NULL;
848
849 if (s->ids) g_tree_destroy(s->ids);
850 s->ids = NULL;
851
852 if (s->msginfo) g_tree_destroy(s->msginfo);
853 s->msginfo = NULL;
854
855 if (s->recent_queue) {
856 g_tree_foreach(s->recent_queue, (GTraverseFunc)_free_recent_queue, s);
857 g_tree_destroy(s->recent_queue);
858 }
859 s->recent_queue = NULL;
860
861 gboolean freepool = s->freepool;
862 Mempool_T pool = s->pool;
863 mempool_push(pool, s, sizeof(*s));
864
865 if (freepool) {
866 mempool_close(&pool);
867 }
868
869 s = NULL;
870 }
871
db_getmailbox_permission(T M,Connection_T c)872 void db_getmailbox_permission(T M, Connection_T c)
873 {
874 ResultSet_T r;
875 PreparedStatement_T stmt;
876 g_return_if_fail(M->id);
877
878 stmt = db_stmt_prepare(c,
879 "SELECT permission FROM %smailboxes WHERE mailbox_idnr = ?",
880 DBPFX);
881 db_stmt_set_u64(stmt, 1, M->id);
882 r = db_stmt_query(stmt);
883
884 if (db_result_next(r))
885 M->permission = db_result_get_int(r, 0);
886 }
887
db_getmailbox_info(T M,Connection_T c)888 static void db_getmailbox_info(T M, Connection_T c)
889 {
890 /* query mailbox for LIST results */
891 ResultSet_T r;
892 char *mbxname, *name, *pattern;
893 struct mailbox_match *mailbox_like = NULL;
894 GString *fqname, *qs;
895 int i=0, prml;
896 PreparedStatement_T stmt;
897
898 stmt = db_stmt_prepare(c,
899 "SELECT "
900 "CASE WHEN user_id IS NULL THEN 0 ELSE 1 END, " // subscription
901 "owner_idnr, name, no_select, no_inferiors "
902 "FROM %smailboxes b LEFT OUTER JOIN %ssubscription s ON "
903 "b.mailbox_idnr = s.mailbox_id WHERE b.mailbox_idnr = ?",
904 DBPFX, DBPFX);
905 db_stmt_set_u64(stmt, 1, M->id);
906 r = db_stmt_query(stmt);
907
908 if (db_result_next(r)) {
909
910 /* subsciption */
911 M->is_subscribed = db_result_get_bool(r, i++);
912
913 /* owner_idnr */
914 M->owner_id = db_result_get_u64(r, i++);
915
916 /* name */
917 name = g_strdup(db_result_get(r,i++));
918 if (MATCH(name, "INBOX")) {
919 M->is_inbox = TRUE;
920 M->is_subscribed = TRUE;
921 }
922
923 mbxname = mailbox_add_namespace(name, M->owner_id, M->owner_id);
924 fqname = g_string_new(mbxname);
925 fqname = g_string_truncate(fqname,IMAP_MAX_MAILBOX_NAMELEN);
926 MailboxState_setName(M, fqname->str);
927 g_string_free(fqname,TRUE);
928 g_free(mbxname);
929
930 /* no_select */
931 M->no_select=db_result_get_bool(r,i++);
932
933 /* no_inferior */
934 M->no_inferiors=db_result_get_bool(r,i++);
935
936 /* no_children search pattern*/
937 pattern = g_strdup_printf("%s/%%", name);
938 mailbox_like = mailbox_match_new(pattern);
939 g_free(pattern);
940 g_free(name);
941 }
942
943 db_con_clear(c);
944
945 qs = g_string_new("");
946 g_string_printf(qs, "SELECT COUNT(*) AS nr_children FROM %smailboxes WHERE owner_idnr = ? ", DBPFX);
947
948 if (mailbox_like && mailbox_like->insensitive)
949 g_string_append_printf(qs, "AND name %s ? ", db_get_sql(SQL_INSENSITIVE_LIKE));
950 if (mailbox_like && mailbox_like->sensitive)
951 g_string_append_printf(qs, "AND name %s ? ", db_get_sql(SQL_SENSITIVE_LIKE));
952
953 stmt = db_stmt_prepare(c, qs->str);
954 prml = 1;
955 db_stmt_set_u64(stmt, prml++, M->owner_id);
956
957 if (mailbox_like && mailbox_like->insensitive)
958 db_stmt_set_str(stmt, prml++, mailbox_like->insensitive);
959 if (mailbox_like && mailbox_like->sensitive)
960 db_stmt_set_str(stmt, prml++, mailbox_like->sensitive);
961
962 r = db_stmt_query(stmt);
963 if (db_result_next(r)) {
964 int nr_children = db_result_get_int(r,0);
965 M->no_children=nr_children ? 0 : 1;
966 } else {
967 M->no_children=1;
968 }
969
970 mailbox_match_free(mailbox_like);
971 g_string_free(qs, TRUE);
972 }
973
db_getmailbox_count(T M,Connection_T c)974 static void db_getmailbox_count(T M, Connection_T c)
975 {
976 ResultSet_T r;
977 PreparedStatement_T stmt;
978 unsigned result[3];
979
980 result[0] = result[1] = result[2] = 0;
981
982 g_return_if_fail(M->id);
983
984 /* count messages */
985 stmt = db_stmt_prepare(c,
986 "SELECT "
987 "SUM( CASE WHEN seen_flag = 0 THEN 1 ELSE 0 END) AS unseen, "
988 "SUM( CASE WHEN seen_flag = 1 THEN 1 ELSE 0 END) AS seen, "
989 "SUM( CASE WHEN recent_flag = 1 THEN 1 ELSE 0 END) AS recent "
990 "FROM %smessages WHERE mailbox_idnr=? AND status < %d ",
991 DBPFX, MESSAGE_STATUS_DELETE);
992
993 db_stmt_set_u64(stmt, 1, M->id);
994
995 r = db_stmt_query(stmt);
996
997 if (db_result_next(r)) {
998 result[0] = (unsigned)db_result_get_int(r,0); // unseen
999 result[1] = (unsigned)db_result_get_int(r,1); // seen
1000 result[2] = (unsigned)db_result_get_int(r,2); // recent
1001 }
1002
1003 M->exists = result[0] + result[1];
1004 M->unseen = result[0];
1005 M->recent = result[2];
1006
1007 TRACE(TRACE_DEBUG, "exists [%d] unseen [%d] recent [%d]", M->exists, M->unseen, M->recent);
1008 /* now determine the next message UID
1009 * NOTE:
1010 * - expunged messages are selected as well in order to be able to restore them
1011 * - the next uit MUST NOT change unless messages are added to THIS mailbox
1012 * */
1013
1014 if (M->exists == 0) {
1015 M->uidnext = 1;
1016 return;
1017 }
1018
1019 db_con_clear(c);
1020 stmt = db_stmt_prepare(c,
1021 "SELECT MAX(message_idnr)+1 FROM %smessages WHERE mailbox_idnr=?",
1022 DBPFX);
1023 db_stmt_set_u64(stmt, 1, M->id);
1024 r = db_stmt_query(stmt);
1025
1026 if (db_result_next(r))
1027 M->uidnext = db_result_get_u64(r,0);
1028 else
1029 M->uidnext = 1;
1030 }
1031
db_getmailbox_keywords(T M,Connection_T c)1032 static void db_getmailbox_keywords(T M, Connection_T c)
1033 {
1034 ResultSet_T r;
1035 PreparedStatement_T stmt;
1036
1037 stmt = db_stmt_prepare(c,
1038 "SELECT DISTINCT(keyword) FROM %skeywords k "
1039 "LEFT JOIN %smessages m ON k.message_idnr=m.message_idnr "
1040 "WHERE m.mailbox_idnr=? AND m.status < %d ",
1041 DBPFX, DBPFX, MESSAGE_STATUS_DELETE);
1042 db_stmt_set_u64(stmt, 1, M->id);
1043 r = db_stmt_query(stmt);
1044
1045 while (db_result_next(r))
1046 MailboxState_addKeyword(M, db_result_get(r, 0));
1047 }
1048
db_getmailbox_seq(T M,Connection_T c)1049 void db_getmailbox_seq(T M, Connection_T c)
1050 {
1051 ResultSet_T r;
1052 PreparedStatement_T stmt;
1053
1054 stmt = db_stmt_prepare(c,
1055 "SELECT name,seq FROM %smailboxes WHERE mailbox_idnr=?",
1056 DBPFX);
1057 db_stmt_set_u64(stmt, 1, M->id);
1058 r = db_stmt_query(stmt);
1059
1060 if (db_result_next(r)) {
1061 if (! M->name)
1062 M->name = p_string_new(M->pool, db_result_get(r, 0));
1063 M->seq = db_result_get_u64(r,1);
1064 TRACE(TRACE_DEBUG,"id: [%" PRIu64 "] name: [%s] seq [%" PRIu64 "]", M->id, p_string_str(M->name), M->seq);
1065 } else {
1066 TRACE(TRACE_ERR,"Aii. No such mailbox mailbox_idnr: [%" PRIu64 "]", M->id);
1067 }
1068 }
1069
MailboxState_info(T M)1070 int MailboxState_info(T M)
1071 {
1072 volatile int t = DM_SUCCESS;
1073 Connection_T c = db_con_get();
1074 TRY
1075 db_begin_transaction(c);
1076 db_getmailbox_info(M, c);
1077 db_commit_transaction(c);
1078 CATCH(SQLException)
1079 LOG_SQLERROR;
1080 db_rollback_transaction(c);
1081 t = DM_EQUERY;
1082 FINALLY
1083 db_con_close(c);
1084 END_TRY;
1085
1086 return t;
1087 }
1088
state_load_metadata(T M,Connection_T c)1089 static void state_load_metadata(T M, Connection_T c)
1090 {
1091 uint64_t oldseq;
1092
1093 g_return_if_fail(M->id);
1094
1095 oldseq = M->seq;
1096 db_getmailbox_seq(M, c);
1097 if (M->uidnext && (M->seq == oldseq))
1098 return;
1099
1100 db_getmailbox_permission(M, c);
1101 db_getmailbox_count(M, c);
1102 db_getmailbox_keywords(M, c);
1103 db_getmailbox_info(M, c);
1104
1105 TRACE(TRACE_DEBUG, "[%" PRIu64 "] exists [%d] recent [%d]",
1106 M->id, M->exists, M->recent);
1107 }
1108
MailboxState_count(T M)1109 int MailboxState_count(T M)
1110 {
1111 Connection_T c;
1112 volatile int t = DM_SUCCESS;
1113
1114 c = db_con_get();
1115 TRY
1116 db_begin_transaction(c);
1117 db_getmailbox_count(M, c);
1118 db_commit_transaction(c);
1119 CATCH(SQLException)
1120 LOG_SQLERROR;
1121 db_rollback_transaction(c);
1122 t = DM_EQUERY;
1123 FINALLY
1124 db_con_close(c);
1125 END_TRY;
1126
1127 return t;
1128 }
1129
MailboxState_flags(T M)1130 char * MailboxState_flags(T M)
1131 {
1132 char *s = NULL;
1133 GString *string = g_string_new("\\Seen \\Answered \\Deleted \\Flagged \\Draft");
1134 assert(M);
1135
1136 if (M->keywords) {
1137 GList *k = g_tree_keys(M->keywords);
1138 GString *keywords = g_list_join(k," ");
1139 g_string_append_printf(string, " %s", keywords->str);
1140 g_string_free(keywords,TRUE);
1141 g_list_free(g_list_first(k));
1142 }
1143
1144 s = string->str;
1145 g_string_free(string, FALSE);
1146 return g_strchomp(s);
1147 }
1148
MailboxState_hasPermission(T M,uint64_t userid,const char * right_flag)1149 int MailboxState_hasPermission(T M, uint64_t userid, const char *right_flag)
1150 {
1151 PreparedStatement_T stmt;
1152 Connection_T c;
1153 ResultSet_T r;
1154 volatile int result = FALSE;
1155 volatile gboolean owner_acl = false;
1156 uint64_t owner_id, mboxid;
1157
1158 mboxid = MailboxState_getId(M);
1159
1160 TRACE(TRACE_DEBUG, "checking ACL [%s] for user [%" PRIu64 "] on mailbox [%" PRIu64 "]",
1161 right_flag, userid, mboxid);
1162
1163 /* If we don't know who owns the mailbox, look it up. */
1164 owner_id = MailboxState_getOwner(M);
1165 if (! owner_id) {
1166 result = db_get_mailbox_owner(mboxid, &owner_id);
1167 MailboxState_setOwner(M, owner_id);
1168 if (! (result > 0))
1169 return result;
1170 }
1171
1172 if (owner_id == userid) {
1173 c = db_con_get();
1174 TRY
1175 stmt = db_stmt_prepare(c,
1176 "SELECT * FROM %sacl WHERE "
1177 "user_id = ? AND mailbox_id = ?",
1178 DBPFX);
1179 db_stmt_set_u64(stmt, 1, userid);
1180 db_stmt_set_u64(stmt, 2, mboxid);
1181 r = db_stmt_query(stmt);
1182
1183 if (db_result_next(r))
1184 owner_acl = true;
1185 CATCH(SQLException)
1186 LOG_SQLERROR;
1187 result = DM_EQUERY;
1188 FINALLY
1189 db_con_close(c);
1190 END_TRY;
1191
1192 if (! owner_acl) {
1193 TRACE(TRACE_DEBUG, "mailbox [%" PRIu64 "] is owned by user [%" PRIu64 "]"
1194 "and no ACL in place. Giving all rights",
1195 mboxid, userid);
1196 return 1;
1197 } else {
1198 TRACE(TRACE_DEBUG, "mailbox [%" PRIu64 "] is owned by user [%" PRIu64 "]"
1199 "but ACL in place. Restricted access for owner.",
1200 mboxid, userid);
1201 }
1202
1203 }
1204
1205 result = FALSE;
1206 c = db_con_get();
1207 TRY
1208 stmt = db_stmt_prepare(c,
1209 "SELECT * FROM %sacl WHERE "
1210 "user_id = ? AND mailbox_id = ? AND %s = 1",
1211 DBPFX, right_flag);
1212 db_stmt_set_u64(stmt, 1, userid);
1213 db_stmt_set_u64(stmt, 2, mboxid);
1214 r = db_stmt_query(stmt);
1215
1216 if (db_result_next(r))
1217 result = TRUE;
1218 CATCH(SQLException)
1219 LOG_SQLERROR;
1220 result = DM_EQUERY;
1221 FINALLY
1222 db_con_close(c);
1223 END_TRY;
1224
1225 return result;
1226 }
1227
MailboxState_getAcl(T M,uint64_t userid,struct ACLMap * map)1228 int MailboxState_getAcl(T M, uint64_t userid, struct ACLMap *map)
1229 {
1230 int i;
1231 volatile int t = DM_SUCCESS;
1232 gboolean gotrow = FALSE;
1233 uint64_t anyone;
1234 Connection_T c; ResultSet_T r; PreparedStatement_T s;
1235
1236 g_return_val_if_fail(MailboxState_getId(M),DM_EGENERAL);
1237
1238 if (! (auth_user_exists(DBMAIL_ACL_ANYONE_USER, &anyone)))
1239 return DM_EQUERY;
1240
1241 c = db_con_get();
1242 TRY
1243 s = db_stmt_prepare(c, "SELECT lookup_flag,read_flag,seen_flag,"
1244 "write_flag,insert_flag,post_flag,"
1245 "create_flag,delete_flag,deleted_flag,expunge_flag,administer_flag "
1246 "FROM %sacl "
1247 "WHERE mailbox_id = ? AND user_id = ?",DBPFX);
1248 db_stmt_set_u64(s, 1, MailboxState_getId(M));
1249 db_stmt_set_u64(s, 2, userid);
1250 r = db_stmt_query(s);
1251 if (! db_result_next(r)) {
1252 /* else check the 'anyone' user */
1253 db_stmt_set_u64(s, 2, anyone);
1254 r = db_stmt_query(s);
1255 if (db_result_next(r))
1256 gotrow = TRUE;
1257 } else {
1258 gotrow = TRUE;
1259 }
1260
1261 if (gotrow) {
1262 i = 0;
1263 map->lookup_flag = db_result_get_bool(r,i++);
1264 map->read_flag = db_result_get_bool(r,i++);
1265 map->seen_flag = db_result_get_bool(r,i++);
1266 map->write_flag = db_result_get_bool(r,i++);
1267 map->insert_flag = db_result_get_bool(r,i++);
1268 map->post_flag = db_result_get_bool(r,i++);
1269 map->create_flag = db_result_get_bool(r,i++);
1270 map->delete_flag = db_result_get_bool(r,i++);
1271 map->deleted_flag = db_result_get_bool(r,i++);
1272 map->expunge_flag = db_result_get_bool(r,i++);
1273 map->administer_flag = db_result_get_bool(r,i++);
1274 }
1275 CATCH(SQLException)
1276 LOG_SQLERROR;
1277 t = DM_EQUERY;
1278 FINALLY
1279 db_con_close(c);
1280 END_TRY;
1281
1282 return t;
1283 }
1284
1285
mailbox_build_recent(uint64_t * uid,MessageInfo * msginfo,T M)1286 static gboolean mailbox_build_recent(uint64_t *uid, MessageInfo *msginfo, T M)
1287 {
1288 if (msginfo->flags[IMAP_FLAG_RECENT]) {
1289 uint64_t *copy = mempool_pop(M->pool, sizeof(uint64_t));
1290 *copy = *uid;
1291 g_tree_insert(M->recent_queue, copy, copy);
1292 }
1293 return FALSE;
1294 }
1295
MailboxState_build_recent(T M)1296 int MailboxState_build_recent(T M)
1297 {
1298 if (MailboxState_getPermission(M) == IMAPPERM_READWRITE && MailboxState_getMsginfo(M)) {
1299 GTree *info = MailboxState_getMsginfo(M);
1300 g_tree_foreach(info, (GTraverseFunc)mailbox_build_recent, M);
1301 TRACE(TRACE_DEBUG, "build list of [%d] [%d] recent messages...",
1302 g_tree_nnodes(info), g_tree_nnodes(M->recent_queue));
1303 }
1304 return 0;
1305 }
1306
_update_recent(volatile GList * slices,uint64_t seq)1307 static long long int _update_recent(volatile GList *slices, uint64_t seq)
1308 {
1309 INIT_QUERY;
1310 Connection_T c;
1311 volatile long long int count = 0;
1312
1313 if (! (slices = g_list_first((GList*)slices)))
1314 return count;
1315
1316 c = db_con_get();
1317 TRY
1318 db_begin_transaction(c);
1319 while (slices) {
1320 Connection_execute(c, "UPDATE %smessages SET recent_flag = 0, seq = %" PRIu64
1321 " WHERE recent_flag = 1 AND seq < %" PRIu64
1322 " AND message_idnr IN (%s)",
1323 DBPFX, seq, seq, (gchar *)slices->data);
1324 count += Connection_rowsChanged(c);
1325 if (! g_list_next(slices)) break;
1326 slices = g_list_next(slices);
1327 }
1328 db_commit_transaction(c);
1329 CATCH(SQLException)
1330 LOG_SQLERROR;
1331 count = DM_EQUERY;
1332 db_rollback_transaction(c);
1333 FINALLY
1334 db_con_close(c);
1335 g_list_destroy((GList*)slices);
1336 END_TRY;
1337
1338 return count;
1339 }
1340
MailboxState_flush_recent(T M)1341 int MailboxState_flush_recent(T M)
1342 {
1343 GList *recent;
1344
1345 if ((!M) || (M && MailboxState_getPermission(M) != IMAPPERM_READWRITE))
1346 return DM_SUCCESS;
1347
1348 if (! g_tree_nnodes(M->recent_queue))
1349 return DM_SUCCESS;
1350
1351 TRACE(TRACE_DEBUG,"flush [%d] recent messages", g_tree_nnodes(M->recent_queue));
1352
1353 recent = g_tree_keys(M->recent_queue);
1354
1355 if (recent) {
1356 long long int changed = 0;
1357 uint64_t seq = MailboxState_getSeq(M);
1358 changed = _update_recent(g_list_slices_u64(recent,100), seq+1);
1359 if (changed)
1360 db_mailbox_seq_update(MailboxState_getId(M), 0);
1361 }
1362
1363 g_list_free(g_list_first(recent));
1364
1365 g_tree_foreach(M->recent_queue, (GTraverseFunc)_free_recent_queue, M);
1366 g_tree_destroy(M->recent_queue);
1367 M->recent_queue = g_tree_new((GCompareFunc)ucmp);
1368
1369 return 0;
1370 }
1371
mailbox_clear_recent(uint64_t * uid,MessageInfo * msginfo,T M)1372 static gboolean mailbox_clear_recent(uint64_t *uid, MessageInfo *msginfo, T M)
1373 {
1374 msginfo->flags[IMAP_FLAG_RECENT] = 0;
1375 gpointer value;
1376 gpointer orig_key;
1377 if (g_tree_lookup_extended(M->recent_queue, uid, &orig_key, &value)) {
1378 g_tree_remove(M->recent_queue, orig_key);
1379 mempool_push(M->pool, orig_key, sizeof(uint64_t));
1380 }
1381 return FALSE;
1382 }
1383
MailboxState_clear_recent(T M)1384 int MailboxState_clear_recent(T M)
1385 {
1386 if (MailboxState_getPermission(M) == IMAPPERM_READWRITE && MailboxState_getMsginfo(M)) {
1387 GTree *info = MailboxState_getMsginfo(M);
1388 g_tree_foreach(info, (GTraverseFunc)mailbox_clear_recent, M);
1389 }
1390
1391 return 0;
1392 }
1393
MailboxState_message_flags(T M,MessageInfo * msginfo)1394 GList * MailboxState_message_flags(T M, MessageInfo *msginfo)
1395 {
1396 GList *t, *sublist = NULL;
1397 int j;
1398 uint64_t uid = msginfo->uid;
1399
1400 for (j = 0; j < IMAP_NFLAGS; j++) {
1401 if (msginfo->flags[j])
1402 sublist = g_list_append(sublist,g_strdup((gchar *)imap_flag_desc_escaped[j]));
1403 }
1404 if ((msginfo->flags[IMAP_FLAG_RECENT] == 0) && g_tree_lookup(M->recent_queue, &uid)) {
1405 TRACE(TRACE_DEBUG,"set \\recent flag");
1406 sublist = g_list_append(sublist, g_strdup((gchar *)imap_flag_desc_escaped[IMAP_FLAG_RECENT]));
1407 }
1408
1409 t = g_list_first(msginfo->keywords);
1410 while (t) {
1411 if (MailboxState_hasKeyword(M, t->data))
1412 sublist = g_list_append(sublist, g_strdup((gchar *)t->data));
1413 if (! g_list_next(t)) break;
1414 t = g_list_next(t);
1415 }
1416
1417 return sublist;
1418 }
1419
MailboxState_merge_recent(T M,T N)1420 int MailboxState_merge_recent(T M, T N)
1421 {
1422 GTree *recent_queue = N->recent_queue;
1423 N->recent_queue = NULL;
1424 g_tree_merge(M->recent_queue, recent_queue, IST_SUBSEARCH_OR);
1425 g_tree_foreach(recent_queue, (GTraverseFunc)_free_recent_queue, M);
1426 g_tree_destroy(recent_queue);
1427 M->recent = g_tree_nnodes(M->recent_queue);
1428 return 0;
1429 }
1430