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