Rename claws_io to file-utils, and move file-related functions
[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 #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
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
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
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
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
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
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
188 void claws_mailmbox_sync(struct claws_mailmbox_folder * folder)
189 {
190   msync(folder->mb_mapping, folder->mb_mapping_size, MS_SYNC);
191 }
192
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
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
236 void claws_mailmbox_close(struct claws_mailmbox_folder * folder)
237 {
238   close(folder->mb_fd);
239   folder->mb_fd = -1;
240 }
241
242
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
1023 void claws_mailmbox_fetch_result_free(char * msg)
1024 {
1025   mmap_string_unref(msg);
1026 }
1027
1028
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
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
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
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, PATH_MAX, "%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
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
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
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
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 }