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