1 /*
2 * libEtPan! -- a mail stuff library
3 *
4 * Copyright (C) 2001, 2005 - DINH Viet Hoa
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the libEtPan! project nor the names of its
16 * contributors may be used to endorse or promote products derived
17 * from this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*
33 * $Id: maildirdriver.c,v 1.17 2008/02/17 13:13:26 hoa Exp $
34 */
35
36
37 /*
38 flags directory MUST be kept so that we can have other flags
39 than standards
40 */
41
42 #ifdef HAVE_CONFIG_H
43 # include <config.h>
44 #endif
45
46 #include "maildirdriver.h"
47
48 #include <stdio.h>
49 #include <sys/types.h>
50 #ifdef WIN32
51 # include "win_etpan.h"
52 #else
53 # include <dirent.h>
54 # include <unistd.h>
55 # include <sys/mman.h>
56 #endif
57 #include <sys/stat.h>
58 #include <ctype.h>
59 #include <fcntl.h>
60 #include <stdlib.h>
61 #include <string.h>
62
63 #include "maildir.h"
64 #include "maildriver_tools.h"
65 #include "maildirdriver_message.h"
66 #include "maildirdriver_tools.h"
67 #include "mailmessage.h"
68 #include "generic_cache.h"
69
70 static int initialize(mailsession * session);
71
72 static void uninitialize(mailsession * session);
73
74 static int connect_path(mailsession * session, const char * path);
75
76 static int logout(mailsession * session);
77
78 static int expunge_folder(mailsession * session);
79
80 static int status_folder(mailsession * session, const char * mb,
81 uint32_t * result_messages, uint32_t * result_recent,
82 uint32_t * result_unseen);
83
84 static int recent_number(mailsession * session, const char * mb,
85 uint32_t * result);
86
87 static int unseen_number(mailsession * session, const char * mb,
88 uint32_t * result);
89
90 static int messages_number(mailsession * session, const char * mb,
91 uint32_t * result);
92
93 static int append_message(mailsession * session,
94 const char * message, size_t size);
95
96 static int append_message_flags(mailsession * session,
97 const char * message, size_t size, struct mail_flags * flags);
98
99 static int get_messages_list(mailsession * session,
100 struct mailmessage_list ** result);
101
102 static int get_envelopes_list(mailsession * session,
103 struct mailmessage_list * env_list);
104
105 static int check_folder(mailsession * session);
106
107 static int get_message_by_uid(mailsession * session,
108 const char * uid, mailmessage ** result);
109
110 static mailsession_driver local_maildir_session_driver = {
111 /* sess_name */ "maildir",
112
113 /* sess_initialize */ initialize,
114 /* sess_uninitialize */ uninitialize,
115
116 /* sess_parameters */ NULL,
117
118 /* sess_connect_stream */ NULL,
119 /* sess_connect_path */ connect_path,
120 /* sess_starttls */ NULL,
121 /* sess_login */ NULL,
122 /* sess_logout */ logout,
123 /* sess_noop */ NULL,
124
125 /* sess_build_folder_name */ NULL,
126 /* sess_create_folder */ NULL,
127 /* sess_delete_folder */ NULL,
128 /* sess_rename_folder */ NULL,
129 /* sess_check_folder */ check_folder,
130 /* sess_examine_folder */ NULL,
131 /* sess_select_folder */ NULL,
132 /* sess_expunge_folder */ expunge_folder,
133 /* sess_status_folder */ status_folder,
134 /* sess_messages_number */ messages_number,
135 /* sess_recent_number */ recent_number,
136 /* sess_unseen_number */ unseen_number,
137 /* sess_list_folders */ NULL,
138 /* sess_lsub_folders */ NULL,
139 /* sess_subscribe_folder */ NULL,
140 /* sess_unsubscribe_folder */ NULL,
141
142 /* sess_append_message */ append_message,
143 /* sess_append_message_flags */ append_message_flags,
144 /* sess_copy_message */ NULL,
145 /* sess_move_message */ NULL,
146
147 /* sess_get_message */ NULL,
148 /* sess_get_message_by_uid */ get_message_by_uid,
149
150 /* sess_get_messages_list */ get_messages_list,
151 /* sess_get_envelopes_list */ get_envelopes_list,
152 /* sess_remove_message */ NULL,
153 #if 0
154 /* sess_search_messages */ maildriver_generic_search_messages,
155 #endif
156 /* sess_login_sasl */ NULL
157 };
158
159 mailsession_driver * maildir_session_driver = &local_maildir_session_driver;
160
161
162 static int flags_store_process(struct maildir * md,
163 struct mail_flags_store * flags_store);
164
165
get_data(mailsession * session)166 static inline struct maildir_session_state_data * get_data(mailsession * session)
167 {
168 return session->sess_data;
169 }
170
get_maildir_session(mailsession * session)171 static struct maildir * get_maildir_session(mailsession * session)
172 {
173 return get_data(session)->md_session;
174 }
175
initialize(mailsession * session)176 static int initialize(mailsession * session)
177 {
178 struct maildir_session_state_data * data;
179
180 data = malloc(sizeof(* data));
181 if (data == NULL)
182 goto err;
183
184 data->md_session = NULL;
185
186 data->md_flags_store = mail_flags_store_new();
187 if (data->md_flags_store == NULL)
188 goto free;
189
190 session->sess_data = data;
191
192 return MAIL_NO_ERROR;
193
194 free:
195 free(data);
196 err:
197 return MAIL_ERROR_MEMORY;
198 }
199
uninitialize(mailsession * session)200 static void uninitialize(mailsession * session)
201 {
202 struct maildir_session_state_data * data;
203
204 data = get_data(session);
205
206 if (data->md_session != NULL)
207 flags_store_process(data->md_session, data->md_flags_store);
208
209 mail_flags_store_free(data->md_flags_store);
210 if (data->md_session != NULL)
211 maildir_free(data->md_session);
212
213 free(data);
214
215 session->sess_data = NULL;
216 }
217
218
connect_path(mailsession * session,const char * path)219 static int connect_path(mailsession * session, const char * path)
220 {
221 struct maildir * md;
222 int res;
223 int r;
224
225 if (get_maildir_session(session) != NULL) {
226 res = MAIL_ERROR_BAD_STATE;
227 goto err;
228 }
229
230 md = maildir_new(path);
231 if (md == NULL) {
232 res = MAIL_ERROR_MEMORY;
233 goto err;
234 }
235
236 r = maildir_update(md);
237 if (r != MAILDIR_NO_ERROR) {
238 res = maildirdriver_maildir_error_to_mail_error(r);
239 goto free;
240 }
241
242 get_data(session)->md_session = md;
243
244 return MAIL_NO_ERROR;
245
246 free:
247 maildir_free(md);
248 err:
249 return res;
250 }
251
logout(mailsession * session)252 static int logout(mailsession * session)
253 {
254 struct maildir * md;
255
256 check_folder(session);
257
258 md = get_maildir_session(session);
259 if (md == NULL)
260 return MAIL_ERROR_BAD_STATE;
261
262 maildir_free(md);
263 get_data(session)->md_session = NULL;
264
265 return MAIL_NO_ERROR;
266 }
267
268 /* folders operations */
269
status_folder(mailsession * session,const char * mb,uint32_t * result_messages,uint32_t * result_recent,uint32_t * result_unseen)270 static int status_folder(mailsession * session, const char * mb,
271 uint32_t * result_messages, uint32_t * result_recent,
272 uint32_t * result_unseen)
273 {
274 int r;
275 struct maildir * md;
276 unsigned int i;
277 uint32_t messages;
278 uint32_t recent;
279 uint32_t unseen;
280
281 check_folder(session);
282
283 md = get_maildir_session(session);
284 if (md == NULL)
285 return MAIL_ERROR_BAD_STATE;
286
287 r = maildir_update(md);
288 if (r != MAILDIR_NO_ERROR)
289 return maildirdriver_maildir_error_to_mail_error(r);
290
291 messages = 0;
292 recent = 0;
293 unseen = 0;
294 for(i = 0 ; i < carray_count(md->mdir_msg_list) ; i ++) {
295 struct maildir_msg * msg;
296
297 msg = carray_get(md->mdir_msg_list, i);
298 if ((msg->msg_flags & MAILDIR_FLAG_NEW) != 0)
299 recent ++;
300 if ((msg->msg_flags & MAILDIR_FLAG_SEEN) == 0)
301 unseen ++;
302 messages ++;
303 }
304
305 * result_messages = messages;
306 * result_recent = recent;
307 * result_unseen = unseen;
308
309 return MAIL_NO_ERROR;
310 }
311
messages_number(mailsession * session,const char * mb,uint32_t * result)312 static int messages_number(mailsession * session, const char * mb,
313 uint32_t * result)
314 {
315 struct maildir * md;
316 int r;
317
318 md = get_maildir_session(session);
319 if (md == NULL)
320 return MAIL_ERROR_BAD_STATE;
321
322 r = maildir_update(md);
323 if (r != MAILDIR_NO_ERROR)
324 return maildirdriver_maildir_error_to_mail_error(r);
325
326 * result = carray_count(md->mdir_msg_list);
327
328 return MAIL_NO_ERROR;
329 }
330
unseen_number(mailsession * session,const char * mb,uint32_t * result)331 static int unseen_number(mailsession * session, const char * mb,
332 uint32_t * result)
333 {
334 uint32_t messages;
335 uint32_t recent;
336 uint32_t unseen;
337 int r;
338
339 r = status_folder(session, mb, &messages, &recent, &unseen);
340 if (r != MAIL_NO_ERROR)
341 return r;
342
343 * result = unseen;
344
345 return MAIL_NO_ERROR;
346 }
347
recent_number(mailsession * session,const char * mb,uint32_t * result)348 static int recent_number(mailsession * session, const char * mb,
349 uint32_t * result)
350 {
351 uint32_t messages;
352 uint32_t recent;
353 uint32_t unseen;
354 int r;
355
356 messages = 0;
357 recent = 0;
358 unseen = 0;
359 r = status_folder(session, mb, &messages, &recent, &unseen);
360 if (r != MAIL_NO_ERROR)
361 return r;
362
363 * result = recent;
364
365 return MAIL_NO_ERROR;
366 }
367
368
369 /* messages operations */
370
append_message(mailsession * session,const char * message,size_t size)371 static int append_message(mailsession * session,
372 const char * message, size_t size)
373 {
374 #if 0
375 struct maildir * md;
376 int r;
377
378 md = get_maildir_session(session);
379 if (md == NULL)
380 return MAIL_ERROR_BAD_STATE;
381
382 r = maildir_message_add(md, message, size);
383 if (r != MAILDIR_NO_ERROR)
384 return maildirdriver_maildir_error_to_mail_error(r);
385
386 return MAIL_NO_ERROR;
387 #endif
388
389 return append_message_flags(session, message, size, NULL);
390 }
391
append_message_flags(mailsession * session,const char * message,size_t size,struct mail_flags * flags)392 static int append_message_flags(mailsession * session,
393 const char * message, size_t size, struct mail_flags * flags)
394 {
395 struct maildir * md;
396 int r;
397 char uid[PATH_MAX];
398 chashdatum key;
399 chashdatum value;
400 uint32_t md_flags;
401
402 md = get_maildir_session(session);
403 if (md == NULL)
404 return MAIL_ERROR_BAD_STATE;
405
406 r = maildir_message_add_uid(md, message, size,
407 uid, sizeof(uid));
408 if (r != MAILDIR_NO_ERROR)
409 return maildirdriver_maildir_error_to_mail_error(r);
410
411 if (flags == NULL)
412 goto exit;
413
414 key.data = uid;
415 key.len = (unsigned int) strlen(uid);
416 r = chash_get(md->mdir_msg_hash, &key, &value);
417 if (r < 0)
418 goto exit;
419
420 md_flags = maildirdriver_flags_to_maildir_flags(flags->fl_flags);
421
422 r = maildir_message_change_flags(md, uid, md_flags);
423 if (r != MAILDIR_NO_ERROR)
424 goto exit;
425
426 return MAIL_NO_ERROR;
427
428 exit:
429 return MAIL_NO_ERROR;
430 }
431
get_messages_list(mailsession * session,struct mailmessage_list ** result)432 static int get_messages_list(mailsession * session,
433 struct mailmessage_list ** result)
434 {
435 struct maildir * md;
436 int r;
437 struct mailmessage_list * env_list;
438 int res;
439
440 md = get_maildir_session(session);
441 if (md == NULL)
442 return MAIL_ERROR_BAD_STATE;
443
444 r = maildir_update(md);
445 if (r != MAILDIR_NO_ERROR) {
446 res = maildirdriver_maildir_error_to_mail_error(r);
447 goto err;
448 }
449
450 r = maildir_get_messages_list(session, md,
451 maildir_message_driver, &env_list);
452 if (r != MAILDIR_NO_ERROR) {
453 res = r;
454 goto free_list;
455 }
456
457 * result = env_list;
458
459 return MAIL_NO_ERROR;
460
461 free_list:
462 mailmessage_list_free(env_list);
463 err:
464 return res;
465 }
466
get_envelopes_list(mailsession * session,struct mailmessage_list * env_list)467 static int get_envelopes_list(mailsession * session,
468 struct mailmessage_list * env_list)
469 {
470 int r;
471 struct maildir * md;
472 unsigned int i;
473 int res;
474
475 check_folder(session);
476
477 md = get_maildir_session(session);
478 if (md == NULL) {
479 res = MAIL_ERROR_BAD_STATE;
480 goto err;
481 }
482
483 r = maildir_update(md);
484 if (r != MAILDIR_NO_ERROR) {
485 res = maildirdriver_maildir_error_to_mail_error(r);
486 goto err;
487 }
488
489 r = maildriver_generic_get_envelopes_list(session, env_list);
490 if (r != MAIL_NO_ERROR) {
491 res = r;
492 goto err;
493 }
494
495 for(i = 0 ; i < carray_count(env_list->msg_tab) ; i++) {
496 struct maildir_msg * md_msg;
497 mailmessage * msg;
498 uint32_t driver_flags;
499 clist * ext;
500 chashdatum key;
501 chashdatum value;
502
503 msg = carray_get(env_list->msg_tab, i);
504
505 key.data = msg->msg_uid;
506 key.len = (unsigned int) strlen(msg->msg_uid);
507 r = chash_get(md->mdir_msg_hash, &key, &value);
508 if (r < 0)
509 continue;
510
511 md_msg = value.data;
512
513 driver_flags = maildirdriver_maildir_flags_to_flags(md_msg->msg_flags);
514
515 if (msg->msg_flags == NULL) {
516 ext = clist_new();
517 if (ext == NULL) {
518 res = MAIL_ERROR_MEMORY;
519 continue;
520 }
521
522 msg->msg_flags = mail_flags_new(driver_flags, ext);
523 if (msg->msg_flags == NULL) {
524 clist_free(ext);
525 res = MAIL_ERROR_MEMORY;
526 continue;
527 }
528
529 if ((md_msg->msg_flags & MAILDIR_FLAG_NEW) != 0) {
530 mail_flags_store_set(get_data(session)->md_flags_store, msg);
531 }
532 }
533 else {
534 msg->msg_flags->fl_flags &= MAIL_FLAG_FORWARDED;
535 msg->msg_flags->fl_flags |= driver_flags;
536 }
537 }
538
539 return MAIL_NO_ERROR;
540
541 err:
542 return res;
543 }
544
545
expunge_folder(mailsession * session)546 static int expunge_folder(mailsession * session)
547 {
548 unsigned int i;
549 int r;
550 int res;
551 struct maildir * md;
552
553 check_folder(session);
554
555 md = get_maildir_session(session);
556 if (md == NULL)
557 return MAIL_ERROR_BAD_STATE;
558
559 r = maildir_update(md);
560 if (r != MAILDIR_NO_ERROR) {
561 res = maildirdriver_maildir_error_to_mail_error(r);
562 goto err;
563 }
564
565 for(i = 0 ; i < carray_count(md->mdir_msg_list) ; i++) {
566 struct maildir_msg * md_msg;
567
568 md_msg = carray_get(md->mdir_msg_list, i);
569
570 if ((md_msg->msg_flags & MAILDIR_FLAG_TRASHED) != 0)
571 maildir_message_remove(md, md_msg->msg_uid);
572 }
573
574 return MAIL_NO_ERROR;
575
576 err:
577 return res;
578 }
579
580
flags_store_process(struct maildir * md,struct mail_flags_store * flags_store)581 static int flags_store_process(struct maildir * md,
582 struct mail_flags_store * flags_store)
583 {
584 unsigned int i;
585
586 if (carray_count(flags_store->fls_tab) == 0)
587 return MAIL_NO_ERROR;
588
589 for(i = 0 ; i < carray_count(flags_store->fls_tab) ; i ++) {
590 mailmessage * msg;
591 uint32_t md_flags;
592
593 msg = carray_get(flags_store->fls_tab, i);
594 md_flags = maildirdriver_flags_to_maildir_flags(msg->msg_flags->fl_flags);
595 md_flags &= ~MAILDIR_FLAG_NEW;
596
597 maildir_message_change_flags(md, msg->msg_uid, md_flags);
598 }
599
600 mail_flags_store_clear(flags_store);
601
602 return MAIL_NO_ERROR;
603 }
604
605
606
check_folder(mailsession * session)607 static int check_folder(mailsession * session)
608 {
609 struct mail_flags_store * flags_store;
610 struct maildir_session_state_data * data;
611 struct maildir * md;
612
613 md = get_maildir_session(session);
614 if (md == NULL)
615 return MAIL_ERROR_BAD_STATE;
616
617 data = get_data(session);
618 flags_store = data->md_flags_store;
619
620 return flags_store_process(md, flags_store);
621 }
622
get_message_by_uid(mailsession * session,const char * uid,mailmessage ** result)623 static int get_message_by_uid(mailsession * session,
624 const char * uid, mailmessage ** result)
625 {
626 int r;
627 struct maildir * md;
628 int res;
629 mailmessage * msg;
630 char * msg_filename;
631 struct stat stat_info;
632
633 md = get_maildir_session(session);
634
635 /* update maildir data */
636
637 r = maildir_update(md);
638 if (r != MAILDIR_NO_ERROR) {
639 res = maildirdriver_maildir_error_to_mail_error(r);
640 goto err;
641 }
642
643 msg_filename = maildir_message_get(md, uid);
644 if (msg_filename == NULL) {
645 res = MAIL_ERROR_INVAL;
646 goto err;
647 }
648
649 r = stat(msg_filename, &stat_info);
650 free(msg_filename);
651 if (r < 0) {
652 res = MAIL_ERROR_INVAL;
653 goto err;
654 }
655
656 /* create message */
657
658 msg = mailmessage_new();
659 if (msg == NULL) {
660 res = MAIL_ERROR_MEMORY;
661 goto err;
662 }
663
664 r = mailmessage_init(msg, session, maildir_message_driver,
665 0, stat_info.st_size);
666 if (r != MAIL_NO_ERROR) {
667 mailmessage_free(msg);
668 res = r;
669 goto err;
670 }
671
672 msg->msg_uid = strdup(uid);
673 if (msg->msg_uid == NULL) {
674 mailmessage_free(msg);
675 res = r;
676 goto err;
677 }
678
679 * result = msg;
680
681 return MAIL_NO_ERROR;
682
683 err:
684 return res;
685 }
686