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 /*
22 *
23 * IMAP-server utility functions implementations
24 */
25
26 #include "dbmail.h"
27 #include "dm_mempool.h"
28
29 #define THIS_MODULE "imap"
30 #define BUFLEN 2048
31 #define SEND_BUF_SIZE 8192
32 #define MAX_ARGS 512
33 #define IDLE_TIMEOUT 30
34
35
36
37 extern DBParam_T db_params;
38 #define DBPFX db_params.pfx
39
40 extern Mempool_T queue_pool;
41 extern Mempool_T small_pool;
42
43 gboolean imap_feature_idle_status = FALSE;
44
45 extern const char *month_desc[];
46 extern const int month_len[];
47 extern const char *imap_flag_desc[];
48 extern const char *imap_flag_desc_escaped[];
49 extern volatile sig_atomic_t alarm_occured;
50
51 extern GAsyncQueue *queue;
52 extern ServerConfig_T *server_conf;
53
54 /*
55 * send_data()
56 *
57 */
send_data(ImapSession * self,const String_T stream,size_t offset,size_t len)58 static void send_data(ImapSession *self, const String_T stream, size_t offset, size_t len)
59 {
60 char buf[SEND_BUF_SIZE];
61 size_t l = 0;
62 char *head;
63
64 assert(stream);
65 if (p_string_len(stream) < (offset+len))
66 return;
67
68 head = (char *)p_string_str(stream)+offset;
69
70 TRACE(TRACE_DEBUG,"[%p] stream [%p] offset [%ld] len [%ld]", self, stream, offset, len);
71 while (len > 0) {
72 l = min(len, sizeof(buf)-1);
73 memset(buf,0,sizeof(buf));
74 strncpy(buf, head, l);
75 dbmail_imap_session_buff_printf(self, "%s", buf);
76 head += l;
77 len -= l;
78 }
79 }
80
mailboxstate_destroy(MailboxState_T M)81 static void mailboxstate_destroy(MailboxState_T M)
82 {
83 MailboxState_free(&M);
84 }
85
86
87 /*
88 * initializer and accessors for ImapSession
89 */
dbmail_imap_session_new(Mempool_T pool)90 ImapSession * dbmail_imap_session_new(Mempool_T pool)
91 {
92 ImapSession * self;
93 Field_T val;
94 gboolean login_disabled = TRUE;
95
96 self = mempool_pop(pool, sizeof(ImapSession));
97
98 if (! queue_pool)
99 self->buff = p_string_new(pool, "");
100 else
101 self->buff = p_string_new(queue_pool, "");
102
103 self->pool = pool;
104
105 pthread_mutex_init(&self->lock, NULL);
106
107 GETCONFIGVALUE("login_disabled", "IMAP", val);
108 if (SMATCH(val, "no"))
109 login_disabled = FALSE;
110
111 self->state = CLIENTSTATE_NON_AUTHENTICATED;
112 self->args = mempool_pop(self->pool, sizeof(String_T) * MAX_ARGS);
113 self->fi = mempool_pop(self->pool, sizeof(fetch_items));
114 self->capa = Capa_new(self->pool);
115 self->preauth_capa = Capa_new(self->pool);
116 Capa_remove(self->preauth_capa, "ACL");
117 Capa_remove(self->preauth_capa, "RIGHTS=texk");
118 Capa_remove(self->preauth_capa, "NAMESPACE");
119 Capa_remove(self->preauth_capa, "CHILDREN");
120 Capa_remove(self->preauth_capa, "SORT");
121 Capa_remove(self->preauth_capa, "QUOTA");
122 Capa_remove(self->preauth_capa, "THREAD=ORDEREDSUBJECT");
123 Capa_remove(self->preauth_capa, "UNSELECT");
124 Capa_remove(self->preauth_capa, "IDLE");
125 Capa_remove(self->preauth_capa, "UIDPLUS");
126 Capa_remove(self->preauth_capa, "WITHIN");
127 Capa_remove(self->preauth_capa, "CONDSTORE");
128 Capa_remove(self->preauth_capa, "ENABLE");
129 Capa_remove(self->preauth_capa, "QRESYNC");
130
131 if (! (server_conf && server_conf->ssl))
132 Capa_remove(self->preauth_capa, "STARTTLS");
133
134 if (! Capa_match(self->preauth_capa, "STARTTLS"))
135 login_disabled = FALSE;
136
137 if (login_disabled) {
138 if (! Capa_match(self->preauth_capa, "LOGINDISABLED"))
139 Capa_add(self->preauth_capa, "LOGINDISABLED");
140 Capa_remove(self->preauth_capa, "AUTH=LOGIN");
141 Capa_remove(self->preauth_capa, "AUTH=CRAM-MD5");
142 } else {
143 Capa_remove(self->preauth_capa, "LOGINDISABLED");
144 }
145 if (MATCH(db_params.authdriver, "LDAP")) {
146 Capa_remove(self->capa, "AUTH=CRAM-MD5");
147 Capa_remove(self->preauth_capa, "AUTH=CRAM-MD5");
148 }
149
150 if (! (server_conf && server_conf->ssl)) {
151 Capa_remove(self->capa, "STARTTLS");
152 Capa_remove(self->preauth_capa, "STARTTLS");
153 }
154
155 self->physids = g_tree_new((GCompareFunc)ucmp);
156 self->mbxinfo = g_tree_new_full((GCompareDataFunc)ucmpdata,NULL,(GDestroyNotify)uint64_free,(GDestroyNotify)mailboxstate_destroy);
157
158 TRACE(TRACE_DEBUG,"imap session [%p] created", self);
159 return self;
160 }
161
dbmail_imap_session_message_load(ImapSession * self)162 static uint64_t dbmail_imap_session_message_load(ImapSession *self)
163 {
164 TRACE(TRACE_DEBUG, "Call: dbmail_imap_session_message_load");
165 uint64_t *id = NULL;
166
167
168 if (! (id = g_tree_lookup(self->physids, &(self->msg_idnr)))) {
169 uint64_t *uid;
170
171 id = mempool_pop(self->pool, sizeof(uint64_t));
172
173 if (self->mailbox->mbstate != NULL){
174 /* the state is ready, get the phys id from state, avoid one query in database */
175 MessageInfo *msginfo = g_tree_lookup(MailboxState_getMsginfo(self->mailbox->mbstate), &self->msg_idnr);
176 *id=msginfo->phys_id;
177 }else{
178 /* previous behavior, a query will be performed in db */
179 if ((db_get_physmessage_id(self->msg_idnr, id)) != DM_SUCCESS) {
180 TRACE(TRACE_ERR,"can't find physmessage_id for message_idnr [%" PRIu64 "]", self->msg_idnr);
181 g_free(id);
182 return 0;
183 }
184 }
185 uid = mempool_pop(self->pool, sizeof(uint64_t));
186 *uid = self->msg_idnr;
187 g_tree_insert(self->physids, uid, id);
188
189 }
190
191 if (self->message) {
192 if (*id != self->message->id) {
193 dbmail_message_free(self->message);
194 self->message = NULL;
195 }
196 }
197
198 assert(id);
199
200 if (! self->message) {
201 DbmailMessage *msg = dbmail_message_new(self->pool);
202 if ((msg = dbmail_message_retrieve(msg, *id)) != NULL)
203 self->message = msg;
204 }
205
206 if (! self->message) {
207 TRACE(TRACE_ERR,"message retrieval failed");
208 return 0;
209 }
210
211 assert(*id == self->message->id);
212 return 1;
213 }
214
dbmail_imap_session_set_command(ImapSession * self,const char * command)215 ImapSession * dbmail_imap_session_set_command(ImapSession * self, const char * command)
216 {
217 g_strlcpy(self->command, command, sizeof(self->command));
218 return self;
219 }
220
_physids_free(gpointer key,gpointer value,gpointer data)221 static gboolean _physids_free(gpointer key, gpointer value, gpointer data)
222 {
223 ImapSession *self = (ImapSession *)data;
224 mempool_push(self->pool, key, sizeof(uint64_t));
225 mempool_push(self->pool, value, sizeof(uint64_t));
226 return FALSE;
227 }
228
dbmail_imap_session_delete(ImapSession ** s)229 void dbmail_imap_session_delete(ImapSession ** s)
230 {
231 ImapSession *self = *s;
232 Mempool_T pool;
233
234 TRACE(TRACE_DEBUG, "[%p]", self);
235 Capa_free(&self->preauth_capa);
236 Capa_free(&self->capa);
237
238 dbmail_imap_session_args_free(self, TRUE);
239 dbmail_imap_session_fetch_free(self, TRUE);
240
241 if (self->mailbox) {
242 dbmail_mailbox_free(self->mailbox);
243 self->mailbox = NULL;
244 }
245 if (self->mbxinfo) {
246 g_tree_destroy(self->mbxinfo);
247 self->mbxinfo = NULL;
248 }
249 if (self->message) {
250 dbmail_message_free(self->message);
251 self->message = NULL;
252 }
253 if (self->physids) {
254 g_tree_foreach(self->physids, (GTraverseFunc)_physids_free, (gpointer)self);
255 g_tree_destroy(self->physids);
256 self->physids = NULL;
257 }
258 if (self->buff) {
259 p_string_free(self->buff, TRUE);
260 self->buff = NULL;
261 }
262
263 pthread_mutex_destroy(&self->lock);
264 pool = self->pool;
265 mempool_push(pool, self, sizeof(ImapSession));
266 mempool_close(&pool);
267 self = NULL;
268 }
269
dbmail_imap_session_fetch_free(ImapSession * self,gboolean all)270 void dbmail_imap_session_fetch_free(ImapSession *self, gboolean all)
271 {
272 if (self->envelopes) {
273 g_tree_destroy(self->envelopes);
274 self->envelopes = NULL;
275 }
276 if (self->ids) {
277 g_tree_destroy(self->ids);
278 self->ids = NULL;
279 }
280 if (self->ids_list) {
281 g_list_free(g_list_first(self->ids_list));
282 self->ids_list = NULL;
283 }
284 if (self->fi->bodyfetch) {
285 dbmail_imap_session_bodyfetch_free(self);
286 self->fi->bodyfetch = NULL;
287 }
288 if (all) {
289 mempool_push(self->pool, self->fi, sizeof(fetch_items));
290 self->fi = NULL;
291 } else {
292 memset(self->fi, 0, sizeof(fetch_items));
293 }
294 }
295
dbmail_imap_session_args_free(ImapSession * self,gboolean all)296 void dbmail_imap_session_args_free(ImapSession *self, gboolean all)
297 {
298 int i;
299 for (i = 0; i < MAX_ARGS && self->args[i]; i++) {
300 if (self->args[i])
301 p_string_free(self->args[i], TRUE);
302 self->args[i] = NULL;
303 }
304 self->args_idx = 0;
305
306 if (all) {
307 mempool_push(self->pool, self->args, sizeof(String_T) * MAX_ARGS);
308 self->args = NULL;
309 }
310 }
311
312 /*************************************************************************************
313 *
314 *
315 * imap utilities using ImapSession
316 *
317 *
318 ************************************************************************************/
dbmail_imap_session_bodyfetch_set_partspec(ImapSession * self,char * partspec,int length)319 static int dbmail_imap_session_bodyfetch_set_partspec(ImapSession *self, char *partspec, int length)
320 {
321 assert(self->fi);
322 body_fetch *bodyfetch = p_list_data(self->fi->bodyfetch);
323 memset(bodyfetch->partspec,'\0',IMAP_MAX_PARTSPEC_LEN);
324 memcpy(bodyfetch->partspec,partspec,length);
325 return 0;
326 }
dbmail_imap_session_bodyfetch_set_itemtype(ImapSession * self,int itemtype)327 int dbmail_imap_session_bodyfetch_set_itemtype(ImapSession *self, int itemtype)
328 {
329 assert(self->fi);
330 body_fetch *bodyfetch = p_list_data(self->fi->bodyfetch);
331 bodyfetch->itemtype = itemtype;
332 return 0;
333 }
dbmail_imap_session_bodyfetch_set_argstart(ImapSession * self)334 int dbmail_imap_session_bodyfetch_set_argstart(ImapSession *self)
335 {
336 assert(self->fi);
337 body_fetch *bodyfetch = p_list_data(self->fi->bodyfetch);
338 bodyfetch->argstart = self->args_idx;
339 return bodyfetch->argstart;
340 }
dbmail_imap_session_bodyfetch_set_argcnt(ImapSession * self)341 int dbmail_imap_session_bodyfetch_set_argcnt(ImapSession *self)
342 {
343 assert(self->fi);
344 body_fetch *bodyfetch = p_list_data(self->fi->bodyfetch);
345 bodyfetch->argcnt = self->args_idx - bodyfetch->argstart;
346 return bodyfetch->argcnt;
347 }
dbmail_imap_session_bodyfetch_get_last_argcnt(ImapSession * self)348 int dbmail_imap_session_bodyfetch_get_last_argcnt(ImapSession *self)
349 {
350 body_fetch *bodyfetch = p_list_data(self->fi->bodyfetch);
351 return bodyfetch->argcnt;
352 }
dbmail_imap_session_bodyfetch_set_octetstart(ImapSession * self,guint64 octet)353 int dbmail_imap_session_bodyfetch_set_octetstart(ImapSession *self, guint64 octet)
354 {
355 body_fetch *bodyfetch = p_list_data(self->fi->bodyfetch);
356 bodyfetch->octetstart = octet;
357 return 0;
358 }
dbmail_imap_session_bodyfetch_get_last_octetstart(ImapSession * self)359 guint64 dbmail_imap_session_bodyfetch_get_last_octetstart(ImapSession *self)
360 {
361 body_fetch *bodyfetch = p_list_data(self->fi->bodyfetch);
362 return bodyfetch->octetstart;
363 }
dbmail_imap_session_bodyfetch_set_octetcnt(ImapSession * self,guint64 octet)364 int dbmail_imap_session_bodyfetch_set_octetcnt(ImapSession *self, guint64 octet)
365 {
366 body_fetch *bodyfetch = p_list_data(self->fi->bodyfetch);
367 bodyfetch->octetcnt = octet;
368 return 0;
369 }
dbmail_imap_session_bodyfetch_get_last_octetcnt(ImapSession * self)370 guint64 dbmail_imap_session_bodyfetch_get_last_octetcnt(ImapSession *self)
371 {
372 body_fetch *bodyfetch = p_list_data(self->fi->bodyfetch);
373 return bodyfetch->octetcnt;
374 }
375
376
_imap_session_fetch_parse_partspec(ImapSession * self)377 static int _imap_session_fetch_parse_partspec(ImapSession *self)
378 {
379 /* check for a partspecifier */
380 /* first check if there is a partspecifier (numbers & dots) */
381 int indigit = 0;
382 unsigned int j = 0;
383 const char *token, *nexttoken;
384
385 token = p_string_str(self->args[self->args_idx]);
386 nexttoken = p_string_str(self->args[self->args_idx+1]);
387
388 TRACE(TRACE_DEBUG,"token [%s], nexttoken [%s]", token, nexttoken);
389
390 for (j = 0; token[j]; j++) {
391 if (isdigit(token[j])) {
392 indigit = 1;
393 continue;
394 } else if (token[j] == '.') {
395 if (!indigit) return -2; /* error, single dot specified */
396 indigit = 0;
397 continue;
398 } else
399 break; /* other char found */
400 }
401
402 if (j > 0) {
403 if (indigit && token[j]) return -2; /* error DONE */
404 /* partspecifier present, save it */
405 if (j >= IMAP_MAX_PARTSPEC_LEN) return -2; /* error DONE */
406 dbmail_imap_session_bodyfetch_set_partspec(self, (char *)token, j);
407 }
408
409 const char *partspec = &token[j];
410 int shouldclose = 0;
411
412 if (MATCH(partspec, "header.fields")) {
413 dbmail_imap_session_bodyfetch_set_itemtype(self, BFIT_HEADER_FIELDS);
414 } else if (MATCH(partspec, "header.fields.not")) {
415 dbmail_imap_session_bodyfetch_set_itemtype(self, BFIT_HEADER_FIELDS_NOT);
416 } else if (MATCH(partspec, "text")) {
417 self->fi->msgparse_needed=1;
418 dbmail_imap_session_bodyfetch_set_itemtype(self, BFIT_TEXT);
419 shouldclose = 1;
420 } else if (MATCH(partspec, "header")) {
421 self->fi->msgparse_needed=1;
422 dbmail_imap_session_bodyfetch_set_itemtype(self, BFIT_HEADER);
423 shouldclose = 1;
424 } else if (MATCH(partspec, "mime")) {
425 self->fi->msgparse_needed=1;
426 if (j == 0) return -2; /* error DONE */
427 dbmail_imap_session_bodyfetch_set_itemtype(self, BFIT_MIME);
428 shouldclose = 1;
429 } else if (token[j] == '\0') {
430 self->fi->msgparse_needed=1;
431 dbmail_imap_session_bodyfetch_set_itemtype(self, BFIT_ALL);
432 shouldclose = 1;
433 } else {
434 return -2; /* error DONE */
435 }
436 if (shouldclose) {
437 if (! MATCH(nexttoken, "]")) return -2; /* error DONE */
438 } else {
439 self->args_idx++; /* should be at '(' now */
440 token = p_string_str(self->args[self->args_idx]);
441 nexttoken = p_string_str(self->args[self->args_idx+1]);
442
443 if (! MATCH(token,"(")) return -2; /* error DONE */
444
445 self->args_idx++; /* at first item of field list now, remember idx */
446 dbmail_imap_session_bodyfetch_set_argstart(self);
447 /* walk on untill list terminates (and it does 'cause parentheses are matched) */
448 while (! MATCH(p_string_str(self->args[self->args_idx]),")") )
449 self->args_idx++;
450
451 token = p_string_str(self->args[self->args_idx]);
452 nexttoken = p_string_str(self->args[self->args_idx+1]);
453
454 TRACE(TRACE_DEBUG,"token [%s], nexttoken [%s]", token, nexttoken);
455
456 dbmail_imap_session_bodyfetch_set_argcnt(self);
457
458 if (dbmail_imap_session_bodyfetch_get_last_argcnt(self) == 0 || ! MATCH(nexttoken,"]") )
459 return -2; /* error DONE */
460 }
461
462 return 0;
463 }
464
465 #define RANGESIZE 128
466
_imap_session_fetch_parse_octet_range(ImapSession * self)467 static int _imap_session_fetch_parse_octet_range(ImapSession *self)
468 {
469 /* check if octet start/cnt is specified */
470 int delimpos;
471 unsigned int j = 0;
472 char token[RANGESIZE];
473
474 if (! self->args[self->args_idx])
475 return 0;
476
477 memset(token, 0, sizeof(token));
478 g_strlcpy(token, p_string_str(self->args[self->args_idx]), sizeof(token));
479
480 if (token[0] == '\0')
481 return 0;
482
483 TRACE(TRACE_DEBUG,"[%p] parse token [%s]", self, token);
484
485 if (token[0] == '<') {
486
487 /* check argument */
488 if (token[strlen(token) - 1] != '>') return -2; /* error DONE */
489 delimpos = -1;
490 for (j = 1; j < strlen(token) - 1; j++) {
491 if (token[j] == '.') {
492 if (delimpos != -1)
493 return -2;
494 delimpos = j;
495 } else if (!isdigit (token[j]))
496 return -2;
497 }
498 if (delimpos == -1 || delimpos == 1 || delimpos == (int) (strlen(token) - 2))
499 return -2; /* no delimiter found or at first/last pos OR invalid args DONE */
500
501 /* read the numbers */
502 token[strlen(token) - 1] = '\0';
503 token[delimpos] = '\0';
504 self->fi->msgparse_needed=1;
505 guint64 octetstart = strtoull(&token[1], NULL, 10);
506 gint64 octetcnt = strtoll(&token [delimpos + 1], NULL, 10);
507 //TRACE(TRACE_DEBUG, "octetstart [%lu] octetcnt [%lu]", octetstart, octetcnt);
508 dbmail_imap_session_bodyfetch_set_octetstart(self, octetstart);
509 dbmail_imap_session_bodyfetch_set_octetcnt(self, octetcnt);
510
511 /* restore argument */
512 token[delimpos] = '.';
513 token[strlen(token) - 1] = '>';
514 } else {
515 self->args_idx--;
516 return self->args_idx;
517 }
518
519 self->args_idx++;
520 return 0; /* DONE */
521 }
522
523
524 #define TOKENAT(a) (self->args[self->args_idx+a]?p_string_str(self->args[self->args_idx+a]):NULL)
525 #define NEXTTOKEN TOKENAT(1)
526 #define TOKEN TOKENAT(0)
527
dbmail_imap_session_fetch_parse_args(ImapSession * self)528 int dbmail_imap_session_fetch_parse_args(ImapSession * self)
529 {
530 int ispeek = 0;
531 const char *token = TOKEN;
532 const char *nexttoken = NEXTTOKEN;
533
534 if (!token) return -1; // done
535 if ((token[0] == ')') && (! nexttoken)) return -1; // done
536 if (token[0] == '(') return 1; // skip
537
538 TRACE(TRACE_DEBUG,"[%p] parse args[%" PRIu64 "] = [%s]", self, self->args_idx, token);
539
540 if ((! nexttoken) && (strcmp(token,")") == 0))
541 return -1; // done
542
543 if (MATCH(token,"flags")) {
544 self->fi->getFlags = 1;
545 } else if (MATCH(token,"internaldate")) {
546 self->fi->getInternalDate=1;
547 } else if (MATCH(token,"uid")) {
548 self->fi->getUID=1;
549 } else if (MATCH(token,"rfc822.size")) {
550 self->fi->getSize = 1;
551 } else if (MATCH(token,"fast")) {
552 self->fi->getInternalDate = 1;
553 self->fi->getFlags = 1;
554 self->fi->getSize = 1;
555 } else if (MATCH(token,"rfc822")) {
556 self->fi->msgparse_needed=1;
557 self->fi->getRFC822=1;
558 } else if (MATCH(token,"rfc822.header")) {
559 self->fi->msgparse_needed=1;
560 self->fi->getRFC822Header = 1;
561 } else if (MATCH(token,"rfc822.peek")) {
562 self->fi->msgparse_needed=1;
563 self->fi->getRFC822Peek = 1;
564 } else if (MATCH(token,"rfc822.text")) {
565 self->fi->msgparse_needed=1;
566 self->fi->getRFC822Text = 1;
567
568 } else if (MATCH(token,"body") || MATCH(token,"body.peek")) {
569
570 body_fetch *bodyfetch = mempool_pop(self->pool, sizeof(body_fetch));
571 self->fi->bodyfetch = p_list_append(self->fi->bodyfetch, bodyfetch);
572
573 if (MATCH(token,"body.peek")) ispeek=1;
574
575 nexttoken = NEXTTOKEN;
576
577 if (! nexttoken || ! MATCH(nexttoken,"[")) {
578 if (ispeek) return -2; /* error DONE */
579 self->fi->msgparse_needed = 1;
580 self->fi->getMIME_IMB_noextension = 1; /* just BODY specified */
581 } else {
582 int res = 0;
583 /* now read the argument list to body */
584 self->args_idx++; /* now pointing at '[' (not the last arg, parentheses are matched) */
585 self->args_idx++; /* now pointing at what should be the item type */
586
587 token = TOKEN;
588 nexttoken = NEXTTOKEN;
589
590 TRACE(TRACE_DEBUG,"[%p] token [%s], nexttoken [%s]", self, token, nexttoken);
591
592 if (MATCH(token,"]")) {
593 if (ispeek) {
594 self->fi->msgparse_needed = 1;
595 self->fi->getBodyTotalPeek = 1;
596 } else {
597 self->fi->msgparse_needed = 1;
598 self->fi->getBodyTotal = 1;
599 }
600 self->args_idx++;
601 res = _imap_session_fetch_parse_octet_range(self);
602 if (res == -2)
603 TRACE(TRACE_DEBUG,"[%p] fetch_parse_octet_range return with error", self);
604 return res;
605
606 }
607
608 if (ispeek) self->fi->noseen = 1;
609
610 if (_imap_session_fetch_parse_partspec(self) < 0) {
611 TRACE(TRACE_DEBUG,"[%p] fetch_parse_partspec return with error", self);
612 return -2;
613 }
614
615 self->args_idx++; // idx points to ']' now
616 self->args_idx++; // idx points to octet range now
617 res = _imap_session_fetch_parse_octet_range(self);
618 if (res == -2)
619 TRACE(TRACE_DEBUG,"[%p] fetch_parse_octet_range return with error", self);
620 return res;
621 }
622 } else if (MATCH(token,"all")) {
623 self->fi->msgparse_needed=1; // because of getEnvelope
624 self->fi->getInternalDate = 1;
625 self->fi->getEnvelope = 1;
626 self->fi->getFlags = 1;
627 self->fi->getSize = 1;
628 } else if (MATCH(token,"full")) {
629 self->fi->msgparse_needed=1;
630 self->fi->getInternalDate = 1;
631 self->fi->getEnvelope = 1;
632 self->fi->getMIME_IMB_noextension = 1;
633 self->fi->getFlags = 1;
634 self->fi->getSize = 1;
635 } else if (MATCH(token,"bodystructure")) {
636 self->fi->msgparse_needed=1;
637 self->fi->getMIME_IMB = 1;
638 } else if (MATCH(token,"envelope")) {
639 self->fi->getEnvelope = 1;
640 } else if (Capa_match(self->capa, "CONDSTORE") && (MATCH(token,"changedsince"))) {
641 char *rest = (char *)nexttoken;
642 uint64_t seq = dm_strtoull(nexttoken, &rest, 10);
643 if (rest == nexttoken)
644 return -2;
645 self->fi->changedsince = seq;
646 self->mailbox->condstore = true;
647 self->args_idx++;
648 } else if (Capa_match(self->capa, "CONDSTORE") && (MATCH(token, "modseq"))) {
649 self->mailbox->condstore = true;
650 } else if (MATCH(token, "vanished")) {
651 if (self->enabled.qresync && self->use_uid)
652 self->fi->vanished = true;
653 else
654 return -2;
655 }
656
657
658 return 1; //theres more...
659 }
660
661
662 #define SEND_SPACE if (self->fi->isfirstfetchout) \
663 self->fi->isfirstfetchout = 0; \
664 else \
665 dbmail_imap_session_buff_printf(self, " ")
666
667 #define QUERY_BATCHSIZE 2000
668
_send_headers(ImapSession * self,const body_fetch * bodyfetch,gboolean not)669 void _send_headers(ImapSession *self, const body_fetch *bodyfetch, gboolean not)
670 {
671 long long cnt = 0;
672 gchar *tmp;
673 gchar *s;
674 String_T ts;
675
676 dbmail_imap_session_buff_printf(self,"HEADER.FIELDS%s %s] ", not ? ".NOT" : "", bodyfetch->hdrplist);
677
678 if (! (s = g_tree_lookup(bodyfetch->headers, &(self->msg_idnr)))) {
679 dbmail_imap_session_buff_printf(self, "{2}\r\n\r\n");
680 return;
681 }
682
683 TRACE(TRACE_DEBUG,"[%p] [%s] [%s]", self, bodyfetch->hdrplist, s);
684
685 ts = p_string_new(self->pool, s);
686 tmp = get_crlf_encoded(p_string_str(ts));
687 cnt = strlen(tmp);
688
689 if (bodyfetch->octetcnt > 0) {
690 char *p = tmp;
691 if (bodyfetch->octetstart > 0 && bodyfetch->octetstart < (guint64)cnt) {
692 p += bodyfetch->octetstart;
693 cnt -= bodyfetch->octetstart;
694 }
695
696 if ((guint64)cnt > bodyfetch->octetcnt) {
697 p[bodyfetch->octetcnt] = '\0';
698 cnt = bodyfetch->octetcnt;
699 }
700
701 dbmail_imap_session_buff_printf(self, "<%" PRIu64 "> {%" PRIu64 "}\r\n%s\r\n",
702 bodyfetch->octetstart, cnt+2, p);
703 } else {
704 dbmail_imap_session_buff_printf(self, "{%" PRIu64 "}\r\n%s\r\n", cnt+2, tmp);
705 }
706
707 p_string_free(ts,TRUE);
708 ts = NULL;
709 g_free(tmp);
710 tmp = NULL;
711 }
712
713
714 /* get headers or not */
_fetch_headers(ImapSession * self,body_fetch * bodyfetch,gboolean not)715 static void _fetch_headers(ImapSession *self, body_fetch *bodyfetch, gboolean not)
716 {
717 Connection_T c; ResultSet_T r; volatile int t = FALSE;
718 gchar *fld, *val, *old, *new = NULL;
719 uint64_t *mid;
720 uint64_t id;
721 GList *last;
722 GString *fieldorder = NULL;
723 GString *headerIDs = NULL;
724 int k;
725 int fieldseq=0;
726 String_T query = NULL;
727 String_T range = NULL;
728
729 if (! bodyfetch->headers) {
730 TRACE(TRACE_DEBUG, "[%p] init bodyfetch->headers", self);
731 bodyfetch->headers = g_tree_new_full((GCompareDataFunc)ucmpdata,NULL,(GDestroyNotify)uint64_free,(GDestroyNotify)g_free);
732 self->ceiling = 0;
733 self->hi = 0;
734 self->lo = 0;
735 }
736
737 if (! bodyfetch->hdrnames) {
738
739 GList *tlist = NULL;
740 GString *h = NULL;
741
742 for (k = 0; k < bodyfetch->argcnt; k++)
743 tlist = g_list_append(tlist, (void *)p_string_str(self->args[k + bodyfetch->argstart]));
744
745 bodyfetch->hdrplist = dbmail_imap_plist_as_string(tlist);
746 h = g_list_join((GList *)tlist,"','");
747 bodyfetch->names = tlist;
748
749 h = g_string_ascii_down(h);
750
751 bodyfetch->hdrnames = h->str;
752
753 g_string_free(h,FALSE);
754 }
755
756 TRACE(TRACE_DEBUG,"[%p] for %" PRIu64 "%s [%s]", self, self->msg_idnr, not?"NOT":"", bodyfetch->hdrplist);
757
758 // did we prefetch this message already?
759 if (self->msg_idnr <= self->ceiling) {
760 _send_headers(self, bodyfetch, not);
761 return;
762 }
763
764 // let's fetch the required message and prefetch a batch if needed.
765 range = p_string_new(self->pool, "");
766
767
768 if (! (last = g_list_nth(self->ids_list, self->lo+(uint64_t)QUERY_BATCHSIZE)))
769 last = g_list_last(self->ids_list);
770 self->hi = *(uint64_t *)last->data;
771
772 if (self->msg_idnr == self->hi)
773 p_string_printf(range, "= %" PRIu64 "", self->msg_idnr);
774 else
775 p_string_printf(range, "BETWEEN %" PRIu64 " AND %" PRIu64 "", self->msg_idnr, self->hi);
776
777 TRACE(TRACE_DEBUG,"[%p] prefetch %" PRIu64 ":%" PRIu64 " ceiling %" PRIu64 " [%s]", self, self->msg_idnr, self->hi, self->ceiling, bodyfetch->hdrplist);
778
779 headerIDs = g_string_new("0");
780 if (! not) {
781 fieldorder = g_string_new(", CASE ");
782
783 fieldseq = 0;
784 bodyfetch->names = g_list_first(bodyfetch->names);
785
786 while (bodyfetch->names) {
787 char *raw = (char *)bodyfetch->names->data;
788 char *name = g_ascii_strdown(raw, strlen(raw));
789 g_string_append_printf(fieldorder, "WHEN n.headername='%s' THEN %d ",
790 name, fieldseq);
791
792 if (! g_list_next(bodyfetch->names)){
793 g_free(name);
794 break;
795 }
796 bodyfetch->names = g_list_next(bodyfetch->names);
797 fieldseq++;
798 /* get the id of the header */
799 query = p_string_new(self->pool, "");
800 p_string_printf(query, "select id from %sheadername "
801 "where headername='%s' ",
802 DBPFX,
803 name);
804
805 c = db_con_get();
806 TRY
807 r = db_query(c, p_string_str(query));
808 while (db_result_next(r)) {
809 id = db_result_get_u64(r, 0);
810 g_string_append_printf(headerIDs, ",%ld",id);
811 }
812 CATCH(SQLException)
813 LOG_SQLERROR;
814 t = DM_EQUERY;
815 FINALLY
816 db_con_close(c);
817 END_TRY;
818 p_string_free(query, TRUE);
819 g_free(name);
820 }
821 fieldseq++;
822 //adding default value, useful in NOT conditions, Cosmin Cioranu
823 g_string_append_printf(fieldorder, "ELSE %d END AS seq",fieldseq);
824 }
825 TRACE(TRACE_DEBUG, "[headername ids %s] ", headerIDs->str);
826 query = p_string_new(self->pool, "");
827 p_string_printf(query, "SELECT m.message_idnr, n.headername, v.headervalue%s "
828 "FROM %sheader h "
829 "LEFT JOIN %smessages m ON h.physmessage_id=m.physmessage_id "
830 "LEFT JOIN %sheadername n ON h.headername_id=n.id "
831 "LEFT JOIN %sheadervalue v ON h.headervalue_id=v.id "
832 "WHERE "
833 "h.headername_id %s IN (%s) "
834 "AND m.mailbox_idnr = %" PRIu64 " "
835 "AND m.message_idnr %s "
836 "AND status < %d "
837
838 //"AND n.headername %s IN ('%s') " //old, from the sql point of view is slow, CC 2020
839 // "GROUP By m.message_idnr, n.headername, v.headervalue "
840 // "having seq %s %d "
841 "ORDER BY m.message_idnr, seq",
842 not?"":fieldorder->str,
843 DBPFX, DBPFX, DBPFX, DBPFX,
844 not?"NOT":"", headerIDs->str,
845 self->mailbox->id, p_string_str(range),
846 //not?"NOT":"", bodyfetch->hdrnames //old
847 MESSAGE_STATUS_DELETE //return information only related to valid messages
848 //not?"=":"<",fieldseq //patch Cosmin Cioranu, added the having conditions and also the 'not' handler
849 );
850
851 if (fieldorder)
852 g_string_free(fieldorder, TRUE);
853 if (headerIDs)
854 g_string_free(headerIDs, TRUE);
855 c = db_con_get();
856 TRY
857 r = db_query(c, p_string_str(query));
858 while (db_result_next(r)) {
859 int l;
860 const void *blob;
861
862 id = db_result_get_u64(r, 0);
863
864 if (! g_tree_lookup(self->ids,&id))
865 continue;
866
867 fld = (char *)db_result_get(r, 1);
868 blob = db_result_get_blob(r, 2, &l);
869 char *str = g_new0(char, l + 1);
870 str = strncpy(str, blob, l);
871 val = dbmail_iconv_db_to_utf7(str);
872 g_free(str);
873 if (! val) {
874 TRACE(TRACE_DEBUG, "[%p] [%" PRIu64 "] no headervalue [%s]", self, id, fld);
875 } else {
876 mid = mempool_pop(small_pool, sizeof(uint64_t));
877 *mid = id;
878
879 old = g_tree_lookup(bodyfetch->headers, (gconstpointer)mid);
880 fld[0] = toupper(fld[0]);
881 /* Build content as Header: value \n */
882 new = g_strdup_printf("%s%s: %s\n", old?old:"", fld, val);
883 g_free(val);
884 g_tree_insert(bodyfetch->headers,mid,new);
885 }
886
887 }
888 CATCH(SQLException)
889 LOG_SQLERROR;
890 t = DM_EQUERY;
891 FINALLY
892 db_con_close(c);
893 END_TRY;
894
895 p_string_free(range, TRUE);
896 p_string_free(query, TRUE);
897
898 if (t == DM_EQUERY) return;
899
900 self->lo += QUERY_BATCHSIZE;
901 self->ceiling = self->hi;
902
903 _send_headers(self, bodyfetch, not);
904
905 return;
906 }
907
get_dumpsize(body_fetch * bodyfetch,uint64_t dumpsize)908 static uint64_t get_dumpsize(body_fetch *bodyfetch, uint64_t dumpsize)
909 {
910 if (bodyfetch->octetstart > dumpsize)
911 return 0;
912 if ((bodyfetch->octetstart + dumpsize) > bodyfetch->octetcnt)
913 return bodyfetch->octetcnt;
914 return (dumpsize - bodyfetch->octetstart);
915 }
916
_imap_send_part(ImapSession * self,GMimeObject * part,body_fetch * bodyfetch,const char * type)917 static void _imap_send_part(ImapSession *self, GMimeObject *part, body_fetch *bodyfetch, const char *type)
918 {
919 TRACE(TRACE_DEBUG,"[%p] type [%s]", self, type?type:"");
920 if ( !part ) {
921 dbmail_imap_session_buff_printf(self, "] NIL");
922 } else {
923 char *tmp = imap_get_logical_part(part,type);
924 String_T str = p_string_new(self->pool, tmp);
925 size_t len = p_string_len(str);
926 g_free(tmp);
927
928 if (len < 1) {
929 dbmail_imap_session_buff_printf(self, "] NIL");
930 } else {
931 uint64_t cnt = 0;
932 if (bodyfetch->octetcnt > 0) {
933 cnt = get_dumpsize(bodyfetch, len);
934 dbmail_imap_session_buff_printf(self, "]<%" PRIu64 "> {%" PRIu64 "}\r\n", bodyfetch->octetstart, cnt);
935 p_string_erase(str,0,min(bodyfetch->octetstart,len));
936 p_string_truncate(str,cnt);
937 } else {
938 dbmail_imap_session_buff_printf(self, "] {%" PRIu64 "}\r\n", len);
939 }
940 dbmail_imap_session_buff_printf(self,"%s", p_string_str(str));
941 }
942 p_string_free(str,TRUE);
943 }
944 }
945
946
_imap_show_body_section(body_fetch * bodyfetch,gpointer data)947 static int _imap_show_body_section(body_fetch *bodyfetch, gpointer data)
948 {
949 GMimeObject *part = NULL;
950 gboolean condition = FALSE;
951 ImapSession *self = (ImapSession *)data;
952
953 if (bodyfetch->itemtype < BFIT_TEXT)
954 return 0;
955
956 TRACE(TRACE_DEBUG,"[%p] itemtype [%d] partspec [%s]", self, bodyfetch->itemtype, bodyfetch->partspec);
957
958 if (self->fi->msgparse_needed) {
959 if (bodyfetch->partspec[0]) {
960 if (bodyfetch->partspec[0] == '0') {
961 dbmail_imap_session_buff_printf(self, "\r\n%s BAD protocol error\r\n", self->tag);
962 TRACE(TRACE_ERR, "[%p] PROTOCOL ERROR", self);
963 return 1;
964 }
965 part = imap_get_partspec(GMIME_OBJECT((self->message)->content), bodyfetch->partspec);
966 } else {
967 part = GMIME_OBJECT((self->message)->content);
968 }
969 }
970
971 SEND_SPACE;
972
973 if (! self->fi->noseen) self->fi->setseen = 1;
974 dbmail_imap_session_buff_printf(self, "BODY[%s", bodyfetch->partspec);
975
976 switch (bodyfetch->itemtype) {
977
978 case BFIT_ALL:
979 _imap_send_part(self, part, bodyfetch, NULL);
980 break;
981 case BFIT_TEXT:
982 dbmail_imap_session_buff_printf(self, "TEXT");
983 _imap_send_part(self, part, bodyfetch, "TEXT");
984 break;
985 // fall-through
986 case BFIT_HEADER:
987 dbmail_imap_session_buff_printf(self, "HEADER");
988 _imap_send_part(self, part, bodyfetch, "HEADER");
989 break;
990 case BFIT_MIME:
991 dbmail_imap_session_buff_printf(self, "MIME");
992 _imap_send_part(self, part, bodyfetch, "MIME");
993 break;
994 case BFIT_HEADER_FIELDS_NOT:
995 condition=TRUE;
996 // fall-through
997 case BFIT_HEADER_FIELDS:
998 _fetch_headers(self, bodyfetch, condition);
999 break;
1000 default:
1001 dbmail_imap_session_buff_clear(self);
1002 dbmail_imap_session_buff_printf(self, "\r\n* BYE internal server error\r\n");
1003 return -1;
1004
1005 }
1006 return 0;
1007 }
1008
1009 /* get envelopes */
_fetch_envelopes(ImapSession * self)1010 static void _fetch_envelopes(ImapSession *self)
1011 {
1012 Connection_T c; ResultSet_T r; volatile int t = FALSE;
1013 INIT_QUERY;
1014 gchar *s;
1015 uint64_t *mid;
1016 uint64_t id;
1017 char range[DEF_FRAGSIZE];
1018 GList *last;
1019 memset(range,0,sizeof(range));
1020
1021 if (! self->envelopes) {
1022 self->envelopes = g_tree_new_full((GCompareDataFunc)ucmpdata,NULL,(GDestroyNotify)uint64_free,(GDestroyNotify)g_free);
1023 self->lo = 0;
1024 self->hi = 0;
1025 }
1026
1027 if ((s = g_tree_lookup(self->envelopes, &(self->msg_idnr))) != NULL) {
1028 dbmail_imap_session_buff_printf(self, "ENVELOPE %s", s);
1029 return;
1030 }
1031
1032 TRACE(TRACE_DEBUG,"[%p] lo: %" PRIu64 "", self, self->lo);
1033
1034 if (! (last = g_list_nth(self->ids_list, self->lo+(uint64_t)QUERY_BATCHSIZE)))
1035 last = g_list_last(self->ids_list);
1036 self->hi = *(uint64_t *)last->data;
1037
1038 if (self->msg_idnr == self->hi)
1039 snprintf(range,DEF_FRAGSIZE-1,"= %" PRIu64 "", self->msg_idnr);
1040 else
1041 snprintf(range,DEF_FRAGSIZE-1,"BETWEEN %" PRIu64 " AND %" PRIu64 "", self->msg_idnr, self->hi);
1042
1043 snprintf(query, DEF_QUERYSIZE-1, "SELECT message_idnr,envelope "
1044 "FROM %senvelope e "
1045 "LEFT JOIN %smessages m USING (physmessage_id) "
1046 "WHERE m.mailbox_idnr = %" PRIu64 " "
1047 "AND message_idnr %s",
1048 DBPFX, DBPFX,
1049 self->mailbox->id, range);
1050 c = db_con_get();
1051 TRY
1052 r = db_query(c, query);
1053 while (db_result_next(r)) {
1054 id = db_result_get_u64(r, 0);
1055
1056 if (! g_tree_lookup(self->ids,&id))
1057 continue;
1058
1059 mid = mempool_pop(small_pool, sizeof(uint64_t));
1060 *mid = id;
1061
1062 g_tree_insert(self->envelopes,mid,g_strdup(ResultSet_getString(r, 2)));
1063 }
1064 CATCH(SQLException)
1065 LOG_SQLERROR;
1066 t = DM_EQUERY;
1067 FINALLY
1068 db_con_close(c);
1069 END_TRY;
1070
1071 if (t == DM_EQUERY) return;
1072
1073 self->lo += QUERY_BATCHSIZE;
1074
1075 s = g_tree_lookup(self->envelopes, &(self->msg_idnr));
1076 dbmail_imap_session_buff_printf(self, "ENVELOPE %s", s?s:"");
1077 }
1078
_imap_show_body_sections(ImapSession * self)1079 static void _imap_show_body_sections(ImapSession *self)
1080 {
1081 List_T head;
1082 if (! self->fi->bodyfetch)
1083 return;
1084
1085 head = p_list_first(self->fi->bodyfetch);
1086 while (head) {
1087 body_fetch *bodyfetch = (body_fetch *)p_list_data(head);
1088 if (bodyfetch)
1089 _imap_show_body_section(bodyfetch, self);
1090 head = p_list_next(head);
1091 }
1092 }
1093
_fetch_get_items(ImapSession * self,uint64_t * uid)1094 static int _fetch_get_items(ImapSession *self, uint64_t *uid)
1095 {
1096
1097 int result;
1098 uint64_t size = 0;
1099 gchar *s = NULL;
1100 uint64_t *id = uid;
1101 gboolean reportflags = FALSE;
1102 String_T stream = NULL;
1103
1104 TRACE(TRACE_DEBUG,"Call: _fetch_get_items");
1105
1106 MessageInfo *msginfo = g_tree_lookup(MailboxState_getMsginfo(self->mailbox->mbstate), uid);
1107
1108 if (! msginfo) {
1109 TRACE(TRACE_INFO, "[%p] failed to lookup msginfo struct for message [%" PRIu64 "]", self, *uid);
1110 return 0;
1111 }
1112
1113 id = g_tree_lookup(MailboxState_getIds(self->mailbox->mbstate),uid);
1114
1115 g_return_val_if_fail(id,-1);
1116
1117 if (self->fi->changedsince && (msginfo->seq <= self->fi->changedsince))
1118 return 0;
1119
1120 self->msg_idnr = *uid;
1121 self->fi->isfirstfetchout = 1;
1122
1123 if (self->fi->msgparse_needed) {
1124 if (! (dbmail_imap_session_message_load(self)))
1125 return 0;
1126
1127 stream = self->message->crlf;
1128 size = p_string_len(stream);
1129 }
1130
1131 dbmail_imap_session_buff_printf(self, "* %" PRIu64 " FETCH (", *id);
1132
1133 if (self->mailbox->condstore || self->enabled.qresync) {
1134 SEND_SPACE;
1135 dbmail_imap_session_buff_printf(self, "MODSEQ (%" PRIu64 ")",
1136 msginfo->seq?msginfo->seq:1);
1137 }
1138 if (self->fi->getInternalDate) {
1139 SEND_SPACE;
1140 char *s =date_sql2imap(msginfo->internaldate);
1141 dbmail_imap_session_buff_printf(self, "INTERNALDATE \"%s\"", s);
1142 g_free(s);
1143 }
1144 if (self->fi->getSize) {
1145 uint64_t rfcsize = msginfo->rfcsize;
1146 SEND_SPACE;
1147 dbmail_imap_session_buff_printf(self, "RFC822.SIZE %" PRIu64 "", rfcsize);
1148 }
1149 if (self->fi->getFlags) {
1150 SEND_SPACE;
1151
1152 GList *sublist = MailboxState_message_flags(self->mailbox->mbstate, msginfo);
1153 s = dbmail_imap_plist_as_string(sublist);
1154 g_list_destroy(sublist);
1155 dbmail_imap_session_buff_printf(self,"FLAGS %s",s);
1156 g_free(s);
1157 }
1158 if (self->fi->getUID) {
1159 SEND_SPACE;
1160 dbmail_imap_session_buff_printf(self, "UID %" PRIu64 "", msginfo->uid);
1161 }
1162 if (self->fi->getMIME_IMB) {
1163 SEND_SPACE;
1164 if ((s = imap_get_structure(GMIME_MESSAGE((self->message)->content), 1))==NULL) {
1165 dbmail_imap_session_buff_clear(self);
1166 dbmail_imap_session_buff_printf(self, "\r\n* BYE error fetching body structure\r\n");
1167 return -1;
1168 }
1169 dbmail_imap_session_buff_printf(self, "BODYSTRUCTURE %s", s);
1170 g_free(s);
1171 }
1172
1173 if (self->fi->getMIME_IMB_noextension) {
1174 SEND_SPACE;
1175 if ((s = imap_get_structure(GMIME_MESSAGE((self->message)->content), 0))==NULL) {
1176 dbmail_imap_session_buff_clear(self);
1177 dbmail_imap_session_buff_printf(self, "\r\n* BYE error fetching body\r\n");
1178 return -1;
1179 }
1180 dbmail_imap_session_buff_printf(self, "BODY %s",s);
1181 g_free(s);
1182 }
1183
1184 if (self->fi->getEnvelope) {
1185 SEND_SPACE;
1186 _fetch_envelopes(self);
1187 }
1188
1189 if (self->fi->getRFC822 || self->fi->getRFC822Peek) {
1190 SEND_SPACE;
1191 dbmail_imap_session_buff_printf(self, "RFC822 {%" PRIu64 "}\r\n", size);
1192 send_data(self, stream, 0, size);
1193 if (self->fi->getRFC822)
1194 self->fi->setseen = 1;
1195
1196 }
1197
1198 if (self->fi->getBodyTotal || self->fi->getBodyTotalPeek) {
1199 SEND_SPACE;
1200 if (dbmail_imap_session_bodyfetch_get_last_octetcnt(self) == 0) {
1201 dbmail_imap_session_buff_printf(self, "BODY[] {%" PRIu64 "}\r\n", size);
1202 send_data(self, stream, 0, size);
1203 } else {
1204 uint64_t start = dbmail_imap_session_bodyfetch_get_last_octetstart(self);
1205 uint64_t count = dbmail_imap_session_bodyfetch_get_last_octetcnt(self);
1206 uint64_t length = 0;
1207 if (start <= size)
1208 length = ((start + count) > size)?(size - start):count;
1209 dbmail_imap_session_buff_printf(self, "BODY[]<%" PRIu64 "> {%" PRIu64 "}\r\n",
1210 start, length);
1211 send_data(self, stream, start, length);
1212 }
1213 if (self->fi->getBodyTotal)
1214 self->fi->setseen = 1;
1215 }
1216
1217 if (self->fi->getRFC822Header) {
1218 SEND_SPACE;
1219 char *tmp = imap_get_logical_part(self->message->content, "HEADER");
1220 dbmail_imap_session_buff_printf(self, "RFC822.HEADER {%ld}\r\n%s", strlen(tmp), tmp);
1221 free(tmp);
1222 tmp = NULL;
1223 }
1224
1225 if (self->fi->getRFC822Text) {
1226 SEND_SPACE;
1227 char *tmp = imap_get_logical_part(self->message->content, "TEXT");
1228 dbmail_imap_session_buff_printf(self, "RFC822.TEXT {%ld}\r\n%s", strlen(tmp), tmp);
1229 free(tmp);
1230 tmp = NULL;
1231 self->fi->setseen = 1;
1232
1233 }
1234
1235 _imap_show_body_sections(self);
1236
1237 /* set \Seen flag if necessary; note the absence of an error-check
1238 * for db_get_msgflag()!
1239 */
1240 int setSeenSet[IMAP_NFLAGS] = { 1, 0, 0, 0, 0, 0 };
1241 if (self->fi->setseen && db_get_msgflag("seen", self->msg_idnr) != 1) {
1242 /* only if the user has an ACL which grants
1243 him rights to set the flag should the
1244 flag be set! */
1245 result = acl_has_right(self->mailbox->mbstate, self->userid, ACL_RIGHT_SEEN);
1246 if (result == -1) {
1247 dbmail_imap_session_buff_clear(self);
1248 dbmail_imap_session_buff_printf(self, "\r\n* BYE internal dbase error\r\n");
1249 return -1;
1250 }
1251
1252 if (result == 1) {
1253 reportflags = TRUE;
1254 result = db_set_msgflag(self->msg_idnr, setSeenSet, NULL, IMAPFA_ADD, 0, msginfo);
1255 if (result == -1) {
1256 dbmail_imap_session_buff_clear(self);
1257 dbmail_imap_session_buff_printf(self, "\r\n* BYE internal dbase error\r\n");
1258 return -1;
1259 }
1260 db_mailbox_seq_update(MailboxState_getId(self->mailbox->mbstate), self->msg_idnr);
1261 }
1262
1263 self->fi->getFlags = 1;
1264 }
1265
1266 dbmail_imap_session_buff_printf(self, ")\r\n");
1267
1268 if (reportflags) {
1269 char *t = NULL;
1270 GList *sublist = NULL;
1271 if (self->use_uid)
1272 t = g_strdup_printf("UID %" PRIu64 " ", *uid);
1273
1274 sublist = MailboxState_message_flags(self->mailbox->mbstate, msginfo);
1275 s = dbmail_imap_plist_as_string(sublist);
1276 g_list_destroy(sublist);
1277
1278 dbmail_imap_session_buff_printf(self,"* %" PRIu64 " FETCH (%sFLAGS %s)\r\n", *id, t?t:"", s);
1279 if (t) g_free(t);
1280 g_free(s);
1281 }
1282
1283 return 0;
1284 }
1285
_do_fetch(uint64_t * uid,gpointer UNUSED value,ImapSession * self)1286 static gboolean _do_fetch(uint64_t *uid, gpointer UNUSED value, ImapSession *self)
1287 {
1288 /* go fetch the items */
1289 if (_fetch_get_items(self,uid) < 0) {
1290 TRACE(TRACE_ERR, "[%p] _fetch_get_items returned with error", self);
1291 dbmail_imap_session_buff_clear(self);
1292 self->error = TRUE;
1293 return TRUE;
1294 }
1295 dbmail_imap_session_buff_flush(self);
1296
1297 return FALSE;
1298 }
1299
dbmail_imap_session_fetch_get_items(ImapSession * self)1300 int dbmail_imap_session_fetch_get_items(ImapSession *self)
1301 {
1302 if (! self->ids)
1303 TRACE(TRACE_INFO, "[%p] self->ids is NULL", self);
1304 else {
1305 self->error = FALSE;
1306 g_tree_foreach(self->ids, (GTraverseFunc) _do_fetch, self);
1307 dbmail_imap_session_buff_flush(self);
1308 if (self->error) return -1;
1309 }
1310 return 0;
1311
1312 }
1313
1314 /*
1315 * check_state_and_args()
1316 *
1317 * checks if the user is in the right state & the numbers of arguments;
1318 * a state of -1 specifies any state
1319 * arguments can be grouped by means of parentheses
1320 *
1321 * returns 1 on succes, 0 on failure
1322 */
dbmail_imap_session_buff_clear(ImapSession * self)1323 void dbmail_imap_session_buff_clear(ImapSession *self)
1324 {
1325 self->buff = p_string_truncate(self->buff, 0);
1326 }
1327
dbmail_imap_session_buff_flush(ImapSession * self)1328 void dbmail_imap_session_buff_flush(ImapSession *self)
1329 {
1330 if (self->state >= CLIENTSTATE_LOGOUT) return;
1331 if (p_string_len(self->buff) < 1) return;
1332
1333 gpointer session = self;
1334 gpointer data = self->buff;
1335 if (! queue_pool)
1336 self->buff = p_string_new(self->pool, "");
1337 else
1338 self->buff = p_string_new(queue_pool, "");
1339
1340 dm_queue_push(dm_thread_data_sendmessage, session, data);
1341 }
1342
1343 #define IMAP_BUF_SIZE 4096
1344
dbmail_imap_session_buff_printf(ImapSession * self,char * message,...)1345 int dbmail_imap_session_buff_printf(ImapSession * self, char * message, ...)
1346 {
1347 va_list ap, cp;
1348 uint64_t j = 0, l;
1349
1350 assert(message);
1351 j = p_string_len(self->buff);
1352
1353 va_start(ap, message);
1354 va_copy(cp, ap);
1355 p_string_append_vprintf(self->buff, message, cp);
1356 va_end(cp);
1357 va_end(ap);
1358 l = p_string_len(self->buff);
1359
1360 if (l >= IMAP_BUF_SIZE) dbmail_imap_session_buff_flush(self);
1361
1362 return (int)(l-j);
1363 }
1364
dbmail_imap_session_handle_auth(ImapSession * self,const char * username,const char * password)1365 int dbmail_imap_session_handle_auth(ImapSession * self, const char * username, const char * password)
1366 {
1367 uint64_t userid = 0;
1368 int valid = 0;
1369
1370 if (self->ci->auth)
1371 username = Cram_getUsername(self->ci->auth);
1372
1373 TRACE(TRACE_DEBUG, "[%p] trying to validate user [%s]", self, username);
1374
1375 valid = auth_validate(self->ci, username, password, &userid);
1376
1377
1378 switch(valid) {
1379 case -1: /* a db-error occurred */
1380 dbmail_imap_session_buff_printf(self, "* BYE internal db error validating user\r\n");
1381 return -1;
1382
1383 case 0:
1384 sleep(2); /* security */
1385 ci_authlog_init(self->ci, THIS_MODULE, username, AUTHLOG_ERR);
1386 if (self->ci->auth) { // CRAM-MD5 auth failed
1387 char *enctype = NULL;
1388 if (userid) enctype = auth_getencryption(userid);
1389 if ((! enctype) || (! MATCH(enctype,""))) {
1390 Capa_remove(self->capa,"AUTH=CRAM-MD5");
1391 dbmail_imap_session_buff_printf(self, "* CAPABILITY %s\r\n", Capa_as_string(self->capa));
1392 }
1393 if (enctype) g_free(enctype);
1394 }
1395 dbmail_imap_session_buff_printf(self, "%s NO login rejected\r\n", self->tag);
1396 TRACE(TRACE_NOTICE, "[%p] login rejected: user [%s] from [%s:%s]", self, username,
1397 self->ci->src_ip, self->ci->src_port);
1398 return 1;
1399
1400 case 1:
1401 self->userid = userid;
1402 ci_authlog_init(self->ci, THIS_MODULE, username, AUTHLOG_ACT);
1403 TRACE(TRACE_NOTICE, "[%p] login accepted: id [%" PRIu64 "] user [%s] from [%s:%s]",
1404 self, userid, username, self->ci->src_ip, self->ci->src_port);
1405 break;
1406
1407 default:
1408 TRACE(TRACE_ERR, "[%p] auth_validate returned [%d]", self, valid);
1409 return -1;
1410 }
1411
1412 dbmail_imap_session_set_state(self,CLIENTSTATE_AUTHENTICATED);
1413
1414 return 0;
1415
1416 }
1417
dbmail_imap_session_prompt(ImapSession * self,char * prompt)1418 static int dbmail_imap_session_prompt(ImapSession * self, char * prompt)
1419 {
1420 char *prompt64, *promptcat;
1421
1422 g_return_val_if_fail(prompt != NULL, -1);
1423
1424 /* base64 encoding increases string length by about 40%. */
1425 promptcat = g_strdup_printf("%s\r\n", prompt);
1426 prompt64 = (char *)g_base64_encode((const guchar *)promptcat, strlen(promptcat));
1427 dbmail_imap_session_buff_printf(self, "+ %s\r\n", prompt64);
1428 dbmail_imap_session_buff_flush(self);
1429
1430 g_free(prompt64);
1431 g_free(promptcat);
1432
1433 return 0;
1434 }
1435
notify_fetch(ImapSession * self,MailboxState_T N,uint64_t * uid)1436 static void notify_fetch(ImapSession *self, MailboxState_T N, uint64_t *uid)
1437 {
1438 uint64_t *msn;
1439
1440 GList *ol = NULL, *nl = NULL;
1441 char *oldflags = NULL, *newflags = NULL;
1442 MessageInfo *old = NULL, *new = NULL;
1443 MailboxState_T M = self->mailbox->mbstate;
1444 gboolean flagschanged = false, modseqchanged = false;
1445
1446 assert(uid);
1447
1448 if (! (MailboxState_getMsginfo(N) && *uid && (new = g_tree_lookup(MailboxState_getMsginfo(N), uid))))
1449 return;
1450
1451 if (! (msn = g_tree_lookup(MailboxState_getIds(M), uid)))
1452 return;
1453
1454 MailboxState_merge_recent(N, M);
1455
1456 // FETCH
1457 if ((old = g_tree_lookup(MailboxState_getMsginfo(M), uid)))
1458 ol = MailboxState_message_flags(M, old);
1459 oldflags = dbmail_imap_plist_as_string(ol);
1460
1461 nl = MailboxState_message_flags(N, new);
1462 newflags = dbmail_imap_plist_as_string(nl);
1463
1464 g_list_destroy(ol);
1465 g_list_destroy(nl);
1466
1467 if ((!old) || (old->seq < new->seq))
1468 modseqchanged = true;
1469 if (oldflags && (! MATCH(oldflags, newflags)))
1470 flagschanged = true;
1471
1472 if (modseqchanged || flagschanged) {
1473 GList *plist = NULL;
1474 char *response = NULL;
1475 if (self->use_uid) {
1476 char *u = g_strdup_printf("UID %" PRIu64, *uid);
1477 plist = g_list_append(plist, u);
1478 }
1479
1480 if (modseqchanged && self->mailbox->condstore) {
1481 TRACE(TRACE_DEBUG, "seq [%" PRIu64 "] -> [%" PRIu64 "]", old?old->seq:0, new->seq);
1482 char *m = g_strdup_printf("MODSEQ (%" PRIu64 ")", new->seq);
1483 plist = g_list_append(plist, m);
1484 }
1485
1486 if (flagschanged) {
1487 TRACE(TRACE_DEBUG, "flags [%s] -> [%s]", oldflags, newflags);
1488 char *f = g_strdup_printf("FLAGS %s", newflags);
1489 plist = g_list_append(plist, f);
1490 }
1491
1492 response = dbmail_imap_plist_as_string(plist);
1493
1494 dbmail_imap_session_buff_printf(self, "* %" PRIu64 " FETCH %s\r\n",
1495 *msn, response);
1496 g_free(response);
1497 g_list_destroy(plist);
1498 }
1499
1500 if (oldflags) g_free(oldflags);
1501 g_free(newflags);
1502 }
1503
notify_expunge(ImapSession * self,uint64_t * uid)1504 static gboolean notify_expunge(ImapSession *self, uint64_t *uid)
1505 {
1506 uint64_t *msn = NULL, m = 0;
1507
1508 if (! (msn = g_tree_lookup(MailboxState_getIds(self->mailbox->mbstate), uid))) {
1509 TRACE(TRACE_DEBUG,"[%p] can't find uid [%" PRIu64 "]", self, *uid);
1510 return TRUE;
1511 }
1512
1513 switch (self->command_type) {
1514 case IMAP_COMM_FETCH:
1515 case IMAP_COMM_STORE:
1516 case IMAP_COMM_SEARCH:
1517 break;
1518 default:
1519 m = *msn;
1520 if (MailboxState_removeUid(self->mailbox->mbstate, *uid) == DM_SUCCESS)
1521 dbmail_imap_session_buff_printf(self, "* %" PRIu64 " EXPUNGE\r\n", m);
1522 else
1523 return TRUE;
1524 break;
1525 }
1526
1527 return FALSE;
1528 }
1529
mailbox_notify_expunge(ImapSession * self,MailboxState_T N)1530 static void mailbox_notify_expunge(ImapSession *self, MailboxState_T N)
1531 {
1532 uint64_t *uid, *msn;
1533 MailboxState_T M;
1534 GList *ids;
1535 if (! N) return;
1536
1537 M = self->mailbox->mbstate;
1538
1539 ids = g_tree_keys(MailboxState_getIds(M));
1540 ids = g_list_reverse(ids);
1541
1542 // send expunge updates
1543
1544 if (ids) {
1545 uid = (uint64_t *)ids->data;
1546 GTree * treeM=MailboxState_getIds(M);
1547 if (treeM != NULL){
1548 msn = g_tree_lookup(treeM, uid);
1549 if (msn && (*msn > MailboxState_getExists(M))) {
1550 TRACE(TRACE_DEBUG,"exists new [%d] old: [%d]", MailboxState_getExists(N), MailboxState_getExists(M));
1551 dbmail_imap_session_buff_printf(self, "* %d EXISTS\r\n", MailboxState_getExists(M));
1552 }
1553 }
1554 }
1555 while (ids) {
1556 uid = (uint64_t *)ids->data;
1557 MessageInfo *messageInfo=g_tree_lookup(MailboxState_getMsginfo(N), uid);
1558 if (messageInfo!=NULL && !g_tree_lookup(MailboxState_getIds(N), uid)) {
1559 /* mark message as expunged, it should be ok to be removed from list, see state_load_message */
1560 messageInfo->expunged=1;
1561 notify_expunge(self, uid);
1562 }
1563
1564 if (! g_list_next(ids)) break;
1565 ids = g_list_next(ids);
1566 }
1567 ids = g_list_first(ids);
1568 g_list_free(ids);
1569 }
1570
mailbox_notify_fetch(ImapSession * self,MailboxState_T N)1571 static void mailbox_notify_fetch(ImapSession *self, MailboxState_T N)
1572 {
1573 uint64_t *uid, *id;
1574 GList *ids;
1575 if (! N) return;
1576
1577 // send fetch updates
1578 ids = g_tree_keys(MailboxState_getIds(self->mailbox->mbstate));
1579 ids = g_list_first(ids);
1580 while (ids) {
1581 uid = (uint64_t *)ids->data;
1582 notify_fetch(self, N, uid);
1583 if (! g_list_next(ids)) break;
1584 ids = g_list_next(ids);
1585 }
1586 g_list_free(g_list_first(ids));
1587
1588 // switch active mailbox view
1589 self->mailbox->mbstate = N;
1590 id = mempool_pop(small_pool, sizeof(uint64_t));
1591 *id = MailboxState_getId(N);
1592
1593
1594
1595 g_tree_replace(self->mbxinfo, id, N);
1596
1597 MailboxState_flush_recent(N);
1598 }
1599
dbmail_imap_session_mailbox_status(ImapSession * self,gboolean update)1600 int dbmail_imap_session_mailbox_status(ImapSession * self, gboolean update)
1601 {
1602 /*
1603 C: a047 NOOP
1604 S: * 22 EXPUNGE
1605 S: * 23 EXISTS
1606 S: * 3 RECENT
1607 S: * 14 FETCH (FLAGS (\Seen \Deleted))
1608 */
1609
1610 MailboxState_T M, N = NULL;
1611 gboolean showexists = FALSE, showrecent = FALSE, showflags = FALSE;
1612 unsigned oldexists;
1613
1614 if (self->state != CLIENTSTATE_SELECTED) return FALSE;
1615
1616 if (update) {
1617 unsigned oldseq, newseq;
1618 uint64_t olduidnext;
1619 char *oldflags, *newflags;
1620
1621 M = self->mailbox->mbstate;
1622 oldseq = MailboxState_getSeq(M);
1623 oldflags = MailboxState_flags(M);
1624 oldexists = MailboxState_getExists(M);
1625 olduidnext = MailboxState_getUidnext(M);
1626
1627 // check the mailbox sequence without a
1628 // full reload
1629 N = MailboxState_new(self->pool, 0);
1630 MailboxState_setId(N, self->mailbox->id);
1631 newseq = MailboxState_getSeq(N);
1632 MailboxState_free(&N);
1633 N = NULL;
1634
1635 TRACE(TRACE_DEBUG, "seq: [%u] -> [%u]", oldseq, newseq);
1636 if (oldseq != newseq) {
1637 int mailbox_update_strategy = config_get_value_default_int("mailbox_update_strategy", "IMAP", 1);
1638
1639 if (mailbox_update_strategy == 1){
1640 TRACE(TRACE_DEBUG, "Strategy reload: 1 (full reload)");
1641 /* do a full reload: re-read flags and counters */
1642 N = MailboxState_new(self->pool, self->mailbox->id);
1643 }else{
1644 if (mailbox_update_strategy == 2){
1645 TRACE(TRACE_DEBUG, "Strategy reload: 2 (differential reload)");
1646 /* do a diff reload, experimental */
1647 N = MailboxState_update(self->pool, M);
1648 }else{
1649 TRACE(TRACE_DEBUG, "Strategy reload: default (full reload)");
1650 /* default strategy is full reload, case 1*/
1651 N = MailboxState_new(self->pool, self->mailbox->id);
1652 }
1653 }
1654
1655 unsigned newexists = MailboxState_getExists(N);
1656 MailboxState_setExists(N, max(oldexists, newexists));
1657
1658 // rebuild uid/msn trees
1659 // ATTN: new messages shouldn't be visible in any way to a
1660 // client session until it has been announced with EXISTS
1661
1662 // EXISTS response may never decrease
1663 if ((MailboxState_getUidnext(N) > olduidnext)) {
1664 showexists = TRUE;
1665 }
1666
1667 if (MailboxState_getRecent(N))
1668 showrecent = TRUE;
1669
1670 newflags = MailboxState_flags(N);
1671 if (! MATCH(newflags,oldflags))
1672 showflags = TRUE;
1673 g_free(newflags);
1674 }
1675 g_free(oldflags);
1676 }
1677
1678 // command specific overrides
1679 switch (self->command_type) {
1680 case IMAP_COMM_EXAMINE:
1681 case IMAP_COMM_SELECT:
1682 case IMAP_COMM_SEARCH:
1683 case IMAP_COMM_SORT:
1684 showexists = showrecent = TRUE;
1685 break;
1686
1687 default:
1688 // ok show them if needed
1689 break;
1690 }
1691
1692 // never decrease without first sending expunge !!
1693 if (N) {
1694 if (showexists && MailboxState_getExists(N))
1695 dbmail_imap_session_buff_printf(self, "* %u EXISTS\r\n", MailboxState_getExists(N));
1696
1697 if (showrecent && MailboxState_getRecent(N))
1698 dbmail_imap_session_buff_printf(self, "* %u RECENT\r\n", MailboxState_getRecent(N));
1699
1700 mailbox_notify_expunge(self, N);
1701
1702 if (showflags) {
1703 char *flags = MailboxState_flags(N);
1704 dbmail_imap_session_buff_printf(self, "* FLAGS (%s)\r\n", flags);
1705 dbmail_imap_session_buff_printf(self, "* OK [PERMANENTFLAGS (%s \\*)] Flags allowed.\r\n", flags);
1706 g_free(flags);
1707 }
1708
1709 mailbox_notify_fetch(self, N);
1710 }
1711
1712 return 0;
1713 }
1714
dbmail_imap_session_mbxinfo_lookup(ImapSession * self,uint64_t mailbox_id)1715 MailboxState_T dbmail_imap_session_mbxinfo_lookup(ImapSession *self, uint64_t mailbox_id)
1716 {
1717 MailboxState_T M = NULL;
1718 uint64_t *id;
1719 if (self->mailbox && self->mailbox->mbstate && (MailboxState_getId(self->mailbox->mbstate) == mailbox_id)) {
1720 // selected state
1721 M = self->mailbox->mbstate;
1722 } else {
1723 M = (MailboxState_T)g_tree_lookup(self->mbxinfo, &mailbox_id);
1724 if (! M) {
1725 id = mempool_pop(small_pool, sizeof(uint64_t));
1726 *id = mailbox_id;
1727 M = MailboxState_new(self->pool, mailbox_id);
1728 g_tree_replace(self->mbxinfo, id, M);
1729 } else {
1730 unsigned newseq = 0, oldseq = 0;
1731 unsigned newexists = 0, oldexists = 0;
1732 MailboxState_T N = NULL;
1733 N = MailboxState_new(self->pool, 0);
1734 MailboxState_setId(N, mailbox_id);
1735 oldseq = MailboxState_getSeq(M);
1736 newseq = MailboxState_getSeq(N);
1737 oldexists = MailboxState_getExists(M);
1738 MailboxState_free(&N);
1739 if (oldseq < newseq) {
1740 id = mempool_pop(small_pool, sizeof(uint64_t));
1741 *id = mailbox_id;
1742 M = MailboxState_new(self->pool, mailbox_id);
1743 newexists = MailboxState_getExists(M);
1744 MailboxState_setExists(M, max(oldexists, newexists));
1745 g_tree_replace(self->mbxinfo, id, M);
1746 }
1747 }
1748
1749
1750 }
1751
1752 assert(M);
1753
1754 return M;
1755 }
1756
dbmail_imap_session_set_state(ImapSession * self,ClientState_T state)1757 int dbmail_imap_session_set_state(ImapSession *self, ClientState_T state)
1758 {
1759 ClientState_T current;
1760
1761 PLOCK(self->lock);
1762 current = self->state;
1763 PUNLOCK(self->lock);
1764
1765 if ((current == state) || (current == CLIENTSTATE_QUIT_QUEUED))
1766 return 1;
1767
1768 switch (state) {
1769 case CLIENTSTATE_ERROR:
1770 assert(self->ci);
1771 if (self->ci->wev) event_del(self->ci->wev);
1772 // fall-through...
1773
1774 case CLIENTSTATE_LOGOUT:
1775 assert(self->ci);
1776 if (self->ci->rev) event_del(self->ci->rev);
1777 break;
1778
1779 case CLIENTSTATE_AUTHENTICATED:
1780 // change from login_timeout to main timeout
1781 assert(self->ci);
1782 TRACE(TRACE_DEBUG,"[%p] set timeout to [%d]", self, server_conf->timeout);
1783 self->ci->timeout.tv_sec = server_conf->timeout;
1784 Capa_remove(self->capa, "AUTH=login");
1785 Capa_remove(self->capa, "AUTH=CRAM-MD5");
1786
1787 break;
1788
1789 default:
1790 break;
1791 }
1792
1793 TRACE(TRACE_DEBUG,"[%p] state [%d]->[%d]", self, current, state);
1794 PLOCK(self->lock);
1795 self->state = state;
1796 PUNLOCK(self->lock);
1797
1798 return 0;
1799 }
1800
_do_expunge(uint64_t * id,ImapSession * self)1801 static gboolean _do_expunge(uint64_t *id, ImapSession *self)
1802 {
1803 MessageInfo *msginfo = g_tree_lookup(MailboxState_getMsginfo(self->mailbox->mbstate), id);
1804 assert(msginfo);
1805
1806 if (! msginfo->flags[IMAP_FLAG_DELETED]) return FALSE;
1807
1808 if (db_exec(self->c, "UPDATE %smessages SET status=%d WHERE message_idnr=%" PRIu64 " ", DBPFX, MESSAGE_STATUS_DELETE, *id) == DM_EQUERY)
1809 return TRUE;
1810
1811 return notify_expunge(self, id);
1812 }
1813
dbmail_imap_session_mailbox_expunge(ImapSession * self,const char * set,uint64_t * modseq)1814 int dbmail_imap_session_mailbox_expunge(ImapSession *self, const char *set, uint64_t *modseq)
1815 {
1816 uint64_t mailbox_size;
1817 int i;
1818 GList *ids;
1819 GTree *uids = NULL;
1820 MailboxState_T M = self->mailbox->mbstate;
1821
1822 if (! (i = g_tree_nnodes(MailboxState_getIds(M))))
1823 return DM_SUCCESS;
1824
1825 if (db_get_mailbox_size(self->mailbox->id, 1, &mailbox_size) == DM_EQUERY)
1826 return DM_EQUERY;
1827
1828 if (set) {
1829 uids = dbmail_mailbox_get_set(self->mailbox, set, self->use_uid);
1830 ids = g_tree_keys(uids);
1831 } else {
1832 ids = g_tree_keys(MailboxState_getIds(M));
1833 }
1834
1835 ids = g_list_reverse(ids);
1836
1837 if (ids) {
1838 self->c = db_con_get();
1839 db_begin_transaction(self->c);
1840 g_list_foreach(ids, (GFunc) _do_expunge, self);
1841 db_commit_transaction(self->c);
1842 db_con_close(self->c);
1843 self->c = NULL;
1844 g_list_free(g_list_first(ids));
1845 }
1846
1847 if (uids)
1848 g_tree_destroy(uids);
1849
1850 *modseq = 0;
1851 if (i > g_tree_nnodes(MailboxState_getIds(M))) {
1852 *modseq = db_mailbox_seq_update(self->mailbox->id, 0);
1853 if (! dm_quota_user_dec(self->userid, mailbox_size))
1854 return DM_EQUERY;
1855 }
1856
1857 return 0;
1858 }
1859
1860 /*****************************************************************************
1861 *
1862 *
1863 * bodyfetch
1864 *
1865 *
1866 ****************************************************************************/
_body_fetch_free(body_fetch * bodyfetch,gpointer data)1867 static void _body_fetch_free(body_fetch *bodyfetch, gpointer data)
1868 {
1869 ImapSession *self = (ImapSession *)data;
1870 if (! bodyfetch) return;
1871 if (bodyfetch->names) {
1872 g_list_free(g_list_first(bodyfetch->names));
1873 bodyfetch->names = NULL;
1874 }
1875
1876 if (bodyfetch->hdrnames) g_free(bodyfetch->hdrnames);
1877 if (bodyfetch->hdrplist) g_free(bodyfetch->hdrplist);
1878 if (bodyfetch->headers) {
1879 g_tree_destroy(bodyfetch->headers);
1880 bodyfetch->headers = NULL;
1881 }
1882 mempool_push(self->pool, bodyfetch, sizeof(body_fetch));
1883 }
1884
dbmail_imap_session_bodyfetch_free(ImapSession * self)1885 void dbmail_imap_session_bodyfetch_free(ImapSession *self)
1886 {
1887 List_T first, head = p_list_first(self->fi->bodyfetch);
1888 first = head;
1889 while (head) {
1890 body_fetch *bodyfetch = p_list_data(head);
1891 _body_fetch_free(bodyfetch, self);
1892 head = p_list_next(head);
1893 }
1894 p_list_free(&first);
1895 return;
1896 }
1897
1898
1899 /* local defines */
1900 #define NORMPAR 1
1901 #define SQUAREPAR 2
1902 #define NOPAR 0
1903
1904 /*
1905 * imap4_tokenizer_main()
1906 *
1907 * imap command tokenizer
1908 *
1909 * builds an dimensional array of strings containing arguments based upon
1910 * strings on cmd line specified by {##}\0
1911 * (\r\n had been removed from string)
1912 *
1913 * normal/square parentheses have special meaning:
1914 * '(body [all header])' will result in the following array:
1915 * [0] = '('
1916 * [1] = 'body'
1917 * [2] = '['
1918 * [3] = 'all'
1919 * [4] = 'header'
1920 * [5] = ']'
1921 * [6] = ')'
1922 *
1923 * quoted strings are those enclosed by double quotation marks and returned as a single argument
1924 * WITHOUT the enclosing quotation marks
1925 *
1926 * parentheses loose their special meaning if inside (double)quotation marks;
1927 * data should be 'clarified' (see clarify_data() function below)
1928 *
1929 * The returned array will be NULL-terminated.
1930 * Will return NULL upon errors.
1931 */
1932
imap4_tokenizer_main(ImapSession * self,const char * buffer)1933 int imap4_tokenizer_main(ImapSession *self, const char *buffer)
1934 {
1935 int inquote = 0, quotestart = 0;
1936 int nnorm = 0, nsquare = 0, paridx = 0, argstart = 0;
1937 unsigned int i = 0;
1938 size_t max;
1939 char parlist[MAX_LINESIZE];
1940 char *s, *lastchar;
1941
1942 assert(buffer);
1943
1944 s = (char *)buffer;
1945 max = strlen(s);
1946
1947 if (max < 1)
1948 goto finalize;
1949
1950
1951 // assert(max <= MAX_LINESIZE);
1952
1953 /* find the arguments */
1954 paridx = 0;
1955 parlist[paridx] = NOPAR;
1956
1957 inquote = 0;
1958
1959 // if we're not fetching string-literals it's safe to strip NL
1960 if (self->ci->rbuff_size) {
1961 assert(max <= self->ci->rbuff_size);
1962
1963 if (! self->args[self->args_idx])
1964 self->args[self->args_idx] = p_string_new(self->pool, "");
1965
1966 p_string_append_len(self->args[self->args_idx], buffer, max);
1967 self->ci->rbuff_size -= max;
1968 if (self->ci->rbuff_size == 0) {
1969 self->args_idx++; // move on to next token
1970 TRACE(TRACE_DEBUG, "string literal complete. last-char [%c]", s[max-1]);
1971 }
1972
1973 return 0;
1974
1975 } else {
1976 g_strchomp(s);
1977 max = strlen(s);
1978 }
1979
1980 TRACE(TRACE_DEBUG,"[%p] tokenize [%" PRIu64 "/%" PRIu64 "] [%s]", self,
1981 (uint64_t)max, (uint64_t)self->ci->rbuff_size, s);
1982
1983 if (self->args[0]) {
1984 if (MATCH(s, "*")) {
1985 /* cancel the authentication exchange */
1986 return -1;
1987 }
1988
1989 if (MATCH(p_string_str(self->args[0]),"LOGIN")) {
1990 uint64_t len;
1991 char *tmp = dm_base64_decode(s, &len);
1992 if (! tmp) {
1993 return -1;
1994 }
1995 self->args[self->args_idx++] = p_string_new(self->pool, tmp);
1996 g_free(tmp);
1997
1998 if (self->args_idx == 3) {
1999 /* got password */
2000 goto finalize;
2001 } else if (self->args_idx == 2) {
2002 /* got username, ask for password */
2003 dbmail_imap_session_prompt(self,"password");
2004 return 0;
2005 }
2006 } else if (MATCH(p_string_str(self->args[0]),"CRAM-MD5")) {
2007 if (self->args_idx == 1) {
2008 /* decode and store the response */
2009 if (! Cram_decode(self->ci->auth, s)) {
2010 Cram_free(&self->ci->auth);
2011 return -1;
2012 }
2013 self->args_idx++;
2014 goto finalize; // done
2015 }
2016 }
2017 }
2018
2019 for (i = 0; (i < max) && s[i] && (self->args_idx < MAX_ARGS - 1); i++) {
2020 /* check quotes */
2021 if ((s[i] == '"') && ((i > 0 && s[i - 1] != '\\') || i == 0)) {
2022 if (inquote) {
2023 /* quotation end, treat quoted string as argument */
2024 self->args[self->args_idx] = p_string_new(self->pool, "");
2025 p_string_append_len(self->args[self->args_idx], &s[quotestart + 1], i - quotestart - 1);
2026 TRACE(TRACE_DEBUG, "arg[%" PRIu64 "] [%s]", self->args_idx, p_string_str(self->args[self->args_idx]));
2027 self->args_idx++;
2028 inquote = 0;
2029 } else {
2030 inquote = 1;
2031 quotestart = i;
2032 }
2033 continue;
2034 }
2035
2036 if (inquote) continue;
2037
2038 //if strchr("[]()",s[i]) {
2039 if (s[i] == '(' || s[i] == ')' || s[i] == '[' || s[i] == ']') {
2040 switch (s[i]) {
2041 /* check parenthese structure */
2042 case ')':
2043
2044 if (paridx < 0 || parlist[paridx] != NORMPAR)
2045 paridx = -1;
2046 else {
2047 nnorm--;
2048 paridx--;
2049 }
2050
2051 break;
2052
2053 case ']':
2054
2055 if (paridx < 0 || parlist[paridx] != SQUAREPAR)
2056 paridx = -1;
2057 else {
2058 paridx--;
2059 nsquare--;
2060 }
2061
2062 break;
2063
2064 case '(':
2065
2066 parlist[++paridx] = NORMPAR;
2067 nnorm++;
2068
2069 break;
2070
2071 case '[':
2072
2073 parlist[++paridx] = SQUAREPAR;
2074 nsquare++;
2075
2076 break;
2077 }
2078
2079 if (paridx < 0) return -1; /* error in parenthesis structure */
2080
2081 /* add this parenthesis to the arg list and continue */
2082 self->args[self->args_idx] = p_string_new(self->pool, "");
2083 p_string_printf(self->args[self->args_idx], "%c", s[i]);
2084 TRACE(TRACE_DEBUG, "arg[%" PRIu64 "] [%s]", self->args_idx, p_string_str(self->args[self->args_idx]));
2085 self->args_idx++;
2086 continue;
2087 }
2088
2089 if (s[i] == ' ') continue;
2090
2091 /* check for {number}\0 */
2092 if (s[i] == '{') {
2093 unsigned long int octets = strtoul(&s[i + 1], &lastchar, 10);
2094
2095 /* only continue if the number is followed by '}\0' */
2096 if (
2097 (*lastchar == '}' && *(lastchar + 1) == '\0') ||
2098 (*lastchar == '+' && *(lastchar + 1) == '}' && *(lastchar + 2) == '\0')
2099 ) {
2100 Field_T maxsize;
2101 unsigned long maxoctets = 0;
2102 config_get_value("MAX_MESSAGE_SIZE", "IMAP", maxsize);
2103 if (strlen(maxsize)) {
2104 maxoctets = strtoul(maxsize, NULL, 0);
2105 TRACE(TRACE_DEBUG, "using MAX_MESSAGE_SIZE [%s -> %" PRIu64 " octets]",
2106 maxsize, (uint64_t)maxoctets);
2107 }
2108
2109 if ((maxoctets > 0) && (octets > maxoctets)) {
2110 dbmail_imap_session_buff_printf(self,
2111 "%s NO %s failed: message size too large\r\n",
2112 self->tag, self->command);
2113 self->command_state = TRUE;
2114 } else {
2115 self->ci->rbuff_size += octets;
2116 if (*lastchar == '}')
2117 dbmail_imap_session_buff_printf(self, "+ OK\r\n");
2118 }
2119 dbmail_imap_session_buff_flush(self);
2120 return 0;
2121 }
2122 }
2123 /* at an argument start now, walk on until next delimiter
2124 * and save argument
2125 */
2126 for (argstart = i; i < strlen(s) && !strchr(" []()", s[i]); i++) {
2127 if (s[i] == '"') {
2128 if (s[i - 1] == '\\')
2129 continue;
2130 else
2131 break;
2132 }
2133 }
2134
2135 self->args[self->args_idx] = p_string_new(self->pool, "");
2136 p_string_append_len(self->args[self->args_idx], &s[argstart], i - argstart);
2137 TRACE(TRACE_DEBUG, "arg[%" PRIu64 "] [%s]", self->args_idx, p_string_str(self->args[self->args_idx]));
2138 self->args_idx++;
2139 i--; /* walked one too far */
2140 }
2141
2142 if (paridx != 0) return -1; /* error in parenthesis structure */
2143
2144 finalize:
2145 if (self->args_idx == 1) {
2146 if (Capa_match(self->preauth_capa, "AUTH=LOGIN") && MATCH(p_string_str(self->args[0]),"LOGIN")) {
2147 TRACE(TRACE_DEBUG, "[%p] prompt for authenticate tokens", self);
2148
2149 /* ask for username */
2150 dbmail_imap_session_prompt(self,"username");
2151 return 0;
2152 } else if (Capa_match(self->preauth_capa, "AUTH=CRAM-MD5") && MATCH(p_string_str(self->args[0]),"CRAM-MD5")) {
2153 const gchar *s;
2154 gchar *t;
2155 self->ci->auth = Cram_new();
2156 s = Cram_getChallenge(self->ci->auth);
2157 t = (char *)g_base64_encode((const guchar *)s, strlen(s));
2158 dbmail_imap_session_buff_printf(self, "+ %s\r\n", t);
2159 dbmail_imap_session_buff_flush(self);
2160 g_free(t);
2161
2162 return 0;
2163 }
2164 }
2165
2166
2167 TRACE(TRACE_DEBUG, "[%p] tag: [%s], command: [%s], [%" PRIu64 "] args", self, self->tag, self->command, self->args_idx);
2168 self->args[self->args_idx] = NULL; /* terminate */
2169
2170 #ifdef DEBUG
2171 for (i = 0; i<=self->args_idx && self->args[i]; i++) {
2172 TRACE(TRACE_DEBUG, "[%p] arg[%d]: '%s'\n", self, i, p_string_str(self->args[i]));
2173 }
2174 #endif
2175 self->args_idx = 0;
2176
2177 return 1;
2178 }
2179
2180 #undef NOPAR
2181 #undef NORMPAR
2182 #undef RIGHTPAR
2183
2184
2185
2186