mbox folder / fix for filtering
[claws.git] / src / mbox_folder.c
1 #include <unistd.h>
2 #include <fcntl.h>
3 #include <glib.h>
4 #include <stdio.h>
5 #include <string.h>
6 #include <sys/types.h>
7 #include <sys/stat.h>
8 #include <unistd.h>
9
10 #include "mbox_folder.h"
11 #include "folder.h"
12 #include "procmsg.h"
13 #include "procheader.h"
14 #include "utils.h"
15 #include "intl.h"
16
17 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
18                                 gchar * new_filename, gint size);
19
20
21 /**********************************************************/
22 /*                                                        */
23 /*                   file lock                            */
24 /*                                                        */
25 /**********************************************************/
26
27
28 static GSList * file_lock = NULL;
29
30 static gboolean mbox_file_lock_file(gchar * base)
31 {
32         gchar *lockfile, *locklink;
33         gint retry = 0;
34         FILE *lockfp;
35
36         lockfile = g_strdup_printf("%s.%d", base, getpid());
37         if ((lockfp = fopen(lockfile, "w")) == NULL) {
38                 FILE_OP_ERROR(lockfile, "fopen");
39                 g_warning(_("can't create lock file %s\n"), lockfile);
40                 g_warning(_("use 'flock' instead of 'file' if possible.\n"));
41                 g_free(lockfile);
42                 return FALSE;
43         }
44         
45         fprintf(lockfp, "%d\n", getpid());
46         fclose(lockfp);
47         
48         locklink = g_strconcat(base, ".lock", NULL);
49         while (link(lockfile, locklink) < 0) {
50                 FILE_OP_ERROR(lockfile, "link");
51                 if (retry >= 5) {
52                         g_warning(_("can't create %s\n"), lockfile);
53                         unlink(lockfile);
54                         g_free(lockfile);
55                         return -1;
56                 }
57                 if (retry == 0)
58                         g_warning(_("mailbox is owned by another"
59                                     " process, waiting...\n"));
60                 retry++;
61                 sleep(5);
62         }
63         unlink(lockfile);
64         g_free(lockfile);
65
66         return TRUE;
67 }
68
69 static gboolean mbox_fcntl_lockwrite_file(FILE * fp)
70 {
71         struct flock lck;
72
73         lck.l_type = F_WRLCK;
74         lck.l_whence = 0;
75         lck.l_start = 0;
76         lck.l_len = 0;
77         
78         if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
79                 return FALSE;
80         else
81                 return TRUE;
82 }
83
84 static gboolean mbox_fcntl_lockread_file(FILE * fp)
85 {
86         struct flock lck;
87
88         lck.l_type = F_RDLCK;
89         lck.l_whence = 0;
90         lck.l_start = 0;
91         lck.l_len = 0;
92         
93         if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
94                 return FALSE;
95         else
96                 return TRUE;
97 }
98
99 static gboolean mbox_fcntl_unlock_file(FILE * fp)
100 {
101         struct flock lck;
102
103         lck.l_type = F_UNLCK;
104         lck.l_whence = 0;
105         lck.l_start = 0;
106         lck.l_len = 0;
107         
108         if (fcntl(fileno(fp), F_SETLK, &lck) < 0)
109                 return FALSE;
110         else
111                 return TRUE;
112 }
113
114 static gboolean mbox_file_unlock_file(gchar * base)
115 {
116         gchar *lockfile, *locklink;
117         gint retry = 0;
118         FILE *lockfp;
119
120         lockfile = g_strdup_printf("%s.lock", base);
121         unlink(lockfile);
122         g_free(lockfile);
123
124         return TRUE;
125 }
126
127 static gboolean mbox_lockread_file(FILE * fp, gchar * base)
128 {
129         gboolean result;
130
131         result = mbox_fcntl_lockread_file(fp);
132         if (!result) {
133                 if (result = mbox_file_lock_file(base)) {
134                         file_lock = g_slist_append(file_lock, g_strdup(base));
135                         debug_print("lockfile lock %s.\n", base);
136                 }
137                 else
138                         g_warning(_("could not lock read file %s\n"), base);
139         }
140         else
141                 debug_print("fcntl lock %s.\n", base);
142
143         return result;
144 }
145
146 static gboolean mbox_lockwrite_file(FILE * fp, gchar * base)
147 {
148         gboolean result;
149
150         result = mbox_fcntl_lockwrite_file(fp);
151         if (!result) {
152                 if (result = mbox_file_lock_file(base)) {
153                         file_lock = g_slist_append(file_lock, g_strdup(base));
154                         debug_print("lockfile lock %s.\n", base);
155                 }
156                 else
157                         g_warning(_("could not lock write file %s\n"), base);
158         }
159         else
160                 debug_print("fcntl lock %s.\n", base);
161
162         return result;
163 }
164
165 static gboolean mbox_unlock_file(FILE * fp, gchar * base)
166 {
167         gboolean result;
168         GSList * l;
169         gboolean unlocked = FALSE;
170
171         for(l = file_lock ; l != NULL ; l = g_slist_next(l)) {
172                 gchar * data = l->data;
173
174                 if (strcmp(data, base) == 0) {
175                         file_lock = g_slist_remove(file_lock, data);
176                         g_free(data);
177                         result = mbox_file_unlock_file(base);
178                         unlocked = TRUE;
179                         break;
180                 }
181         }
182         
183         if (!unlocked)
184                 result = mbox_fcntl_unlock_file(fp);
185
186         return result;
187 }
188
189 /**********************************************************/
190 /*                                                        */
191 /*                   mbox parsing                         */
192 /*                                                        */
193 /**********************************************************/
194
195 #define MAILFILE_ERROR_NO_ERROR          0x000
196 #define MAILFILE_ERROR_FILE_NOT_FOUND    0x001
197 #define MAILFILE_ERROR_MEMORY            0x002
198 #define MAILFILE_ERROR_MESSAGE_NOT_FOUND 0x003
199
200 static int mailfile_error = MAILFILE_ERROR_NO_ERROR;
201
202 #define STATE_BEGIN       0x000
203 #define STATE_TEXT_READ   0x001
204 #define STATE_FROM_READ   0x002
205 #define STATE_FIELD_READ  0x003
206 #define STATE_END         0x004
207 #define STATE_END_OF_FILE 0x005
208 #define STATE_MEM_ERROR   0x006
209 #define STATE_TEXT_BEGIN  0x007
210
211 #define STATE_MASK        0x0FF // filter state from functions
212
213 #define STATE_RESTORE_POS       0x100 // go back while reading
214
215 typedef struct _mailfile mailfile;
216
217 struct _mailfile
218 {
219         gint count;
220         gchar * filename;
221         GList * msg_list;
222 };
223
224 struct _message
225 {
226         int msgnum;
227         int deleted;
228         int offset;
229         int header;
230         int content;
231         int end;
232         int marked;
233         gchar * messageid;
234         gchar * fromspace;
235         MsgFlags flags;
236         gboolean fetched;
237 };
238
239
240 static int startFrom(char * s)
241 {
242         return (strncmp(s, "From ", 5) == 0);
243 }
244
245 static int startSpace(char * s)
246 {
247         return ((*s == ' ') || (*s == '\t'));
248 }
249
250 static int startEmpty(char * s)
251 {
252         return (*s == '\n');
253 }
254
255 static void free_msg_list(GList * l)
256 {
257         GList * elt = g_list_first(l);
258
259         while (elt)
260                 {
261                         g_free(elt->data);
262                         elt = g_list_next(elt);
263                 }
264
265         g_list_free(l);
266 }
267
268
269 static mailfile * mailfile_init_from_file(FILE * f, gchar * filename)
270 {
271         int state;
272         GList * msg_list = NULL;
273         char * r;
274         char s[256];
275         int lastpos = 0;
276         int former_pos = 0;
277         int ignore_next = 0;
278         int msgnum = 0;
279         struct _message * data = NULL;
280         mailfile * mf;
281
282         state = STATE_BEGIN;
283
284         while (state != STATE_END_OF_FILE) {
285                 if ((state & STATE_RESTORE_POS) == 0) {
286                         former_pos = lastpos;
287                         lastpos = ftell(f);
288
289                         r = fgets(s, 256, f);
290
291                         if (r != NULL && *r)
292                                 ignore_next = (s[strlen(s) - 1] != '\n');
293                         else
294                                 ignore_next = 0;
295                 }
296
297                 switch(state & 0x0F) {
298                 case STATE_BEGIN:
299                         if (r == NULL)
300                                 state = STATE_END_OF_FILE;
301                         else if (startFrom(s)) {
302                                 state = STATE_FROM_READ;
303
304                                 data = g_new0(struct _message, 1);
305                                 if (data == NULL) {
306                                         free_msg_list(msg_list);
307                                         return NULL;
308                                 }
309                                 
310                                 msgnum ++;
311                                 data->msgnum = msgnum;
312                                 data->offset = lastpos;
313                                 data->header = lastpos;
314                                 data->end = 0;
315                                 data->content = 0;
316                                 data->deleted = 0;
317                                 data->messageid = NULL;
318                                 data->fromspace = NULL;
319                                 data->flags = -1;
320                                 data->fetched = FALSE;
321                                 msg_list = g_list_append(msg_list, (gpointer) data);
322                         }
323                         else
324                                 state = STATE_BEGIN;
325
326                         break;
327
328                 case STATE_TEXT_READ:
329                         if (r == NULL)
330                                 state = STATE_END;
331                         else if (startFrom(s))
332                                 state = STATE_END | STATE_RESTORE_POS;
333                         else
334                                 state = STATE_TEXT_READ;
335                         break;
336
337                 case STATE_TEXT_BEGIN:
338                         if (r == NULL)
339                                 state = STATE_END;
340                         else if (startFrom(s)) {
341                                 data->content = lastpos;
342
343                                 state = STATE_END | STATE_RESTORE_POS;
344                         }
345                         else
346                                 state = STATE_TEXT_READ;
347                         break;
348           
349                 case STATE_FROM_READ:
350                         data->content = lastpos;
351                         if (r == NULL)
352                                 state = STATE_END;
353                         else if (startSpace(s))
354                                 state = STATE_FROM_READ;
355                         else if (startEmpty(s))
356                                 state = STATE_TEXT_READ;
357                         else
358                                 state = STATE_FIELD_READ;
359                         break;
360           
361                 case STATE_FIELD_READ:
362                         data->content = lastpos;
363                         if (r == NULL)
364                                 state = STATE_END;
365                         else if (startSpace(s))
366                                 state = STATE_FIELD_READ;
367                         else if (startEmpty(s))
368                                 state = STATE_TEXT_BEGIN;
369                         else
370                                 state = STATE_FIELD_READ;
371                         break;
372                 }
373       
374                 if ((state & STATE_MASK) == STATE_END) {
375                         state = STATE_BEGIN | (state & STATE_RESTORE_POS);
376                         if (state & STATE_RESTORE_POS)
377                                 data->end = former_pos;
378                         else
379                                 data->end = lastpos;
380                 }
381
382                 if (ignore_next) {
383                         do {
384                                 r = fgets(s, 256, f);
385                                 if (r == NULL || *r == '\0')
386                                         break;
387                         }
388                         while (s[strlen(s) - 1] != '\n');
389                 }
390         }
391
392         mf = (mailfile *) g_new0(struct _mailfile, 1);
393         if (mf == NULL) {
394                 free_msg_list(msg_list);
395                 mailfile_error = MAILFILE_ERROR_MEMORY;
396                 return NULL;
397         }
398
399         mf->msg_list = msg_list;
400
401         mf->filename = g_strdup(filename);
402         mf->count = msgnum;
403
404         mailfile_error = MAILFILE_ERROR_NO_ERROR;
405
406         return mf;
407 }
408
409 static mailfile * mailfile_init(char * filename)
410 {
411
412         FILE * f;
413         mailfile * mf;
414   
415         f = fopen(filename, "r");
416
417         if (f == NULL) {
418                 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
419                 return NULL;
420         }
421
422         mbox_lockread_file(f, filename);
423
424         mf = mailfile_init_from_file(f, filename);
425
426         mbox_unlock_file(f, filename);
427
428         fclose(f);
429
430         return mf;
431 }
432
433 static void mailfile_done(mailfile * f)
434 {
435         free_msg_list(f->msg_list);
436         g_free(f->filename);
437
438         g_free(f);
439 }
440
441 /*
442 #define MAX_READ 4096
443
444 static char * readfile(char * filename, int offset, int max_offset)
445 {
446         char * message;
447         int size;
448         int pos;
449         int max;
450         int bread;
451         FILE * handle;
452
453         handle = fopen(filename, "r");
454
455         if (handle == NULL) {
456                 mailfile_error = MAILFILE_ERROR_FILE_NOT_FOUND;
457                 return NULL;
458         }
459
460         size = max_offset - offset;
461
462         message = (char *) malloc(size + 1);
463         if (message == NULL) {
464                 fclose(handle);
465                 mailfile_error = MAILFILE_ERROR_MEMORY;
466                 return NULL;
467         }
468
469         fseek(handle, offset, SEEK_SET);
470
471         pos = 0;
472         while (pos < size) {
473                 if ((size - pos) > MAX_READ)
474                         max = MAX_READ;
475                 else
476                         max = (size - pos);
477
478                 bread = fread(message + pos, 1, max, handle);
479
480                 if (bread != -1)
481                         pos += bread;
482
483                 if (bread < max)
484                         break;
485         }
486
487         message[pos] = 0;
488
489         fclose(handle);
490
491         return message;
492 }
493
494 static char * mailfile_readmsg(mailfile f, int index)
495 {
496         GList * nth;
497         int max_offset;
498         int offset;
499         char * message;
500         struct _message * msginfo;
501
502         nth = g_list_nth(f->msg_list, index);
503
504         if (!nth) {
505                 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
506                 return NULL;
507         }
508
509         msginfo = (struct _message *)nth->data;
510
511         offset = msginfo->offset;
512         max_offset = msginfo->end;
513         message = readfile(f->filename, offset, max_offset);
514
515         mailfile_error = MAILFILE_ERROR_NO_ERROR;
516
517         return message;
518 }
519
520 static char * mailfile_readheader(mailfile f, int index)
521 {
522         GList * nth;
523         int max_offset;
524         int offset;
525         char * message;
526         struct _message * msginfo;
527
528         nth = g_list_nth(f->msg_list, index);
529
530         if (!nth) {
531                 mailfile_error = MAILFILE_ERROR_MESSAGE_NOT_FOUND;
532                 return NULL;
533         }
534
535         msginfo = (struct _message *)nth->data;
536
537         offset = msginfo->offset;
538         max_offset = msginfo->content;
539         message = readfile(f->filename, offset, max_offset);
540
541         mailfile_error = MAILFILE_ERROR_NO_ERROR;
542
543         return message;
544 }
545
546 static int mailfile_count(mailfile * f)
547 {
548         return g_list_length(f->msg_list);
549 }
550
551 static int mailfile_find_deleted(mailfile f, char * filename)
552 {
553         FILE * handle;
554
555         handle = fopen(filename, "r");
556
557         while (elt) {
558                 struct _message m = elt->data;
559                 n = fread(&m.deleted, sizeof(int), 1, handle);
560                 if (!n)
561                         break;
562                 elt = g_list_next(elt);
563         }
564
565         fclose(handle);
566 }
567 */
568
569
570
571 /**********************************************************/
572 /*                                                        */
573 /*                   mbox cache operations                */
574 /*                                                        */
575 /**********************************************************/
576
577 struct _mboxcache {
578         gchar * filename;
579         mailfile * mf;
580         gint mtime;
581 };
582
583 typedef struct _mboxcache mboxcache;
584
585 static GHashTable * mbox_cache_table = NULL;
586 static mboxcache * current_mbox = NULL;
587
588 static void mbox_cache_init()
589 {
590         mbox_cache_table = g_hash_table_new(g_str_hash, g_str_equal);
591 }
592
593 static void mbox_cache_done()
594 {
595         g_hash_table_destroy(mbox_cache_table);
596 }
597
598 static void mbox_cache_free_mbox(mboxcache * cache)
599 {
600         if (cache->filename)
601                 g_free(cache->filename);
602         g_free(cache);
603 }
604
605 static mboxcache * mbox_cache_read_mbox(gchar * filename)
606 {
607         mboxcache * cache;
608         struct stat s;
609         mailfile * mf;
610
611         if (stat(filename, &s) < 0)
612                 return NULL;
613
614         mf = mailfile_init(filename);
615         if (mf == NULL)
616                 return NULL;
617
618         cache = g_new0(mboxcache, 1);
619
620         cache->mtime = s.st_mtime;
621         cache->mf = mf;
622         cache->filename = g_strdup(filename);
623
624         return cache;
625 }
626
627 static mboxcache * mbox_cache_read_mbox_from_file(FILE * fp, gchar * filename)
628 {
629         mboxcache * cache;
630         struct stat s;
631         mailfile * mf;
632
633         if (stat(filename, &s) < 0)
634                 return NULL;
635
636         mf = mailfile_init_from_file(fp, filename);
637         if (mf == NULL)
638                 return NULL;
639
640         cache = g_new0(mboxcache, 1);
641
642         cache->mtime = s.st_mtime;
643         cache->mf = mf;
644         cache->filename = g_strdup(filename);
645
646         return cache;
647 }
648
649 static void mbox_cache_insert_mbox(gchar * filename, mboxcache * data)
650 {
651         if (mbox_cache_table == NULL)
652                 mbox_cache_init();
653
654         g_hash_table_insert(mbox_cache_table, filename, data);
655 }
656
657 static mboxcache * mbox_cache_get_mbox(gchar * filename)
658 {
659         if (mbox_cache_table == NULL)
660                 mbox_cache_init();
661
662         return g_hash_table_lookup(mbox_cache_table, filename);
663 }
664
665
666 static gint mbox_cache_get_count(gchar * filename)
667 {
668         mboxcache * cache;
669
670         cache = mbox_cache_get_mbox(filename);
671         if (cache == NULL)
672                 return -1;
673         if (cache->mf == NULL)
674                 return -1;
675         return cache->mf->count;
676 }
677
678 static gint mbox_cache_get_mtime(gchar * filename)
679 {
680         mboxcache * cache;
681
682         cache = mbox_cache_get_mbox(filename);
683         if (cache == NULL)
684                 return -1;
685         return cache->mtime;
686 }
687
688 static GList * mbox_cache_get_msg_list(gchar * filename)
689 {
690         mboxcache * cache;
691
692         cache = mbox_cache_get_mbox(filename);
693
694         if (cache == NULL)
695                 return NULL;
696
697         if (cache->mf == NULL)
698                 return NULL;
699
700         return cache->mf->msg_list;
701 }
702
703 static void mbox_cache_synchronize_lists(GList * msg_new_list,
704                                          GList * msg_old_list)
705 {
706         struct _message * msg_new;
707         struct _message * msg_old;
708         GList * l_new;
709         GList * l_old;
710
711         // could be improved with a hash table
712         for(l_old = msg_old_list ; l_old != NULL ;
713             l_old = g_list_next(l_old)) {
714                 msg_old = (struct _message *) l_old->data;
715                 
716                 if ((msg_old->messageid == NULL) ||
717                     (msg_old->fromspace == NULL))
718                         continue;
719                 
720                 for(l_new = msg_new_list ; l_new != NULL ;
721                     l_new = g_list_next(l_new)) {
722                         msg_new = (struct _message *) l_new->data;
723                         
724                         if ((msg_new->messageid == NULL) ||
725                             (msg_new->fromspace == NULL))
726                                 continue;
727                         
728                         if (!strcmp(msg_new->messageid, msg_old->messageid) &&
729                             !strcmp(msg_new->fromspace, msg_old->fromspace)) {
730                                 // copy flags
731                                 msg_new->deleted = msg_old->deleted;
732                                 msg_new->flags = msg_old->flags;
733                                 break;
734                         }
735                 }
736         }
737 }
738
739 static void mbox_cache_synchronize(gchar * filename)
740 {
741         mboxcache * new_cache;
742         mboxcache * old_cache;
743         gboolean scan_new = TRUE;
744         struct stat s;
745
746         old_cache = mbox_cache_get_mbox(filename);
747
748         if (old_cache != NULL) {
749                 if (stat(filename, &s) < 0) {
750                         FILE_OP_ERROR(filename, "stat");
751                 } else if (old_cache->mtime == s.st_mtime) {
752                         debug_print("Folder is not modified.\n");
753                         scan_new = FALSE;
754                 }
755         }
756
757         if (scan_new) {
758                 new_cache = mbox_cache_read_mbox(filename);
759                 
760                 if (new_cache == NULL) {
761                         if (old_cache != NULL)
762                                 mbox_cache_free_mbox(old_cache);
763                         return;
764                 }
765
766                 if (old_cache != NULL) {
767                         if ((old_cache->mf != NULL) &&
768                             (new_cache->mf != NULL))
769                                 mbox_cache_synchronize_lists(new_cache->mf->msg_list, old_cache->mf->msg_list);
770                         mbox_cache_free_mbox(old_cache);
771                 }
772         
773                 mbox_cache_insert_mbox(filename, new_cache);
774         }
775 }
776
777 static void mbox_cache_synchronize_from_file(FILE * fp, gchar * filename)
778 {
779         mboxcache * new_cache;
780         mboxcache * old_cache;
781         gboolean scan_new = TRUE;
782         struct stat s;
783
784         old_cache = mbox_cache_get_mbox(filename);
785
786         if (old_cache != NULL) {
787                 if (stat(filename, &s) < 0) {
788                         FILE_OP_ERROR(filename, "stat");
789                 } else if (old_cache->mtime == s.st_mtime) {
790                         debug_print("Folder is not modified.\n");
791                         scan_new = FALSE;
792                 }
793         }
794
795         if (scan_new) {
796                 new_cache = mbox_cache_read_mbox_from_file(fp, filename);
797                 
798                 if (new_cache == NULL) {
799                         if (old_cache != NULL)
800                                 mbox_cache_free_mbox(old_cache);
801                         return;
802                 }
803
804                 if (old_cache != NULL) {
805                         if ((old_cache->mf != NULL) &&
806                             (new_cache->mf != NULL))
807                                 mbox_cache_synchronize_lists(new_cache->mf->msg_list, old_cache->mf->msg_list);
808                         mbox_cache_free_mbox(old_cache);
809                 }
810         
811                 mbox_cache_insert_mbox(filename, new_cache);
812         }
813 }
814
815 gboolean mbox_cache_msg_fetched(gchar * filename, gint num)
816 {
817         GList * msg_list;
818         struct _message * msg;
819
820         msg_list = mbox_cache_get_msg_list(filename);
821         msg = (struct _message *) g_list_nth_data(msg_list, num - 1);
822
823         if (msg == NULL)
824                 return FALSE;
825
826         return msg->fetched;
827 }
828
829 void mbox_cache_msg_set_fetched(gchar * filename, gint num)
830 {
831         GList * msg_list;
832         struct _message * msg;
833
834         msg_list = mbox_cache_get_msg_list(filename);
835         if ((msg = (struct _message *) g_list_nth_data(msg_list, num - 1))
836             == NULL)
837                 return;
838         msg->fetched = TRUE;
839 }
840
841
842 /**********************************************************/
843 /*                                                        */
844 /*                   mbox operations                      */
845 /*                                                        */
846 /**********************************************************/
847
848
849 static MsgInfo *mbox_parse_msg(FILE * fp, gint msgnum, FolderItem *item)
850 {
851         struct stat s;
852         MsgInfo *msginfo;
853         MsgFlags flags = MSG_NEW|MSG_UNREAD;
854
855         g_return_val_if_fail(item != NULL, NULL);
856         g_return_val_if_fail(fp != NULL, NULL);
857
858         if (item->stype == F_QUEUE) {
859                 MSG_SET_FLAGS(flags, MSG_QUEUED);
860         } else if (item->stype == F_DRAFT) {
861                 MSG_SET_FLAGS(flags, MSG_DRAFT);
862         }
863
864         msginfo = procheader_file_parse(fp, flags, FALSE);
865
866         if (!msginfo) return NULL;
867
868         msginfo->msgnum = msgnum;
869         msginfo->folder = item;
870
871         if (stat(item->path, &s) < 0) {
872                 FILE_OP_ERROR(item->path, "stat");
873                 msginfo->size = 0;
874                 msginfo->mtime = 0;
875         } else {
876                 msginfo->size = s.st_size;
877                 msginfo->mtime = s.st_mtime;
878         }
879
880         return msginfo;
881 }
882
883 GSList *mbox_get_msg_list(Folder *folder, FolderItem *item, gboolean use_cache)
884 {
885         GSList *mlist;
886         GHashTable *msg_table;
887         MsgFlags flags = MSG_NEW|MSG_UNREAD;
888         MsgInfo * msginfo;
889         GList * l;
890         FILE * fp;
891
892 #ifdef MEASURE_TIME
893         struct timeval tv_before, tv_after, tv_result;
894
895         gettimeofday(&tv_before, NULL);
896 #endif
897
898         mlist = NULL;
899
900         fp = fopen(item->path, "r");
901
902         mbox_lockread_file(fp, item->path);
903
904         mbox_cache_synchronize_from_file(fp, item->path);
905
906         if (mbox_cache_get_mbox(item->path) == NULL) {
907                 g_warning(_("parsing of %s failed.\n"), item->path);
908                 mbox_unlock_file(fp, item->path);
909                 fclose(fp);
910                 return NULL;
911         }
912
913         for(l = mbox_cache_get_msg_list(item->path) ; l != NULL ;
914             l = g_list_next(l)) {
915                 struct _message * msg;
916
917                 msg = (struct _message *) l->data;
918
919                 if (!msg->deleted) {
920                         fseek(fp, msg->header, SEEK_SET);
921                         msginfo = mbox_parse_msg(fp, msg->msgnum, item);
922                         if (msginfo) {
923                                 if (msginfo->msgid)
924                                         msg->messageid =
925                                                 g_strdup(msginfo->msgid);
926                                 if (msginfo->fromspace)
927                                         msg->fromspace =
928                                                 g_strdup(msginfo->fromspace);
929                                 msginfo->data = msg;
930                         }
931                         if (msg->flags != -1)
932                                 msginfo->flags = msg->flags;
933                         mlist = g_slist_append(mlist, msginfo);
934                 }
935         }
936
937         mbox_unlock_file(fp, item->path);
938
939         fclose(fp);
940
941 #ifdef MEASURE_TIME
942         gettimeofday(&tv_after, NULL);
943
944         timersub(&tv_after, &tv_before, &tv_result);
945         g_print("mbox_get_msg_list: %s: elapsed time: %ld.%06ld sec\n",
946                 item->path, tv_result.tv_sec, tv_result.tv_usec);
947 #endif
948
949         return mlist;
950 }
951
952 static gboolean mbox_extract_msg(gchar * mbox_filename, gint msgnum,
953                                  gchar * dest_filename)
954 {
955         struct _message * msg;
956         gint offset;
957         gint max_offset;
958         gint size;
959         FILE * src;
960         FILE * dest;
961         gboolean err;
962         GList * msg_list;
963         gboolean already_fetched;
964
965         src = fopen(mbox_filename, "r");
966         if (src == NULL)
967                 return FALSE;
968
969         mbox_lockread_file(src, mbox_filename);
970
971         mbox_cache_synchronize_from_file(src, mbox_filename);
972
973         already_fetched = mbox_cache_msg_fetched(mbox_filename, msgnum);
974
975         if (already_fetched) {
976                 mbox_unlock_file(src, mbox_filename);
977                 fclose(src);
978                 return TRUE;
979         }
980
981         msg_list = mbox_cache_get_msg_list(mbox_filename);
982
983         msg = (struct _message *) g_list_nth_data(msg_list, msgnum - 1);
984
985         if (msg == NULL) {
986                 mbox_unlock_file(src, mbox_filename);
987                 fclose(src);
988                 return FALSE;
989         }
990
991         offset = msg->offset;
992         max_offset = msg->end;
993         
994         size = max_offset - offset;
995
996         fseek(src, offset, SEEK_SET);
997
998         dest = fopen(dest_filename, "w");
999         if (dest == NULL) {
1000                 mbox_unlock_file(src, mbox_filename);
1001                 fclose(src);
1002                 return FALSE;
1003         }
1004
1005         if (change_file_mode_rw(dest, dest_filename) < 0) {
1006                 FILE_OP_ERROR(dest_filename, "chmod");
1007                 g_warning(_("can't change file mode\n"));
1008         }
1009
1010         if (!mbox_write_data(src, dest, dest_filename, size)) {
1011                 mbox_unlock_file(src, mbox_filename);
1012                 fclose(dest);
1013                 fclose(src);
1014                 unlink(dest_filename);
1015                 return FALSE;
1016         }
1017
1018         err = FALSE;
1019
1020         if (ferror(src)) {
1021                 FILE_OP_ERROR(mbox_filename, "fread");
1022                 err = TRUE;
1023         }
1024
1025         mbox_cache_msg_set_fetched(mbox_filename, msgnum);
1026
1027         if (fclose(dest) == -1) {
1028                 FILE_OP_ERROR(dest_filename, "fclose");
1029                 return FALSE;
1030         }
1031
1032         mbox_unlock_file(src, mbox_filename);
1033
1034         if (fclose(src) == -1) {
1035                 FILE_OP_ERROR(mbox_filename, "fclose");
1036                 err = TRUE;
1037         }
1038
1039         if (err) {
1040                 unlink(dest_filename);
1041                 return FALSE;
1042         }
1043
1044         return TRUE;
1045 }
1046
1047 gchar *mbox_fetch_msg(Folder *folder, FolderItem *item, gint num)
1048 {
1049         gchar *path;
1050         gchar *filename;
1051         
1052         g_return_val_if_fail(item != NULL, NULL);
1053         /*
1054         g_return_val_if_fail(num > 0 && num <= item->last_num, NULL);
1055         */
1056
1057         path = folder_item_get_path(item);
1058         if (!is_dir_exist(path))
1059                 make_dir_hier(path);
1060
1061         filename = g_strconcat(path, G_DIR_SEPARATOR_S, itos(num), NULL);
1062
1063         g_free(path);
1064
1065         if (!mbox_extract_msg(item->path, num, filename)) {
1066                 g_free(filename);
1067                 return NULL;
1068         }
1069
1070         return filename;
1071 }
1072
1073 gint mbox_add_msg(Folder *folder, FolderItem *dest, const gchar *file,
1074                   gboolean remove_source)
1075 {
1076         FILE * src_fp;
1077         FILE * dest_fp;
1078         gchar buf[BUFSIZ];
1079         gint old_size;
1080         gint n_read;
1081         gboolean err;
1082
1083         if (dest->last_num < 0) {
1084                 mbox_scan_folder(folder, dest);
1085                 if (dest->last_num < 0) return -1;
1086         }
1087
1088         src_fp = fopen(file, "r");
1089         if (src_fp == NULL) {
1090                 return -1;
1091         }
1092
1093         dest_fp = fopen(dest->path, "a");
1094         if (dest_fp == NULL) {
1095                 fclose(src_fp);
1096                 return -1;
1097         }
1098         old_size = ftell(dest_fp);
1099
1100         mbox_lockwrite_file(dest_fp, dest->path);
1101         
1102         while (1) {
1103                 n_read = fread(buf, 1, sizeof(buf), src_fp);
1104                 if (n_read < sizeof(buf) && ferror(src_fp))
1105                         break;
1106                 if (fwrite(buf, n_read, 1, dest_fp) < 1) {
1107                         g_warning(_("writing to %s failed.\n"), dest->path);
1108                         ftruncate(fileno(dest_fp), old_size);
1109                         fclose(dest_fp);
1110                         fclose(src_fp);
1111                         return -1;
1112                 }
1113
1114                 if (n_read < sizeof(buf))
1115                         break;
1116         }
1117
1118         err = FALSE;
1119
1120         if (ferror(src_fp)) {
1121                 FILE_OP_ERROR(dest->path, "fread");
1122         }
1123
1124         mbox_unlock_file(src_fp, dest->path);
1125
1126         if (fclose(src_fp) == -1) {
1127                 FILE_OP_ERROR(file, "fclose");
1128                 err = TRUE;
1129         }
1130
1131         if (fclose(dest_fp) == -1) {
1132                 FILE_OP_ERROR(dest->path, "fclose");
1133                 return -1;
1134         }
1135
1136         if (err) {
1137                 ftruncate(fileno(dest_fp), old_size);
1138                 return -1;
1139         }
1140
1141         if (remove_source) {
1142                 if (unlink(file) < 0)
1143                         FILE_OP_ERROR(file, "unlink");
1144         }
1145
1146         dest->last_num++;
1147         return dest->last_num;
1148
1149 }
1150
1151 gint mbox_remove_msg(Folder *folder, FolderItem *item, gint num)
1152 {
1153         struct _message * msg;
1154         GList * msg_list;
1155
1156         mbox_cache_synchronize(item->path);
1157
1158         msg_list = mbox_cache_get_msg_list(item->path);
1159
1160         msg = (struct _message *) g_list_nth_data(msg_list,
1161                                                   num - 1);
1162         msg->deleted = 1;
1163
1164         /*
1165         if (!mbox_rewrite(item->path)) {
1166                 printf("rewrite %s\n", item->path);
1167                 return -1;
1168         }
1169         */
1170
1171         return 0;
1172 }
1173
1174 gint mbox_remove_all_msg(Folder *folder, FolderItem *item)
1175 {
1176         FILE * fp;
1177
1178         fp = fopen(item->path, "w");
1179         if (fp == NULL) {
1180                 return -1;
1181         }
1182
1183         fclose(fp);
1184
1185         return 0;
1186 }
1187
1188 gint mbox_move_msg(Folder *folder, FolderItem *dest, MsgInfo *msginfo)
1189 {
1190         /*
1191
1192         mbox_cache_synchronize(item->path);
1193
1194         size = msg->end - msg->offset;
1195
1196         src = fopen(mbox_filename, "r");
1197         if (src == NULL)
1198                 return FALSE;
1199         fseek(src, msg->offset, SEEK_SET);
1200
1201         mbox_lockread_file(src, mbox_filename);
1202
1203         dest = fopen(dest_filename, "a");
1204         if (dest == NULL) {
1205                 fclose(src);
1206                 return FALSE;
1207         }
1208
1209         if (change_file_mode_rw(dest, dest_filename) < 0) {
1210                 FILE_OP_ERROR(dest_filename, "chmod");
1211                 g_warning(_("can't change file mode\n"));
1212         }
1213
1214         if (!mbox_write_data(src, dest, dest_filename, size)) {
1215                         fclose(dest);
1216                         fclose(src);
1217                         unlink(dest_filename);
1218                         return FALSE;
1219         }
1220         
1221         return -1;
1222         */
1223         gchar * filename;
1224
1225         filename = mbox_fetch_msg(folder, msginfo->folder, msginfo->msgnum);
1226         if (filename == NULL)
1227                 return -1;
1228
1229         ((struct _message *) msginfo->data)->deleted = TRUE;
1230
1231         return mbox_add_msg(folder, dest, filename, TRUE);
1232 }
1233
1234 gint mbox_move_msgs_with_dest(Folder *folder, FolderItem *dest, GSList *msglist)
1235 {
1236         GSList * l;
1237         gchar * path = NULL;
1238
1239         for(l = msglist ; l != NULL ; l = g_slist_next(l)) {
1240                 MsgInfo * msginfo = (MsgInfo *) l->data;
1241
1242                 if (msginfo->folder)
1243                         path = msginfo->folder->path;
1244
1245                 mbox_move_msg(folder, dest, msginfo);
1246         }
1247
1248         if (path) {
1249                 mbox_rewrite(path);
1250                 printf("fini\n");
1251                 mbox_cache_synchronize(path);
1252         }
1253         mbox_cache_synchronize(dest->path);
1254
1255         return dest->last_num;
1256 }
1257
1258 void mbox_scan_folder(Folder *folder, FolderItem *item)
1259 {
1260         gchar *path;
1261         struct stat s;
1262         gint max = 0;
1263         gint num;
1264         gint n_msg;
1265         mboxcache * cached;
1266
1267         mbox_cache_synchronize(item->path);
1268
1269         cached = mbox_cache_get_mbox(item->path);
1270
1271         if (cached == NULL) {
1272                 item->new = 0;
1273                 item->unread = 0;
1274                 item->total = 0;
1275                 item->last_num = 0;
1276                 return;
1277         }
1278
1279         n_msg = mbox_cache_get_count(item->path);
1280
1281         if (n_msg == 0)
1282                 item->new = item->unread = item->total = 0;
1283         else {
1284                 gint new, unread, total;
1285
1286                 //              procmsg_get_mark_sum(".", &new, &unread, &total);
1287                 if (n_msg > total) {
1288                         new += n_msg - total;
1289                         unread += n_msg - total;
1290                 }
1291                 item->new = new;
1292                 item->unread = unread;
1293                 item->total = n_msg;
1294         }
1295
1296         debug_print(_("Last number in dir %s = %d\n"), item->path,
1297                     item->total);
1298         item->last_num = item->total;
1299 }
1300
1301 gchar * mbox_get_virtual_path(FolderItem * item)
1302 {
1303         if (item == NULL)
1304                 return NULL;
1305
1306         if (item->parent == NULL) {
1307                 return NULL;
1308         }
1309         else {
1310                 gchar * parent_path;
1311                 gchar * result_path;
1312
1313                 parent_path = mbox_get_virtual_path(item->parent);
1314                 if (parent_path == NULL)
1315                         result_path = g_strdup(item->name);
1316                 else
1317                         result_path = g_strconcat(parent_path,
1318                                                   G_DIR_SEPARATOR_S,
1319                                                   item->name, NULL);
1320                 g_free(parent_path);
1321
1322                 return result_path;
1323         }
1324 }
1325
1326 static gboolean mbox_write_data(FILE * mbox_fp, FILE * new_fp,
1327                                 gchar * new_filename, gint size)
1328 {       
1329         gint n_read;
1330         gint pos;
1331         gchar buf[BUFSIZ];
1332         gint max;
1333
1334         pos = 0;
1335         while (pos < size) {
1336                 if ((size - pos) > sizeof(buf))
1337                         max = sizeof(buf);
1338                 else
1339                         max = (size - pos);
1340                 
1341                 n_read = fread(buf, 1, max, mbox_fp);
1342                 if (n_read < max && ferror(mbox_fp)) {
1343                         return FALSE;
1344                 }
1345                 if (fwrite(buf, n_read, 1, new_fp) < 1) {
1346                         g_warning(_("writing to %s failed.\n"), new_filename);
1347                         return FALSE;
1348                 }
1349                 
1350                 if (n_read != -1)
1351                         pos += n_read;
1352                 
1353                 if (n_read < max)
1354                         break;
1355         }
1356         return TRUE;
1357 }
1358
1359 static gboolean mbox_write_message(FILE * mbox_fp, FILE * new_fp,
1360                             gchar * new_filename,
1361                             struct _message * msg)
1362 {
1363         GSList * headers;
1364         GSList * cur;
1365         gint size;
1366         
1367         fseek(mbox_fp, msg->header, SEEK_SET);
1368         headers = procheader_get_header_list(mbox_fp);
1369         for(cur = headers ; cur != NULL ;
1370             cur = g_slist_next(cur)) {
1371                 Header * h = (Header *) cur->data;
1372                 
1373                 if (!procheader_headername_equal(h->name, 
1374                                                  "Status") &&
1375                     !procheader_headername_equal(h->name, 
1376                                                  "X-Status")) {
1377                         fwrite(h->name, strlen(h->name),
1378                                1, new_fp);
1379                         fwrite(" ", 1, 1, new_fp);
1380                         fwrite(h->body, strlen(h->body),
1381                                1, new_fp);
1382                         fwrite("\r\n", 2, 1, new_fp);
1383                 }
1384         }
1385         fwrite("\r\n", 2, 1, new_fp);
1386         
1387         size = msg->end - msg->content;
1388         fseek(mbox_fp, msg->content, SEEK_SET);
1389
1390         return mbox_write_data(mbox_fp, new_fp, new_filename, size);
1391 }
1392
1393 void mbox_update_mark(Folder * folder, FolderItem * item)
1394 {
1395         GList * msg_list;
1396         GList * l;
1397
1398         /*      
1399         msg_list = mbox_cache_get_msg_list(item->path);
1400         
1401         for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1402                 struct _message * msg = (struct _message *) l->data;
1403
1404                 if (msg->msginfo != NULL) {
1405                         msg->flags = msg->msginfo->flags &
1406                                 MSG_PERMANENT_FLAG_MASK;
1407                 }
1408         }
1409         */
1410 }
1411
1412 void mbox_change_flags(Folder * folder, FolderItem * item, MsgInfo * info)
1413 {
1414         struct _message * msg = (struct _message *) info->data;
1415
1416         msg->flags = info->flags;
1417 }
1418
1419 gboolean mbox_rewrite(gchar * mbox)
1420 {
1421         FILE * mbox_fp;
1422         FILE * new_fp;
1423         gchar * new;
1424         GList * l;
1425         gboolean result;
1426         gboolean modification = FALSE;
1427         GList * msg_list;
1428
1429         msg_list = mbox_cache_get_msg_list(mbox);
1430
1431         for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1432                 struct _message * msg = (struct _message *) l->data;
1433                 if (msg->deleted) {
1434                         printf("modification\n");
1435                         modification = TRUE;
1436                         break;
1437                 }
1438         }
1439
1440         if (!modification)
1441                 return FALSE;
1442
1443         mbox_fp = fopen(mbox, "r+");
1444         mbox_lockwrite_file(mbox_fp, mbox);
1445
1446         mbox_cache_synchronize_from_file(mbox_fp, mbox);
1447
1448         new = g_strconcat(mbox, ".new", NULL);
1449         new_fp = fopen(new, "w");
1450
1451         mbox_lockwrite_file(new_fp, new);
1452
1453         result = TRUE;
1454
1455         msg_list = mbox_cache_get_msg_list(mbox);
1456         for(l = msg_list ; l != NULL ; l = g_list_next(l)) {
1457                 struct _message * msg = (struct _message *) l->data;
1458                 if (!msg->deleted)
1459                         if (!mbox_write_message(mbox_fp, new_fp, new, msg)) {
1460                                 result = FALSE;
1461                                 break;
1462                         }
1463         }
1464
1465         unlink(mbox);
1466
1467         g_warning(_("can't rename %s to %s\n"), new, mbox);
1468         if (rename(new_fp, mbox) == -1) {
1469                 g_warning(_("can't rename %s to %s\n"), new, mbox);
1470                 mbox_unlock_file(new_fp, new);
1471                 fclose(new_fp);
1472                 mbox_unlock_file(mbox_fp, mbox);
1473                 fclose(mbox_fp);
1474                 return -1;
1475         }
1476
1477         mbox_unlock_file(new_fp, new);
1478
1479         fclose(new_fp);
1480
1481         mbox_unlock_file(mbox_fp, mbox);
1482
1483         fclose(mbox_fp);
1484         return result;
1485 }