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