7cb25fda4e31a2036126266f46fb6e81ed04a20c
[claws.git] / src / plugins / mailmbox / mailmbox.c
1 /*
2  * libEtPan! -- a mail stuff library
3  *
4  * Copyright (C) 2001, 2002 - DINH Viet Hoa
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the libEtPan! project nor the names of its
16  *    contributors may be used to endorse or promote products derived
17  *    from this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31
32 /*
33  * $Id$
34  */
35
36 #include "mailmbox.h"
37
38 #include <sys/file.h>
39 #include <sys/stat.h>
40 #include <unistd.h>
41 #include <sys/mman.h>
42 #include <fcntl.h>
43 #include <time.h>
44 #include <sys/types.h>
45 #include <unistd.h>
46
47 #include <string.h>
48 #include <ctype.h>
49 #include <stdlib.h>
50 #include <stdio.h>
51 #include <limits.h>
52
53 #include "mmapstring.h"
54 #include "mailmbox_parse.h"
55 #include "maillock.h"
56 #include "utils.h"
57
58 /*
59   http://www.qmail.org/qmail-manual-html/man5/mbox.html
60   RFC 2076
61 */
62
63 #define TMPDIR "/tmp"
64
65 /* mbox is a file with a corresponding filename */
66
67 #define UID_HEADER "X-LibEtPan-UID:"
68
69 #ifndef TRUE
70 #define TRUE 1
71 #endif
72
73 #ifndef FALSE
74 #define FALSE 0
75 #endif
76
77 int claws_mailmbox_write_lock(struct claws_mailmbox_folder * folder)
78 {
79   int r;
80
81   if (folder->mb_read_only)
82     return MAILMBOX_ERROR_READONLY;
83
84   r = maillock_write_lock(folder->mb_filename, folder->mb_fd);
85   if (r == 0)
86     return MAILMBOX_NO_ERROR;
87   else
88     return MAILMBOX_ERROR_FILE;
89 }
90
91 int claws_mailmbox_write_unlock(struct claws_mailmbox_folder * folder)
92 {
93   int r;
94
95   r = maillock_write_unlock(folder->mb_filename, folder->mb_fd);
96   if (r == 0)
97     return MAILMBOX_NO_ERROR;
98   else
99     return MAILMBOX_ERROR_FILE;
100 }
101
102 int claws_mailmbox_read_lock(struct claws_mailmbox_folder * folder)
103 {
104   int r;
105
106   r = maillock_read_lock(folder->mb_filename, folder->mb_fd);
107   if (r == 0)
108     return MAILMBOX_NO_ERROR;
109   else
110     return MAILMBOX_ERROR_FILE;
111 }
112
113 int claws_mailmbox_read_unlock(struct claws_mailmbox_folder * folder)
114 {
115   int r;
116
117   r = maillock_read_unlock(folder->mb_filename, folder->mb_fd);
118   if (r == 0)
119     return MAILMBOX_NO_ERROR;
120   else
121     return MAILMBOX_ERROR_FILE;
122 }
123
124
125 /*
126   map the file into memory.
127   the file must be locked.
128 */
129
130 int claws_mailmbox_map(struct claws_mailmbox_folder * folder)
131 {
132   char * str;
133   struct stat buf;
134   int res;
135   int r;
136
137   r = stat(folder->mb_filename, &buf);
138   if (r < 0) {
139     debug_print("stat failed %d\n", r);
140     res = MAILMBOX_ERROR_FILE;
141     goto err;
142   }
143
144   if (buf.st_size == 0) {
145     folder->mb_mapping = NULL;
146     folder->mb_mapping_size = 0;
147     return MAILMBOX_NO_ERROR;
148   }
149   if (folder->mb_read_only)
150     str = (char *) mmap(NULL, buf.st_size, PROT_READ,
151                         MAP_PRIVATE, folder->mb_fd, 0);
152   else
153     str = (char *) mmap(NULL, buf.st_size, PROT_READ | PROT_WRITE,
154                         MAP_SHARED, folder->mb_fd, 0);
155   if (str == MAP_FAILED) {
156     perror("mmap");
157     debug_print("map of %lu bytes failed\n", buf.st_size);
158     res = MAILMBOX_ERROR_FILE;
159     goto err;
160   }
161   
162   folder->mb_mapping = str;
163   folder->mb_mapping_size = buf.st_size;
164
165   return MAILMBOX_NO_ERROR;
166
167  err:
168   return res;
169 }
170
171 /*
172   unmap the file
173 */
174
175 void claws_mailmbox_unmap(struct claws_mailmbox_folder * folder)
176 {
177   munmap(folder->mb_mapping, folder->mb_mapping_size);
178   folder->mb_mapping = NULL;
179   folder->mb_mapping_size = 0;
180 }
181
182 void claws_mailmbox_sync(struct claws_mailmbox_folder * folder)
183 {
184   msync(folder->mb_mapping, folder->mb_mapping_size, MS_SYNC);
185 }
186
187 void claws_mailmbox_timestamp(struct claws_mailmbox_folder * folder)
188 {
189   int r;
190   struct stat buf;
191
192   r = stat(folder->mb_filename, &buf);
193   if (r < 0)
194     folder->mb_mtime = (time_t) -1;
195   else
196     folder->mb_mtime = buf.st_mtime;
197 }
198
199 /*
200   open the file
201 */
202
203 int claws_mailmbox_open(struct claws_mailmbox_folder * folder)
204 {
205   int fd = -1;
206   int read_only;
207
208   if (!folder->mb_read_only) {
209     read_only = FALSE;
210     fd = open(folder->mb_filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
211   }
212
213   if (folder->mb_read_only || (fd < 0)) {
214     read_only = TRUE;
215     fd = open(folder->mb_filename, O_RDONLY);
216     if (fd < 0)
217       return MAILMBOX_ERROR_FILE_NOT_FOUND;
218   }
219
220   folder->mb_fd = fd;
221   folder->mb_read_only = read_only;
222
223   return MAILMBOX_NO_ERROR;
224 }
225
226 /*
227   close the file
228 */
229
230 void claws_mailmbox_close(struct claws_mailmbox_folder * folder)
231 {
232   close(folder->mb_fd);
233   folder->mb_fd = -1;
234 }
235
236
237 static int claws_mailmbox_validate_lock(struct claws_mailmbox_folder * folder,
238     int (* custom_lock)(struct claws_mailmbox_folder *),
239     int (* custom_unlock)(struct claws_mailmbox_folder *))
240 {
241   struct stat buf;
242   int res;
243   int r;
244
245   r = stat(folder->mb_filename, &buf);
246   if (r < 0) {
247     buf.st_mtime = (time_t) -1;
248   }
249
250   if ((buf.st_mtime != folder->mb_mtime) ||
251       ((size_t) buf.st_size != folder->mb_mapping_size)) {
252     int r;
253
254     claws_mailmbox_unmap(folder);
255     claws_mailmbox_close(folder);
256
257     r = claws_mailmbox_open(folder);
258     if (r != MAILMBOX_NO_ERROR) {
259       res = r;
260       goto err;
261     }
262
263     r = custom_lock(folder);
264     if (r != MAILMBOX_NO_ERROR) {
265       res = r;
266       goto err;
267     }
268
269     r = claws_mailmbox_map(folder);
270     if (r != MAILMBOX_NO_ERROR) {
271       res = r;
272       goto err_unlock;
273     }
274
275     r = claws_mailmbox_parse(folder);
276     if (r != MAILMBOX_NO_ERROR) {
277       res = r;
278       goto err_unlock;
279     }
280
281     folder->mb_mtime = buf.st_mtime;
282
283     return MAILMBOX_NO_ERROR;
284   }
285   else {
286     r = custom_lock(folder);
287     if (r != MAILMBOX_NO_ERROR) {
288       res = r;
289       goto err;
290     }
291   }
292
293   return MAILMBOX_NO_ERROR;
294
295  err_unlock:
296   custom_unlock(folder);
297  err:
298   return res;
299 }
300
301
302 int claws_mailmbox_validate_write_lock(struct claws_mailmbox_folder * folder)
303 {
304   return claws_mailmbox_validate_lock(folder,
305                                 claws_mailmbox_write_lock,
306                                 claws_mailmbox_write_unlock);
307 }
308
309
310 int claws_mailmbox_validate_read_lock(struct claws_mailmbox_folder * folder)
311 {
312   return claws_mailmbox_validate_lock(folder,
313                                 claws_mailmbox_read_lock,
314                                 claws_mailmbox_read_unlock);
315 }
316
317
318 /* ********************************************************************** */
319 /* append messages */
320
321 #define MAX_FROM_LINE_SIZE 256
322
323 static inline size_t get_line(const char * line, size_t length,
324                               const char ** pnext_line, size_t * pcount)
325 {
326   size_t count;
327
328   count = 0;
329
330   while (1) {
331     if (length == 0)
332       break;
333
334     if (* line == '\r') {
335       line ++;
336
337       count ++;
338       length --;
339
340       if (length > 0) {
341         if (* line == '\n') {
342           line ++;
343           
344           count ++;
345           length --;
346           
347           break;
348         }
349       }
350     }
351     else if (* line == '\n') {
352       line ++;
353
354       count ++;
355       length --;
356
357       break;
358     }
359     else {
360       line ++;
361       length --;
362       count ++;
363     }
364   }
365
366   * pnext_line = line;
367   * pcount = count;
368
369   return count;
370 }
371
372 /*
373   TODO : should strip \r\n if any
374   see also in write_fixed_line
375 */
376
377 static inline size_t get_fixed_line_size(const char * line, size_t length,
378     const char ** pnext_line, size_t * pcount,
379     size_t * pfixed_count)
380 {
381   size_t count;
382   const char * next_line;
383   size_t fixed_count;
384   
385   if (!get_line(line, length, &next_line, &count))
386     return 0;
387
388   fixed_count = count;
389   if (count >= 5) {
390     if (line[0] == 'F') {
391       if (strncmp(line, "From ", 5) == 0)
392         fixed_count ++;
393     }
394   }
395
396   * pnext_line = next_line;
397   * pcount = count;
398   * pfixed_count = fixed_count;
399
400   return count;
401 }
402
403 static size_t get_fixed_message_size(const char * message, size_t size,
404                                      uint32_t uid, int force_no_uid)
405 {
406   size_t fixed_size;
407   size_t cur_token;
408   size_t left;
409   const char * next;
410   const char * cur;
411   int end;
412   int r;
413   uint32_t tmp_uid;
414
415   cur_token = 0;
416
417   fixed_size = 0;
418
419   /* headers */
420
421   end = FALSE;
422   while (!end) {
423     size_t begin;
424     int ignore;
425
426     ignore = FALSE;
427     begin = cur_token;
428     if (cur_token + strlen(UID_HEADER) <= size) {
429       if (message[cur_token] == 'X') {
430         if (strncasecmp(message + cur_token, UID_HEADER,
431                         strlen(UID_HEADER)) == 0) {
432           ignore = TRUE;
433         }
434       }
435     }
436
437     r = mailimf_ignore_field_parse(message, size, &cur_token);
438     switch (r) {
439     case MAILIMF_NO_ERROR:
440       if (!ignore)
441         fixed_size += cur_token - begin;
442       break;
443     case MAILIMF_ERROR_PARSE:
444     default:
445       end = TRUE;
446       break;
447     }
448   }
449
450   if (!force_no_uid) {
451     /* UID header */
452
453 #if CRLF_BADNESS    
454     fixed_size += strlen(UID_HEADER " \r\n");
455 #else
456     fixed_size += strlen(UID_HEADER " \n");
457 #endif
458     
459     tmp_uid = uid;
460     while (tmp_uid >= 10) {
461       tmp_uid /= 10;
462       fixed_size ++;
463     }
464     fixed_size ++;
465   }
466
467   /* body */
468
469   left = size - cur_token;
470   next = message + cur_token;
471   while (left > 0) {
472     size_t count;
473     size_t fixed_count;
474
475     cur = next;
476
477     if (!get_fixed_line_size(cur, left, &next, &count, &fixed_count))
478       break;
479
480     fixed_size += fixed_count;
481     left -= count;
482   }
483
484   return fixed_size;
485 }
486
487 static inline char * write_fixed_line(char * str,
488     const char * line, size_t length,
489     const char ** pnext_line, size_t * pcount)
490 {
491   size_t count;
492   const char * next_line;
493   
494   if (!get_line(line, length, &next_line, &count))
495     return str;
496
497   if (count >= 5) {
498     if (line[0] == 'F') {
499       if (strncmp(line, "From ", 5) == 0) {
500         * str = '>';
501         str ++;
502       }
503     }
504   }
505
506   memcpy(str, line, count);
507   
508   * pnext_line = next_line;
509   * pcount = count;
510   str += count;
511
512   return str;
513 }
514
515 static char * write_fixed_message(char * str,
516                                   const char * message, size_t size,
517                                   uint32_t uid, int force_no_uid)
518 {
519   size_t fixed_size;
520   size_t cur_token;
521   size_t left;
522   int end;
523   int r;
524   const char * cur_src;
525   size_t numlen;
526
527   cur_token = 0;
528
529   fixed_size = 0;
530
531   /* headers */
532
533   end = FALSE;
534   while (!end) {
535     size_t begin;
536     int ignore;
537
538     ignore = FALSE;
539     begin = cur_token;
540     if (cur_token + strlen(UID_HEADER) <= size) {
541       if (message[cur_token] == 'X') {
542         if (strncasecmp(message + cur_token, UID_HEADER,
543                         strlen(UID_HEADER)) == 0) {
544           ignore = TRUE;
545         }
546       }
547     }
548
549     r = mailimf_ignore_field_parse(message, size, &cur_token);
550     switch (r) {
551     case MAILIMF_NO_ERROR:
552       if (!ignore) {
553         memcpy(str, message + begin, cur_token - begin);
554         str += cur_token - begin;
555       }
556       break;
557     case MAILIMF_ERROR_PARSE:
558     default:
559       end = TRUE;
560       break;
561     }
562   }
563
564   if (!force_no_uid) {
565     /* UID header */
566     
567     memcpy(str, UID_HEADER " ", strlen(UID_HEADER " "));
568     str += strlen(UID_HEADER " ");
569 #if CRLF_BADNESS    
570     numlen = snprintf(str, 20, "%i\r\n", uid);
571 #else
572     numlen = snprintf(str, 20, "%i\n", uid);
573 #endif
574     str += numlen;
575   }
576
577   /* body */
578
579   cur_src = message + cur_token;
580   left = size - cur_token;
581   while (left > 0) {
582     size_t count = 0;
583     const char * next = NULL;
584
585     str = write_fixed_line(str, cur_src, left, &next, &count);
586     
587     cur_src = next;
588     left -= count;
589   }
590
591   return str;
592 }
593
594 #define DEFAULT_FROM_LINE "From - Wed Jun 30 21:49:08 1993\n"
595
596 int
597 claws_mailmbox_append_message_list_no_lock(struct claws_mailmbox_folder * folder,
598                                      carray * append_tab)
599 {
600   size_t extra_size;
601   int r;
602   char from_line[MAX_FROM_LINE_SIZE] = DEFAULT_FROM_LINE;
603   struct tm time_info;
604   time_t date;
605   int res;
606   size_t old_size;
607   char * str;
608   unsigned int i;
609   size_t from_size;
610   size_t maxuid;
611   size_t left;
612   size_t crlf_count;
613
614   if (folder->mb_read_only) {
615     res = MAILMBOX_ERROR_READONLY;
616     goto err;
617   }
618
619   date = time(NULL);
620   from_size = strlen(DEFAULT_FROM_LINE);
621   if (localtime_r(&date, &time_info) != NULL)
622     from_size = strftime(from_line, MAX_FROM_LINE_SIZE, "From - %c\n", &time_info);
623
624   maxuid = /* */ folder->mb_max_uid;
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     ftruncate(folder->mb_fd, old_size);
686     return MAILMBOX_ERROR_FILE;
687   }
688
689   str = folder->mb_mapping + old_size;
690
691   if (old_size != 0) {
692     for(i = 0 ; i < 2 - crlf_count ; i ++) {
693 #if CRLF_BADNESS    
694       * str = '\r';
695       str ++;
696 #endif      
697       * str = '\n';
698       str ++;
699     }
700   }
701
702   for(i = 0 ; i < carray_count(append_tab) ; i ++) {
703     struct claws_mailmbox_append_info * info;
704
705     info = carray_get(append_tab, i);
706
707     memcpy(str, from_line, from_size);
708
709     str += strlen(from_line);
710
711     str = write_fixed_message(str, info->ai_message, info->ai_size,
712         folder->mb_max_uid + i + 1,
713         folder->mb_no_uid);
714
715 #if CRLF_BADNESS
716     * str = '\r';
717     str ++;
718 #endif    
719     * str = '\n';
720     str ++;
721   }
722
723   folder->mb_max_uid += carray_count(append_tab);
724
725   return MAILMBOX_NO_ERROR;
726
727  err:
728   return res;
729 }
730
731 int
732 claws_mailmbox_append_message_list(struct claws_mailmbox_folder * folder,
733                              carray * append_tab)
734 {
735   int r;
736   int res;
737   size_t cur_token;
738
739   r = claws_mailmbox_validate_write_lock(folder);
740   if (r != MAILMBOX_NO_ERROR) {
741     res = r;
742     goto err;
743   }
744
745   r = claws_mailmbox_expunge_no_lock(folder);
746   if (r != MAILMBOX_NO_ERROR) {
747     res = r;
748     goto unlock;
749   }
750
751   cur_token = folder->mb_mapping_size;
752
753   r = claws_mailmbox_append_message_list_no_lock(folder, append_tab);
754   if (r != MAILMBOX_NO_ERROR) {
755     res = r;
756     goto unlock;
757   }
758
759   claws_mailmbox_sync(folder);
760
761   r = claws_mailmbox_parse_additionnal(folder, &cur_token);
762   if (r != MAILMBOX_NO_ERROR) {
763     res = r;
764     goto unlock;
765   }
766
767   claws_mailmbox_timestamp(folder);
768
769   claws_mailmbox_write_unlock(folder);
770
771   return MAILMBOX_NO_ERROR;
772
773  unlock:
774   claws_mailmbox_write_unlock(folder);
775  err:
776   return res;
777 }
778
779 int
780 claws_mailmbox_append_message(struct claws_mailmbox_folder * folder,
781                         const char * data, size_t len)
782 {
783   carray * tab;
784   struct claws_mailmbox_append_info * append_info;
785   int res;
786   int r;
787
788   tab = carray_new(1);
789   if (tab == NULL) {
790     res = MAILMBOX_ERROR_MEMORY;
791     goto err;
792   }
793
794   append_info = claws_mailmbox_append_info_new(data, len);
795   if (append_info == NULL) {
796     res = MAILMBOX_ERROR_MEMORY;
797     goto free_list;
798   }
799
800   r = carray_add(tab, append_info, NULL);
801   if (r < 0) {
802     res = MAILMBOX_ERROR_MEMORY;
803     goto free_append_info;
804   }
805
806   r = claws_mailmbox_append_message_list(folder, tab);
807
808   claws_mailmbox_append_info_free(append_info);
809   carray_free(tab);
810
811   return r;
812
813  free_append_info:
814   claws_mailmbox_append_info_free(append_info);
815  free_list:
816   carray_free(tab);
817  err:
818   return res;
819 }
820
821 /* ********************************************************************** */
822
823 int claws_mailmbox_fetch_msg_no_lock(struct claws_mailmbox_folder * folder,
824                                uint32_t num, const char ** result,
825                                size_t * result_len)
826 {
827   struct claws_mailmbox_msg_info * info;
828   int res;
829   chashdatum key;
830   chashdatum data;
831   int r;
832   
833   key.data = &num;
834   key.len = sizeof(num);
835   
836   r = chash_get(folder->mb_hash, &key, &data);
837   if (r < 0) {
838     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
839     goto err;
840   }
841   
842   info = data.data;
843   
844   if (info->msg_deleted) {
845     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
846     goto err;
847   }
848
849   * result = folder->mb_mapping + info->msg_headers;
850   * result_len = info->msg_size - info->msg_start_len;
851
852   return MAILMBOX_NO_ERROR;
853
854  err:
855   return res;
856 }
857
858 int claws_mailmbox_fetch_msg_headers_no_lock(struct claws_mailmbox_folder * folder,
859                                        uint32_t num, const char ** result,
860                                        size_t * result_len)
861 {
862   struct claws_mailmbox_msg_info * info;
863   int res;
864   chashdatum key;
865   chashdatum data;
866   int r;
867   
868   key.data = &num;
869   key.len = sizeof(num);
870   
871   r = chash_get(folder->mb_hash, &key, &data);
872   if (r < 0) {
873     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
874     goto err;
875   }
876
877   info = data.data;
878
879   if (info->msg_deleted) {
880     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
881     goto err;
882   }
883
884   * result = folder->mb_mapping + info->msg_headers;
885   * result_len = info->msg_headers_len;
886
887   return MAILMBOX_NO_ERROR;
888
889  err:
890   return res;
891 }
892
893 int claws_mailmbox_fetch_msg(struct claws_mailmbox_folder * folder,
894                        uint32_t num, const char ** result,
895                        size_t * result_len)
896 {
897   MMAPString * mmapstr;
898   int res;
899   const char * data;
900   size_t len;
901   int r;
902   size_t fixed_size;
903   char * end;
904   
905   r = claws_mailmbox_validate_read_lock(folder);
906   if (r != MAILMBOX_NO_ERROR) {
907     res = r;
908     goto err;
909   }
910
911   r = claws_mailmbox_fetch_msg_no_lock(folder, num, &data, &len);
912   if (r != MAILMBOX_NO_ERROR) {
913     res = r;
914     goto unlock;
915   }
916   
917   /* size with no uid */
918   fixed_size = get_fixed_message_size(data, len, 0, 1 /* force no uid */);
919   
920 #if 0
921   mmapstr = mmap_string_new_len(data, fixed_size);
922   if (mmapstr == NULL) {
923     res = MAILMBOX_ERROR_MEMORY;
924     goto unlock;
925   }
926 #endif
927   mmapstr = mmap_string_sized_new(fixed_size);
928   if (mmapstr == NULL) {
929     res = MAILMBOX_ERROR_MEMORY;
930     goto unlock;
931   }
932   
933   end = write_fixed_message(mmapstr->str, data, len, 0, 1 /* force no uid */);
934   * end = '\0';
935   mmapstr->len = fixed_size;
936   
937   r = mmap_string_ref(mmapstr);
938   if (r < 0) {
939     mmap_string_free(mmapstr);
940     res = MAILMBOX_ERROR_MEMORY;
941     goto unlock;
942   }
943
944   * result = mmapstr->str;
945   * result_len = mmapstr->len;
946
947   claws_mailmbox_read_unlock(folder);
948
949   return MAILMBOX_NO_ERROR;
950
951  unlock:
952   claws_mailmbox_read_unlock(folder);
953  err:
954   return res;
955 }
956
957 int claws_mailmbox_fetch_msg_headers(struct claws_mailmbox_folder * folder,
958                                uint32_t num, const char ** result,
959                                size_t * result_len)
960 {
961   MMAPString * mmapstr;
962   int res;
963   const char * data;
964   size_t len;
965   int r;
966   size_t fixed_size;
967   char * end;
968
969   r = claws_mailmbox_validate_read_lock(folder);
970   if (r != MAILMBOX_NO_ERROR) {
971     res = r;
972     goto err;
973   }
974
975   r = claws_mailmbox_fetch_msg_headers_no_lock(folder, num, &data, &len);
976   if (r != MAILMBOX_NO_ERROR) {
977     res = r;
978     goto unlock;
979   }
980
981 #if 0
982   mmapstr = mmap_string_new_len(data, len);
983   if (mmapstr == NULL) {
984     res = MAILMBOX_ERROR_MEMORY;
985     goto unlock;
986   }
987 #endif
988   /* size with no uid */
989   fixed_size = get_fixed_message_size(data, len, 0, 1 /* force no uid */);
990   
991   mmapstr = mmap_string_sized_new(fixed_size);
992   if (mmapstr == NULL) {
993     res = MAILMBOX_ERROR_MEMORY;
994     goto unlock;
995   }
996   
997   end = write_fixed_message(mmapstr->str, data, len, 0, 1 /* force no uid */);
998   * end = '\0';
999   mmapstr->len = fixed_size;
1000
1001   r = mmap_string_ref(mmapstr);
1002   if (r < 0) {
1003     mmap_string_free(mmapstr);
1004     res = MAILMBOX_ERROR_MEMORY;
1005     goto unlock;
1006   }
1007
1008   * result = mmapstr->str;
1009   * result_len = mmapstr->len;
1010
1011   claws_mailmbox_read_unlock(folder);
1012
1013   return MAILMBOX_NO_ERROR;
1014
1015  unlock:
1016   claws_mailmbox_read_unlock(folder);
1017  err:
1018   return res;
1019 }
1020
1021 void claws_mailmbox_fetch_result_free(char * msg)
1022 {
1023   mmap_string_unref(msg);
1024 }
1025
1026
1027 int claws_mailmbox_copy_msg_list(struct claws_mailmbox_folder * dest_folder,
1028                            struct claws_mailmbox_folder * src_folder,
1029                            carray * tab)
1030 {
1031   int r;
1032   int res;
1033   carray * append_tab;
1034   unsigned int i;
1035
1036   r = claws_mailmbox_validate_read_lock(src_folder);
1037   if (r != MAILMBOX_NO_ERROR) {
1038     res = r;
1039     goto err;
1040   }
1041
1042   append_tab = carray_new(carray_count(tab));
1043   if (append_tab == NULL) {
1044     res = MAILMBOX_ERROR_MEMORY;
1045     goto src_unlock;
1046   }
1047
1048   for(i = 0 ; i < carray_count(tab) ; i ++) {
1049     struct claws_mailmbox_append_info * append_info;
1050     const char * data;
1051     size_t len;
1052     uint32_t uid;
1053
1054     uid = * ((uint32_t *) carray_get(tab, i));
1055
1056     r = claws_mailmbox_fetch_msg_no_lock(src_folder, uid, &data, &len);
1057     if (r != MAILMBOX_NO_ERROR) {
1058       res = r;
1059       goto free_list;
1060     }
1061     
1062     append_info = claws_mailmbox_append_info_new(data, len);
1063     if (append_info == NULL) {
1064       res = MAILMBOX_ERROR_MEMORY;
1065       goto free_list;
1066     }
1067     
1068     r = carray_add(append_tab, append_info, NULL);
1069     if (r < 0) {
1070       claws_mailmbox_append_info_free(append_info);
1071       res = MAILMBOX_ERROR_MEMORY;
1072       goto free_list;
1073     }
1074   }    
1075
1076   r = claws_mailmbox_append_message_list(dest_folder, append_tab);
1077   if (r != MAILMBOX_NO_ERROR) {
1078     res = r;
1079     goto src_unlock;
1080   }
1081
1082   for(i = 0 ; i < carray_count(append_tab) ; i ++) {
1083     struct claws_mailmbox_append_info * append_info;
1084
1085     append_info = carray_get(append_tab, i);
1086     claws_mailmbox_append_info_free(append_info);
1087   }
1088   carray_free(append_tab);
1089
1090   claws_mailmbox_read_unlock(src_folder);
1091
1092   return MAILMBOX_NO_ERROR;
1093
1094  free_list:
1095   for(i = 0 ; i < carray_count(append_tab) ; i ++) {
1096     struct claws_mailmbox_append_info * append_info;
1097
1098     append_info = carray_get(append_tab, i);
1099     claws_mailmbox_append_info_free(append_info);
1100   }
1101   carray_free(append_tab);
1102  src_unlock:
1103   claws_mailmbox_read_unlock(src_folder);
1104  err:
1105   return res;
1106 }
1107
1108 int claws_mailmbox_copy_msg(struct claws_mailmbox_folder * dest_folder,
1109                       struct claws_mailmbox_folder * src_folder,
1110                       uint32_t uid)
1111 {
1112   carray * tab;
1113   int res;
1114   uint32_t * puid;
1115   int r;
1116
1117   tab = carray_new(1);
1118   if (tab == NULL) {
1119     res = MAILMBOX_ERROR_MEMORY;
1120     goto err;
1121   }
1122
1123   puid = malloc(sizeof(* puid));
1124   if (puid == NULL) {
1125     res = MAILMBOX_ERROR_MEMORY;
1126     goto free_array;
1127   }
1128   * puid = uid;
1129   
1130   r = claws_mailmbox_copy_msg_list(dest_folder, src_folder, tab);
1131   res = r;
1132
1133   free(puid);
1134  free_array:
1135   carray_free(tab);
1136  err:
1137   return res;
1138 }
1139
1140 static int claws_mailmbox_expunge_to_file_no_lock(char * dest_filename, int dest_fd,
1141     struct claws_mailmbox_folder * folder,
1142     size_t * result_size)
1143 {
1144   int r;
1145   int res;
1146   unsigned long i;
1147   size_t cur_offset;
1148   char * dest;
1149   size_t size;
1150
1151   size = 0;
1152   for(i = 0 ; i < carray_count(folder->mb_tab) ; i ++) {
1153     struct claws_mailmbox_msg_info * info;
1154
1155     info = carray_get(folder->mb_tab, i);
1156
1157     if (!info->msg_deleted) {
1158       size += info->msg_size + info->msg_padding;
1159
1160       if (!folder->mb_no_uid) {
1161         if (!info->msg_written_uid) {
1162           uint32_t uid;
1163          
1164 #if CRLF_BADNESS
1165           size += strlen(UID_HEADER " \r\n");
1166 #else
1167           size += strlen(UID_HEADER " \n");
1168 #endif    
1169           uid = info->msg_uid;
1170           while (uid >= 10) {
1171             uid /= 10;
1172             size ++;
1173           }
1174           size ++;
1175         }
1176       }
1177     }
1178   }
1179
1180   r = ftruncate(dest_fd, size);
1181   if (r < 0) {
1182     res = MAILMBOX_ERROR_FILE;
1183     goto err;
1184   }
1185
1186   if (size) {
1187     dest = (char *) mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, dest_fd, 0);
1188     if (dest == MAP_FAILED) {
1189       res = MAILMBOX_ERROR_FILE;
1190       goto err;
1191     }
1192   }
1193
1194   cur_offset = 0;
1195   for(i = 0 ; i < carray_count(folder->mb_tab) ; i ++) {
1196     struct claws_mailmbox_msg_info * info;
1197     
1198     info = carray_get(folder->mb_tab, i);
1199
1200     if (!info->msg_deleted) {
1201       memcpy(dest + cur_offset, folder->mb_mapping + info->msg_start,
1202              info->msg_headers_len + info->msg_start_len);
1203       cur_offset += info->msg_headers_len + info->msg_start_len;
1204       
1205       if (!folder->mb_no_uid) {
1206         if (!info->msg_written_uid) {
1207           size_t numlen;
1208           
1209           memcpy(dest + cur_offset, UID_HEADER " ", strlen(UID_HEADER " "));
1210           cur_offset += strlen(UID_HEADER " ");
1211 #if CRLF_BADNESS
1212           numlen = snprintf(dest + cur_offset, size - cur_offset,
1213                             "%i\r\n", info->msg_uid);
1214 #else
1215           numlen = snprintf(dest + cur_offset, size - cur_offset,
1216                             "%i\n", info->msg_uid);
1217 #endif                      
1218           cur_offset += numlen;
1219         }
1220       }
1221       
1222       memcpy(dest + cur_offset,
1223              folder->mb_mapping + info->msg_headers + info->msg_headers_len,
1224              info->msg_size - (info->msg_start_len + info->msg_headers_len)
1225              + info->msg_padding);
1226       
1227       cur_offset += info->msg_size -
1228         (info->msg_start_len + info->msg_headers_len)
1229         + info->msg_padding;
1230     }
1231   }
1232   fflush(stdout);
1233
1234   if (size) {
1235     msync(dest, size, MS_SYNC);
1236     munmap(dest, size);
1237   }
1238
1239   * result_size = size;
1240
1241   return MAILMBOX_NO_ERROR;
1242
1243  err:
1244   return res;
1245 }
1246
1247 int claws_mailmbox_expunge_no_lock(struct claws_mailmbox_folder * folder)
1248 {
1249   char tmpfile[PATH_MAX];
1250   int r;
1251   int res;
1252   int dest_fd;
1253   size_t size;
1254
1255   if (folder->mb_read_only)
1256     return MAILMBOX_ERROR_READONLY;
1257
1258   if (((folder->mb_written_uid >= folder->mb_max_uid) || folder->mb_no_uid) &&
1259       (!folder->mb_changed)) {
1260     /* no need to expunge */
1261     return MAILMBOX_NO_ERROR;
1262   }
1263
1264   snprintf(tmpfile, PATH_MAX, "%sXXXXXX", folder->mb_filename);
1265   dest_fd = mkstemp(tmpfile);
1266
1267   if (dest_fd < 0) {
1268     res = MAILMBOX_ERROR_FILE;
1269     goto unlink;
1270   }
1271
1272   r = claws_mailmbox_expunge_to_file_no_lock(tmpfile, dest_fd,
1273                                        folder, &size);
1274   if (r != MAILMBOX_NO_ERROR) {
1275     res = r;
1276     goto unlink;
1277   }
1278
1279   close(dest_fd);
1280
1281   r = rename(tmpfile, folder->mb_filename);
1282   if (r < 0) {
1283     res = r;
1284     goto err;
1285   }
1286
1287   claws_mailmbox_unmap(folder);
1288   claws_mailmbox_close(folder);
1289
1290   r = claws_mailmbox_open(folder);
1291   if (r != MAILMBOX_NO_ERROR) {
1292     res = r;
1293     goto err;
1294   }
1295
1296   r = claws_mailmbox_map(folder);
1297   if (r != MAILMBOX_NO_ERROR) {
1298     res = r;
1299     goto err;
1300   }
1301       
1302   r = claws_mailmbox_parse(folder);
1303   if (r != MAILMBOX_NO_ERROR) {
1304     res = r;
1305     goto err;
1306   }
1307   
1308   claws_mailmbox_timestamp(folder);
1309
1310   folder->mb_changed = FALSE;
1311   folder->mb_deleted_count = 0;
1312   
1313   return MAILMBOX_NO_ERROR;
1314
1315  unlink:
1316   close(dest_fd);
1317   unlink(tmpfile);
1318  err:
1319   return res;
1320 }
1321
1322 int claws_mailmbox_expunge(struct claws_mailmbox_folder * folder)
1323 {
1324   int r;
1325   int res;
1326
1327   r = claws_mailmbox_validate_write_lock(folder);
1328   if (r != MAILMBOX_NO_ERROR) {
1329     res = r;
1330     goto err;
1331   }
1332
1333   r = claws_mailmbox_expunge_no_lock(folder);
1334   res = r;
1335
1336   claws_mailmbox_write_unlock(folder);
1337  err:
1338   return res;
1339 }
1340
1341 int claws_mailmbox_delete_msg(struct claws_mailmbox_folder * folder, uint32_t uid)
1342 {
1343   struct claws_mailmbox_msg_info * info;
1344   int res;
1345   chashdatum key;
1346   chashdatum data;
1347   int r;
1348
1349   if (folder->mb_read_only) {
1350     res = MAILMBOX_ERROR_READONLY;
1351     goto err;
1352   }
1353   
1354   key.data = &uid;
1355   key.len = sizeof(uid);
1356   
1357   r = chash_get(folder->mb_hash, &key, &data);
1358   if (r < 0) {
1359     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
1360     goto err;
1361   }
1362
1363   info = data.data;
1364
1365   if (info->msg_deleted) {
1366     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
1367     goto err;
1368   }
1369
1370   info->msg_deleted = TRUE;
1371   folder->mb_changed = TRUE;
1372   folder->mb_deleted_count ++;
1373
1374   return MAILMBOX_NO_ERROR;
1375
1376  err:
1377   return res;
1378 }
1379
1380
1381 /*
1382   INIT of MBOX
1383
1384   - open file
1385   - map the file
1386
1387   - lock the file
1388
1389   - parse memory
1390
1391   - unlock the file
1392 */
1393
1394 int claws_mailmbox_init(const char * filename,
1395                   int force_readonly,
1396                   int force_no_uid,
1397                   uint32_t default_written_uid,
1398                   struct claws_mailmbox_folder ** result_folder)
1399 {
1400   struct claws_mailmbox_folder * folder;
1401   int r;
1402   int res;
1403   
1404   folder = claws_mailmbox_folder_new(filename);
1405   if (folder == NULL) {
1406     debug_print("folder is null for %s\n", filename);
1407     res = MAILMBOX_ERROR_MEMORY;
1408     goto err;
1409   }
1410   folder->mb_no_uid = force_no_uid;
1411   folder->mb_read_only = force_readonly;
1412   folder->mb_written_uid = default_written_uid;
1413   
1414   folder->mb_changed = FALSE;
1415   folder->mb_deleted_count = 0;
1416   
1417   r = claws_mailmbox_open(folder);
1418   if (r != MAILMBOX_NO_ERROR) {
1419     debug_print("folder can't be opened %d\n", r);
1420     res = r;
1421     goto free;
1422   }
1423
1424   r = claws_mailmbox_map(folder);
1425   if (r != MAILMBOX_NO_ERROR) {
1426     debug_print("folder can't be mapped %d\n", r);
1427     res = r;
1428     goto close;
1429   }
1430
1431   r = claws_mailmbox_validate_read_lock(folder);
1432   if (r != MAILMBOX_NO_ERROR) {
1433     debug_print("folder can't be locked %d\n", r);
1434     res = r;
1435     goto unmap;
1436   }
1437
1438   claws_mailmbox_read_unlock(folder);
1439
1440   * result_folder = folder;
1441
1442   return MAILMBOX_NO_ERROR;
1443
1444  unmap:
1445   claws_mailmbox_unmap(folder);
1446  close:
1447   claws_mailmbox_close(folder);
1448  free:
1449   claws_mailmbox_folder_free(folder);
1450  err:
1451   return res;
1452 }
1453
1454
1455 /*
1456   when MBOX is DONE
1457
1458   - check for changes
1459
1460   - unmap the file
1461   - close file
1462 */
1463
1464 void claws_mailmbox_done(struct claws_mailmbox_folder * folder)
1465 {
1466   if (!folder->mb_read_only)
1467     claws_mailmbox_expunge(folder);
1468   
1469   claws_mailmbox_unmap(folder);
1470   claws_mailmbox_close(folder);
1471
1472   claws_mailmbox_folder_free(folder);
1473 }