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: mboxdriver.c,v 1.45 2008/02/17 13:13:26 hoa Exp $
34  */
35 
36 #ifdef HAVE_CONFIG_H
37 #	include <config.h>
38 #endif
39 
40 #include "mboxdriver.h"
41 
42 #include <stdio.h>
43 #include <string.h>
44 #include <sys/types.h>
45 #ifdef WIN32
46 #	include "win_etpan.h"
47 #else
48 #	include <dirent.h>
49 #	include <unistd.h>
50 #endif
51 #include <sys/stat.h>
52 #include <ctype.h>
53 #include <stdlib.h>
54 #ifndef WIN32
55 #include <sys/times.h>
56 #endif
57 
58 #include "mail.h"
59 #include "maildriver_tools.h"
60 #include "mailmbox.h"
61 #include "mboxdriver_tools.h"
62 #include "maildriver.h"
63 #include "carray.h"
64 #include "mboxdriver_message.h"
65 #include "mailmessage.h"
66 
67 static int mboxdriver_initialize(mailsession * session);
68 
69 static void mboxdriver_uninitialize(mailsession * session);
70 
71 static int mboxdriver_parameters(mailsession * session,
72 				 int id, void * value);
73 
74 static int mboxdriver_connect_path(mailsession * session, const char * path);
75 
76 static int mboxdriver_logout(mailsession * session);
77 
78 static int mboxdriver_expunge_folder(mailsession * session);
79 
80 static int mboxdriver_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 mboxdriver_messages_number(mailsession * session, const char * mb,
85 				      uint32_t * result);
86 
87 static int mboxdriver_append_message(mailsession * session,
88 				     const char * message, size_t size);
89 
90 static int mboxdriver_append_message_flags(mailsession * session,
91     const char * message, size_t size, struct mail_flags * flags);
92 
93 static int mboxdriver_get_messages_list(mailsession * session,
94 					struct mailmessage_list ** result);
95 
96 static int
97 mboxdriver_get_envelopes_list(mailsession * session,
98 			      struct mailmessage_list * env_list);
99 
100 static int mboxdriver_remove_message(mailsession * session, uint32_t num);
101 
102 static int mboxdriver_get_message(mailsession * session,
103 				  uint32_t num, mailmessage ** result);
104 
105 static int mboxdriver_get_message_by_uid(mailsession * session,
106     const char * uid,
107     mailmessage ** result);
108 
109 static mailsession_driver local_mbox_session_driver = {
110   /* sess_name */ "mbox",
111 
112   /* sess_initialize */ mboxdriver_initialize,
113   /* sess_uninitialize */ mboxdriver_uninitialize,
114 
115   /* sess_parameters */ mboxdriver_parameters,
116 
117   /* sess_connect_stream */ NULL,
118 
119   /* sess_connect_path */ mboxdriver_connect_path,
120   /* sess_starttls */ NULL,
121   /* sess_login */ NULL,
122   /* sess_logout */ mboxdriver_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 */ NULL,
130   /* sess_examine_folder */ NULL,
131   /* sess_select_folder */ NULL,
132   /* sess_expunge_folder */ mboxdriver_expunge_folder,
133   /* sess_status_folder */ mboxdriver_status_folder,
134   /* sess_messages_number */ mboxdriver_messages_number,
135   /* sess_recent_number */ mboxdriver_messages_number,
136   /* sess_unseen_number */ mboxdriver_messages_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 */ mboxdriver_append_message,
143   /* sess_append_message_flags */ mboxdriver_append_message_flags,
144   /* sess_copy_message */ NULL,
145   /* sess_move_message */ NULL,
146 
147   /* sess_get_message */ mboxdriver_get_message,
148   /* sess_get_message_by_uid */ mboxdriver_get_message_by_uid,
149 
150   /* sess_get_messages_list */ mboxdriver_get_messages_list,
151   /* sess_get_envelopes_list */ mboxdriver_get_envelopes_list,
152   /* sess_remove_message */ mboxdriver_remove_message,
153 #if 0
154   /* sess_search_messages */ maildriver_generic_search_messages,
155 #endif
156   /* sess_login_sasl */ NULL
157 };
158 
159 mailsession_driver * mbox_session_driver = &local_mbox_session_driver;
160 
get_data(mailsession * session)161 static inline struct mbox_session_state_data * get_data(mailsession * session)
162 {
163   return session->sess_data;
164 }
165 
get_mbox_session(mailsession * session)166 static inline struct mailmbox_folder * get_mbox_session(mailsession * session)
167 {
168   return get_data(session)->mbox_folder;
169 }
170 
mboxdriver_initialize(mailsession * session)171 static int mboxdriver_initialize(mailsession * session)
172 {
173   struct mbox_session_state_data * data;
174 
175   data = malloc(sizeof(* data));
176   if (data == NULL)
177     goto err;
178 
179   data->mbox_folder = NULL;
180 
181   data->mbox_force_read_only = FALSE;
182   data->mbox_force_no_uid = TRUE;
183 
184   session->sess_data = data;
185 
186   return MAIL_NO_ERROR;
187 
188  err:
189   return MAIL_ERROR_MEMORY;
190 }
191 
free_state(struct mbox_session_state_data * mbox_data)192 static void free_state(struct mbox_session_state_data * mbox_data)
193 {
194   if (mbox_data->mbox_folder != NULL) {
195     mailmbox_done(mbox_data->mbox_folder);
196     mbox_data->mbox_folder = NULL;
197   }
198 }
199 
mboxdriver_uninitialize(mailsession * session)200 static void mboxdriver_uninitialize(mailsession * session)
201 {
202   struct mbox_session_state_data * data;
203 
204   data = get_data(session);
205 
206   free_state(data);
207 
208   free(data);
209 }
210 
mboxdriver_parameters(mailsession * session,int id,void * value)211 static int mboxdriver_parameters(mailsession * session,
212 				 int id, void * value)
213 {
214   struct mbox_session_state_data * data;
215 
216   data = get_data(session);
217 
218   switch (id) {
219   case MBOXDRIVER_SET_READ_ONLY:
220     {
221       int * param;
222 
223       param = value;
224 
225       data->mbox_force_read_only = * param;
226       return MAIL_NO_ERROR;
227     }
228 
229   case MBOXDRIVER_SET_NO_UID:
230     {
231       int * param;
232 
233       param = value;
234 
235       data->mbox_force_no_uid = * param;
236       return MAIL_NO_ERROR;
237     }
238   }
239 
240   return MAIL_ERROR_INVAL;
241 }
242 
243 
mboxdriver_connect_path(mailsession * session,const char * path)244 static int mboxdriver_connect_path(mailsession * session, const char * path)
245 {
246   struct mbox_session_state_data * mbox_data;
247   struct mailmbox_folder * folder;
248   int r;
249 
250   mbox_data = get_data(session);
251 
252   if (mbox_data->mbox_folder != NULL)
253     return MAIL_ERROR_BAD_STATE;
254 
255   r = mailmbox_init(path,
256 		    mbox_data->mbox_force_read_only,
257 		    mbox_data->mbox_force_no_uid,
258 		    0,
259 		    &folder);
260 
261   if (r != MAILMBOX_NO_ERROR)
262     return mboxdriver_mbox_error_to_mail_error(r);
263 
264   mbox_data->mbox_folder = folder;
265 
266   return MAIL_NO_ERROR;
267 }
268 
mboxdriver_logout(mailsession * session)269 static int mboxdriver_logout(mailsession * session)
270 {
271   struct mbox_session_state_data * mbox_data;
272 
273   mbox_data = get_data(session);
274 
275   if (mbox_data->mbox_folder == NULL)
276     return MAIL_ERROR_BAD_STATE;
277 
278   free_state(mbox_data);
279 
280   mbox_data->mbox_folder = NULL;
281 
282   return MAIL_NO_ERROR;
283 }
284 
mboxdriver_expunge_folder(mailsession * session)285 static int mboxdriver_expunge_folder(mailsession * session)
286 {
287   int r;
288   struct mbox_session_state_data * mbox_data;
289 
290   mbox_data = get_data(session);
291 
292   if (mbox_data->mbox_folder == NULL)
293     return MAIL_ERROR_BAD_STATE;
294 
295   r = mailmbox_expunge(mbox_data->mbox_folder);
296   if (r != MAILMBOX_NO_ERROR)
297     return mboxdriver_mbox_error_to_mail_error(r);
298 
299   return MAIL_NO_ERROR;
300 }
301 
mboxdriver_status_folder(mailsession * session,const char * mb,uint32_t * result_messages,uint32_t * result_recent,uint32_t * result_unseen)302 static int mboxdriver_status_folder(mailsession * session, const char * mb,
303     uint32_t * result_messages, uint32_t * result_recent,
304     uint32_t * result_unseen)
305 {
306   uint32_t count;
307   int r;
308 
309   r = mboxdriver_messages_number(session, mb, &count);
310   if (r != MAIL_NO_ERROR)
311     return r;
312 
313   * result_messages = count;
314   * result_recent = count;
315   * result_unseen = count;
316 
317   return MAIL_NO_ERROR;
318 }
319 
mboxdriver_messages_number(mailsession * session,const char * mb,uint32_t * result)320 static int mboxdriver_messages_number(mailsession * session, const char * mb,
321 				      uint32_t * result)
322 {
323   struct mailmbox_folder * folder;
324   int r;
325 
326   folder = get_mbox_session(session);
327   if (folder == NULL)
328     return MAIL_ERROR_STATUS;
329 
330   r = mailmbox_validate_read_lock(folder);
331   if (r != MAIL_NO_ERROR)
332     return r;
333 
334   mailmbox_read_unlock(folder);
335 
336   * result = carray_count(folder->mb_tab) - folder->mb_deleted_count;
337 
338   return MAILMBOX_NO_ERROR;
339 }
340 
341 /* messages operations */
342 
mboxdriver_append_message(mailsession * session,const char * message,size_t size)343 static int mboxdriver_append_message(mailsession * session,
344 				     const char * message, size_t size)
345 {
346   int r;
347   struct mailmbox_folder * folder;
348 
349   folder = get_mbox_session(session);
350   if (folder == NULL)
351     return MAIL_ERROR_APPEND;
352 
353   r = mailmbox_append_message(folder, message, size);
354 
355   switch (r) {
356   case MAILMBOX_ERROR_FILE:
357     return MAIL_ERROR_DISKSPACE;
358   default:
359     return mboxdriver_mbox_error_to_mail_error(r);
360   }
361 }
362 
mboxdriver_append_message_flags(mailsession * session,const char * message,size_t size,struct mail_flags * flags)363 static int mboxdriver_append_message_flags(mailsession * session,
364     const char * message, size_t size, struct mail_flags * flags)
365 {
366   return mboxdriver_append_message(session, message, size);
367 }
368 
mboxdriver_get_messages_list(mailsession * session,struct mailmessage_list ** result)369 static int mboxdriver_get_messages_list(mailsession * session,
370 					struct mailmessage_list ** result)
371 {
372   struct mailmbox_folder * folder;
373   int res;
374 
375   folder = get_mbox_session(session);
376   if (folder == NULL) {
377     res = MAIL_ERROR_BAD_STATE;
378     goto err;
379   }
380 
381   return mbox_get_messages_list(folder, session, mbox_message_driver, result);
382 
383  err:
384   return res;
385 }
386 
387 static int
mboxdriver_get_envelopes_list(mailsession * session,struct mailmessage_list * env_list)388 mboxdriver_get_envelopes_list(mailsession * session,
389 			      struct mailmessage_list * env_list)
390 {
391   struct mailmbox_folder * folder;
392   unsigned int i;
393   int r;
394   int res;
395 
396   folder = get_mbox_session(session);
397   if (folder == NULL) {
398     res = MAIL_ERROR_BAD_STATE;
399     goto err;
400   }
401 
402   r = mailmbox_validate_read_lock(folder);
403   if (r != MAILMBOX_NO_ERROR) {
404     res = mboxdriver_mbox_error_to_mail_error(r);
405     goto err;
406   }
407 
408   for(i = 0 ; i < carray_count(env_list->msg_tab) ; i ++) {
409     mailmessage * msg;
410     struct mailimf_fields * fields;
411     char * headers;
412     size_t headers_len;
413     size_t cur_token;
414 
415     msg = carray_get(env_list->msg_tab, i);
416     if (msg == NULL)
417       continue;
418 
419     if (msg->msg_fields != NULL)
420       continue;
421 
422     r = mailmbox_fetch_msg_headers_no_lock(folder,
423         msg->msg_index, &headers, &headers_len);
424     if (r != MAILMBOX_NO_ERROR) {
425       res = mboxdriver_mbox_error_to_mail_error(r);
426       goto unlock;
427     }
428 
429     cur_token = 0;
430     r = mailimf_envelope_fields_parse(headers, headers_len,
431 				      &cur_token, &fields);
432 
433     if (r != MAILIMF_NO_ERROR)
434       continue;
435 
436     msg->msg_fields = fields;
437   }
438 
439   mailmbox_read_unlock(folder);
440 
441   return MAIL_NO_ERROR;
442 
443  unlock:
444   mailmbox_read_unlock(folder);
445  err:
446   return res;
447 }
448 
449 
mboxdriver_remove_message(mailsession * session,uint32_t num)450 static int mboxdriver_remove_message(mailsession * session, uint32_t num)
451 {
452   int r;
453   struct mailmbox_folder * folder;
454 
455   folder = get_mbox_session(session);
456   if (folder == NULL)
457     return MAIL_ERROR_DELETE;
458 
459   r = mailmbox_delete_msg(folder, num);
460 
461   return mboxdriver_mbox_error_to_mail_error(r);
462 }
463 
mboxdriver_get_message(mailsession * session,uint32_t num,mailmessage ** result)464 static int mboxdriver_get_message(mailsession * session,
465 				  uint32_t num, mailmessage ** result)
466 {
467   mailmessage * msg_info;
468   int r;
469 
470   msg_info = mailmessage_new();
471   if (msg_info == NULL)
472     return MAIL_ERROR_MEMORY;
473 
474   r = mailmessage_init(msg_info, session, mbox_message_driver, num, 0);
475   if (r != MAIL_NO_ERROR) {
476     mailmessage_free(msg_info);
477     return r;
478   }
479 
480   * result = msg_info;
481 
482   return MAIL_NO_ERROR;
483 }
484 
mboxdriver_get_message_by_uid(mailsession * session,const char * uid,mailmessage ** result)485 static int mboxdriver_get_message_by_uid(mailsession * session,
486     const char * uid,
487     mailmessage ** result)
488 {
489   uint32_t num;
490   char * p;
491   chashdatum key;
492   chashdatum data;
493   struct mailmbox_msg_info * info;
494   struct mailmbox_folder * folder;
495   int r;
496 
497   if (uid == NULL)
498     return MAIL_ERROR_INVAL;
499 
500   num = (uint32_t) strtoul(uid, &p, 10);
501   if (p == uid || * p != '-')
502     return MAIL_ERROR_INVAL;
503 
504   folder = get_mbox_session(session);
505   if (folder == NULL)
506     return MAIL_ERROR_BAD_STATE;
507 
508   key.data = &num;
509   key.len = sizeof(num);
510 
511   r = chash_get(folder->mb_hash, &key, &data);
512   if (r == 0) {
513     char * body_len_p = p + 1;
514     size_t body_len;
515 
516     info = data.data;
517     /* Check if the cached message has the same UID */
518     body_len = strtoul(body_len_p, &p, 10);
519     if (p == body_len_p || * p != '\0')
520       return MAIL_ERROR_INVAL;
521 
522     if (body_len == info->msg_body_len)
523       return mboxdriver_get_message(session, num, result);
524   }
525 
526   return MAIL_ERROR_MSG_NOT_FOUND;
527 }
528