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 = #
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