500d0263cf6d220804508e895fe4dd1efbbdccee
[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 cur_token;
520   size_t left;
521   int end;
522   int r;
523   const char * cur_src;
524   size_t numlen;
525
526   cur_token = 0;
527
528   /* headers */
529
530   end = FALSE;
531   while (!end) {
532     size_t begin;
533     int ignore;
534
535     ignore = FALSE;
536     begin = cur_token;
537     if (cur_token + strlen(UID_HEADER) <= size) {
538       if (message[cur_token] == 'X') {
539         if (strncasecmp(message + cur_token, UID_HEADER,
540                         strlen(UID_HEADER)) == 0) {
541           ignore = TRUE;
542         }
543       }
544     }
545
546     r = mailimf_ignore_field_parse(message, size, &cur_token);
547     switch (r) {
548     case MAILIMF_NO_ERROR:
549       if (!ignore) {
550         memcpy(str, message + begin, cur_token - begin);
551         str += cur_token - begin;
552       }
553       break;
554     case MAILIMF_ERROR_PARSE:
555     default:
556       end = TRUE;
557       break;
558     }
559   }
560
561   if (!force_no_uid) {
562     /* UID header */
563     
564     memcpy(str, UID_HEADER " ", strlen(UID_HEADER " "));
565     str += strlen(UID_HEADER " ");
566 #if CRLF_BADNESS    
567     numlen = snprintf(str, 20, "%i\r\n", uid);
568 #else
569     numlen = snprintf(str, 20, "%i\n", uid);
570 #endif
571     str += numlen;
572   }
573
574   /* body */
575
576   cur_src = message + cur_token;
577   left = size - cur_token;
578   while (left > 0) {
579     size_t count = 0;
580     const char * next = NULL;
581
582     str = write_fixed_line(str, cur_src, left, &next, &count);
583     
584     cur_src = next;
585     left -= count;
586   }
587
588   return str;
589 }
590
591 #define DEFAULT_FROM_LINE "From - Wed Jun 30 21:49:08 1993\n"
592
593 int
594 claws_mailmbox_append_message_list_no_lock(struct claws_mailmbox_folder * folder,
595                                      carray * append_tab)
596 {
597   size_t extra_size;
598   int r;
599   char from_line[MAX_FROM_LINE_SIZE] = DEFAULT_FROM_LINE;
600   struct tm time_info;
601   time_t date;
602   int res;
603   size_t old_size;
604   char * str;
605   unsigned int i;
606   size_t from_size;
607   size_t left;
608   size_t crlf_count;
609
610   if (folder->mb_read_only) {
611     res = MAILMBOX_ERROR_READONLY;
612     goto err;
613   }
614
615   date = time(NULL);
616   from_size = strlen(DEFAULT_FROM_LINE);
617   if (localtime_r(&date, &time_info) != NULL)
618     from_size = strftime(from_line, MAX_FROM_LINE_SIZE, "From - %c\n", &time_info);
619
620   extra_size = 0;
621   for(i = 0 ; i < carray_count(append_tab) ; i ++) {
622     struct claws_mailmbox_append_info * info;
623
624     info = carray_get(append_tab, i);
625     extra_size += from_size;
626     extra_size += get_fixed_message_size(info->ai_message, info->ai_size,
627         folder->mb_max_uid + i + 1,
628         folder->mb_no_uid);
629 #if CRLF_BADNESS
630     extra_size += 2; /* CR LF */
631 #else
632     extra_size += 1; /* CR LF */
633 #endif    
634   }
635
636   left = folder->mb_mapping_size;
637   crlf_count = 0;
638   while (left >= 1) {
639     if (folder->mb_mapping[left - 1] == '\n') {
640       crlf_count ++;
641       left --;
642     }
643 #if CRLF_BADNESS    
644     else if (folder->mb_mapping[left - 1] == '\r') {
645       left --;
646     }
647 #endif
648     else
649       break;
650
651     if (crlf_count == 2)
652       break;
653   }
654
655   old_size = folder->mb_mapping_size;
656   claws_mailmbox_unmap(folder);
657
658   if (old_size != 0) {
659     if (crlf_count != 2)
660 #if CRLF_BADNESS
661       extra_size += (2 - crlf_count) * 2;
662 #else
663       /* Need the number of LFs, not CRLFs */
664       extra_size += (2 - crlf_count) * 1; /* 2 */
665 #endif      
666   }
667
668   r = ftruncate(folder->mb_fd, extra_size + old_size);
669   if (r < 0) {
670     debug_print("ftruncate failed with %d\n", r);
671     claws_mailmbox_map(folder);
672     res = MAILMBOX_ERROR_FILE;
673     goto err;
674   }
675
676   r = claws_mailmbox_map(folder);
677   if (r < 0) {
678     debug_print("claws_mailmbox_map failed with %d\n", r);
679     r = ftruncate(folder->mb_fd, old_size);
680     if (r < 0)
681       debug_print("ftruncate failed with %d\n", r);
682     return MAILMBOX_ERROR_FILE;
683   }
684
685   str = folder->mb_mapping + old_size;
686
687   if (old_size != 0) {
688     for(i = 0 ; i < 2 - crlf_count ; i ++) {
689 #if CRLF_BADNESS    
690       * str = '\r';
691       str ++;
692 #endif      
693       * str = '\n';
694       str ++;
695     }
696   }
697
698   for(i = 0 ; i < carray_count(append_tab) ; i ++) {
699     struct claws_mailmbox_append_info * info;
700
701     info = carray_get(append_tab, i);
702
703     memcpy(str, from_line, from_size);
704
705     str += strlen(from_line);
706
707     str = write_fixed_message(str, info->ai_message, info->ai_size,
708         folder->mb_max_uid + i + 1,
709         folder->mb_no_uid);
710
711 #if CRLF_BADNESS
712     * str = '\r';
713     str ++;
714 #endif    
715     * str = '\n';
716     str ++;
717   }
718
719   folder->mb_max_uid += carray_count(append_tab);
720
721   return MAILMBOX_NO_ERROR;
722
723  err:
724   return res;
725 }
726
727 int
728 claws_mailmbox_append_message_list(struct claws_mailmbox_folder * folder,
729                              carray * append_tab)
730 {
731   int r;
732   int res;
733   size_t cur_token;
734
735   r = claws_mailmbox_validate_write_lock(folder);
736   if (r != MAILMBOX_NO_ERROR) {
737     res = r;
738     goto err;
739   }
740
741   r = claws_mailmbox_expunge_no_lock(folder);
742   if (r != MAILMBOX_NO_ERROR) {
743     res = r;
744     goto unlock;
745   }
746
747   cur_token = folder->mb_mapping_size;
748
749   r = claws_mailmbox_append_message_list_no_lock(folder, append_tab);
750   if (r != MAILMBOX_NO_ERROR) {
751     res = r;
752     goto unlock;
753   }
754
755   claws_mailmbox_sync(folder);
756
757   r = claws_mailmbox_parse_additionnal(folder, &cur_token);
758   if (r != MAILMBOX_NO_ERROR) {
759     res = r;
760     goto unlock;
761   }
762
763   claws_mailmbox_timestamp(folder);
764
765   claws_mailmbox_write_unlock(folder);
766
767   return MAILMBOX_NO_ERROR;
768
769  unlock:
770   claws_mailmbox_write_unlock(folder);
771  err:
772   return res;
773 }
774
775 int
776 claws_mailmbox_append_message(struct claws_mailmbox_folder * folder,
777                         const char * data, size_t len)
778 {
779   carray * tab;
780   struct claws_mailmbox_append_info * append_info;
781   int res;
782   int r;
783
784   tab = carray_new(1);
785   if (tab == NULL) {
786     res = MAILMBOX_ERROR_MEMORY;
787     goto err;
788   }
789
790   append_info = claws_mailmbox_append_info_new(data, len);
791   if (append_info == NULL) {
792     res = MAILMBOX_ERROR_MEMORY;
793     goto free_list;
794   }
795
796   r = carray_add(tab, append_info, NULL);
797   if (r < 0) {
798     res = MAILMBOX_ERROR_MEMORY;
799     goto free_append_info;
800   }
801
802   r = claws_mailmbox_append_message_list(folder, tab);
803
804   claws_mailmbox_append_info_free(append_info);
805   carray_free(tab);
806
807   return r;
808
809  free_append_info:
810   claws_mailmbox_append_info_free(append_info);
811  free_list:
812   carray_free(tab);
813  err:
814   return res;
815 }
816
817 /* ********************************************************************** */
818
819 int claws_mailmbox_fetch_msg_no_lock(struct claws_mailmbox_folder * folder,
820                                uint32_t num, const char ** result,
821                                size_t * result_len)
822 {
823   struct claws_mailmbox_msg_info * info;
824   int res;
825   chashdatum key;
826   chashdatum data;
827   int r;
828   
829   key.data = &num;
830   key.len = sizeof(num);
831   
832   r = chash_get(folder->mb_hash, &key, &data);
833   if (r < 0) {
834     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
835     goto err;
836   }
837   
838   info = data.data;
839   
840   if (info->msg_deleted) {
841     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
842     goto err;
843   }
844
845   * result = folder->mb_mapping + info->msg_headers;
846   * result_len = info->msg_size - info->msg_start_len;
847
848   return MAILMBOX_NO_ERROR;
849
850  err:
851   return res;
852 }
853
854 int claws_mailmbox_fetch_msg_headers_no_lock(struct claws_mailmbox_folder * folder,
855                                        uint32_t num, const char ** result,
856                                        size_t * result_len)
857 {
858   struct claws_mailmbox_msg_info * info;
859   int res;
860   chashdatum key;
861   chashdatum data;
862   int r;
863   
864   key.data = &num;
865   key.len = sizeof(num);
866   
867   r = chash_get(folder->mb_hash, &key, &data);
868   if (r < 0) {
869     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
870     goto err;
871   }
872
873   info = data.data;
874
875   if (info->msg_deleted) {
876     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
877     goto err;
878   }
879
880   * result = folder->mb_mapping + info->msg_headers;
881   * result_len = info->msg_headers_len;
882
883   return MAILMBOX_NO_ERROR;
884
885  err:
886   return res;
887 }
888
889 int claws_mailmbox_fetch_msg(struct claws_mailmbox_folder * folder,
890                        uint32_t num, const char ** result,
891                        size_t * result_len)
892 {
893   MMAPString * mmapstr;
894   int res;
895   const char * data;
896   size_t len;
897   int r;
898   size_t fixed_size;
899   char * end;
900   
901   r = claws_mailmbox_validate_read_lock(folder);
902   if (r != MAILMBOX_NO_ERROR) {
903     res = r;
904     goto err;
905   }
906
907   r = claws_mailmbox_fetch_msg_no_lock(folder, num, &data, &len);
908   if (r != MAILMBOX_NO_ERROR) {
909     res = r;
910     goto unlock;
911   }
912   
913   /* size with no uid */
914   fixed_size = get_fixed_message_size(data, len, 0, 1 /* force no uid */);
915   
916 #if 0
917   mmapstr = mmap_string_new_len(data, fixed_size);
918   if (mmapstr == NULL) {
919     res = MAILMBOX_ERROR_MEMORY;
920     goto unlock;
921   }
922 #endif
923   mmapstr = mmap_string_sized_new(fixed_size);
924   if (mmapstr == NULL) {
925     res = MAILMBOX_ERROR_MEMORY;
926     goto unlock;
927   }
928   
929   end = write_fixed_message(mmapstr->str, data, len, 0, 1 /* force no uid */);
930   * end = '\0';
931   mmapstr->len = fixed_size;
932   
933   r = mmap_string_ref(mmapstr);
934   if (r < 0) {
935     mmap_string_free(mmapstr);
936     res = MAILMBOX_ERROR_MEMORY;
937     goto unlock;
938   }
939
940   * result = mmapstr->str;
941   * result_len = mmapstr->len;
942
943   claws_mailmbox_read_unlock(folder);
944
945   return MAILMBOX_NO_ERROR;
946
947  unlock:
948   claws_mailmbox_read_unlock(folder);
949  err:
950   return res;
951 }
952
953 int claws_mailmbox_fetch_msg_headers(struct claws_mailmbox_folder * folder,
954                                uint32_t num, const char ** result,
955                                size_t * result_len)
956 {
957   MMAPString * mmapstr;
958   int res;
959   const char * data;
960   size_t len;
961   int r;
962   size_t fixed_size;
963   char * end;
964
965   r = claws_mailmbox_validate_read_lock(folder);
966   if (r != MAILMBOX_NO_ERROR) {
967     res = r;
968     goto err;
969   }
970
971   r = claws_mailmbox_fetch_msg_headers_no_lock(folder, num, &data, &len);
972   if (r != MAILMBOX_NO_ERROR) {
973     res = r;
974     goto unlock;
975   }
976
977 #if 0
978   mmapstr = mmap_string_new_len(data, len);
979   if (mmapstr == NULL) {
980     res = MAILMBOX_ERROR_MEMORY;
981     goto unlock;
982   }
983 #endif
984   /* size with no uid */
985   fixed_size = get_fixed_message_size(data, len, 0, 1 /* force no uid */);
986   
987   mmapstr = mmap_string_sized_new(fixed_size);
988   if (mmapstr == NULL) {
989     res = MAILMBOX_ERROR_MEMORY;
990     goto unlock;
991   }
992   
993   end = write_fixed_message(mmapstr->str, data, len, 0, 1 /* force no uid */);
994   * end = '\0';
995   mmapstr->len = fixed_size;
996
997   r = mmap_string_ref(mmapstr);
998   if (r < 0) {
999     mmap_string_free(mmapstr);
1000     res = MAILMBOX_ERROR_MEMORY;
1001     goto unlock;
1002   }
1003
1004   * result = mmapstr->str;
1005   * result_len = mmapstr->len;
1006
1007   claws_mailmbox_read_unlock(folder);
1008
1009   return MAILMBOX_NO_ERROR;
1010
1011  unlock:
1012   claws_mailmbox_read_unlock(folder);
1013  err:
1014   return res;
1015 }
1016
1017 void claws_mailmbox_fetch_result_free(char * msg)
1018 {
1019   mmap_string_unref(msg);
1020 }
1021
1022
1023 int claws_mailmbox_copy_msg_list(struct claws_mailmbox_folder * dest_folder,
1024                            struct claws_mailmbox_folder * src_folder,
1025                            carray * tab)
1026 {
1027   int r;
1028   int res;
1029   carray * append_tab;
1030   unsigned int i;
1031
1032   r = claws_mailmbox_validate_read_lock(src_folder);
1033   if (r != MAILMBOX_NO_ERROR) {
1034     res = r;
1035     goto err;
1036   }
1037
1038   append_tab = carray_new(carray_count(tab));
1039   if (append_tab == NULL) {
1040     res = MAILMBOX_ERROR_MEMORY;
1041     goto src_unlock;
1042   }
1043
1044   for(i = 0 ; i < carray_count(tab) ; i ++) {
1045     struct claws_mailmbox_append_info * append_info;
1046     const char * data;
1047     size_t len;
1048     uint32_t uid;
1049
1050     uid = * ((uint32_t *) carray_get(tab, i));
1051
1052     r = claws_mailmbox_fetch_msg_no_lock(src_folder, uid, &data, &len);
1053     if (r != MAILMBOX_NO_ERROR) {
1054       res = r;
1055       goto free_list;
1056     }
1057     
1058     append_info = claws_mailmbox_append_info_new(data, len);
1059     if (append_info == NULL) {
1060       res = MAILMBOX_ERROR_MEMORY;
1061       goto free_list;
1062     }
1063     
1064     r = carray_add(append_tab, append_info, NULL);
1065     if (r < 0) {
1066       claws_mailmbox_append_info_free(append_info);
1067       res = MAILMBOX_ERROR_MEMORY;
1068       goto free_list;
1069     }
1070   }    
1071
1072   r = claws_mailmbox_append_message_list(dest_folder, append_tab);
1073   if (r != MAILMBOX_NO_ERROR) {
1074     res = r;
1075     goto src_unlock;
1076   }
1077
1078   for(i = 0 ; i < carray_count(append_tab) ; i ++) {
1079     struct claws_mailmbox_append_info * append_info;
1080
1081     append_info = carray_get(append_tab, i);
1082     claws_mailmbox_append_info_free(append_info);
1083   }
1084   carray_free(append_tab);
1085
1086   claws_mailmbox_read_unlock(src_folder);
1087
1088   return MAILMBOX_NO_ERROR;
1089
1090  free_list:
1091   for(i = 0 ; i < carray_count(append_tab) ; i ++) {
1092     struct claws_mailmbox_append_info * append_info;
1093
1094     append_info = carray_get(append_tab, i);
1095     claws_mailmbox_append_info_free(append_info);
1096   }
1097   carray_free(append_tab);
1098  src_unlock:
1099   claws_mailmbox_read_unlock(src_folder);
1100  err:
1101   return res;
1102 }
1103
1104 int claws_mailmbox_copy_msg(struct claws_mailmbox_folder * dest_folder,
1105                       struct claws_mailmbox_folder * src_folder,
1106                       uint32_t uid)
1107 {
1108   carray * tab;
1109   int res;
1110   uint32_t * puid;
1111   int r;
1112
1113   tab = carray_new(1);
1114   if (tab == NULL) {
1115     res = MAILMBOX_ERROR_MEMORY;
1116     goto err;
1117   }
1118
1119   puid = malloc(sizeof(* puid));
1120   if (puid == NULL) {
1121     res = MAILMBOX_ERROR_MEMORY;
1122     goto free_array;
1123   }
1124   * puid = uid;
1125   
1126   r = claws_mailmbox_copy_msg_list(dest_folder, src_folder, tab);
1127   res = r;
1128
1129   free(puid);
1130  free_array:
1131   carray_free(tab);
1132  err:
1133   return res;
1134 }
1135
1136 static int claws_mailmbox_expunge_to_file_no_lock(char * dest_filename, int dest_fd,
1137     struct claws_mailmbox_folder * folder,
1138     size_t * result_size)
1139 {
1140   int r;
1141   int res;
1142   unsigned long i;
1143   size_t cur_offset;
1144   char * dest = NULL;
1145   size_t size;
1146
1147   size = 0;
1148   for(i = 0 ; i < carray_count(folder->mb_tab) ; i ++) {
1149     struct claws_mailmbox_msg_info * info;
1150
1151     info = carray_get(folder->mb_tab, i);
1152
1153     if (!info->msg_deleted) {
1154       size += info->msg_size + info->msg_padding;
1155
1156       if (!folder->mb_no_uid) {
1157         if (!info->msg_written_uid) {
1158           uint32_t uid;
1159          
1160 #if CRLF_BADNESS
1161           size += strlen(UID_HEADER " \r\n");
1162 #else
1163           size += strlen(UID_HEADER " \n");
1164 #endif    
1165           uid = info->msg_uid;
1166           while (uid >= 10) {
1167             uid /= 10;
1168             size ++;
1169           }
1170           size ++;
1171         }
1172       }
1173     }
1174   }
1175
1176   r = ftruncate(dest_fd, size);
1177   if (r < 0) {
1178     res = MAILMBOX_ERROR_FILE;
1179     goto err;
1180   }
1181
1182   if (size) {
1183     dest = (char *) mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, dest_fd, 0);
1184     if (dest == MAP_FAILED) {
1185       res = MAILMBOX_ERROR_FILE;
1186       goto err;
1187     }
1188   }
1189
1190   cur_offset = 0;
1191   for(i = 0 ; i < carray_count(folder->mb_tab) ; i ++) {
1192     struct claws_mailmbox_msg_info * info;
1193     
1194     info = carray_get(folder->mb_tab, i);
1195
1196     if (!info->msg_deleted) {
1197       memcpy(dest + cur_offset, folder->mb_mapping + info->msg_start,
1198              info->msg_headers_len + info->msg_start_len);
1199       cur_offset += info->msg_headers_len + info->msg_start_len;
1200       
1201       if (!folder->mb_no_uid) {
1202         if (!info->msg_written_uid) {
1203           size_t numlen;
1204           
1205           memcpy(dest + cur_offset, UID_HEADER " ", strlen(UID_HEADER " "));
1206           cur_offset += strlen(UID_HEADER " ");
1207 #if CRLF_BADNESS
1208           numlen = snprintf(dest + cur_offset, size - cur_offset,
1209                             "%i\r\n", info->msg_uid);
1210 #else
1211           numlen = snprintf(dest + cur_offset, size - cur_offset,
1212                             "%i\n", info->msg_uid);
1213 #endif                      
1214           cur_offset += numlen;
1215         }
1216       }
1217       
1218       memcpy(dest + cur_offset,
1219              folder->mb_mapping + info->msg_headers + info->msg_headers_len,
1220              info->msg_size - (info->msg_start_len + info->msg_headers_len)
1221              + info->msg_padding);
1222       
1223       cur_offset += info->msg_size -
1224         (info->msg_start_len + info->msg_headers_len)
1225         + info->msg_padding;
1226     }
1227   }
1228   fflush(stdout);
1229
1230   if (size) {
1231     msync(dest, size, MS_SYNC);
1232     munmap(dest, size);
1233   }
1234
1235   * result_size = size;
1236
1237   return MAILMBOX_NO_ERROR;
1238
1239  err:
1240   return res;
1241 }
1242
1243 int claws_mailmbox_expunge_no_lock(struct claws_mailmbox_folder * folder)
1244 {
1245   char tmpfile[PATH_MAX];
1246   int r;
1247   int res;
1248   int dest_fd;
1249   size_t size;
1250
1251   if (folder->mb_read_only)
1252     return MAILMBOX_ERROR_READONLY;
1253
1254   if (((folder->mb_written_uid >= folder->mb_max_uid) || folder->mb_no_uid) &&
1255       (!folder->mb_changed)) {
1256     /* no need to expunge */
1257     return MAILMBOX_NO_ERROR;
1258   }
1259
1260   snprintf(tmpfile, PATH_MAX, "%sXXXXXX", folder->mb_filename);
1261   dest_fd = mkstemp(tmpfile);
1262
1263   if (dest_fd < 0) {
1264     res = MAILMBOX_ERROR_FILE;
1265     goto unlink;
1266   }
1267
1268   r = claws_mailmbox_expunge_to_file_no_lock(tmpfile, dest_fd,
1269                                        folder, &size);
1270   if (r != MAILMBOX_NO_ERROR) {
1271     res = r;
1272     goto unlink;
1273   }
1274
1275   close(dest_fd);
1276
1277   r = rename(tmpfile, folder->mb_filename);
1278   if (r < 0) {
1279     res = r;
1280     goto err;
1281   }
1282
1283   claws_mailmbox_unmap(folder);
1284   claws_mailmbox_close(folder);
1285
1286   r = claws_mailmbox_open(folder);
1287   if (r != MAILMBOX_NO_ERROR) {
1288     res = r;
1289     goto err;
1290   }
1291
1292   r = claws_mailmbox_map(folder);
1293   if (r != MAILMBOX_NO_ERROR) {
1294     res = r;
1295     goto err;
1296   }
1297       
1298   r = claws_mailmbox_parse(folder);
1299   if (r != MAILMBOX_NO_ERROR) {
1300     res = r;
1301     goto err;
1302   }
1303   
1304   claws_mailmbox_timestamp(folder);
1305
1306   folder->mb_changed = FALSE;
1307   folder->mb_deleted_count = 0;
1308   
1309   return MAILMBOX_NO_ERROR;
1310
1311  unlink:
1312   close(dest_fd);
1313   unlink(tmpfile);
1314  err:
1315   return res;
1316 }
1317
1318 int claws_mailmbox_expunge(struct claws_mailmbox_folder * folder)
1319 {
1320   int r;
1321   int res;
1322
1323   r = claws_mailmbox_validate_write_lock(folder);
1324   if (r != MAILMBOX_NO_ERROR) {
1325     res = r;
1326     goto err;
1327   }
1328
1329   r = claws_mailmbox_expunge_no_lock(folder);
1330   res = r;
1331
1332   claws_mailmbox_write_unlock(folder);
1333  err:
1334   return res;
1335 }
1336
1337 int claws_mailmbox_delete_msg(struct claws_mailmbox_folder * folder, uint32_t uid)
1338 {
1339   struct claws_mailmbox_msg_info * info;
1340   int res;
1341   chashdatum key;
1342   chashdatum data;
1343   int r;
1344
1345   if (folder->mb_read_only) {
1346     res = MAILMBOX_ERROR_READONLY;
1347     goto err;
1348   }
1349   
1350   key.data = &uid;
1351   key.len = sizeof(uid);
1352   
1353   r = chash_get(folder->mb_hash, &key, &data);
1354   if (r < 0) {
1355     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
1356     goto err;
1357   }
1358
1359   info = data.data;
1360
1361   if (info->msg_deleted) {
1362     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
1363     goto err;
1364   }
1365
1366   info->msg_deleted = TRUE;
1367   folder->mb_changed = TRUE;
1368   folder->mb_deleted_count ++;
1369
1370   return MAILMBOX_NO_ERROR;
1371
1372  err:
1373   return res;
1374 }
1375
1376
1377 /*
1378   INIT of MBOX
1379
1380   - open file
1381   - map the file
1382
1383   - lock the file
1384
1385   - parse memory
1386
1387   - unlock the file
1388 */
1389
1390 int claws_mailmbox_init(const char * filename,
1391                   int force_readonly,
1392                   int force_no_uid,
1393                   uint32_t default_written_uid,
1394                   struct claws_mailmbox_folder ** result_folder)
1395 {
1396   struct claws_mailmbox_folder * folder;
1397   int r;
1398   int res;
1399   
1400   folder = claws_mailmbox_folder_new(filename);
1401   if (folder == NULL) {
1402     debug_print("folder is null for %s\n", filename);
1403     res = MAILMBOX_ERROR_MEMORY;
1404     goto err;
1405   }
1406   folder->mb_no_uid = force_no_uid;
1407   folder->mb_read_only = force_readonly;
1408   folder->mb_written_uid = default_written_uid;
1409   
1410   folder->mb_changed = FALSE;
1411   folder->mb_deleted_count = 0;
1412   
1413   r = claws_mailmbox_open(folder);
1414   if (r != MAILMBOX_NO_ERROR) {
1415     debug_print("folder can't be opened %d\n", r);
1416     res = r;
1417     goto free;
1418   }
1419
1420   r = claws_mailmbox_map(folder);
1421   if (r != MAILMBOX_NO_ERROR) {
1422     debug_print("folder can't be mapped %d\n", r);
1423     res = r;
1424     goto close;
1425   }
1426
1427   r = claws_mailmbox_validate_read_lock(folder);
1428   if (r != MAILMBOX_NO_ERROR) {
1429     debug_print("folder can't be locked %d\n", r);
1430     res = r;
1431     goto unmap;
1432   }
1433
1434   claws_mailmbox_read_unlock(folder);
1435
1436   * result_folder = folder;
1437
1438   return MAILMBOX_NO_ERROR;
1439
1440  unmap:
1441   claws_mailmbox_unmap(folder);
1442  close:
1443   claws_mailmbox_close(folder);
1444  free:
1445   claws_mailmbox_folder_free(folder);
1446  err:
1447   return res;
1448 }
1449
1450
1451 /*
1452   when MBOX is DONE
1453
1454   - check for changes
1455
1456   - unmap the file
1457   - close file
1458 */
1459
1460 void claws_mailmbox_done(struct claws_mailmbox_folder * folder)
1461 {
1462   if (!folder->mb_read_only)
1463     claws_mailmbox_expunge(folder);
1464   
1465   claws_mailmbox_unmap(folder);
1466   claws_mailmbox_close(folder);
1467
1468   claws_mailmbox_folder_free(folder);
1469 }