summaryrefslogtreecommitdiffabout
path: root/libetpan/src/low-level/mbox/mailmbox.c
Unidiff
Diffstat (limited to 'libetpan/src/low-level/mbox/mailmbox.c') (more/less context) (ignore whitespace changes)
-rw-r--r--libetpan/src/low-level/mbox/mailmbox.c1525
1 files changed, 1525 insertions, 0 deletions
diff --git a/libetpan/src/low-level/mbox/mailmbox.c b/libetpan/src/low-level/mbox/mailmbox.c
new file mode 100644
index 0000000..9937f3a
--- a/dev/null
+++ b/libetpan/src/low-level/mbox/mailmbox.c
@@ -0,0 +1,1525 @@
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$
34 */
35
36#include "mailmbox.h"
37
38#include <sys/file.h>
39#include <sys/stat.h>
40#include <unistd.h>
41#include <sys/mman.h>
42#include <fcntl.h>
43#include <time.h>
44#include <sys/types.h>
45#include <unistd.h>
46
47#include <string.h>
48#include <ctype.h>
49#include <stdlib.h>
50#include <stdio.h>
51
52#include "libetpan-config.h"
53
54#include "mmapstring.h"
55#include "mailmbox_parse.h"
56#include "maillock.h"
57
58/*
59 http://www.qmail.org/qmail-manual-html/man5/mbox.html
60 RFC 2076
61*/
62
63#define TMPDIR "/tmp"
64
65/* mbox is a file with a corresponding filename */
66
67#define UID_HEADER "X-LibEtPan-UID:"
68
69#ifndef TRUE
70#define TRUE 1
71#endif
72
73#ifndef FALSE
74#define FALSE 0
75#endif
76
77int mailmbox_write_lock(struct mailmbox_folder * folder)
78{
79 int r;
80
81 if (folder->mb_read_only)
82 return MAILMBOX_ERROR_READONLY;
83
84 r = maillock_write_lock(folder->mb_filename, folder->mb_fd);
85 if (r == 0)
86 return MAILMBOX_NO_ERROR;
87 else
88 return MAILMBOX_ERROR_FILE;
89}
90
91int mailmbox_write_unlock(struct mailmbox_folder * folder)
92{
93 int r;
94
95 r = maillock_write_unlock(folder->mb_filename, folder->mb_fd);
96 if (r == 0)
97 return MAILMBOX_NO_ERROR;
98 else
99 return MAILMBOX_ERROR_FILE;
100}
101
102int mailmbox_read_lock(struct mailmbox_folder * folder)
103{
104 int r;
105
106 r = maillock_read_lock(folder->mb_filename, folder->mb_fd);
107 if (r == 0)
108 return MAILMBOX_NO_ERROR;
109 else
110 return MAILMBOX_ERROR_FILE;
111}
112
113int mailmbox_read_unlock(struct mailmbox_folder * folder)
114{
115 int r;
116
117 r = maillock_read_unlock(folder->mb_filename, folder->mb_fd);
118 if (r == 0)
119 return MAILMBOX_NO_ERROR;
120 else
121 return MAILMBOX_ERROR_FILE;
122}
123
124
125/*
126 map the file into memory.
127 the file must be locked.
128*/
129
130int mailmbox_map(struct mailmbox_folder * folder)
131{
132 char * str;
133 struct stat buf;
134 int res;
135 int r;
136
137 r = stat(folder->mb_filename, &buf);
138 if (r < 0) {
139 res = MAILMBOX_ERROR_FILE;
140 goto err;
141 }
142
143 if (folder->mb_read_only)
144 str = (char *) mmap(0, buf.st_size, PROT_READ,
145 MAP_PRIVATE, folder->mb_fd, 0);
146 else
147 str = (char *) mmap(0, buf.st_size, PROT_READ | PROT_WRITE,
148 MAP_SHARED, folder->mb_fd, 0);
149 if (str == MAP_FAILED) {
150 res = MAILMBOX_ERROR_FILE;
151 goto err;
152 }
153
154 folder->mb_mapping = str;
155 folder->mb_mapping_size = buf.st_size;
156
157 return MAILMBOX_NO_ERROR;
158
159 err:
160 return res;
161}
162
163/*
164 unmap the file
165*/
166
167void mailmbox_unmap(struct mailmbox_folder * folder)
168{
169 munmap(folder->mb_mapping, folder->mb_mapping_size);
170 folder->mb_mapping = NULL;
171 folder->mb_mapping_size = 0;
172}
173
174void mailmbox_sync(struct mailmbox_folder * folder)
175{
176 msync(folder->mb_mapping, folder->mb_mapping_size, MS_SYNC);
177}
178
179void mailmbox_timestamp(struct mailmbox_folder * folder)
180{
181 int r;
182 struct stat buf;
183
184 r = stat(folder->mb_filename, &buf);
185 if (r < 0)
186 folder->mb_mtime = (time_t) -1;
187 else
188 folder->mb_mtime = buf.st_mtime;
189}
190
191/*
192 open the file
193*/
194
195int mailmbox_open(struct mailmbox_folder * folder)
196{
197 int fd;
198 int read_only;
199
200 fd = -1;
201 read_only = TRUE;
202
203 if (!folder->mb_read_only) {
204 read_only = FALSE;
205 fd = open(folder->mb_filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
206 }
207
208 if (folder->mb_read_only || (fd < 0)) {
209 read_only = TRUE;
210 fd = open(folder->mb_filename, O_RDONLY);
211 if (fd < 0)
212 return MAILMBOX_ERROR_FILE_NOT_FOUND;
213 }
214
215 folder->mb_fd = fd;
216 folder->mb_read_only = read_only;
217
218 return MAILMBOX_NO_ERROR;
219}
220
221/*
222 close the file
223*/
224
225void mailmbox_close(struct mailmbox_folder * folder)
226{
227 close(folder->mb_fd);
228 folder->mb_fd = -1;
229}
230
231
232static int mailmbox_validate_lock(struct mailmbox_folder * folder,
233 int (* custom_lock)(struct mailmbox_folder *),
234 int (* custom_unlock)(struct mailmbox_folder *))
235{
236 struct stat buf;
237 int res;
238 int r;
239
240 r = stat(folder->mb_filename, &buf);
241 if (r < 0) {
242 buf.st_mtime = (time_t) -1;
243 }
244
245 if ((buf.st_mtime != folder->mb_mtime) ||
246 ((size_t) buf.st_size != folder->mb_mapping_size)) {
247 int r;
248
249 mailmbox_unmap(folder);
250 mailmbox_close(folder);
251
252 r = mailmbox_open(folder);
253 if (r != MAILMBOX_NO_ERROR) {
254 res = r;
255 goto err;
256 }
257
258 r = custom_lock(folder);
259 if (r != MAILMBOX_NO_ERROR) {
260 res = r;
261 goto err;
262 }
263
264 r = mailmbox_map(folder);
265 if (r != MAILMBOX_NO_ERROR) {
266 res = r;
267 goto err_unlock;
268 }
269
270 r = mailmbox_parse(folder);
271 if (r != MAILMBOX_NO_ERROR) {
272 res = r;
273 goto err_unlock;
274 }
275
276 folder->mb_mtime = buf.st_mtime;
277
278 return MAILMBOX_NO_ERROR;
279 }
280 else {
281 r = custom_lock(folder);
282 if (r != MAILMBOX_NO_ERROR) {
283 res = r;
284 goto err;
285 }
286 }
287
288 return MAILMBOX_NO_ERROR;
289
290 err_unlock:
291 custom_unlock(folder);
292 err:
293 return res;
294}
295
296
297int mailmbox_validate_write_lock(struct mailmbox_folder * folder)
298{
299 return mailmbox_validate_lock(folder,
300 mailmbox_write_lock,
301 mailmbox_write_unlock);
302}
303
304
305int mailmbox_validate_read_lock(struct mailmbox_folder * folder)
306{
307 return mailmbox_validate_lock(folder,
308 mailmbox_read_lock,
309 mailmbox_read_unlock);
310}
311
312
313/* ********************************************************************** */
314/* append messages */
315
316#define MAX_FROM_LINE_SIZE 256
317
318static inline size_t get_line(const char * line, size_t length,
319 const char ** pnext_line, size_t * pcount)
320{
321 size_t count;
322
323 count = 0;
324
325 while (1) {
326 if (length == 0)
327 break;
328
329 if (* line == '\r') {
330 line ++;
331
332 count ++;
333 length --;
334
335 if (length > 0) {
336 if (* line == '\n') {
337 line ++;
338
339 count ++;
340 length --;
341
342 break;
343 }
344 }
345 }
346 else if (* line == '\n') {
347 line ++;
348
349 count ++;
350 length --;
351
352 break;
353 }
354 else {
355 line ++;
356 length --;
357 count ++;
358 }
359 }
360
361 * pnext_line = line;
362 * pcount = count;
363
364 return count;
365}
366
367/*
368 TODO : should strip \r\n if any
369 see also in write_fixed_line
370*/
371
372static inline size_t get_fixed_line_size(const char * line, size_t length,
373 const char ** pnext_line, size_t * pcount,
374 size_t * pfixed_count)
375{
376 size_t count;
377 const char * next_line;
378 size_t fixed_count;
379
380 if (!get_line(line, length, &next_line, &count))
381 return 0;
382
383 fixed_count = count;
384 if (count >= 5) {
385 if (line[0] == 'F') {
386 if (strncmp(line, "From ", 5) == 0)
387 fixed_count ++;
388 }
389 }
390
391 * pnext_line = next_line;
392 * pcount = count;
393 * pfixed_count = fixed_count;
394
395 return count;
396}
397
398static size_t get_fixed_message_size(const char * message, size_t size,
399 uint32_t uid, int force_no_uid)
400{
401 size_t fixed_size;
402 size_t cur_token;
403 size_t left;
404 const char * next;
405 const char * cur;
406 int end;
407 int r;
408 uint32_t tmp_uid;
409
410 cur_token = 0;
411
412 fixed_size = 0;
413
414 /* headers */
415
416 end = FALSE;
417 while (!end) {
418 size_t begin;
419 int ignore;
420
421 ignore = FALSE;
422 begin = cur_token;
423 if (cur_token + strlen(UID_HEADER) <= size) {
424 if (message[cur_token] == 'X') {
425 if (strncasecmp(message + cur_token, UID_HEADER,
426 strlen(UID_HEADER)) == 0) {
427 ignore = TRUE;
428 }
429 }
430 }
431
432 r = mailimf_ignore_field_parse(message, size, &cur_token);
433 switch (r) {
434 case MAILIMF_NO_ERROR:
435 if (!ignore)
436 fixed_size += cur_token - begin;
437 break;
438 case MAILIMF_ERROR_PARSE:
439 default:
440 end = TRUE;
441 break;
442 }
443 }
444
445 if (!force_no_uid) {
446 /* UID header */
447
448 fixed_size += strlen(UID_HEADER " \r\n");
449
450 tmp_uid = uid;
451 while (tmp_uid >= 10) {
452 tmp_uid /= 10;
453 fixed_size ++;
454 }
455 fixed_size ++;
456 }
457
458 /* body */
459
460 left = size - cur_token;
461 next = message + cur_token;
462 while (left > 0) {
463 size_t count;
464 size_t fixed_count;
465
466 cur = next;
467
468 if (!get_fixed_line_size(cur, left, &next, &count, &fixed_count))
469 break;
470
471 fixed_size += fixed_count;
472 left -= count;
473 }
474
475 return fixed_size;
476}
477
478static inline char * write_fixed_line(char * str,
479 const char * line, size_t length,
480 const char ** pnext_line, size_t * pcount)
481{
482 size_t count;
483 const char * next_line;
484
485 if (!get_line(line, length, &next_line, &count))
486 return str;
487
488 if (count >= 5) {
489 if (line[0] == 'F') {
490 if (strncmp(line, "From ", 5) == 0) {
491 * str = '>';
492 str ++;
493 }
494 }
495 }
496
497 memcpy(str, line, count);
498
499 * pnext_line = next_line;
500 * pcount = count;
501 str += count;
502
503 return str;
504}
505
506static char * write_fixed_message(char * str,
507 const char * message, size_t size,
508 uint32_t uid, int force_no_uid)
509{
510 size_t fixed_size;
511 size_t cur_token;
512 size_t left;
513 int end;
514 int r;
515 const char * cur_src;
516 size_t numlen;
517
518 cur_token = 0;
519
520 fixed_size = 0;
521
522 /* headers */
523
524 end = FALSE;
525 while (!end) {
526 size_t begin;
527 int ignore;
528
529 ignore = FALSE;
530 begin = cur_token;
531 if (cur_token + strlen(UID_HEADER) <= size) {
532 if (message[cur_token] == 'X') {
533 if (strncasecmp(message + cur_token, UID_HEADER,
534 strlen(UID_HEADER)) == 0) {
535 ignore = TRUE;
536 }
537 }
538 }
539
540 r = mailimf_ignore_field_parse(message, size, &cur_token);
541 switch (r) {
542 case MAILIMF_NO_ERROR:
543 if (!ignore) {
544 memcpy(str, message + begin, cur_token - begin);
545 str += cur_token - begin;
546 }
547 break;
548 case MAILIMF_ERROR_PARSE:
549 default:
550 end = TRUE;
551 break;
552 }
553 }
554
555 if (!force_no_uid) {
556 /* UID header */
557
558 memcpy(str, UID_HEADER " ", strlen(UID_HEADER " "));
559 str += strlen(UID_HEADER " ");
560 numlen = snprintf(str, 20, "%i\r\n", uid);
561 str += numlen;
562 }
563
564 /* body */
565
566 cur_src = message + cur_token;
567 left = size - cur_token;
568 while (left > 0) {
569 size_t count;
570 const char * next;
571
572 str = write_fixed_line(str, cur_src, left, &next, &count);
573
574 cur_src = next;
575 left -= count;
576 }
577
578 return str;
579}
580
581#define DEFAULT_FROM_LINE "From - Wed Jun 30 21:49:08 1993\n"
582
583int
584mailmbox_append_message_list_no_lock(struct mailmbox_folder * folder,
585 carray * append_tab)
586{
587 size_t extra_size;
588 int r;
589 char from_line[MAX_FROM_LINE_SIZE] = DEFAULT_FROM_LINE;
590 struct tm time_info;
591 time_t date;
592 int res;
593 size_t old_size;
594 char * str;
595 unsigned int i;
596 size_t from_size;
597 size_t maxuid;
598 size_t left;
599 size_t crlf_count;
600
601 if (folder->mb_read_only) {
602 res = MAILMBOX_ERROR_READONLY;
603 goto err;
604 }
605
606 date = time(NULL);
607 from_size = strlen(DEFAULT_FROM_LINE);
608 if (localtime_r(&date, &time_info) != NULL)
609 from_size = strftime(from_line, MAX_FROM_LINE_SIZE, "From - %c\n", &time_info);
610
611 maxuid = /* */ folder->mb_max_uid;
612
613 extra_size = 0;
614 for(i = 0 ; i < carray_count(append_tab) ; i ++) {
615 struct mailmbox_append_info * info;
616
617 info = carray_get(append_tab, i);
618 extra_size += from_size;
619 extra_size += get_fixed_message_size(info->ai_message, info->ai_size,
620 folder->mb_max_uid + i + 1,
621 folder->mb_no_uid);
622 extra_size += 2; /* CR LF */
623
624 info->ai_uid = folder->mb_max_uid + i + 1;
625 }
626
627 left = folder->mb_mapping_size;
628 crlf_count = 0;
629 while (left >= 1) {
630 if (folder->mb_mapping[left - 1] == '\n') {
631 crlf_count ++;
632 left --;
633 }
634 else if (folder->mb_mapping[left - 1] == '\r') {
635 left --;
636 }
637 else
638 break;
639
640 if (crlf_count == 2)
641 break;
642 }
643
644 old_size = folder->mb_mapping_size;
645 mailmbox_unmap(folder);
646
647 if (old_size != 0) {
648 if (crlf_count != 2)
649 extra_size += (2 - crlf_count) * 2;
650 }
651
652 r = ftruncate(folder->mb_fd, extra_size + old_size);
653 if (r < 0) {
654 mailmbox_map(folder);
655 res = MAILMBOX_ERROR_FILE;
656 goto err;
657 }
658
659 r = mailmbox_map(folder);
660 if (r < 0) {
661 ftruncate(folder->mb_fd, old_size);
662 return MAILMBOX_ERROR_FILE;
663 }
664
665 str = folder->mb_mapping + old_size;
666
667 if (old_size != 0) {
668 for(i = 0 ; i < 2 - crlf_count ; i ++) {
669 * str = '\r';
670 str ++;
671 * str = '\n';
672 str ++;
673 }
674 }
675
676 for(i = 0 ; i < carray_count(append_tab) ; i ++) {
677 struct mailmbox_append_info * info;
678
679 info = carray_get(append_tab, i);
680
681 memcpy(str, from_line, from_size);
682
683 str += strlen(from_line);
684
685 str = write_fixed_message(str, info->ai_message, info->ai_size,
686 folder->mb_max_uid + i + 1,
687 folder->mb_no_uid);
688
689 * str = '\r';
690 str ++;
691 * str = '\n';
692 str ++;
693 }
694
695 folder->mb_max_uid += carray_count(append_tab);
696
697 return MAILMBOX_NO_ERROR;
698
699 err:
700 return res;
701}
702
703int
704mailmbox_append_message_list(struct mailmbox_folder * folder,
705 carray * append_tab)
706{
707 int r;
708 int res;
709 size_t cur_token;
710
711 r = mailmbox_validate_write_lock(folder);
712 if (r != MAILMBOX_NO_ERROR) {
713 res = r;
714 goto err;
715 }
716
717 r = mailmbox_expunge_no_lock(folder);
718 if (r != MAILMBOX_NO_ERROR) {
719 res = r;
720 goto unlock;
721 }
722
723 cur_token = folder->mb_mapping_size;
724
725 r = mailmbox_append_message_list_no_lock(folder, append_tab);
726 if (r != MAILMBOX_NO_ERROR) {
727 res = r;
728 goto unlock;
729 }
730
731 mailmbox_sync(folder);
732
733 r = mailmbox_parse_additionnal(folder, &cur_token);
734 if (r != MAILMBOX_NO_ERROR) {
735 res = r;
736 goto unlock;
737 }
738
739 mailmbox_timestamp(folder);
740
741 mailmbox_write_unlock(folder);
742
743 return MAILMBOX_NO_ERROR;
744
745 unlock:
746 mailmbox_write_unlock(folder);
747 err:
748 return res;
749}
750
751int
752mailmbox_append_message_uid(struct mailmbox_folder * folder,
753 const char * data, size_t len, unsigned int * puid)
754{
755 carray * tab;
756 struct mailmbox_append_info * append_info;
757 int res;
758 int r;
759
760 tab = carray_new(1);
761 if (tab == NULL) {
762 res = MAILMBOX_ERROR_MEMORY;
763 goto err;
764 }
765
766 append_info = mailmbox_append_info_new(data, len);
767 if (append_info == NULL) {
768 res = MAILMBOX_ERROR_MEMORY;
769 goto free_list;
770 }
771
772 r = carray_add(tab, append_info, NULL);
773 if (r < 0) {
774 res = MAILMBOX_ERROR_MEMORY;
775 goto free_append_info;
776 }
777
778 r = mailmbox_append_message_list(folder, tab);
779
780 if (puid != NULL)
781 * puid = append_info->ai_uid;
782
783 mailmbox_append_info_free(append_info);
784 carray_free(tab);
785
786 return r;
787
788 free_append_info:
789 mailmbox_append_info_free(append_info);
790 free_list:
791 carray_free(tab);
792 err:
793 return res;
794}
795
796int
797mailmbox_append_message(struct mailmbox_folder * folder,
798 const char * data, size_t len)
799{
800 return mailmbox_append_message_uid(folder, data, len, NULL);
801}
802
803/* ********************************************************************** */
804
805int mailmbox_fetch_msg_no_lock(struct mailmbox_folder * folder,
806 uint32_t num, char ** result,
807 size_t * result_len)
808{
809 struct mailmbox_msg_info * info;
810 int res;
811 chashdatum key;
812 chashdatum data;
813 int r;
814
815 key.data = &num;
816 key.len = sizeof(num);
817
818 r = chash_get(folder->mb_hash, &key, &data);
819 if (r < 0) {
820 res = MAILMBOX_ERROR_MSG_NOT_FOUND;
821 goto err;
822 }
823
824 info = data.data;
825
826 if (info->msg_deleted) {
827 res = MAILMBOX_ERROR_MSG_NOT_FOUND;
828 goto err;
829 }
830
831 * result = folder->mb_mapping + info->msg_headers;
832 * result_len = info->msg_size - info->msg_start_len;
833
834 return MAILMBOX_NO_ERROR;
835
836 err:
837 return res;
838}
839
840int mailmbox_fetch_msg_headers_no_lock(struct mailmbox_folder * folder,
841 uint32_t num, char ** result,
842 size_t * result_len)
843{
844 struct mailmbox_msg_info * info;
845 int res;
846 chashdatum key;
847 chashdatum data;
848 int r;
849
850 key.data = &num;
851 key.len = sizeof(num);
852
853 r = chash_get(folder->mb_hash, &key, &data);
854 if (r < 0) {
855 res = MAILMBOX_ERROR_MSG_NOT_FOUND;
856 goto err;
857 }
858
859 info = data.data;
860
861 if (info->msg_deleted) {
862 res = MAILMBOX_ERROR_MSG_NOT_FOUND;
863 goto err;
864 }
865
866 * result = folder->mb_mapping + info->msg_headers;
867 * result_len = info->msg_headers_len;
868
869 return MAILMBOX_NO_ERROR;
870
871 err:
872 return res;
873}
874
875int mailmbox_fetch_msg(struct mailmbox_folder * folder,
876 uint32_t num, char ** result,
877 size_t * result_len)
878{
879 MMAPString * mmapstr;
880 int res;
881 char * data;
882 size_t len;
883 int r;
884 size_t fixed_size;
885 char * end;
886
887 r = mailmbox_validate_read_lock(folder);
888 if (r != MAILMBOX_NO_ERROR) {
889 res = r;
890 goto err;
891 }
892
893 r = mailmbox_fetch_msg_no_lock(folder, num, &data, &len);
894 if (r != MAILMBOX_NO_ERROR) {
895 res = r;
896 goto unlock;
897 }
898
899 /* size with no uid */
900 fixed_size = get_fixed_message_size(data, len, 0, 1 /* force no uid */);
901
902#if 0
903 mmapstr = mmap_string_new_len(data, fixed_size);
904 if (mmapstr == NULL) {
905 res = MAILMBOX_ERROR_MEMORY;
906 goto unlock;
907 }
908#endif
909 mmapstr = mmap_string_sized_new(fixed_size);
910 if (mmapstr == NULL) {
911 res = MAILMBOX_ERROR_MEMORY;
912 goto unlock;
913 }
914
915 end = write_fixed_message(mmapstr->str, data, len, 0, 1 /* force no uid */);
916 * end = '\0';
917 mmapstr->len = fixed_size;
918
919 r = mmap_string_ref(mmapstr);
920 if (r < 0) {
921 mmap_string_free(mmapstr);
922 res = MAILMBOX_ERROR_MEMORY;
923 goto unlock;
924 }
925
926 * result = mmapstr->str;
927 * result_len = mmapstr->len;
928
929 mailmbox_read_unlock(folder);
930
931 return MAILMBOX_NO_ERROR;
932
933 unlock:
934 mailmbox_read_unlock(folder);
935 err:
936 return res;
937}
938
939int mailmbox_fetch_msg_headers(struct mailmbox_folder * folder,
940 uint32_t num, char ** result,
941 size_t * result_len)
942{
943 MMAPString * mmapstr;
944 int res;
945 char * data;
946 size_t len;
947 int r;
948 size_t fixed_size;
949 char * end;
950
951 r = mailmbox_validate_read_lock(folder);
952 if (r != MAILMBOX_NO_ERROR) {
953 res = r;
954 goto err;
955 }
956
957 r = mailmbox_fetch_msg_headers_no_lock(folder, num, &data, &len);
958 if (r != MAILMBOX_NO_ERROR) {
959 res = r;
960 goto unlock;
961 }
962
963#if 0
964 mmapstr = mmap_string_new_len(data, len);
965 if (mmapstr == NULL) {
966 res = MAILMBOX_ERROR_MEMORY;
967 goto unlock;
968 }
969#endif
970 /* size with no uid */
971 fixed_size = get_fixed_message_size(data, len, 0, 1 /* force no uid */);
972
973 mmapstr = mmap_string_sized_new(fixed_size);
974 if (mmapstr == NULL) {
975 res = MAILMBOX_ERROR_MEMORY;
976 goto unlock;
977 }
978
979 end = write_fixed_message(mmapstr->str, data, len, 0, 1 /* force no uid */);
980 * end = '\0';
981 mmapstr->len = fixed_size;
982
983 r = mmap_string_ref(mmapstr);
984 if (r < 0) {
985 mmap_string_free(mmapstr);
986 res = MAILMBOX_ERROR_MEMORY;
987 goto unlock;
988 }
989
990 * result = mmapstr->str;
991 * result_len = mmapstr->len;
992
993 mailmbox_read_unlock(folder);
994
995 return MAILMBOX_NO_ERROR;
996
997 unlock:
998 mailmbox_read_unlock(folder);
999 err:
1000 return res;
1001}
1002
1003void mailmbox_fetch_result_free(char * msg)
1004{
1005 mmap_string_unref(msg);
1006}
1007
1008
1009int mailmbox_copy_msg_list(struct mailmbox_folder * dest_folder,
1010 struct mailmbox_folder * src_folder,
1011 carray * tab)
1012{
1013 int r;
1014 int res;
1015 carray * append_tab;
1016 unsigned int i;
1017
1018 r = mailmbox_validate_read_lock(src_folder);
1019 if (r != MAILMBOX_NO_ERROR) {
1020 res = r;
1021 goto err;
1022 }
1023
1024 append_tab = carray_new(carray_count(tab));
1025 if (append_tab == NULL) {
1026 res = MAILMBOX_ERROR_MEMORY;
1027 goto src_unlock;
1028 }
1029
1030 for(i = 0 ; i < carray_count(tab) ; i ++) {
1031 struct mailmbox_append_info * append_info;
1032 char * data;
1033 size_t len;
1034 uint32_t uid;
1035
1036 uid = * ((uint32_t *) carray_get(tab, i));
1037
1038 r = mailmbox_fetch_msg_no_lock(src_folder, uid, &data, &len);
1039 if (r != MAILMBOX_NO_ERROR) {
1040 res = r;
1041 goto free_list;
1042 }
1043
1044 append_info = mailmbox_append_info_new(data, len);
1045 if (append_info == NULL) {
1046 res = MAILMBOX_ERROR_MEMORY;
1047 goto free_list;
1048 }
1049
1050 r = carray_add(append_tab, append_info, NULL);
1051 if (r < 0) {
1052 mailmbox_append_info_free(append_info);
1053 res = MAILMBOX_ERROR_MEMORY;
1054 goto free_list;
1055 }
1056 }
1057
1058 r = mailmbox_append_message_list(dest_folder, append_tab);
1059 if (r != MAILMBOX_NO_ERROR) {
1060 res = r;
1061 goto src_unlock;
1062 }
1063
1064 for(i = 0 ; i < carray_count(append_tab) ; i ++) {
1065 struct mailmbox_append_info * append_info;
1066
1067 append_info = carray_get(append_tab, i);
1068 mailmbox_append_info_free(append_info);
1069 }
1070 carray_free(append_tab);
1071
1072 mailmbox_read_unlock(src_folder);
1073
1074 return MAILMBOX_NO_ERROR;
1075
1076 free_list:
1077 for(i = 0 ; i < carray_count(append_tab) ; i ++) {
1078 struct mailmbox_append_info * append_info;
1079
1080 append_info = carray_get(append_tab, i);
1081 mailmbox_append_info_free(append_info);
1082 }
1083 carray_free(append_tab);
1084 src_unlock:
1085 mailmbox_read_unlock(src_folder);
1086 err:
1087 return res;
1088}
1089
1090int mailmbox_copy_msg(struct mailmbox_folder * dest_folder,
1091 struct mailmbox_folder * src_folder,
1092 uint32_t uid)
1093{
1094 carray * tab;
1095 int res;
1096 uint32_t * puid;
1097 int r;
1098
1099 tab = carray_new(1);
1100 if (tab == NULL) {
1101 res = MAILMBOX_ERROR_MEMORY;
1102 goto err;
1103 }
1104
1105 puid = malloc(sizeof(* puid));
1106 if (puid == NULL) {
1107 res = MAILMBOX_ERROR_MEMORY;
1108 goto free_array;
1109 }
1110 * puid = uid;
1111
1112 r = mailmbox_copy_msg_list(dest_folder, src_folder, tab);
1113 res = r;
1114
1115 free(puid);
1116 free_array:
1117 carray_free(tab);
1118 err:
1119 return res;
1120}
1121
1122static int mailmbox_expunge_to_file_no_lock(char * dest_filename, int dest_fd,
1123 struct mailmbox_folder * folder,
1124 size_t * result_size)
1125{
1126 int r;
1127 int res;
1128 unsigned long i;
1129 size_t cur_offset;
1130 char * dest;
1131 size_t size;
1132
1133 size = 0;
1134 for(i = 0 ; i < carray_count(folder->mb_tab) ; i ++) {
1135 struct mailmbox_msg_info * info;
1136
1137 info = carray_get(folder->mb_tab, i);
1138
1139 if (!info->msg_deleted) {
1140 size += info->msg_size + info->msg_padding;
1141
1142 if (!folder->mb_no_uid) {
1143 if (!info->msg_written_uid) {
1144 uint32_t uid;
1145
1146 size += strlen(UID_HEADER " \r\n");
1147
1148 uid = info->msg_uid;
1149 while (uid >= 10) {
1150 uid /= 10;
1151 size ++;
1152 }
1153 size ++;
1154 }
1155 }
1156 }
1157 }
1158
1159 r = ftruncate(dest_fd, size);
1160 if (r < 0) {
1161 res = MAILMBOX_ERROR_FILE;
1162 goto err;
1163 }
1164
1165 dest = (char *) mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, dest_fd, 0);
1166 if (dest == MAP_FAILED) {
1167 res = MAILMBOX_ERROR_FILE;
1168 goto err;
1169 }
1170
1171 cur_offset = 0;
1172 for(i = 0 ; i < carray_count(folder->mb_tab) ; i ++) {
1173 struct mailmbox_msg_info * info;
1174
1175 info = carray_get(folder->mb_tab, i);
1176
1177 if (!info->msg_deleted) {
1178 memcpy(dest + cur_offset, folder->mb_mapping + info->msg_start,
1179 info->msg_headers_len + info->msg_start_len);
1180 cur_offset += info->msg_headers_len + info->msg_start_len;
1181
1182 if (!folder->mb_no_uid) {
1183 if (!info->msg_written_uid) {
1184 size_t numlen;
1185
1186 memcpy(dest + cur_offset, UID_HEADER " ", strlen(UID_HEADER " "));
1187 cur_offset += strlen(UID_HEADER " ");
1188 numlen = snprintf(dest + cur_offset, size - cur_offset,
1189 "%i\r\n", info->msg_uid);
1190 cur_offset += numlen;
1191 }
1192 }
1193
1194 memcpy(dest + cur_offset,
1195 folder->mb_mapping + info->msg_headers + info->msg_headers_len,
1196 info->msg_size - (info->msg_start_len + info->msg_headers_len)
1197 + info->msg_padding);
1198
1199 cur_offset += info->msg_size -
1200 (info->msg_start_len + info->msg_headers_len)
1201 + info->msg_padding;
1202 }
1203 }
1204 fflush(stdout);
1205
1206 msync(dest, size, MS_SYNC);
1207 munmap(dest, size);
1208
1209 * result_size = size;
1210
1211 return MAILMBOX_NO_ERROR;
1212
1213 err:
1214 return res;
1215}
1216
1217static int copy_to_old_file(char * source_filename,
1218 char * destination_filename, size_t size)
1219{
1220 int source_fd;
1221 int dest_fd;
1222 char * source;
1223 char * dest;
1224 int res;
1225 int r;
1226
1227 source_fd = open(source_filename, O_RDONLY);
1228 if (source_fd < 0) {
1229 res = MAILMBOX_ERROR_FILE;
1230 goto err;
1231 }
1232
1233 source = (char *) mmap(0, size, PROT_READ, MAP_PRIVATE, source_fd, 0);
1234 if (source == MAP_FAILED) {
1235 res = MAILMBOX_ERROR_FILE;
1236 goto close_source;
1237 }
1238
1239 dest_fd = open(destination_filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
1240 if (dest_fd < 0) {
1241 res = MAILMBOX_ERROR_FILE;
1242 goto unmap_source;
1243 }
1244
1245 r = ftruncate(dest_fd, size);
1246 if (r < 0) {
1247 res = MAILMBOX_ERROR_FILE;
1248 goto close_dest;
1249 }
1250
1251 dest = (char *) mmap(0, size, PROT_READ | PROT_WRITE,
1252 MAP_SHARED, dest_fd, 0);
1253 if (dest == MAP_FAILED) {
1254 res = MAILMBOX_ERROR_FILE;
1255 goto close_dest;
1256 }
1257
1258 memcpy(dest, source, size);
1259
1260 munmap(dest, size);
1261 close(source_fd);
1262 munmap(source, size);
1263 close(source_fd);
1264
1265 return MAILMBOX_NO_ERROR;
1266
1267 unmap_dest:
1268 munmap(dest, size);
1269 close_dest:
1270 close(source_fd);
1271 unmap_source:
1272 munmap(source, size);
1273 close_source:
1274 close(source_fd);
1275 err:
1276 return res;
1277}
1278
1279int mailmbox_expunge_no_lock(struct mailmbox_folder * folder)
1280{
1281 char tmpfile[PATH_MAX];
1282 int r;
1283 int res;
1284 int dest_fd;
1285 size_t size;
1286 mode_t old_mask;
1287
1288 if (folder->mb_read_only)
1289 return MAILMBOX_ERROR_READONLY;
1290
1291 if (((folder->mb_written_uid >= folder->mb_max_uid) || folder->mb_no_uid) &&
1292 (!folder->mb_changed)) {
1293 /* no need to expunge */
1294 return MAILMBOX_NO_ERROR;
1295 }
1296
1297 snprintf(tmpfile, PATH_MAX, "%sXXXXXX", folder->mb_filename);
1298 old_mask = umask(0077);
1299 dest_fd = mkstemp(tmpfile);
1300 umask(old_mask);
1301
1302 if (dest_fd < 0) {
1303 /* fallback to tmp dir */
1304
1305 snprintf(tmpfile, PATH_MAX, TMPDIR "/etpan-unsafe-XXXXXX");
1306
1307 old_mask = umask(0077);
1308 dest_fd = mkstemp(tmpfile);
1309 umask(old_mask);
1310
1311 if (dest_fd < 0) {
1312 res = MAILMBOX_ERROR_FILE;
1313 goto err;
1314 }
1315 }
1316
1317 r = mailmbox_expunge_to_file_no_lock(tmpfile, dest_fd,
1318 folder, &size);
1319 if (r != MAILMBOX_NO_ERROR) {
1320 res = r;
1321 goto unlink;
1322 }
1323
1324 close(dest_fd);
1325
1326 r = rename(tmpfile, folder->mb_filename);
1327 if (r < 0) {
1328 mailmbox_unmap(folder);
1329 mailmbox_close(folder);
1330
1331 /* fallback on copy to old file */
1332
1333 r = copy_to_old_file(tmpfile, folder->mb_filename, size);
1334 if (r != MAILMBOX_NO_ERROR) {
1335 res = r;
1336 goto err;
1337 }
1338
1339 unlink(tmpfile);
1340 }
1341 else {
1342 mailmbox_unmap(folder);
1343 mailmbox_close(folder);
1344 }
1345
1346 r = mailmbox_open(folder);
1347 if (r != MAILMBOX_NO_ERROR) {
1348 res = r;
1349 goto err;
1350 }
1351
1352 r = mailmbox_map(folder);
1353 if (r != MAILMBOX_NO_ERROR) {
1354 res = r;
1355 goto err;
1356 }
1357
1358 r = mailmbox_parse(folder);
1359 if (r != MAILMBOX_NO_ERROR) {
1360 res = r;
1361 goto err;
1362 }
1363
1364 mailmbox_timestamp(folder);
1365
1366 folder->mb_changed = FALSE;
1367 folder->mb_deleted_count = 0;
1368
1369 return MAILMBOX_NO_ERROR;
1370
1371 unlink:
1372 close(dest_fd);
1373 unlink(tmpfile);
1374 err:
1375 return res;
1376}
1377
1378int mailmbox_expunge(struct mailmbox_folder * folder)
1379{
1380 int r;
1381 int res;
1382
1383 r = mailmbox_validate_write_lock(folder);
1384 if (r != MAILMBOX_NO_ERROR) {
1385 res = r;
1386 goto err;
1387 }
1388
1389 r = mailmbox_expunge_no_lock(folder);
1390 res = r;
1391
1392 mailmbox_write_unlock(folder);
1393 err:
1394 return res;
1395}
1396
1397int mailmbox_delete_msg(struct mailmbox_folder * folder, uint32_t uid)
1398{
1399 struct mailmbox_msg_info * info;
1400 int res;
1401 chashdatum key;
1402 chashdatum data;
1403 int r;
1404
1405 if (folder->mb_read_only) {
1406 res = MAILMBOX_ERROR_READONLY;
1407 goto err;
1408 }
1409
1410 key.data = &uid;
1411 key.len = sizeof(uid);
1412
1413 r = chash_get(folder->mb_hash, &key, &data);
1414 if (r < 0) {
1415 res = MAILMBOX_ERROR_MSG_NOT_FOUND;
1416 goto err;
1417 }
1418
1419 info = data.data;
1420
1421 if (info->msg_deleted) {
1422 res = MAILMBOX_ERROR_MSG_NOT_FOUND;
1423 goto err;
1424 }
1425
1426 info->msg_deleted = TRUE;
1427 folder->mb_changed = TRUE;
1428 folder->mb_deleted_count ++;
1429
1430 return MAILMBOX_NO_ERROR;
1431
1432 err:
1433 return res;
1434}
1435
1436
1437/*
1438 INIT of MBOX
1439
1440 - open file
1441 - map the file
1442
1443 - lock the file
1444
1445 - parse memory
1446
1447 - unlock the file
1448*/
1449
1450int mailmbox_init(const char * filename,
1451 int force_readonly,
1452 int force_no_uid,
1453 uint32_t default_written_uid,
1454 struct mailmbox_folder ** result_folder)
1455{
1456 struct mailmbox_folder * folder;
1457 int r;
1458 int res;
1459
1460 folder = mailmbox_folder_new(filename);
1461 if (folder == NULL) {
1462 res = MAILMBOX_ERROR_MEMORY;
1463 goto err;
1464 }
1465 folder->mb_no_uid = force_no_uid;
1466 folder->mb_read_only = force_readonly;
1467 folder->mb_written_uid = default_written_uid;
1468
1469 folder->mb_changed = FALSE;
1470 folder->mb_deleted_count = 0;
1471
1472 r = mailmbox_open(folder);
1473 if (r != MAILMBOX_NO_ERROR) {
1474 res = r;
1475 goto free;
1476 }
1477
1478 r = mailmbox_map(folder);
1479 if (r != MAILMBOX_NO_ERROR) {
1480 res = r;
1481 goto close;
1482 }
1483
1484 r = mailmbox_validate_read_lock(folder);
1485 if (r != MAILMBOX_NO_ERROR) {
1486 res = r;
1487 goto unmap;
1488 }
1489
1490 mailmbox_read_unlock(folder);
1491
1492 * result_folder = folder;
1493
1494 return MAILMBOX_NO_ERROR;
1495
1496 unmap:
1497 mailmbox_unmap(folder);
1498 close:
1499 mailmbox_close(folder);
1500 free:
1501 mailmbox_folder_free(folder);
1502 err:
1503 return res;
1504}
1505
1506
1507/*
1508 when MBOX is DONE
1509
1510 - check for changes
1511
1512 - unmap the file
1513 - close file
1514*/
1515
1516void mailmbox_done(struct mailmbox_folder * folder)
1517{
1518 if (!folder->mb_read_only)
1519 mailmbox_expunge(folder);
1520
1521 mailmbox_unmap(folder);
1522 mailmbox_close(folder);
1523
1524 mailmbox_folder_free(folder);
1525}