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 #ifdef HAVE_CONFIG_H
37 # include "config.h"
38 #include "claws-features.h"
39 #endif
40
41 #include "mailmbox.h"
42
43 #include <sys/file.h>
44 #include <sys/stat.h>
45 #include <unistd.h>
46 #include <sys/mman.h>
47 #include <fcntl.h>
48 #include <time.h>
49 #include <sys/types.h>
50 #include <unistd.h>
51
52 #include <string.h>
53 #include <ctype.h>
54 #include <stdlib.h>
55 #include <stdio.h>
56 #include <limits.h>
57
58 #include "mmapstring.h"
59 #include "mailmbox_parse.h"
60 #include "maillock.h"
61 #include "file-utils.h"
62 #include "utils.h"
63
64 /*
65 http://www.qmail.org/qmail-manual-html/man5/mbox.html
66 RFC 2076
67 */
68
69 #define TMPDIR "/tmp"
70
71 /* mbox is a file with a corresponding filename */
72
73 #define UID_HEADER "X-LibEtPan-UID:"
74
75 #ifndef TRUE
76 #define TRUE 1
77 #endif
78
79 #ifndef FALSE
80 #define FALSE 0
81 #endif
82
claws_mailmbox_write_lock(struct claws_mailmbox_folder * folder)83 int claws_mailmbox_write_lock(struct claws_mailmbox_folder * folder)
84 {
85 int r;
86
87 if (folder->mb_read_only)
88 return MAILMBOX_ERROR_READONLY;
89
90 r = maillock_write_lock(folder->mb_filename, folder->mb_fd);
91 if (r == 0)
92 return MAILMBOX_NO_ERROR;
93 else
94 return MAILMBOX_ERROR_FILE;
95 }
96
claws_mailmbox_write_unlock(struct claws_mailmbox_folder * folder)97 int claws_mailmbox_write_unlock(struct claws_mailmbox_folder * folder)
98 {
99 int r;
100
101 r = maillock_write_unlock(folder->mb_filename, folder->mb_fd);
102 if (r == 0)
103 return MAILMBOX_NO_ERROR;
104 else
105 return MAILMBOX_ERROR_FILE;
106 }
107
claws_mailmbox_read_lock(struct claws_mailmbox_folder * folder)108 int claws_mailmbox_read_lock(struct claws_mailmbox_folder * folder)
109 {
110 int r;
111
112 r = maillock_read_lock(folder->mb_filename, folder->mb_fd);
113 if (r == 0)
114 return MAILMBOX_NO_ERROR;
115 else
116 return MAILMBOX_ERROR_FILE;
117 }
118
claws_mailmbox_read_unlock(struct claws_mailmbox_folder * folder)119 int claws_mailmbox_read_unlock(struct claws_mailmbox_folder * folder)
120 {
121 int r;
122
123 r = maillock_read_unlock(folder->mb_filename, folder->mb_fd);
124 if (r == 0)
125 return MAILMBOX_NO_ERROR;
126 else
127 return MAILMBOX_ERROR_FILE;
128 }
129
130
131 /*
132 map the file into memory.
133 the file must be locked.
134 */
135
claws_mailmbox_map(struct claws_mailmbox_folder * folder)136 int claws_mailmbox_map(struct claws_mailmbox_folder * folder)
137 {
138 char * str;
139 GStatBuf buf;
140 int res;
141 int r;
142
143 r = g_stat(folder->mb_filename, &buf);
144 if (r < 0) {
145 debug_print("stat failed %d\n", r);
146 res = MAILMBOX_ERROR_FILE;
147 goto err;
148 }
149
150 if (buf.st_size == 0) {
151 folder->mb_mapping = NULL;
152 folder->mb_mapping_size = 0;
153 return MAILMBOX_NO_ERROR;
154 }
155 if (folder->mb_read_only)
156 str = (char *) mmap(NULL, buf.st_size, PROT_READ,
157 MAP_PRIVATE, folder->mb_fd, 0);
158 else
159 str = (char *) mmap(NULL, buf.st_size, PROT_READ | PROT_WRITE,
160 MAP_SHARED, folder->mb_fd, 0);
161 if (str == MAP_FAILED) {
162 perror("mmap");
163 debug_print("map of %lld bytes failed\n", (long long)buf.st_size);
164 res = MAILMBOX_ERROR_FILE;
165 goto err;
166 }
167
168 folder->mb_mapping = str;
169 folder->mb_mapping_size = buf.st_size;
170
171 return MAILMBOX_NO_ERROR;
172
173 err:
174 return res;
175 }
176
177 /*
178 unmap the file
179 */
180
claws_mailmbox_unmap(struct claws_mailmbox_folder * folder)181 void claws_mailmbox_unmap(struct claws_mailmbox_folder * folder)
182 {
183 munmap(folder->mb_mapping, folder->mb_mapping_size);
184 folder->mb_mapping = NULL;
185 folder->mb_mapping_size = 0;
186 }
187
claws_mailmbox_sync(struct claws_mailmbox_folder * folder)188 void claws_mailmbox_sync(struct claws_mailmbox_folder * folder)
189 {
190 msync(folder->mb_mapping, folder->mb_mapping_size, MS_SYNC);
191 }
192
claws_mailmbox_timestamp(struct claws_mailmbox_folder * folder)193 void claws_mailmbox_timestamp(struct claws_mailmbox_folder * folder)
194 {
195 int r;
196 GStatBuf buf;
197
198 r = g_stat(folder->mb_filename, &buf);
199 if (r < 0)
200 folder->mb_mtime = (time_t) -1;
201 else
202 folder->mb_mtime = buf.st_mtime;
203 }
204
205 /*
206 open the file
207 */
208
claws_mailmbox_open(struct claws_mailmbox_folder * folder)209 int claws_mailmbox_open(struct claws_mailmbox_folder * folder)
210 {
211 int fd = -1;
212 int read_only;
213
214 if (!folder->mb_read_only) {
215 read_only = FALSE;
216 fd = open(folder->mb_filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
217 }
218
219 if (folder->mb_read_only || (fd < 0)) {
220 read_only = TRUE;
221 fd = open(folder->mb_filename, O_RDONLY);
222 if (fd < 0)
223 return MAILMBOX_ERROR_FILE_NOT_FOUND;
224 }
225
226 folder->mb_fd = fd;
227 folder->mb_read_only = read_only;
228
229 return MAILMBOX_NO_ERROR;
230 }
231
232 /*
233 close the file
234 */
235
claws_mailmbox_close(struct claws_mailmbox_folder * folder)236 void claws_mailmbox_close(struct claws_mailmbox_folder * folder)
237 {
238 close(folder->mb_fd);
239 folder->mb_fd = -1;
240 }
241
242
claws_mailmbox_validate_lock(struct claws_mailmbox_folder * folder,int (* custom_lock)(struct claws_mailmbox_folder *),int (* custom_unlock)(struct claws_mailmbox_folder *))243 static int claws_mailmbox_validate_lock(struct claws_mailmbox_folder * folder,
244 int (* custom_lock)(struct claws_mailmbox_folder *),
245 int (* custom_unlock)(struct claws_mailmbox_folder *))
246 {
247 GStatBuf buf;
248 int res;
249 int r;
250
251 r = g_stat(folder->mb_filename, &buf);
252 if (r < 0) {
253 buf.st_mtime = (time_t) -1;
254 }
255
256 if ((buf.st_mtime != folder->mb_mtime) ||
257 ((size_t) buf.st_size != folder->mb_mapping_size)) {
258 int r;
259
260 claws_mailmbox_unmap(folder);
261 claws_mailmbox_close(folder);
262
263 r = claws_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 = claws_mailmbox_map(folder);
276 if (r != MAILMBOX_NO_ERROR) {
277 res = r;
278 goto err_unlock;
279 }
280
281 r = claws_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
claws_mailmbox_validate_write_lock(struct claws_mailmbox_folder * folder)308 int claws_mailmbox_validate_write_lock(struct claws_mailmbox_folder * folder)
309 {
310 return claws_mailmbox_validate_lock(folder,
311 claws_mailmbox_write_lock,
312 claws_mailmbox_write_unlock);
313 }
314
315
claws_mailmbox_validate_read_lock(struct claws_mailmbox_folder * folder)316 int claws_mailmbox_validate_read_lock(struct claws_mailmbox_folder * folder)
317 {
318 return claws_mailmbox_validate_lock(folder,
319 claws_mailmbox_read_lock,
320 claws_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 #if 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 #if 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 = 0;
586 const char * next = NULL;
587
588 str = write_fixed_line(str, cur_src, left, &next, &count);
589
590 cur_src = next;
591 left -= count;
592 }
593
594 return str;
595 }
596
597 #define DEFAULT_FROM_LINE "From - Wed Jun 30 21:49:08 1993\n"
598
599 int
claws_mailmbox_append_message_list_no_lock(struct claws_mailmbox_folder * folder,carray * append_tab)600 claws_mailmbox_append_message_list_no_lock(struct claws_mailmbox_folder * folder,
601 carray * append_tab)
602 {
603 size_t extra_size;
604 int r;
605 char from_line[MAX_FROM_LINE_SIZE] = DEFAULT_FROM_LINE;
606 struct tm time_info;
607 time_t date;
608 int res;
609 size_t old_size;
610 char * str;
611 unsigned int i;
612 size_t from_size;
613 size_t left;
614 size_t crlf_count;
615
616 if (folder->mb_read_only) {
617 res = MAILMBOX_ERROR_READONLY;
618 goto err;
619 }
620
621 date = time(NULL);
622 from_size = strlen(DEFAULT_FROM_LINE);
623 if (localtime_r(&date, &time_info) != NULL)
624 from_size = strftime(from_line, MAX_FROM_LINE_SIZE, "From - %a %b %_2d %T %Y\n", &time_info);
625
626 extra_size = 0;
627 for(i = 0 ; i < carray_count(append_tab) ; i ++) {
628 struct claws_mailmbox_append_info * info;
629
630 info = carray_get(append_tab, i);
631 extra_size += from_size;
632 extra_size += get_fixed_message_size(info->ai_message, info->ai_size,
633 folder->mb_max_uid + i + 1,
634 folder->mb_no_uid);
635 #if CRLF_BADNESS
636 extra_size += 2; /* CR LF */
637 #else
638 extra_size += 1; /* CR LF */
639 #endif
640 }
641
642 left = folder->mb_mapping_size;
643 crlf_count = 0;
644 while (left >= 1) {
645 if (folder->mb_mapping[left - 1] == '\n') {
646 crlf_count ++;
647 left --;
648 }
649 #if CRLF_BADNESS
650 else if (folder->mb_mapping[left - 1] == '\r') {
651 left --;
652 }
653 #endif
654 else
655 break;
656
657 if (crlf_count == 2)
658 break;
659 }
660
661 old_size = folder->mb_mapping_size;
662 claws_mailmbox_unmap(folder);
663
664 if (old_size != 0) {
665 if (crlf_count != 2)
666 #if CRLF_BADNESS
667 extra_size += (2 - crlf_count) * 2;
668 #else
669 /* Need the number of LFs, not CRLFs */
670 extra_size += (2 - crlf_count) * 1; /* 2 */
671 #endif
672 }
673
674 r = ftruncate(folder->mb_fd, extra_size + old_size);
675 if (r < 0) {
676 debug_print("ftruncate failed with %d\n", r);
677 claws_mailmbox_map(folder);
678 res = MAILMBOX_ERROR_FILE;
679 goto err;
680 }
681
682 r = claws_mailmbox_map(folder);
683 if (r < 0) {
684 debug_print("claws_mailmbox_map failed with %d\n", r);
685 r = ftruncate(folder->mb_fd, old_size);
686 if (r < 0)
687 debug_print("ftruncate failed with %d\n", r);
688 return MAILMBOX_ERROR_FILE;
689 }
690
691 str = folder->mb_mapping + old_size;
692
693 if (old_size != 0) {
694 for(i = 0 ; i < 2 - crlf_count ; i ++) {
695 #if CRLF_BADNESS
696 * str = '\r';
697 str ++;
698 #endif
699 * str = '\n';
700 str ++;
701 }
702 }
703
704 for(i = 0 ; i < carray_count(append_tab) ; i ++) {
705 struct claws_mailmbox_append_info * info;
706
707 info = carray_get(append_tab, i);
708
709 memcpy(str, from_line, from_size);
710
711 str += strlen(from_line);
712
713 str = write_fixed_message(str, info->ai_message, info->ai_size,
714 folder->mb_max_uid + i + 1,
715 folder->mb_no_uid);
716
717 #if CRLF_BADNESS
718 * str = '\r';
719 str ++;
720 #endif
721 * str = '\n';
722 str ++;
723 }
724
725 folder->mb_max_uid += carray_count(append_tab);
726
727 return MAILMBOX_NO_ERROR;
728
729 err:
730 return res;
731 }
732
733 int
claws_mailmbox_append_message_list(struct claws_mailmbox_folder * folder,carray * append_tab)734 claws_mailmbox_append_message_list(struct claws_mailmbox_folder * folder,
735 carray * append_tab)
736 {
737 int r;
738 int res;
739 size_t cur_token;
740
741 r = claws_mailmbox_validate_write_lock(folder);
742 if (r != MAILMBOX_NO_ERROR) {
743 res = r;
744 goto err;
745 }
746
747 r = claws_mailmbox_expunge_no_lock(folder);
748 if (r != MAILMBOX_NO_ERROR) {
749 res = r;
750 goto unlock;
751 }
752
753 cur_token = folder->mb_mapping_size;
754
755 r = claws_mailmbox_append_message_list_no_lock(folder, append_tab);
756 if (r != MAILMBOX_NO_ERROR) {
757 res = r;
758 goto unlock;
759 }
760
761 claws_mailmbox_sync(folder);
762
763 r = claws_mailmbox_parse_additionnal(folder, &cur_token);
764 if (r != MAILMBOX_NO_ERROR) {
765 res = r;
766 goto unlock;
767 }
768
769 claws_mailmbox_timestamp(folder);
770
771 claws_mailmbox_write_unlock(folder);
772
773 return MAILMBOX_NO_ERROR;
774
775 unlock:
776 claws_mailmbox_write_unlock(folder);
777 err:
778 return res;
779 }
780
781 int
claws_mailmbox_append_message(struct claws_mailmbox_folder * folder,const char * data,size_t len)782 claws_mailmbox_append_message(struct claws_mailmbox_folder * folder,
783 const char * data, size_t len)
784 {
785 carray * tab;
786 struct claws_mailmbox_append_info * append_info;
787 int res;
788 int r;
789
790 tab = carray_new(1);
791 if (tab == NULL) {
792 res = MAILMBOX_ERROR_MEMORY;
793 goto err;
794 }
795
796 append_info = claws_mailmbox_append_info_new(data, len);
797 if (append_info == NULL) {
798 res = MAILMBOX_ERROR_MEMORY;
799 goto free_list;
800 }
801
802 r = carray_add(tab, append_info, NULL);
803 if (r < 0) {
804 res = MAILMBOX_ERROR_MEMORY;
805 goto free_append_info;
806 }
807
808 r = claws_mailmbox_append_message_list(folder, tab);
809
810 claws_mailmbox_append_info_free(append_info);
811 carray_free(tab);
812
813 return r;
814
815 free_append_info:
816 claws_mailmbox_append_info_free(append_info);
817 free_list:
818 carray_free(tab);
819 err:
820 return res;
821 }
822
823 /* ********************************************************************** */
824
claws_mailmbox_fetch_msg_no_lock(struct claws_mailmbox_folder * folder,uint32_t num,const char ** result,size_t * result_len)825 int claws_mailmbox_fetch_msg_no_lock(struct claws_mailmbox_folder * folder,
826 uint32_t num, const char ** result,
827 size_t * result_len)
828 {
829 struct claws_mailmbox_msg_info * info;
830 int res;
831 chashdatum key;
832 chashdatum data;
833 int r;
834
835 key.data = #
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_size - info->msg_start_len;
853
854 return MAILMBOX_NO_ERROR;
855
856 err:
857 return res;
858 }
859
claws_mailmbox_fetch_msg_headers_no_lock(struct claws_mailmbox_folder * folder,uint32_t num,const char ** result,size_t * result_len)860 int claws_mailmbox_fetch_msg_headers_no_lock(struct claws_mailmbox_folder * folder,
861 uint32_t num, const char ** result,
862 size_t * result_len)
863 {
864 struct claws_mailmbox_msg_info * info;
865 int res;
866 chashdatum key;
867 chashdatum data;
868 int r;
869
870 key.data = #
871 key.len = sizeof(num);
872
873 r = chash_get(folder->mb_hash, &key, &data);
874 if (r < 0) {
875 res = MAILMBOX_ERROR_MSG_NOT_FOUND;
876 goto err;
877 }
878
879 info = data.data;
880
881 if (info->msg_deleted) {
882 res = MAILMBOX_ERROR_MSG_NOT_FOUND;
883 goto err;
884 }
885
886 * result = folder->mb_mapping + info->msg_headers;
887 * result_len = info->msg_headers_len;
888
889 return MAILMBOX_NO_ERROR;
890
891 err:
892 return res;
893 }
894
claws_mailmbox_fetch_msg(struct claws_mailmbox_folder * folder,uint32_t num,const char ** result,size_t * result_len)895 int claws_mailmbox_fetch_msg(struct claws_mailmbox_folder * folder,
896 uint32_t num, const char ** result,
897 size_t * result_len)
898 {
899 MMAPString * mmapstr;
900 int res;
901 const char * data;
902 size_t len;
903 int r;
904 size_t fixed_size;
905 char * end;
906
907 r = claws_mailmbox_validate_read_lock(folder);
908 if (r != MAILMBOX_NO_ERROR) {
909 res = r;
910 goto err;
911 }
912
913 r = claws_mailmbox_fetch_msg_no_lock(folder, num, &data, &len);
914 if (r != MAILMBOX_NO_ERROR) {
915 res = r;
916 goto unlock;
917 }
918
919 /* size with no uid */
920 fixed_size = get_fixed_message_size(data, len, 0, 1 /* force no uid */);
921
922 #if 0
923 mmapstr = mmap_string_new_len(data, fixed_size);
924 if (mmapstr == NULL) {
925 res = MAILMBOX_ERROR_MEMORY;
926 goto unlock;
927 }
928 #endif
929 mmapstr = mmap_string_sized_new(fixed_size);
930 if (mmapstr == NULL) {
931 res = MAILMBOX_ERROR_MEMORY;
932 goto unlock;
933 }
934
935 end = write_fixed_message(mmapstr->str, data, len, 0, 1 /* force no uid */);
936 * end = '\0';
937 mmapstr->len = fixed_size;
938
939 r = mmap_string_ref(mmapstr);
940 if (r < 0) {
941 mmap_string_free(mmapstr);
942 res = MAILMBOX_ERROR_MEMORY;
943 goto unlock;
944 }
945
946 * result = mmapstr->str;
947 * result_len = mmapstr->len;
948
949 claws_mailmbox_read_unlock(folder);
950
951 return MAILMBOX_NO_ERROR;
952
953 unlock:
954 claws_mailmbox_read_unlock(folder);
955 err:
956 return res;
957 }
958
claws_mailmbox_fetch_msg_headers(struct claws_mailmbox_folder * folder,uint32_t num,const char ** result,size_t * result_len)959 int claws_mailmbox_fetch_msg_headers(struct claws_mailmbox_folder * folder,
960 uint32_t num, const char ** result,
961 size_t * result_len)
962 {
963 MMAPString * mmapstr;
964 int res;
965 const char * data;
966 size_t len;
967 int r;
968 size_t fixed_size;
969 char * end;
970
971 r = claws_mailmbox_validate_read_lock(folder);
972 if (r != MAILMBOX_NO_ERROR) {
973 res = r;
974 goto err;
975 }
976
977 r = claws_mailmbox_fetch_msg_headers_no_lock(folder, num, &data, &len);
978 if (r != MAILMBOX_NO_ERROR) {
979 res = r;
980 goto unlock;
981 }
982
983 #if 0
984 mmapstr = mmap_string_new_len(data, len);
985 if (mmapstr == NULL) {
986 res = MAILMBOX_ERROR_MEMORY;
987 goto unlock;
988 }
989 #endif
990 /* size with no uid */
991 fixed_size = get_fixed_message_size(data, len, 0, 1 /* force no uid */);
992
993 mmapstr = mmap_string_sized_new(fixed_size);
994 if (mmapstr == NULL) {
995 res = MAILMBOX_ERROR_MEMORY;
996 goto unlock;
997 }
998
999 end = write_fixed_message(mmapstr->str, data, len, 0, 1 /* force no uid */);
1000 * end = '\0';
1001 mmapstr->len = fixed_size;
1002
1003 r = mmap_string_ref(mmapstr);
1004 if (r < 0) {
1005 mmap_string_free(mmapstr);
1006 res = MAILMBOX_ERROR_MEMORY;
1007 goto unlock;
1008 }
1009
1010 * result = mmapstr->str;
1011 * result_len = mmapstr->len;
1012
1013 claws_mailmbox_read_unlock(folder);
1014
1015 return MAILMBOX_NO_ERROR;
1016
1017 unlock:
1018 claws_mailmbox_read_unlock(folder);
1019 err:
1020 return res;
1021 }
1022
claws_mailmbox_fetch_result_free(char * msg)1023 void claws_mailmbox_fetch_result_free(char * msg)
1024 {
1025 mmap_string_unref(msg);
1026 }
1027
1028
claws_mailmbox_copy_msg_list(struct claws_mailmbox_folder * dest_folder,struct claws_mailmbox_folder * src_folder,carray * tab)1029 int claws_mailmbox_copy_msg_list(struct claws_mailmbox_folder * dest_folder,
1030 struct claws_mailmbox_folder * src_folder,
1031 carray * tab)
1032 {
1033 int r;
1034 int res;
1035 carray * append_tab;
1036 unsigned int i;
1037
1038 r = claws_mailmbox_validate_read_lock(src_folder);
1039 if (r != MAILMBOX_NO_ERROR) {
1040 res = r;
1041 goto err;
1042 }
1043
1044 append_tab = carray_new(carray_count(tab));
1045 if (append_tab == NULL) {
1046 res = MAILMBOX_ERROR_MEMORY;
1047 goto src_unlock;
1048 }
1049
1050 for(i = 0 ; i < carray_count(tab) ; i ++) {
1051 struct claws_mailmbox_append_info * append_info;
1052 const char * data;
1053 size_t len;
1054 uint32_t uid;
1055
1056 uid = * ((uint32_t *) carray_get(tab, i));
1057
1058 r = claws_mailmbox_fetch_msg_no_lock(src_folder, uid, &data, &len);
1059 if (r != MAILMBOX_NO_ERROR) {
1060 res = r;
1061 goto free_list;
1062 }
1063
1064 append_info = claws_mailmbox_append_info_new(data, len);
1065 if (append_info == NULL) {
1066 res = MAILMBOX_ERROR_MEMORY;
1067 goto free_list;
1068 }
1069
1070 r = carray_add(append_tab, append_info, NULL);
1071 if (r < 0) {
1072 claws_mailmbox_append_info_free(append_info);
1073 res = MAILMBOX_ERROR_MEMORY;
1074 goto free_list;
1075 }
1076 }
1077
1078 r = claws_mailmbox_append_message_list(dest_folder, append_tab);
1079 if (r != MAILMBOX_NO_ERROR) {
1080 res = r;
1081 goto free_list;
1082 }
1083
1084 for(i = 0 ; i < carray_count(append_tab) ; i ++) {
1085 struct claws_mailmbox_append_info * append_info;
1086
1087 append_info = carray_get(append_tab, i);
1088 claws_mailmbox_append_info_free(append_info);
1089 }
1090 carray_free(append_tab);
1091
1092 claws_mailmbox_read_unlock(src_folder);
1093
1094 return MAILMBOX_NO_ERROR;
1095
1096 free_list:
1097 for(i = 0 ; i < carray_count(append_tab) ; i ++) {
1098 struct claws_mailmbox_append_info * append_info;
1099
1100 append_info = carray_get(append_tab, i);
1101 claws_mailmbox_append_info_free(append_info);
1102 }
1103 carray_free(append_tab);
1104 src_unlock:
1105 claws_mailmbox_read_unlock(src_folder);
1106 err:
1107 return res;
1108 }
1109
claws_mailmbox_copy_msg(struct claws_mailmbox_folder * dest_folder,struct claws_mailmbox_folder * src_folder,uint32_t uid)1110 int claws_mailmbox_copy_msg(struct claws_mailmbox_folder * dest_folder,
1111 struct claws_mailmbox_folder * src_folder,
1112 uint32_t uid)
1113 {
1114 carray * tab;
1115 int res;
1116 uint32_t * puid;
1117 int r;
1118
1119 tab = carray_new(1);
1120 if (tab == NULL) {
1121 res = MAILMBOX_ERROR_MEMORY;
1122 goto err;
1123 }
1124
1125 puid = malloc(sizeof(* puid));
1126 if (puid == NULL) {
1127 res = MAILMBOX_ERROR_MEMORY;
1128 goto free_array;
1129 }
1130 * puid = uid;
1131
1132 r = claws_mailmbox_copy_msg_list(dest_folder, src_folder, tab);
1133 res = r;
1134
1135 free(puid);
1136 free_array:
1137 carray_free(tab);
1138 err:
1139 return res;
1140 }
1141
claws_mailmbox_expunge_to_file_no_lock(char * dest_filename,int dest_fd,struct claws_mailmbox_folder * folder,size_t * result_size)1142 static int claws_mailmbox_expunge_to_file_no_lock(char * dest_filename, int dest_fd,
1143 struct claws_mailmbox_folder * folder,
1144 size_t * result_size)
1145 {
1146 int r;
1147 int res;
1148 unsigned long i;
1149 size_t cur_offset;
1150 char * dest = NULL;
1151 size_t size;
1152
1153 size = 0;
1154 for(i = 0 ; i < carray_count(folder->mb_tab) ; i ++) {
1155 struct claws_mailmbox_msg_info * info;
1156
1157 info = carray_get(folder->mb_tab, i);
1158
1159 if (!info->msg_deleted) {
1160 size += info->msg_size + info->msg_padding;
1161
1162 if (!folder->mb_no_uid) {
1163 if (!info->msg_written_uid) {
1164 uint32_t uid;
1165
1166 #if CRLF_BADNESS
1167 size += strlen(UID_HEADER " \r\n");
1168 #else
1169 size += strlen(UID_HEADER " \n");
1170 #endif
1171 uid = info->msg_uid;
1172 while (uid >= 10) {
1173 uid /= 10;
1174 size ++;
1175 }
1176 size ++;
1177 }
1178 }
1179 }
1180 }
1181
1182 r = ftruncate(dest_fd, size);
1183 if (r < 0) {
1184 res = MAILMBOX_ERROR_FILE;
1185 goto err;
1186 }
1187
1188 if (size) {
1189 dest = (char *) mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, dest_fd, 0);
1190 if (dest == MAP_FAILED) {
1191 res = MAILMBOX_ERROR_FILE;
1192 goto err;
1193 }
1194 }
1195
1196 cur_offset = 0;
1197 for(i = 0 ; i < carray_count(folder->mb_tab) ; i ++) {
1198 struct claws_mailmbox_msg_info * info;
1199
1200 info = carray_get(folder->mb_tab, i);
1201
1202 if (!info->msg_deleted) {
1203 memcpy(dest + cur_offset, folder->mb_mapping + info->msg_start,
1204 info->msg_headers_len + info->msg_start_len);
1205 cur_offset += info->msg_headers_len + info->msg_start_len;
1206
1207 if (!folder->mb_no_uid) {
1208 if (!info->msg_written_uid) {
1209 size_t numlen;
1210
1211 memcpy(dest + cur_offset, UID_HEADER " ", strlen(UID_HEADER " "));
1212 cur_offset += strlen(UID_HEADER " ");
1213 #if CRLF_BADNESS
1214 numlen = snprintf(dest + cur_offset, size - cur_offset,
1215 "%i\r\n", info->msg_uid);
1216 #else
1217 numlen = snprintf(dest + cur_offset, size - cur_offset,
1218 "%i\n", info->msg_uid);
1219 #endif
1220 cur_offset += numlen;
1221 }
1222 }
1223
1224 memcpy(dest + cur_offset,
1225 folder->mb_mapping + info->msg_headers + info->msg_headers_len,
1226 info->msg_size - (info->msg_start_len + info->msg_headers_len)
1227 + info->msg_padding);
1228
1229 cur_offset += info->msg_size -
1230 (info->msg_start_len + info->msg_headers_len)
1231 + info->msg_padding;
1232 }
1233 }
1234 fflush(stdout);
1235
1236 if (size) {
1237 msync(dest, size, MS_SYNC);
1238 munmap(dest, size);
1239 }
1240
1241 * result_size = size;
1242
1243 return MAILMBOX_NO_ERROR;
1244
1245 err:
1246 return res;
1247 }
1248
claws_mailmbox_expunge_no_lock(struct claws_mailmbox_folder * folder)1249 int claws_mailmbox_expunge_no_lock(struct claws_mailmbox_folder * folder)
1250 {
1251 char tmpfile[PATH_MAX + 8]; /* for the extra Xs */
1252 int r;
1253 int res;
1254 int dest_fd;
1255 size_t size;
1256
1257 if (folder->mb_read_only)
1258 return MAILMBOX_ERROR_READONLY;
1259
1260 if (((folder->mb_written_uid >= folder->mb_max_uid) || folder->mb_no_uid) &&
1261 (!folder->mb_changed)) {
1262 /* no need to expunge */
1263 return MAILMBOX_NO_ERROR;
1264 }
1265
1266 snprintf(tmpfile, sizeof(tmpfile), "%sXXXXXX", folder->mb_filename);
1267 dest_fd = g_mkstemp(tmpfile);
1268
1269 if (dest_fd < 0) {
1270 res = MAILMBOX_ERROR_FILE;
1271 goto unlink;
1272 }
1273
1274 r = claws_mailmbox_expunge_to_file_no_lock(tmpfile, dest_fd,
1275 folder, &size);
1276 if (r != MAILMBOX_NO_ERROR) {
1277 res = r;
1278 goto unlink;
1279 }
1280
1281 close(dest_fd);
1282
1283 r = rename(tmpfile, folder->mb_filename);
1284 if (r < 0) {
1285 res = r;
1286 goto err;
1287 }
1288
1289 claws_mailmbox_unmap(folder);
1290 claws_mailmbox_close(folder);
1291
1292 r = claws_mailmbox_open(folder);
1293 if (r != MAILMBOX_NO_ERROR) {
1294 res = r;
1295 goto err;
1296 }
1297
1298 r = claws_mailmbox_map(folder);
1299 if (r != MAILMBOX_NO_ERROR) {
1300 res = r;
1301 goto err;
1302 }
1303
1304 r = claws_mailmbox_parse(folder);
1305 if (r != MAILMBOX_NO_ERROR) {
1306 res = r;
1307 goto err;
1308 }
1309
1310 claws_mailmbox_timestamp(folder);
1311
1312 folder->mb_changed = FALSE;
1313 folder->mb_deleted_count = 0;
1314
1315 return MAILMBOX_NO_ERROR;
1316
1317 unlink:
1318 close(dest_fd);
1319 unlink(tmpfile);
1320 err:
1321 return res;
1322 }
1323
claws_mailmbox_expunge(struct claws_mailmbox_folder * folder)1324 int claws_mailmbox_expunge(struct claws_mailmbox_folder * folder)
1325 {
1326 int r;
1327 int res;
1328
1329 r = claws_mailmbox_validate_write_lock(folder);
1330 if (r != MAILMBOX_NO_ERROR) {
1331 res = r;
1332 goto err;
1333 }
1334
1335 r = claws_mailmbox_expunge_no_lock(folder);
1336 res = r;
1337
1338 claws_mailmbox_write_unlock(folder);
1339 err:
1340 return res;
1341 }
1342
claws_mailmbox_delete_msg(struct claws_mailmbox_folder * folder,uint32_t uid)1343 int claws_mailmbox_delete_msg(struct claws_mailmbox_folder * folder, uint32_t uid)
1344 {
1345 struct claws_mailmbox_msg_info * info;
1346 int res;
1347 chashdatum key;
1348 chashdatum data;
1349 int r;
1350
1351 if (folder->mb_read_only) {
1352 res = MAILMBOX_ERROR_READONLY;
1353 goto err;
1354 }
1355
1356 key.data = &uid;
1357 key.len = sizeof(uid);
1358
1359 r = chash_get(folder->mb_hash, &key, &data);
1360 if (r < 0) {
1361 res = MAILMBOX_ERROR_MSG_NOT_FOUND;
1362 goto err;
1363 }
1364
1365 info = data.data;
1366
1367 if (info->msg_deleted) {
1368 res = MAILMBOX_ERROR_MSG_NOT_FOUND;
1369 goto err;
1370 }
1371
1372 info->msg_deleted = TRUE;
1373 folder->mb_changed = TRUE;
1374 folder->mb_deleted_count ++;
1375
1376 return MAILMBOX_NO_ERROR;
1377
1378 err:
1379 return res;
1380 }
1381
1382
1383 /*
1384 INIT of MBOX
1385
1386 - open file
1387 - map the file
1388
1389 - lock the file
1390
1391 - parse memory
1392
1393 - unlock the file
1394 */
1395
claws_mailmbox_init(const char * filename,int force_readonly,int force_no_uid,uint32_t default_written_uid,struct claws_mailmbox_folder ** result_folder)1396 int claws_mailmbox_init(const char * filename,
1397 int force_readonly,
1398 int force_no_uid,
1399 uint32_t default_written_uid,
1400 struct claws_mailmbox_folder ** result_folder)
1401 {
1402 struct claws_mailmbox_folder * folder;
1403 int r;
1404 int res;
1405
1406 folder = claws_mailmbox_folder_new(filename);
1407 if (folder == NULL) {
1408 debug_print("folder is null for %s\n", filename);
1409 res = MAILMBOX_ERROR_MEMORY;
1410 goto err;
1411 }
1412 folder->mb_no_uid = force_no_uid;
1413 folder->mb_read_only = force_readonly;
1414 folder->mb_written_uid = default_written_uid;
1415
1416 folder->mb_changed = FALSE;
1417 folder->mb_deleted_count = 0;
1418
1419 r = claws_mailmbox_open(folder);
1420 if (r != MAILMBOX_NO_ERROR) {
1421 debug_print("folder can't be opened %d\n", r);
1422 res = r;
1423 goto free;
1424 }
1425
1426 r = claws_mailmbox_map(folder);
1427 if (r != MAILMBOX_NO_ERROR) {
1428 debug_print("folder can't be mapped %d\n", r);
1429 res = r;
1430 goto close;
1431 }
1432
1433 r = claws_mailmbox_validate_read_lock(folder);
1434 if (r != MAILMBOX_NO_ERROR) {
1435 debug_print("folder can't be locked %d\n", r);
1436 res = r;
1437 goto unmap;
1438 }
1439
1440 claws_mailmbox_read_unlock(folder);
1441
1442 * result_folder = folder;
1443
1444 return MAILMBOX_NO_ERROR;
1445
1446 unmap:
1447 claws_mailmbox_unmap(folder);
1448 close:
1449 claws_mailmbox_close(folder);
1450 free:
1451 claws_mailmbox_folder_free(folder);
1452 err:
1453 return res;
1454 }
1455
1456
1457 /*
1458 when MBOX is DONE
1459
1460 - check for changes
1461
1462 - unmap the file
1463 - close file
1464 */
1465
claws_mailmbox_done(struct claws_mailmbox_folder * folder)1466 void claws_mailmbox_done(struct claws_mailmbox_folder * folder)
1467 {
1468 if (!folder->mb_read_only)
1469 claws_mailmbox_expunge(folder);
1470
1471 claws_mailmbox_unmap(folder);
1472 claws_mailmbox_close(folder);
1473
1474 claws_mailmbox_folder_free(folder);
1475 }
1476