implementation of mbox folder with unique messages numbers
[claws.git] / src / 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 "mailmbox_parse.h"
54 #include "maillock.h"
55
56 /*
57   http://www.qmail.org/qmail-manual-html/man5/mbox.html
58   RFC 2076
59 */
60
61 #define TMPDIR "/tmp"
62
63 /* mbox is a file with a corresponding filename */
64
65 #define UID_HEADER "X-LibEtPan-UID:"
66
67 #ifndef TRUE
68 #define TRUE 1
69 #endif
70
71 #ifndef FALSE
72 #define FALSE 0
73 #endif
74
75 int mailmbox_write_lock(struct mailmbox_folder * folder)
76 {
77   int r;
78
79   if (folder->read_only)
80     return MAILMBOX_ERROR_READONLY;
81
82   r = maillock_write_lock(folder->filename, folder->fd);
83   if (r == 0)
84     return MAILMBOX_NO_ERROR;
85   else
86     return MAILMBOX_ERROR_FILE;
87 }
88
89 int mailmbox_write_unlock(struct mailmbox_folder * folder)
90 {
91   int r;
92
93   r = maillock_write_unlock(folder->filename, folder->fd);
94   if (r == 0)
95     return MAILMBOX_NO_ERROR;
96   else
97     return MAILMBOX_ERROR_FILE;
98 }
99
100 int mailmbox_read_lock(struct mailmbox_folder * folder)
101 {
102   int r;
103
104   r = maillock_read_lock(folder->filename, folder->fd);
105   if (r == 0)
106     return MAILMBOX_NO_ERROR;
107   else
108     return MAILMBOX_ERROR_FILE;
109 }
110
111 int mailmbox_read_unlock(struct mailmbox_folder * folder)
112 {
113   int r;
114
115   r = maillock_read_unlock(folder->filename, folder->fd);
116   if (r == 0)
117     return MAILMBOX_NO_ERROR;
118   else
119     return MAILMBOX_ERROR_FILE;
120 }
121
122
123 /*
124   map the file into memory.
125   the file must be locked.
126 */
127
128 int mailmbox_map(struct mailmbox_folder * folder)
129 {
130   char * str;
131   struct stat buf;
132   int res;
133   int r;
134
135   r = stat(folder->filename, &buf);
136   if (r < 0) {
137     res = MAILMBOX_ERROR_FILE;
138     goto err;
139   }
140
141   if (folder->read_only)
142     str = (char *) mmap(0, buf.st_size, PROT_READ,
143                         MAP_PRIVATE, folder->fd, 0);
144   else
145     str = (char *) mmap(0, buf.st_size, PROT_READ | PROT_WRITE,
146                         MAP_SHARED, folder->fd, 0);
147   if (str == MAP_FAILED) {
148     res = MAILMBOX_ERROR_FILE;
149     goto err;
150   }
151   
152   folder->mapping = str;
153   folder->mapping_size = buf.st_size;
154
155   return MAILMBOX_NO_ERROR;
156
157  err:
158   return res;
159 }
160
161 /*
162   unmap the file
163 */
164
165 void mailmbox_unmap(struct mailmbox_folder * folder)
166 {
167   munmap(folder->mapping, folder->mapping_size);
168   folder->mapping = NULL;
169   folder->mapping_size = 0;
170 }
171
172 void mailmbox_sync(struct mailmbox_folder * folder)
173 {
174   msync(folder->mapping, folder->mapping_size, MS_SYNC);
175 }
176
177 void mailmbox_timestamp(struct mailmbox_folder * folder)
178 {
179   int r;
180   struct stat buf;
181
182   r = stat(folder->filename, &buf);
183   if (r < 0)
184     folder->mtime = (time_t) -1;
185   else
186     folder->mtime = buf.st_mtime;
187 }
188
189 /*
190   open the file
191 */
192
193 int mailmbox_open(struct mailmbox_folder * folder)
194 {
195   int fd;
196   int read_only;
197
198   if (!folder->read_only) {
199     read_only = FALSE;
200     fd = open(folder->filename, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
201   }
202
203   if (folder->read_only || (fd < 0)) {
204     read_only = TRUE;
205     fd = open(folder->filename, O_RDONLY);
206     if (fd < 0)
207       return MAILMBOX_ERROR_FILE_NOT_FOUND;
208   }
209
210   folder->fd = fd;
211   folder->read_only = read_only;
212
213   return MAILMBOX_NO_ERROR;
214 }
215
216 /*
217   close the file
218 */
219
220 void mailmbox_close(struct mailmbox_folder * folder)
221 {
222   close(folder->fd);
223   folder->fd = -1;
224 }
225
226
227 static int mailmbox_validate_lock(struct mailmbox_folder * folder,
228                                   int (* custom_lock)(struct mailmbox_folder *),
229                                   int (* custom_unlock)(struct mailmbox_folder *))
230 {
231   struct stat buf;
232   int res;
233   int r;
234
235   r = stat(folder->filename, &buf);
236   if (r < 0) {
237     buf.st_mtime = (time_t) -1;
238   }
239
240   if ((buf.st_mtime != folder->mtime) ||
241       ((size_t) buf.st_size != folder->mapping_size)) {
242     int r;
243
244     mailmbox_unmap(folder);
245     mailmbox_close(folder);
246
247     r = mailmbox_open(folder);
248     if (r != MAILMBOX_NO_ERROR) {
249       res = r;
250       goto err;
251     }
252
253     r = custom_lock(folder);
254     if (r != MAILMBOX_NO_ERROR) {
255       res = r;
256       goto err;
257     }
258
259     r = mailmbox_map(folder);
260     if (r != MAILMBOX_NO_ERROR) {
261       res = r;
262       goto err_unlock;
263     }
264
265     r = mailmbox_parse(folder);
266     if (r != MAILMBOX_NO_ERROR) {
267       res = r;
268       goto err_unlock;
269     }
270
271     folder->mtime = buf.st_mtime;
272
273     return MAILMBOX_NO_ERROR;
274   }
275   else {
276     r = custom_lock(folder);
277     if (r != MAILMBOX_NO_ERROR) {
278       res = r;
279       goto err;
280     }
281   }
282
283   return MAILMBOX_NO_ERROR;
284
285  err_unlock:
286   custom_unlock(folder);
287  err:
288   return res;
289 }
290
291
292 int mailmbox_validate_write_lock(struct mailmbox_folder * folder)
293 {
294   return mailmbox_validate_lock(folder,
295                                 mailmbox_write_lock,
296                                 mailmbox_write_unlock);
297 }
298
299
300 int mailmbox_validate_read_lock(struct mailmbox_folder * folder)
301 {
302   return mailmbox_validate_lock(folder,
303                                 mailmbox_read_lock,
304                                 mailmbox_read_unlock);
305 }
306
307
308 /* ********************************************************************** */
309 /* append messages */
310
311 #define MAX_FROM_LINE_SIZE 256
312
313 static inline size_t get_line(char * line, size_t length,
314                               char ** pnext_line, size_t * pcount)
315 {
316   size_t count;
317
318   count = 0;
319
320   while (1) {
321     if (length == 0)
322       break;
323
324     if (* line == '\r') {
325       line ++;
326
327       count ++;
328       length --;
329
330       if (* line == '\n') {
331         line ++;
332
333         count ++;
334         length --;
335         
336         break;
337       }
338     }
339     else if (* line == '\n') {
340       line ++;
341
342       count ++;
343       length --;
344
345       break;
346     }
347     else {
348       line ++;
349       length --;
350       count ++;
351     }
352   }
353
354   * pnext_line = line;
355   * pcount = count;
356
357   return count;
358 }
359
360 /*
361   TODO : should strip \r\n if any
362   see also in write_fixed_line
363 */
364
365 static inline size_t get_fixed_line_size(char * line, size_t length,
366                                          char ** pnext_line, size_t * pcount,
367                                          size_t * pfixed_count)
368 {
369   size_t count;
370   char * next_line;
371   size_t fixed_count;
372   
373   if (!get_line(line, length, &next_line, &count))
374     return 0;
375
376   fixed_count = count;
377   if (count >= 5) {
378     if (line[0] == 'F') {
379       if (strncmp(line, "From ", 5) == 0)
380         fixed_count ++;
381     }
382   }
383
384   * pnext_line = next_line;
385   * pcount = count;
386   * pfixed_count = fixed_count;
387
388   return count;
389 }
390
391 static size_t get_fixed_message_size(char * message, size_t size,
392                                      uint32_t uid, int force_no_uid)
393 {
394   size_t fixed_size;
395   size_t cur_token;
396   size_t left;
397   char * next;
398   char * cur;
399   int end;
400   int r;
401   uint32_t tmp_uid;
402
403   cur_token = 0;
404
405   fixed_size = 0;
406
407   /* headers */
408
409   end = FALSE;
410   while (!end) {
411     size_t begin;
412     int ignore;
413
414     ignore = FALSE;
415     begin = cur_token;
416     if (cur_token + strlen(UID_HEADER) <= size) {
417       if (message[cur_token] == 'X') {
418         if (strncasecmp(message + cur_token, UID_HEADER,
419                         strlen(UID_HEADER)) == 0) {
420           ignore = TRUE;
421         }
422       }
423     }
424
425     r = mailimf_ignore_field_parse(message, size, &cur_token);
426     switch (r) {
427     case MAILIMF_NO_ERROR:
428       if (!ignore)
429         fixed_size += cur_token - begin;
430       break;
431     case MAILIMF_ERROR_PARSE:
432     default:
433       end = TRUE;
434       break;
435     }
436   }
437
438   if (!force_no_uid) {
439     /* UID header */
440     
441     fixed_size += strlen(UID_HEADER " \r\n");
442     
443     tmp_uid = uid;
444     while (tmp_uid >= 10) {
445       tmp_uid /= 10;
446       fixed_size ++;
447     }
448     fixed_size ++;
449   }
450
451   /* body */
452
453   left = size - cur_token;
454   next = message + cur_token;
455   while (left > 0) {
456     size_t count;
457     size_t fixed_count;
458
459     cur = next;
460
461     if (!get_fixed_line_size(cur, left, &next, &count, &fixed_count))
462       break;
463
464     fixed_size += fixed_count;
465     left -= count;
466   }
467
468   return fixed_size;
469 }
470
471 static inline char * write_fixed_line(char * str,
472                                       char * line, size_t length,
473                                       char ** pnext_line, size_t * pcount)
474 {
475   size_t count;
476   char * next_line;
477   
478   if (!get_line(line, length, &next_line, &count))
479     return str;
480
481   if (count >= 5) {
482     if (line[0] == 'F') {
483       if (strncmp(line, "From ", 5) == 0) {
484         * str = '>';
485         str ++;
486       }
487     }
488   }
489
490   memcpy(str, line, count);
491   
492   * pnext_line = next_line;
493   * pcount = count;
494   str += count;
495
496   return str;
497 }
498
499 static char * write_fixed_message(char * str,
500                                   char * message, size_t size,
501                                   uint32_t uid, int force_no_uid)
502 {
503   size_t fixed_size;
504   size_t cur_token;
505   size_t left;
506   int end;
507   int r;
508   char * cur_src;
509   size_t numlen;
510
511   cur_token = 0;
512
513   fixed_size = 0;
514
515   /* headers */
516
517   end = FALSE;
518   while (!end) {
519     size_t begin;
520     int ignore;
521
522     ignore = FALSE;
523     begin = cur_token;
524     if (cur_token + strlen(UID_HEADER) <= size) {
525       if (message[cur_token] == 'X') {
526         if (strncasecmp(message + cur_token, UID_HEADER,
527                         strlen(UID_HEADER)) == 0) {
528           ignore = TRUE;
529         }
530       }
531     }
532
533     r = mailimf_ignore_field_parse(message, size, &cur_token);
534     switch (r) {
535     case MAILIMF_NO_ERROR:
536       if (!ignore) {
537         memcpy(str, message + begin, cur_token - begin);
538         str += cur_token - begin;
539       }
540       break;
541     case MAILIMF_ERROR_PARSE:
542     default:
543       end = TRUE;
544       break;
545     }
546   }
547
548   if (!force_no_uid) {
549     /* UID header */
550     
551     memcpy(str, UID_HEADER " ", strlen(UID_HEADER " "));
552     str += strlen(UID_HEADER " ");
553     numlen = snprintf(str, 20, "%i\r\n", uid);
554     str += numlen;
555   }
556
557   /* body */
558
559   cur_src = message + cur_token;
560   left = size - cur_token;
561   while (left > 0) {
562     size_t count;
563     char * next;
564
565     str = write_fixed_line(str, cur_src, left, &next, &count);
566     
567     cur_src = next;
568     left -= count;
569   }
570
571   return str;
572 }
573
574 #define DEFAULT_FROM_LINE "From - Wed Jun 30 21:49:08 1993\n"
575
576 int
577 mailmbox_append_message_list_no_lock(struct mailmbox_folder * folder,
578                                      carray * append_tab)
579 {
580   size_t extra_size;
581   int r;
582   char from_line[MAX_FROM_LINE_SIZE] = DEFAULT_FROM_LINE;
583   struct tm time_info;
584   time_t date;
585   int res;
586   size_t old_size;
587   char * str;
588   uint32_t i;
589   size_t from_size;
590   size_t maxuid;
591   size_t left;
592   size_t crlf_count;
593
594   if (folder->read_only) {
595     res = MAILMBOX_ERROR_READONLY;
596     goto err;
597   }
598
599   date = time(NULL);
600   from_size = strlen(DEFAULT_FROM_LINE);
601   if (localtime_r(&date, &time_info) != NULL)
602     from_size = strftime(from_line, MAX_FROM_LINE_SIZE, "From - %c\n", &time_info);
603
604   maxuid = /* */ folder->max_uid;
605
606   extra_size = 0;
607   for(i = 0 ; i < append_tab->len ; i ++) {
608     struct mailmbox_append_info * info;
609
610     info = carray_get(append_tab, i);
611     extra_size += from_size;
612     extra_size += get_fixed_message_size(info->message, info->size,
613                                          folder->max_uid + i + 1,
614                                          folder->no_uid);
615     extra_size += 2; /* CR LF */
616   }
617
618   left = folder->mapping_size;
619   crlf_count = 0;
620   while (left >= 1) {
621     if (folder->mapping[left - 1] == '\n') {
622       crlf_count ++;
623       left --;
624     }
625     else if (folder->mapping[left - 1] == '\r') {
626       left --;
627     }
628     else
629       break;
630
631     if (crlf_count == 2)
632       break;
633   }
634
635   old_size = folder->mapping_size;
636   mailmbox_unmap(folder);
637
638   if (old_size != 0) {
639     if (crlf_count != 2)
640       extra_size += (2 - crlf_count) * 2;
641   }
642
643   r = ftruncate(folder->fd, extra_size + old_size);
644   if (r < 0) {
645     mailmbox_map(folder);
646     res = MAILMBOX_ERROR_FILE;
647     goto err;
648   }
649
650   r = mailmbox_map(folder);
651   if (r < 0) {
652     ftruncate(folder->fd, old_size);
653     return MAILMBOX_ERROR_FILE;
654   }
655
656   str = folder->mapping + old_size;
657
658   if (old_size != 0) {
659     for(i = 0 ; i < 2 - crlf_count ; i ++) {
660       * str = '\r';
661       str ++;
662       * str = '\n';
663       str ++;
664     }
665   }
666
667   for(i = 0 ; i < append_tab->len ; i ++) {
668     struct mailmbox_append_info * info;
669
670     info = carray_get(append_tab, i);
671
672     memcpy(str, from_line, from_size);
673
674     str += strlen(from_line);
675
676     str = write_fixed_message(str, info->message, info->size,
677                               folder->max_uid + i + 1,
678                               folder->no_uid);
679
680     * str = '\r';
681     str ++;
682     * str = '\n';
683     str ++;
684   }
685
686   folder->max_uid += append_tab->len;
687
688   return MAILMBOX_NO_ERROR;
689
690  err:
691   return res;
692 }
693
694 int
695 mailmbox_append_message_list(struct mailmbox_folder * folder,
696                              carray * append_tab)
697 {
698   int r;
699   int res;
700   size_t cur_token;
701
702   r = mailmbox_validate_write_lock(folder);
703   if (r != MAILMBOX_NO_ERROR) {
704     res = r;
705     goto err;
706   }
707
708   r = mailmbox_expunge_no_lock(folder);
709   if (r != MAILMBOX_NO_ERROR) {
710     res = r;
711     goto unlock;
712   }
713
714   cur_token = folder->mapping_size;
715
716   r = mailmbox_append_message_list_no_lock(folder, append_tab);
717   if (r != MAILMBOX_NO_ERROR) {
718     res = r;
719     goto unlock;
720   }
721
722   mailmbox_sync(folder);
723
724   r = mailmbox_parse_additionnal(folder, &cur_token);
725   if (r != MAILMBOX_NO_ERROR) {
726     res = r;
727     goto unlock;
728   }
729
730   mailmbox_timestamp(folder);
731
732   mailmbox_write_unlock(folder);
733
734   return MAILMBOX_NO_ERROR;
735
736  unlock:
737   mailmbox_write_unlock(folder);
738  err:
739   return res;
740 }
741
742 int
743 mailmbox_append_message(struct mailmbox_folder * folder,
744                         char * data, size_t len)
745 {
746   carray * tab;
747   struct mailmbox_append_info * append_info;
748   int res;
749   int r;
750
751   tab = carray_new(1);
752   if (tab == NULL) {
753     res = MAILMBOX_ERROR_MEMORY;
754     goto err;
755   }
756
757   append_info = mailmbox_append_info_new(data, len);
758   if (append_info == NULL) {
759     res = MAILMBOX_ERROR_MEMORY;
760     goto free_list;
761   }
762
763   r = carray_add(tab, append_info, NULL);
764   if (r < 0) {
765     res = MAILMBOX_ERROR_MEMORY;
766     goto free_append_info;
767   }
768
769   r = mailmbox_append_message_list(folder, tab);
770
771   mailmbox_append_info_free(append_info);
772   carray_free(tab);
773
774   return r;
775
776  free_append_info:
777   mailmbox_append_info_free(append_info);
778  free_list:
779   carray_free(tab);
780  err:
781   return res;
782 }
783
784 /* ********************************************************************** */
785
786 int mailmbox_fetch_msg_no_lock(struct mailmbox_folder * folder,
787                                uint32_t num, char ** result,
788                                size_t * result_len)
789 {
790   struct mailmbox_msg_info * info;
791   int res;
792   chashdatum key;
793   chashdatum data;
794   int r;
795   
796   key.data = (char *) &num;
797   key.len = sizeof(num);
798   
799   r = chash_get(folder->hash, &key, &data);
800   if (r < 0) {
801     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
802     goto err;
803   }
804   
805   info = (struct mailmbox_msg_info *) data.data;
806   
807   if (info->deleted) {
808     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
809     goto err;
810   }
811
812   * result = folder->mapping + info->headers;
813   * result_len = info->size - info->start_len;
814
815   return MAILMBOX_NO_ERROR;
816
817  err:
818   return res;
819 }
820
821 int mailmbox_fetch_msg_headers_no_lock(struct mailmbox_folder * folder,
822                                        uint32_t num, char ** result,
823                                        size_t * result_len)
824 {
825   struct mailmbox_msg_info * info;
826   int res;
827   chashdatum key;
828   chashdatum data;
829   int r;
830   
831   key.data = (char *) &num;
832   key.len = sizeof(num);
833   
834   r = chash_get(folder->hash, &key, &data);
835   if (r < 0) {
836     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
837     goto err;
838   }
839
840   info = (struct mailmbox_msg_info *) data.data;
841
842   if (info->deleted) {
843     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
844     goto err;
845   }
846
847   * result = folder->mapping + info->headers;
848   * result_len = info->headers_len;
849
850   return MAILMBOX_NO_ERROR;
851
852  err:
853   return res;
854 }
855
856 int mailmbox_fetch_msg(struct mailmbox_folder * folder,
857                        uint32_t num, char ** result,
858                        size_t * result_len)
859 {
860   int res;
861   char * data;
862   size_t len;
863   int r;
864   size_t fixed_size;
865   char * end;
866   char * new_data;
867   
868   r = mailmbox_validate_read_lock(folder);
869   if (r != MAILMBOX_NO_ERROR) {
870     res = r;
871     goto err;
872   }
873
874   r = mailmbox_fetch_msg_no_lock(folder, num, &data, &len);
875   if (r != MAILMBOX_NO_ERROR) {
876     res = r;
877     goto unlock;
878   }
879   
880   /* size with no uid */
881   fixed_size = get_fixed_message_size(data, len, 0, 1 /* force no uid */);
882   
883   new_data = malloc(fixed_size + 1);
884   if (new_data == NULL) {
885     res = MAILMBOX_ERROR_MEMORY;
886     goto unlock;
887   }
888   
889   end = write_fixed_message(new_data, data, len, 0, 1 /* force no uid */);
890   * end = '\0';
891   
892   * result = new_data;
893   * result_len = fixed_size;
894
895   mailmbox_read_unlock(folder);
896
897   return MAILMBOX_NO_ERROR;
898
899  unlock:
900   mailmbox_read_unlock(folder);
901  err:
902   return res;
903 }
904
905 int mailmbox_fetch_msg_headers(struct mailmbox_folder * folder,
906                                uint32_t num, char ** result,
907                                size_t * result_len)
908 {
909   int res;
910   char * data;
911   size_t len;
912   int r;
913   size_t fixed_size;
914   char * end;
915   char * new_data;
916
917   r = mailmbox_validate_read_lock(folder);
918   if (r != MAILMBOX_NO_ERROR) {
919     res = r;
920     goto err;
921   }
922
923   r = mailmbox_fetch_msg_headers_no_lock(folder, num, &data, &len);
924   if (r != MAILMBOX_NO_ERROR) {
925     res = r;
926     goto unlock;
927   }
928
929   /* size with no uid */
930   fixed_size = get_fixed_message_size(data, len, 0, 1 /* force no uid */);
931   
932   new_data = malloc(fixed_size + 1);
933   if (new_data == NULL) {
934     res = MAILMBOX_ERROR_MEMORY;
935     goto unlock;
936   }
937   
938   end = write_fixed_message(new_data, data, len, 0, 1 /* force no uid */);
939   * end = '\0';
940
941   * result = new_data;
942   * result_len = fixed_size;
943
944   mailmbox_read_unlock(folder);
945
946   return MAILMBOX_NO_ERROR;
947
948  unlock:
949   mailmbox_read_unlock(folder);
950  err:
951   return res;
952 }
953
954 void mailmbox_fetch_result_free(char * msg)
955 {
956         free(msg);
957 }
958
959
960 int mailmbox_copy_msg_list(struct mailmbox_folder * dest_folder,
961                            struct mailmbox_folder * src_folder,
962                            carray * tab)
963 {
964   int r;
965   int res;
966   carray * append_tab;
967   uint32_t i;
968
969   r = mailmbox_validate_read_lock(src_folder);
970   if (r != MAILMBOX_NO_ERROR) {
971     res = r;
972     goto err;
973   }
974
975   append_tab = carray_new(tab->len);
976   if (append_tab == NULL) {
977     res = MAILMBOX_ERROR_MEMORY;
978     goto src_unlock;
979   }
980
981   for(i = 0 ; i < tab->len ; i ++) {
982     struct mailmbox_append_info * append_info;
983     char * data;
984     size_t len;
985     uint32_t uid;
986
987     uid = * ((uint32_t *) carray_get(tab, i));
988
989     r = mailmbox_fetch_msg_no_lock(src_folder, uid, &data, &len);
990     if (r != MAILMBOX_NO_ERROR) {
991       res = r;
992       goto free_list;
993     }
994     
995     append_info = mailmbox_append_info_new(data, len);
996     if (append_info == NULL) {
997       res = MAILMBOX_ERROR_MEMORY;
998       goto free_list;
999     }
1000     
1001     r = carray_add(append_tab, append_info, NULL);
1002     if (r < 0) {
1003       mailmbox_append_info_free(append_info);
1004       res = MAILMBOX_ERROR_MEMORY;
1005       goto free_list;
1006     }
1007   }    
1008
1009   r = mailmbox_append_message_list(dest_folder, append_tab);
1010   if (r != MAILMBOX_NO_ERROR) {
1011     res = r;
1012     goto src_unlock;
1013   }
1014
1015   for(i = 0 ; i < append_tab->len ; i ++) {
1016     struct mailmbox_append_info * append_info;
1017
1018     append_info = carray_get(append_tab, i);
1019     mailmbox_append_info_free(append_info);
1020   }
1021   carray_free(append_tab);
1022
1023   mailmbox_read_unlock(src_folder);
1024
1025   return MAILMBOX_NO_ERROR;
1026
1027  free_list:
1028   for(i = 0 ; i < append_tab->len ; i ++) {
1029     struct mailmbox_append_info * append_info;
1030
1031     append_info = carray_get(append_tab, i);
1032     mailmbox_append_info_free(append_info);
1033   }
1034   carray_free(append_tab);
1035  src_unlock:
1036   mailmbox_read_unlock(src_folder);
1037  err:
1038   return res;
1039 }
1040
1041 int mailmbox_copy_msg(struct mailmbox_folder * dest_folder,
1042                       struct mailmbox_folder * src_folder,
1043                       uint32_t uid)
1044 {
1045   carray * tab;
1046   int res;
1047   uint32_t * puid;
1048   int r;
1049
1050   tab = carray_new(1);
1051   if (tab == NULL) {
1052     res = MAILMBOX_ERROR_MEMORY;
1053     goto err;
1054   }
1055
1056   puid = malloc(sizeof(* puid));
1057   if (puid == NULL) {
1058     res = MAILMBOX_ERROR_MEMORY;
1059     goto free_array;
1060   }
1061   * puid = uid;
1062   
1063   r = mailmbox_copy_msg_list(dest_folder, src_folder, tab);
1064   res = r;
1065
1066   free(puid);
1067  free_array:
1068   carray_free(tab);
1069  err:
1070   return res;
1071 }
1072
1073 static int mailmbox_expunge_to_file_no_lock(char * dest_filename, int dest_fd,
1074     struct mailmbox_folder * folder,
1075     size_t * result_size)
1076 {
1077   int r;
1078   int res;
1079   unsigned long i;
1080   size_t cur_offset;
1081   char * dest;
1082   size_t size;
1083
1084   size = 0;
1085   for(i = 0 ; i < folder->tab->len ; i ++) {
1086     struct mailmbox_msg_info * info;
1087
1088     info = carray_get(folder->tab, i);
1089
1090     if (!info->deleted) {
1091       size += info->size + info->padding;
1092
1093       if (!folder->no_uid) {
1094         if (!info->written_uid) {
1095           uint32_t uid;
1096           
1097           size += strlen(UID_HEADER " \r\n");
1098           
1099           uid = info->uid;
1100           while (uid >= 10) {
1101             uid /= 10;
1102             size ++;
1103           }
1104           size ++;
1105         }
1106       }
1107     }
1108   }
1109
1110   r = ftruncate(dest_fd, size);
1111   if (r < 0) {
1112     res = MAILMBOX_ERROR_FILE;
1113     goto err;
1114   }
1115
1116   dest = (char *) mmap(0, size, PROT_READ | PROT_WRITE, MAP_SHARED, dest_fd, 0);
1117   if (dest == MAP_FAILED) {
1118     res = MAILMBOX_ERROR_FILE;
1119     goto err;
1120   }
1121
1122   cur_offset = 0;
1123   for(i = 0 ; i < folder->tab->len ; i ++) {
1124     struct mailmbox_msg_info * info;
1125     
1126     info = carray_get(folder->tab, i);
1127
1128     if (!info->deleted) {
1129       memcpy(dest + cur_offset, folder->mapping + info->start,
1130              info->headers_len + info->start_len);
1131       cur_offset += info->headers_len + info->start_len;
1132       
1133       if (!folder->no_uid) {
1134         if (!info->written_uid) {
1135           size_t numlen;
1136           
1137           memcpy(dest + cur_offset, UID_HEADER " ", strlen(UID_HEADER " "));
1138           cur_offset += strlen(UID_HEADER " ");
1139           numlen = snprintf(dest + cur_offset, size - cur_offset,
1140                             "%i\r\n", info->uid);
1141           cur_offset += numlen;
1142         }
1143       }
1144       
1145       memcpy(dest + cur_offset,
1146              folder->mapping + info->headers + info->headers_len,
1147              info->size - (info->start_len + info->headers_len)
1148              + info->padding);
1149       
1150       cur_offset += info->size - (info->start_len + info->headers_len)
1151         + info->padding;
1152     }
1153   }
1154   fflush(stdout);
1155
1156   munmap(dest, size);
1157
1158   * result_size = size;
1159
1160   return MAILMBOX_NO_ERROR;
1161
1162  err:
1163   return res;
1164 }
1165
1166 int mailmbox_expunge_no_lock(struct mailmbox_folder * folder)
1167 {
1168   char tmpfile[PATH_MAX];
1169   int r;
1170   int res;
1171   int dest_fd;
1172   size_t size;
1173
1174   if (folder->read_only)
1175     return MAILMBOX_ERROR_READONLY;
1176
1177   if (((folder->written_uid >= folder->max_uid) || folder->no_uid) &&
1178       (!folder->changed)) {
1179     /* no need to expunge */
1180     return MAILMBOX_NO_ERROR;
1181   }
1182
1183   snprintf(tmpfile, PATH_MAX, "%sXXXXXX", folder->filename);
1184   dest_fd = mkstemp(tmpfile);
1185
1186   if (dest_fd < 0) {
1187     res = MAILMBOX_ERROR_FILE;
1188     goto unlink;
1189   }
1190
1191   r = mailmbox_expunge_to_file_no_lock(tmpfile, dest_fd,
1192                                        folder, &size);
1193   if (r != MAILMBOX_NO_ERROR) {
1194     res = r;
1195     goto unlink;
1196   }
1197
1198   close(dest_fd);
1199
1200   r = rename(tmpfile, folder->filename);
1201   if (r < 0) {
1202     res = r;
1203     goto err;
1204   }
1205
1206   mailmbox_unmap(folder);
1207   mailmbox_close(folder);
1208
1209   r = mailmbox_open(folder);
1210   if (r != MAILMBOX_NO_ERROR) {
1211     res = r;
1212     goto err;
1213   }
1214
1215   r = mailmbox_map(folder);
1216   if (r != MAILMBOX_NO_ERROR) {
1217     res = r;
1218     goto err;
1219   }
1220       
1221   r = mailmbox_parse(folder);
1222   if (r != MAILMBOX_NO_ERROR) {
1223     res = r;
1224     goto err;
1225   }
1226   
1227   mailmbox_timestamp(folder);
1228
1229   folder->changed = FALSE;
1230   folder->deleted_count = 0;
1231   
1232   return MAILMBOX_NO_ERROR;
1233
1234  unlink:
1235   close(dest_fd);
1236   unlink(tmpfile);
1237  err:
1238   return res;
1239 }
1240
1241 int mailmbox_expunge(struct mailmbox_folder * folder)
1242 {
1243   int r;
1244   int res;
1245
1246   r = mailmbox_validate_write_lock(folder);
1247   if (r != MAILMBOX_NO_ERROR) {
1248     res = r;
1249     goto err;
1250   }
1251
1252   r = mailmbox_expunge_no_lock(folder);
1253   res = r;
1254
1255   mailmbox_write_unlock(folder);
1256  err:
1257   return res;
1258 }
1259
1260 int mailmbox_delete_msg(struct mailmbox_folder * folder, uint32_t uid)
1261 {
1262   struct mailmbox_msg_info * info;
1263   int res;
1264   chashdatum key;
1265   chashdatum data;
1266   int r;
1267
1268   if (folder->read_only) {
1269     res = MAILMBOX_ERROR_READONLY;
1270     goto err;
1271   }
1272   
1273   key.data = (char *) &uid;
1274   key.len = sizeof(uid);
1275   
1276   r = chash_get(folder->hash, &key, &data);
1277   if (r < 0) {
1278     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
1279     goto err;
1280   }
1281
1282   info = (struct mailmbox_msg_info *) data.data;
1283
1284   if (info->deleted) {
1285     res = MAILMBOX_ERROR_MSG_NOT_FOUND;
1286     goto err;
1287   }
1288
1289   info->deleted = TRUE;
1290   folder->changed = TRUE;
1291   folder->deleted_count ++;
1292
1293   return MAILMBOX_NO_ERROR;
1294
1295  err:
1296   return res;
1297 }
1298
1299
1300 /*
1301   INIT of MBOX
1302
1303   - open file
1304   - map the file
1305
1306   - lock the file
1307
1308   - parse memory
1309
1310   - unlock the file
1311 */
1312
1313 int mailmbox_init(char * filename,
1314                   int force_readonly,
1315                   int force_no_uid,
1316                   uint32_t default_written_uid,
1317                   struct mailmbox_folder ** result_folder)
1318 {
1319   struct mailmbox_folder * folder;
1320   int r;
1321   int res;
1322   
1323   folder = mailmbox_folder_new(filename);
1324   if (folder == NULL) {
1325     res = MAILMBOX_ERROR_MEMORY;
1326     goto err;
1327   }
1328   folder->no_uid = force_no_uid;
1329   folder->read_only = force_readonly;
1330   folder->written_uid = default_written_uid;
1331   
1332   folder->changed = FALSE;
1333   folder->deleted_count = 0;
1334   
1335   r = mailmbox_open(folder);
1336   if (r != MAILMBOX_NO_ERROR) {
1337     res = r;
1338     goto free;
1339   }
1340
1341   r = mailmbox_map(folder);
1342   if (r != MAILMBOX_NO_ERROR) {
1343     res = r;
1344     goto close;
1345   }
1346
1347   r = mailmbox_validate_read_lock(folder);
1348   if (r != MAILMBOX_NO_ERROR) {
1349     res = r;
1350     goto unmap;
1351   }
1352
1353   mailmbox_read_unlock(folder);
1354
1355   * result_folder = folder;
1356
1357   return MAILMBOX_NO_ERROR;
1358
1359  unmap:
1360   mailmbox_unmap(folder);
1361  close:
1362   mailmbox_close(folder);
1363  free:
1364   mailmbox_folder_free(folder);
1365  err:
1366   return res;
1367 }
1368
1369
1370 /*
1371   when MBOX is DONE
1372
1373   - check for changes
1374
1375   - unmap the file
1376   - close file
1377 */
1378
1379 void mailmbox_done(struct mailmbox_folder * folder)
1380 {
1381   if (!folder->read_only)
1382     mailmbox_expunge(folder);
1383   
1384   mailmbox_unmap(folder);
1385   mailmbox_close(folder);
1386
1387   mailmbox_folder_free(folder);
1388 }