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 = &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_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 = &num;
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