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