Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / providers / local / camel-maildir-summary.c
1 /*
2  *  Copyright (C) 2000 Ximian Inc.
3  *
4  *  Authors: Not Zed <notzed@lostzed.mmc.com.au>
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of version 2 of the GNU Lesser General Public
8  * License as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <ctype.h>
26 #include <dirent.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <sys/stat.h>
33 #include <sys/types.h>
34 #include <sys/types.h>
35 #include <sys/uio.h>
36
37 #include <glib/gi18n-lib.h>
38
39 #include <libedataserver/e-memory.h>
40
41 #include "camel-mime-message.h"
42 #include "camel-operation.h"
43 #include "camel-private.h"
44
45 #include "camel-maildir-summary.h"
46
47 #define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/
48
49 #define CAMEL_MAILDIR_SUMMARY_VERSION (0x2000)
50
51 static CamelMessageInfo *message_info_load(CamelFolderSummary *s, FILE *in);
52 static CamelMessageInfo *message_info_new_from_header(CamelFolderSummary *, struct _camel_header_raw *);
53 static void message_info_free(CamelFolderSummary *, CamelMessageInfo *mi);
54
55 static int maildir_summary_load(CamelLocalSummary *cls, int forceindex, CamelException *ex);
56 static int maildir_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changeinfo, CamelException *ex);
57 static int maildir_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changeinfo, CamelException *ex);
58 static CamelMessageInfo *maildir_summary_add(CamelLocalSummary *cls, CamelMimeMessage *msg, const CamelMessageInfo *info, CamelFolderChangeInfo *, CamelException *ex);
59
60 static char *maildir_summary_next_uid_string(CamelFolderSummary *s);
61 static int maildir_summary_decode_x_evolution(CamelLocalSummary *cls, const char *xev, CamelLocalMessageInfo *mi);
62 static char *maildir_summary_encode_x_evolution(CamelLocalSummary *cls, const CamelLocalMessageInfo *mi);
63
64 static void camel_maildir_summary_class_init    (CamelMaildirSummaryClass *class);
65 static void camel_maildir_summary_init  (CamelMaildirSummary *gspaper);
66 static void camel_maildir_summary_finalise      (CamelObject *obj);
67
68 #define _PRIVATE(x) (((CamelMaildirSummary *)(x))->priv)
69
70 struct _CamelMaildirSummaryPrivate {
71         char *current_file;
72         char *hostname;
73
74         GHashTable *load_map;
75 };
76
77 static CamelLocalSummaryClass *parent_class;
78
79 CamelType
80 camel_maildir_summary_get_type (void)
81 {
82         static CamelType type = CAMEL_INVALID_TYPE;
83         
84         if (type == CAMEL_INVALID_TYPE) {
85                 type = camel_type_register(camel_local_summary_get_type (), "CamelMaildirSummary",
86                                            sizeof(CamelMaildirSummary),
87                                            sizeof(CamelMaildirSummaryClass),
88                                            (CamelObjectClassInitFunc)camel_maildir_summary_class_init,
89                                            NULL,
90                                            (CamelObjectInitFunc)camel_maildir_summary_init,
91                                            (CamelObjectFinalizeFunc)camel_maildir_summary_finalise);
92         }
93         
94         return type;
95 }
96
97 static void
98 camel_maildir_summary_class_init (CamelMaildirSummaryClass *class)
99 {
100         CamelFolderSummaryClass *sklass = (CamelFolderSummaryClass *) class;
101         CamelLocalSummaryClass *lklass = (CamelLocalSummaryClass *)class;
102
103         parent_class = (CamelLocalSummaryClass *)camel_type_get_global_classfuncs(camel_local_summary_get_type ());
104
105         /* override methods */
106         sklass->message_info_load = message_info_load;
107         sklass->message_info_new_from_header = message_info_new_from_header;
108         sklass->message_info_free = message_info_free;
109         sklass->next_uid_string = maildir_summary_next_uid_string;
110
111         lklass->load = maildir_summary_load;
112         lklass->check = maildir_summary_check;
113         lklass->sync = maildir_summary_sync;
114         lklass->add = maildir_summary_add;
115         lklass->encode_x_evolution = maildir_summary_encode_x_evolution;
116         lklass->decode_x_evolution = maildir_summary_decode_x_evolution;
117 }
118
119 static void
120 camel_maildir_summary_init (CamelMaildirSummary *o)
121 {
122         struct _CamelFolderSummary *s = (CamelFolderSummary *) o;
123         char hostname[256];
124
125         o->priv = g_malloc0(sizeof(*o->priv));
126         /* set unique file version */
127         s->version += CAMEL_MAILDIR_SUMMARY_VERSION;
128
129         s->message_info_size = sizeof(CamelMaildirMessageInfo);
130         s->content_info_size = sizeof(CamelMaildirMessageContentInfo);
131
132 #if defined (DOEPOOLV) || defined (DOESTRV)
133         s->message_info_strings = CAMEL_MAILDIR_INFO_LAST;
134 #endif
135
136         if (gethostname(hostname, 256) == 0) {
137                 o->priv->hostname = g_strdup(hostname);
138         } else {
139                 o->priv->hostname = g_strdup("localhost");
140         }
141 }
142
143 static void
144 camel_maildir_summary_finalise(CamelObject *obj)
145 {
146         CamelMaildirSummary *o = (CamelMaildirSummary *)obj;
147
148         g_free(o->priv->hostname);
149         g_free(o->priv);
150 }
151
152 /**
153  * camel_maildir_summary_new:
154  * @folder: parent folder.
155  * @filename: Path to root of this maildir directory (containing new/tmp/cur directories).
156  * @index: Index if one is reqiured.
157  *
158  * Create a new CamelMaildirSummary object.
159  * 
160  * Return value: A new #CamelMaildirSummary object.
161  **/
162 CamelMaildirSummary     *camel_maildir_summary_new(struct _CamelFolder *folder, const char *filename, const char *maildirdir, CamelIndex *index)
163 {
164         CamelMaildirSummary *o = (CamelMaildirSummary *)camel_object_new(camel_maildir_summary_get_type ());
165
166         ((CamelFolderSummary *)o)->folder = folder;
167
168         camel_local_summary_construct((CamelLocalSummary *)o, filename, maildirdir, index);
169         return o;
170 }
171
172 /* the 'standard' maildir flags.  should be defined in sorted order. */
173 static struct {
174         char flag;
175         guint32 flagbit;
176 } flagbits[] = {
177         { 'D', CAMEL_MESSAGE_DRAFT },
178         { 'F', CAMEL_MESSAGE_FLAGGED },
179         /*{ 'P', CAMEL_MESSAGE_FORWARDED },*/
180         { 'R', CAMEL_MESSAGE_ANSWERED },
181         { 'S', CAMEL_MESSAGE_SEEN },
182         { 'T', CAMEL_MESSAGE_DELETED },
183 };
184
185 /* convert the uid + flags into a unique:info maildir format */
186 char *camel_maildir_summary_info_to_name(const CamelMaildirMessageInfo *info)
187 {
188         const char *uid;
189         char *p, *buf;
190         int i;
191
192         uid = camel_message_info_uid (info);
193         buf = g_alloca (strlen (uid) + strlen (":2,") +  (sizeof (flagbits) / sizeof (flagbits[0])) + 1);
194         p = buf + sprintf (buf, "%s:2,", uid);
195         for (i = 0; i < sizeof (flagbits) / sizeof (flagbits[0]); i++) {
196                 if (info->info.info.flags & flagbits[i].flagbit)
197                         *p++ = flagbits[i].flag;
198         }
199         *p = 0;
200         
201         return g_strdup(buf);
202 }
203
204 /* returns 0 if the info matches (or there was none), otherwise we changed it */
205 int camel_maildir_summary_name_to_info(CamelMaildirMessageInfo *info, const char *name)
206 {
207         char *p, c;
208         guint32 set = 0;        /* what we set */
209         /*guint32 all = 0;*/    /* all flags */
210         int i;
211
212         p = strstr(name, ":2,");
213         if (p) {
214                 p+=3;
215                 while ((c = *p++)) {
216                         /* we could assume that the flags are in order, but its just as easy not to require */
217                         for (i=0;i<sizeof(flagbits)/sizeof(flagbits[0]);i++) {
218                                 if (flagbits[i].flag == c && (info->info.info.flags & flagbits[i].flagbit) == 0) {
219                                         set |= flagbits[i].flagbit;
220                                 }
221                                 /*all |= flagbits[i].flagbit;*/
222                         }
223                 }
224
225                 /* changed? */
226                 /*if ((info->flags & all) != set) {*/
227                 if ((info->info.info.flags & set) != set) {
228                         /* ok, they did change, only add the new flags ('merge flags'?) */
229                         /*info->flags &= all;  if we wanted to set only the new flags, which we probably dont */
230                         info->info.info.flags |= set;
231                         return 1;
232                 }
233         }
234
235         return 0;
236 }
237
238 /* for maildir, x-evolution isn't used, so dont try and get anything out of it */
239 static int maildir_summary_decode_x_evolution(CamelLocalSummary *cls, const char *xev, CamelLocalMessageInfo *mi)
240 {
241         return -1;
242 }
243
244 static char *maildir_summary_encode_x_evolution(CamelLocalSummary *cls, const CamelLocalMessageInfo *mi)
245 {
246         return NULL;
247 }
248
249 /* FIXME:
250    both 'new' and 'add' will try and set the filename, this is not ideal ...
251 */
252 static CamelMessageInfo *maildir_summary_add(CamelLocalSummary *cls, CamelMimeMessage *msg, const CamelMessageInfo *info, CamelFolderChangeInfo *changes, CamelException *ex)
253 {
254         CamelMaildirMessageInfo *mi;
255
256         mi = (CamelMaildirMessageInfo *)((CamelLocalSummaryClass *) parent_class)->add(cls, msg, info, changes, ex);
257         if (mi) {
258                 if (info) {
259                         camel_maildir_info_set_filename(mi, camel_maildir_summary_info_to_name(mi));
260                         d(printf("Setting filename to %s\n", camel_maildir_info_filename(mi)));
261                 }
262         }
263
264         return (CamelMessageInfo *)mi;
265 }
266
267 static CamelMessageInfo *message_info_new_from_header(CamelFolderSummary * s, struct _camel_header_raw *h)
268 {
269         CamelMessageInfo *mi, *info;
270         CamelMaildirSummary *mds = (CamelMaildirSummary *)s;
271         CamelMaildirMessageInfo *mdi;
272         const char *uid;
273
274         mi = ((CamelFolderSummaryClass *) parent_class)->message_info_new_from_header(s, h);
275         /* assign the uid and new filename */
276         if (mi) {
277                 mdi = (CamelMaildirMessageInfo *)mi;
278
279                 uid = camel_message_info_uid(mi);
280                 if (uid==NULL || uid[0] == 0)
281                         mdi->info.info.uid = camel_folder_summary_next_uid_string(s);
282
283                 /* handle 'duplicates' */
284                 info = camel_folder_summary_uid(s, uid);
285                 if (info) {
286                         d(printf("already seen uid '%s', just summarising instead\n", uid));
287                         camel_message_info_free(mi);
288                         mdi = (CamelMaildirMessageInfo *)(mi = info);
289                 }
290
291                 /* with maildir we know the real received date, from the filename */
292                 mdi->info.info.date_received = strtoul(camel_message_info_uid(mi), NULL, 10);
293
294                 if (mds->priv->current_file) {
295 #if 0
296                         char *p1, *p2, *p3;
297                         unsigned long uid;
298 #endif
299                         /* if setting from a file, grab the flags from it */
300                         camel_maildir_info_set_filename(mi, g_strdup(mds->priv->current_file));
301                         camel_maildir_summary_name_to_info(mdi, mds->priv->current_file);
302
303 #if 0
304                         /* Actually, I dont think all this effort is worth it at all ... */
305
306                         /* also, see if we can extract the next-id from tne name, and safe-if-fy ourselves against collisions */
307                         /* we check for something.something_number.something */
308                         p1 = strchr(mdi->filename, '.');
309                         if (p1) {
310                                 p2 = strchr(p1+1, '.');
311                                 p3 = strchr(p1+1, '_');
312                                 if (p2 && p3 && p3<p2) {
313                                         uid = strtoul(p3+1, &p1, 10);
314                                         if (p1 == p2 && uid>0)
315                                                 camel_folder_summary_set_uid(s, uid);
316                                 }
317                         }
318 #endif
319                 } else {
320                         /* if creating a file, set its name from the flags we have */
321                         camel_maildir_info_set_filename(mdi, camel_maildir_summary_info_to_name(mdi));
322                         d(printf("Setting filename to %s\n", camel_maildir_info_filename(mi)));
323                 }
324         }
325
326         return mi;
327 }
328
329
330 static void message_info_free(CamelFolderSummary *s, CamelMessageInfo *mi)
331 {
332 #if !defined (DOEPOOLV) && !defined (DOESTRV)
333         CamelMaildirMessageInfo *mdi = (CamelMaildirMessageInfo *)mi;
334
335         g_free(mdi->filename);
336 #endif
337         ((CamelFolderSummaryClass *) parent_class)->message_info_free(s, mi);
338 }
339
340
341 static char *maildir_summary_next_uid_string(CamelFolderSummary *s)
342 {
343         CamelMaildirSummary *mds = (CamelMaildirSummary *)s;
344
345         d(printf("next uid string called?\n"));
346
347         /* if we have a current file, then use that to get the uid */
348         if (mds->priv->current_file) {
349                 char *cln;
350
351                 cln = strchr(mds->priv->current_file, ':');
352                 if (cln)
353                         return g_strndup(mds->priv->current_file, cln-mds->priv->current_file);
354                 else
355                         return g_strdup(mds->priv->current_file);
356         } else {
357                 /* the first would probably work, but just to be safe, check for collisions */
358 #if 0
359                 return g_strdup_printf("%ld.%d_%u.%s", time(0), getpid(), camel_folder_summary_next_uid(s), mds->priv->hostname);
360 #else
361                 CamelLocalSummary *cls = (CamelLocalSummary *)s;
362                 char *name = NULL, *uid = NULL;
363                 struct stat st;
364                 int retry = 0;
365                 guint32 nextuid = camel_folder_summary_next_uid(s);
366
367                 /* we use time.pid_count.hostname */
368                 do {
369                         if (retry > 0) {
370                                 g_free(name);
371                                 g_free(uid);
372                                 sleep(2);
373                         }
374                         uid = g_strdup_printf("%ld.%d_%u.%s", time(0), getpid(), nextuid, mds->priv->hostname);
375                         name = g_strdup_printf("%s/tmp/%s", cls->folder_path, uid);
376                         retry++;
377                 } while (stat(name, &st) == 0 && retry<3);
378
379                 /* I dont know what we're supposed to do if it fails to find a unique name?? */
380
381                 g_free(name);
382                 return uid;
383 #endif
384         }
385 }
386
387 static CamelMessageInfo *
388 message_info_load(CamelFolderSummary *s, FILE *in)
389 {
390         CamelMessageInfo *mi;
391         CamelMaildirSummary *mds = (CamelMaildirSummary *)s;
392
393         mi = ((CamelFolderSummaryClass *) parent_class)->message_info_load(s, in);
394         if (mi) {
395                 char *name;
396
397                 if (mds->priv->load_map
398                     && (name = g_hash_table_lookup(mds->priv->load_map, camel_message_info_uid(mi)))) {
399                         d(printf("Setting filename of %s to %s\n", camel_message_info_uid(mi), name));
400                         camel_maildir_info_set_filename(mi, g_strdup(name));
401                         camel_maildir_summary_name_to_info((CamelMaildirMessageInfo *)mi, name);
402                 }
403         }
404
405         return mi;
406 }
407
408 static int maildir_summary_load(CamelLocalSummary *cls, int forceindex, CamelException *ex)
409 {
410         char *cur;
411         DIR *dir;
412         struct dirent *d;
413         CamelMaildirSummary *mds = (CamelMaildirSummary *)cls;
414         char *uid;
415         EMemPool *pool;
416         int ret;
417
418         cur = g_strdup_printf("%s/cur", cls->folder_path);
419
420         d(printf("pre-loading uid <> filename map\n"));
421
422         dir = opendir(cur);
423         if (dir == NULL) {
424                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
425                                       _("Cannot open maildir directory path: %s: %s"),
426                                       cls->folder_path, g_strerror (errno));
427                 g_free(cur);
428                 return -1;
429         }
430
431         mds->priv->load_map = g_hash_table_new(g_str_hash, g_str_equal);
432         pool = e_mempool_new(1024, 512, E_MEMPOOL_ALIGN_BYTE);
433
434         while ( (d = readdir(dir)) ) {
435                 if (d->d_name[0] == '.')
436                         continue;
437                 
438                 /* map the filename -> uid */
439                 uid = strchr(d->d_name, ':');
440                 if (uid) {
441                         int len = uid-d->d_name;
442                         uid = e_mempool_alloc(pool, len+1);
443                         memcpy(uid, d->d_name, len);
444                         uid[len] = 0;
445                         g_hash_table_insert(mds->priv->load_map, uid, e_mempool_strdup(pool, d->d_name));
446                 } else {
447                         uid = e_mempool_strdup(pool, d->d_name);
448                         g_hash_table_insert(mds->priv->load_map, uid, uid);
449                 }
450         }
451         closedir(dir);
452         g_free(cur);
453
454         ret = ((CamelLocalSummaryClass *) parent_class)->load(cls, forceindex, ex);
455
456         g_hash_table_destroy(mds->priv->load_map);
457         mds->priv->load_map = NULL;
458         e_mempool_destroy(pool);
459
460         return ret;
461 }
462
463 static int camel_maildir_summary_add(CamelLocalSummary *cls, const char *name, int forceindex)
464 {
465         CamelMaildirSummary *maildirs = (CamelMaildirSummary *)cls;
466         char *filename = g_strdup_printf("%s/cur/%s", cls->folder_path, name);
467         int fd;
468         CamelMimeParser *mp;
469
470         d(printf("summarising: %s\n", name));
471
472         fd = open(filename, O_RDONLY);
473         if (fd == -1) {
474                 g_warning ("Cannot summarise/index: %s: %s", filename, strerror (errno));
475                 g_free(filename);
476                 return -1;
477         }
478         mp = camel_mime_parser_new();
479         camel_mime_parser_scan_from(mp, FALSE);
480         camel_mime_parser_init_with_fd(mp, fd);
481         if (cls->index && (forceindex || !camel_index_has_name(cls->index, name))) {
482                 d(printf("forcing indexing of message content\n"));
483                 camel_folder_summary_set_index((CamelFolderSummary *)maildirs, cls->index);
484         } else {
485                 camel_folder_summary_set_index((CamelFolderSummary *)maildirs, NULL);
486         }
487         maildirs->priv->current_file = (char *)name;
488         camel_folder_summary_add_from_parser((CamelFolderSummary *)maildirs, mp);
489         camel_object_unref((CamelObject *)mp);
490         maildirs->priv->current_file = NULL;
491         camel_folder_summary_set_index((CamelFolderSummary *)maildirs, NULL);
492         g_free(filename);
493         return 0;
494 }
495
496 struct _remove_data {
497         CamelLocalSummary *cls;
498         CamelFolderChangeInfo *changes;
499 };
500
501 static void
502 remove_summary(char *key, CamelMessageInfo *info, struct _remove_data *rd)
503 {
504         d(printf("removing message %s from summary\n", key));
505         if (rd->cls->index)
506                 camel_index_delete_name(rd->cls->index, camel_message_info_uid(info));
507         if (rd->changes)
508                 camel_folder_change_info_remove_uid(rd->changes, key);
509         camel_folder_summary_remove((CamelFolderSummary *)rd->cls, info);
510         camel_message_info_free(info);
511 }
512
513 static int
514 sort_receive_cmp(const void *ap, const void *bp)
515 {
516         const CamelMaildirMessageInfo
517                 *a = *((CamelMaildirMessageInfo **)ap),
518                 *b = *((CamelMaildirMessageInfo **)bp);
519
520         if (a->info.info.date_received < b->info.info.date_received)
521                 return -1;
522         else if (a->info.info.date_received > b->info.info.date_received)
523                 return 1;
524
525         return 0;
526 }
527
528 static int
529 maildir_summary_check(CamelLocalSummary *cls, CamelFolderChangeInfo *changes, CamelException *ex)
530 {
531         DIR *dir;
532         struct dirent *d;
533         char *p;
534         CamelMessageInfo *info;
535         CamelMaildirMessageInfo *mdi;
536         CamelFolderSummary *s = (CamelFolderSummary *)cls;
537         GHashTable *left;
538         int i, count, total;
539         int forceindex;
540         char *new, *cur;
541         char *uid;
542         struct _remove_data rd = { cls, changes };
543
544         new = g_strdup_printf("%s/new", cls->folder_path);
545         cur = g_strdup_printf("%s/cur", cls->folder_path);
546
547         d(printf("checking summary ...\n"));
548
549         camel_operation_start(NULL, _("Checking folder consistency"));
550
551         /* scan the directory, check for mail files not in the index, or index entries that
552            no longer exist */
553         dir = opendir(cur);
554         if (dir == NULL) {
555                 camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
556                                       _("Cannot open maildir directory path: %s: %s"),
557                                       cls->folder_path, g_strerror (errno));
558                 g_free(cur);
559                 g_free(new);
560                 camel_operation_end(NULL);
561                 return -1;
562         }
563
564         /* keeps track of all uid's that have not been processed */
565         left = g_hash_table_new(g_str_hash, g_str_equal);
566         count = camel_folder_summary_count((CamelFolderSummary *)cls);
567         forceindex = count == 0;
568         for (i=0;i<count;i++) {
569                 info = camel_folder_summary_index((CamelFolderSummary *)cls, i);
570                 if (info) {
571                         g_hash_table_insert(left, (char *)camel_message_info_uid(info), info);
572                 }
573         }
574
575         /* joy, use this to pre-count the total, so we can report progress meaningfully */
576         total = 0;
577         count = 0;
578         while ( (d = readdir(dir)) )
579                 total++;
580         rewinddir(dir);
581
582         while ( (d = readdir(dir)) ) {
583                 int pc = count * 100 / total;
584
585                 camel_operation_progress(NULL, pc);
586                 count++;
587
588                 /* FIXME: also run stat to check for regular file */
589                 p = d->d_name;
590                 if (p[0] == '.')
591                         continue;
592
593                 /* map the filename -> uid */
594                 uid = strchr(d->d_name, ':');
595                 if (uid)
596                         uid = g_strndup(d->d_name, uid-d->d_name);
597                 else
598                         uid = g_strdup(d->d_name);
599                 
600                 info = g_hash_table_lookup(left, uid);
601                 if (info) {
602                         camel_message_info_free(info);
603                         g_hash_table_remove(left, uid);
604                 }
605
606                 info = camel_folder_summary_uid((CamelFolderSummary *)cls, uid);
607                 if (info == NULL) {
608                         /* must be a message incorporated by another client, this is not a 'recent' uid */
609                         if (camel_maildir_summary_add(cls, d->d_name, forceindex) == 0)
610                                 if (changes)
611                                         camel_folder_change_info_add_uid(changes, uid);
612                 } else {
613                         const char *filename;
614
615                         if (cls->index && (!camel_index_has_name(cls->index, uid))) {
616                                 /* message_info_new will handle duplicates */
617                                 camel_maildir_summary_add(cls, d->d_name, forceindex);
618                         }
619
620                         mdi = (CamelMaildirMessageInfo *)info;
621                         filename = camel_maildir_info_filename(mdi);
622                         /* TODO: only store the extension in the mdi->filename struct, not the whole lot */
623                         if (filename == NULL || strcmp(filename, d->d_name) != 0) {
624 #ifdef DOESTRV
625 #warning "cannot modify the estrv after its been setup, for mt-safe code"
626                                 CAMEL_SUMMARY_LOCK(s, summary_lock);
627                                 /* need to update the summary hash ref */
628                                 g_hash_table_remove(s->messages_uid, camel_message_info_uid(info));
629                                 info->strings = e_strv_set_ref(info->strings, CAMEL_MAILDIR_INFO_FILENAME, d->d_name);
630                                 info->strings = e_strv_pack(info->strings);
631                                 g_hash_table_insert(s->messages_uid, (char *)camel_message_info_uid(info), info);
632                                 CAMEL_SUMMARY_UNLOCK(s, summary_lock);
633 #else
634 # ifdef DOEPOOLV
635                                 info->strings = e_poolv_set(info->strings, CAMEL_MAILDIR_INFO_FILENAME, d->d_name, FALSE);
636 # else  
637                                 g_free(mdi->filename);
638                                 mdi->filename = g_strdup(d->d_name);
639 # endif
640 #endif
641                         }
642                         camel_message_info_free(info);
643                 }
644                 g_free(uid);
645         }
646         closedir(dir);
647         g_hash_table_foreach(left, (GHFunc)remove_summary, &rd);
648         g_hash_table_destroy(left);
649
650         camel_operation_end(NULL);
651
652         camel_operation_start(NULL, _("Checking for new messages"));
653
654         /* now, scan new for new messages, and copy them to cur, and so forth */
655         dir = opendir(new);
656         if (dir != NULL) {
657                 total = 0;
658                 count = 0;
659                 while ( (d = readdir(dir)) )
660                         total++;
661                 rewinddir(dir);
662
663                 while ( (d = readdir(dir)) ) {
664                         char *name, *newname, *destname, *destfilename;
665                         char *src, *dest;
666                         int pc = count * 100 / total;
667
668                         camel_operation_progress(NULL, pc);
669                         count++;
670
671                         name = d->d_name;
672                         if (name[0] == '.')
673                                 continue;
674
675                         /* already in summary?  shouldn't happen, but just incase ... */
676                         if ((info = camel_folder_summary_uid((CamelFolderSummary *)cls, name))) {
677                                 camel_message_info_free(info);
678                                 newname = destname = camel_folder_summary_next_uid_string(s);
679                         } else {
680                                 newname = NULL;
681                                 destname = name;
682                         }
683
684                         /* copy this to the destination folder, use 'standard' semantics for maildir info field */
685                         src = g_strdup_printf("%s/%s", new, name);
686                         destfilename = g_strdup_printf("%s:2,", destname);
687                         dest = g_strdup_printf("%s/%s", cur, destfilename);
688
689                         /* FIXME: This should probably use link/unlink */
690
691                         if (rename(src, dest) == 0) {
692                                 camel_maildir_summary_add(cls, destfilename, forceindex);
693                                 if (changes) {
694                                         camel_folder_change_info_add_uid(changes, destname);
695                                         camel_folder_change_info_recent_uid(changes, destname);
696                                 }
697                         } else {
698                                 /* else?  we should probably care about failures, but wont */
699                                 g_warning("Failed to move new maildir message %s to cur %s", src, dest);
700                         }
701
702                         /* c strings are painful to work with ... */
703                         g_free(destfilename);
704                         g_free(newname);
705                         g_free(src);
706                         g_free(dest);
707                 }
708                 camel_operation_end(NULL);
709         }
710         closedir(dir);
711
712         g_free(new);
713         g_free(cur);
714
715         /* sort the summary based on receive time, since the directory order is not useful */
716         CAMEL_SUMMARY_LOCK(s, summary_lock);
717         qsort(s->messages->pdata, s->messages->len, sizeof(CamelMessageInfo *), sort_receive_cmp);
718         CAMEL_SUMMARY_UNLOCK(s, summary_lock);
719
720         return 0;
721 }
722
723 /* sync the summary with the ondisk files. */
724 static int
725 maildir_summary_sync(CamelLocalSummary *cls, gboolean expunge, CamelFolderChangeInfo *changes, CamelException *ex)
726 {
727         int count, i;
728         CamelMessageInfo *info;
729         CamelMaildirMessageInfo *mdi;
730 #ifdef DOESTRV
731         CamelFolderSummary *s = (CamelFolderSummary *)cls;
732 #endif
733         char *name;
734         struct stat st;
735
736         d(printf("summary_sync(expunge=%s)\n", expunge?"true":"false"));
737
738         if (camel_local_summary_check(cls, changes, ex) == -1)
739                 return -1;
740
741         camel_operation_start(NULL, _("Storing folder"));
742
743         count = camel_folder_summary_count((CamelFolderSummary *)cls);
744         for (i=count-1;i>=0;i--) {
745                 camel_operation_progress(NULL, (count-i)*100/count);
746
747                 info = camel_folder_summary_index((CamelFolderSummary *)cls, i);
748                 mdi = (CamelMaildirMessageInfo *)info;
749                 if (mdi && (mdi->info.info.flags & CAMEL_MESSAGE_DELETED) && expunge) {
750                         name = g_strdup_printf("%s/cur/%s", cls->folder_path, camel_maildir_info_filename(mdi));
751                         d(printf("deleting %s\n", name));
752                         if (unlink(name) == 0 || errno==ENOENT) {
753
754                                 /* FIXME: put this in folder_summary::remove()? */
755                                 if (cls->index)
756                                         camel_index_delete_name(cls->index, camel_message_info_uid(info));
757
758                                 camel_folder_change_info_remove_uid(changes, camel_message_info_uid(info));
759                                 camel_folder_summary_remove((CamelFolderSummary *)cls, info);
760                         }
761                         g_free(name);
762                 } else if (mdi && (mdi->info.info.flags & CAMEL_MESSAGE_FOLDER_FLAGGED)) {
763                         char *newname = camel_maildir_summary_info_to_name(mdi);
764                         char *dest;
765
766                         /* do we care about additional metainfo stored inside the message? */
767                         /* probably should all go in the filename? */
768
769                         /* have our flags/ i.e. name changed? */
770                         if (strcmp(newname, camel_maildir_info_filename(mdi))) {
771                                 name = g_strdup_printf("%s/cur/%s", cls->folder_path, camel_maildir_info_filename(mdi));
772                                 dest = g_strdup_printf("%s/cur/%s", cls->folder_path, newname);
773                                 rename(name, dest);
774                                 if (stat(dest, &st) == -1) {
775                                         /* we'll assume it didn't work, but dont change anything else */
776                                         g_free(newname);
777                                 } else {
778                                         /* TODO: If this is made mt-safe, then this code could be a problem, since
779                                            the estrv is being modified.
780                                            Sigh, this may mean the maildir name has to be cached another way */
781 #ifdef DOESTRV
782 #warning "cannot modify the estrv after its been setup, for mt-safe code"
783                                         CAMEL_SUMMARY_LOCK(s, summary_lock);
784                                         /* need to update the summary hash ref */
785                                         g_hash_table_remove(s->messages_uid, camel_message_info_uid(info));
786                                         info->strings = e_strv_set_ref_free(info->strings, CAMEL_MAILDIR_INFO_FILENAME, newname);
787                                         info->strings = e_strv_pack(info->strings);
788                                         g_hash_table_insert(s->messages_uid, (char *)camel_message_info_uid(info), info);
789                                         CAMEL_SUMMARY_UNLOCK(s, summary_lock);
790 #else
791 # ifdef DOEPOOLV
792                                         info->strings = e_poolv_set(info->strings, CAMEL_MAILDIR_INFO_FILENAME, newname, TRUE);
793 # else
794                                         g_free(mdi->filename);
795                                         mdi->filename = newname;
796 # endif
797 #endif
798                                 }
799                                 g_free(name);
800                                 g_free(dest);
801                         } else {
802                                 g_free(newname);
803                         }
804
805                         /* strip FOLDER_MESSAGE_FLAGED, etc */
806                         mdi->info.info.flags &= 0xffff;
807                 }
808                 camel_message_info_free(info);
809         }
810
811         camel_operation_end(NULL);
812
813         return ((CamelLocalSummaryClass *)parent_class)->sync(cls, expunge, changes, ex);
814 }
815