Split camel-imapx library and merge into camel so that providers can be written on...
[platform/upstream/evolution-data-server.git] / camel / camel-imapx-store-summary.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4  *
5  * Authors: Michael Zucchi <notzed@ximian.com>
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of version 2 of the GNU Lesser General Public
9  * License as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this program; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <ctype.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31
32 #include <camel/camel-file-utils.h>
33 #include "camel-imapx-utils.h"
34 #include "camel-imapx-store-summary.h"
35
36 #define d(...) camel_imapx_debug(debug, '?', __VA_ARGS__)
37
38 #define CAMEL_IMAPX_STORE_SUMMARY_VERSION_0 (0)
39
40 #define CAMEL_IMAPX_STORE_SUMMARY_VERSION (0)
41
42 static gint summary_header_load (CamelStoreSummary *, FILE *);
43 static gint summary_header_save (CamelStoreSummary *, FILE *);
44
45 /*static CamelStoreInfo * store_info_new(CamelStoreSummary *, const gchar *);*/
46 static CamelStoreInfo * store_info_load (CamelStoreSummary *, FILE *);
47 static gint              store_info_save (CamelStoreSummary *, FILE *, CamelStoreInfo *);
48 static void              store_info_free (CamelStoreSummary *, CamelStoreInfo *);
49
50 static const gchar *store_info_string (CamelStoreSummary *, const CamelStoreInfo *, gint);
51 static void store_info_set_string (CamelStoreSummary *, CamelStoreInfo *, int, const gchar *);
52
53 G_DEFINE_TYPE (CamelIMAPXStoreSummary, camel_imapx_store_summary, CAMEL_TYPE_STORE_SUMMARY)
54
55 static void
56 imapx_store_summary_finalize (GObject *object)
57 {
58         CamelIMAPXStoreSummary *summary;
59
60         summary = CAMEL_IMAPX_STORE_SUMMARY (object);
61
62         camel_imapx_namespace_list_clear (summary->namespaces);
63
64         /* Chain up to parent's finalize() method. */
65         G_OBJECT_CLASS (camel_imapx_store_summary_parent_class)->finalize (object);
66 }
67
68 static void
69 camel_imapx_store_summary_class_init (CamelIMAPXStoreSummaryClass *class)
70 {
71         GObjectClass *object_class;
72         CamelStoreSummaryClass *store_summary_class;
73
74         object_class = G_OBJECT_CLASS (class);
75         object_class->finalize = imapx_store_summary_finalize;
76
77         store_summary_class = CAMEL_STORE_SUMMARY_CLASS (class);
78         store_summary_class->summary_header_load = summary_header_load;
79         store_summary_class->summary_header_save = summary_header_save;
80         store_summary_class->store_info_load = store_info_load;
81         store_summary_class->store_info_save = store_info_save;
82         store_summary_class->store_info_free = store_info_free;
83         store_summary_class->store_info_string = store_info_string;
84         store_summary_class->store_info_set_string = store_info_set_string;
85 }
86
87 static void
88 camel_imapx_store_summary_init (CamelIMAPXStoreSummary *s)
89 {
90         ((CamelStoreSummary *) s)->store_info_size = sizeof (CamelIMAPXStoreInfo);
91         s->version = CAMEL_IMAPX_STORE_SUMMARY_VERSION;
92 }
93
94 /**
95  * camel_imapx_store_summary_new:
96  *
97  * Create a new CamelIMAPXStoreSummary object.
98  *
99  * Returns: A new CamelIMAPXStoreSummary widget.
100  **/
101 CamelIMAPXStoreSummary *
102 camel_imapx_store_summary_new (void)
103 {
104         return g_object_new (CAMEL_TYPE_IMAPX_STORE_SUMMARY, NULL);
105 }
106
107 /**
108  * camel_imapx_store_summary_full_name:
109  * @s:
110  * @full_name:
111  *
112  * Retrieve a summary item by full name.
113  *
114  * A referenced to the summary item is returned, which may be
115  * ref'd or free'd as appropriate.
116  *
117  * Returns: The summary item, or NULL if the @full_name name
118  * is not available.
119  * It must be freed using camel_store_summary_info_free().
120  **/
121 CamelIMAPXStoreInfo *
122 camel_imapx_store_summary_full_name (CamelIMAPXStoreSummary *s,
123                                      const gchar *full_name)
124 {
125         gint count, i;
126         CamelIMAPXStoreInfo *info;
127
128         count = camel_store_summary_count ((CamelStoreSummary *) s);
129         for (i = 0; i < count; i++) {
130                 info = (CamelIMAPXStoreInfo *) camel_store_summary_index ((CamelStoreSummary *) s, i);
131                 if (info) {
132                         if (strcmp (info->full_name, full_name) == 0)
133                                 return info;
134                         camel_store_summary_info_free ((CamelStoreSummary *) s, (CamelStoreInfo *) info);
135                 }
136         }
137
138         return NULL;
139 }
140
141 gchar *
142 camel_imapx_store_summary_full_to_path (CamelIMAPXStoreSummary *s,
143                                         const gchar *full_name,
144                                         gchar dir_sep)
145 {
146         gchar *path, *p;
147
148         p = path = g_strdup (full_name);
149
150         if (dir_sep && dir_sep != '/') {
151                 while (*p) {
152                         if (*p == '/')
153                                 *p = dir_sep;
154                         else if (*p == dir_sep)
155                                 *p = '/';
156                         p++;
157                 }
158         }
159         return path;
160 }
161
162 gchar *
163 camel_imapx_store_summary_path_to_full (CamelIMAPXStoreSummary *s,
164                                         const gchar *path,
165                                         gchar dir_sep)
166 {
167         gchar *full, *f;
168         const gchar *p;
169         gchar *subpath, *last = NULL;
170         CamelStoreInfo *si;
171         CamelIMAPXStoreNamespace *ns;
172
173         /* check to see if we have a subpath of path already defined */
174         subpath = alloca (strlen (path) + 1);
175         strcpy (subpath, path);
176         do {
177                 si = camel_store_summary_path ((CamelStoreSummary *) s, subpath);
178                 if (si == NULL) {
179                         last = strrchr (subpath, '/');
180                         if (last)
181                                 *last = 0;
182                 }
183         } while (si == NULL && last);
184
185         /* path is already present, use the raw version we have */
186         if (si && strlen (subpath) == strlen (path)) {
187                 f = g_strdup (camel_imapx_store_info_full_name (s, si));
188                 camel_store_summary_info_free ((CamelStoreSummary *) s, si);
189                 return f;
190         }
191
192         ns = camel_imapx_store_summary_namespace_find_path (s, path);
193
194         if (si)
195                 p = path + strlen (subpath);
196         else if (ns)
197                 p = path + strlen (ns->path);
198         else
199                 p = path;
200
201         f = full = g_strdup (p);
202         if (dir_sep != '/') {
203                 while (*f) {
204                         if (*f == '/')
205                                 *f = dir_sep;
206                         else if (*f == dir_sep)
207                                 *f = '/';
208                         f++;
209                 }
210         }
211
212         /* merge old path part if required */
213         f = full;
214         if (si) {
215                 full = g_strdup_printf("%s%s", camel_imapx_store_info_full_name(s, si), f);
216                 g_free (f);
217                 camel_store_summary_info_free ((CamelStoreSummary *) s, si);
218                 f = full;
219         } else if (ns) {
220                 full = g_strdup_printf("%s%s", ns->full_name, f);
221                 g_free (f);
222                 f = full;
223         }
224
225         return f;
226 }
227
228 CamelIMAPXStoreInfo *
229 camel_imapx_store_summary_add_from_full (CamelIMAPXStoreSummary *s,
230                                          const gchar *full,
231                                          gchar dir_sep)
232 {
233         CamelIMAPXStoreInfo *info;
234         gchar *pathu8, *prefix;
235         gint len;
236         gchar *full_name;
237         CamelIMAPXStoreNamespace *ns;
238
239         d("adding full name '%s' '%c'\n", full, dir_sep);
240
241         len = strlen (full);
242         full_name = alloca (len + 1);
243         strcpy (full_name, full);
244         if (full_name[len - 1] == dir_sep)
245                 full_name[len - 1] = 0;
246
247         info = camel_imapx_store_summary_full_name (s, full_name);
248         if (info) {
249                 camel_store_summary_info_free ((CamelStoreSummary *) s, (CamelStoreInfo *) info);
250                 d("  already there\n");
251                 return info;
252         }
253
254         ns = camel_imapx_store_summary_namespace_find_full (s, full_name);
255         if (ns) {
256                 d("(found namespace for '%s' ns '%s') ", full_name, ns->path);
257                 len = strlen (ns->full_name);
258                 if (len >= strlen (full_name)) {
259                         pathu8 = g_strdup (ns->path);
260                 } else {
261                         if (full_name[len] == ns->sep)
262                                 len++;
263
264                         prefix = camel_imapx_store_summary_full_to_path (s, full_name + len, ns->sep);
265                         if (*ns->path) {
266                                 pathu8 = g_strdup_printf ("%s/%s", ns->path, prefix);
267                                 g_free (prefix);
268                         } else {
269                                 pathu8 = prefix;
270                         }
271                 }
272                 d(" (pathu8 = '%s')", pathu8);
273         } else {
274                 d("(Cannot find namespace for '%s')\n", full_name);
275                 pathu8 = camel_imapx_store_summary_full_to_path (s, full_name, dir_sep);
276         }
277
278         info = (CamelIMAPXStoreInfo *) camel_store_summary_add_from_path ((CamelStoreSummary *) s, pathu8);
279         if (info) {
280                 d("  '%s' -> '%s'\n", pathu8, full_name);
281                 camel_store_info_set_string ((CamelStoreSummary *) s, (CamelStoreInfo *) info, CAMEL_IMAPX_STORE_INFO_FULL_NAME, full_name);
282
283                 if (!g_ascii_strcasecmp(full_name, "inbox"))
284                         info->info.flags |= CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_INBOX;
285         } else {
286                 d("  failed\n");
287         }
288
289         g_free (pathu8);
290
291         return info;
292 }
293
294 /* should this be const? */
295 /* TODO: deprecate/merge this function with path_to_full */
296 gchar *
297 camel_imapx_store_summary_full_from_path (CamelIMAPXStoreSummary *s,
298                                           const gchar *path)
299 {
300         CamelIMAPXStoreNamespace *ns;
301         gchar *name = NULL;
302
303         ns = camel_imapx_store_summary_namespace_find_path (s, path);
304         if (ns)
305                 name = camel_imapx_store_summary_path_to_full (s, path, ns->sep);
306
307         d("looking up path %s -> %s\n", path, name?name:"not found");
308
309         return name;
310 }
311
312 /* TODO: this api needs some more work */
313 CamelIMAPXStoreNamespace *
314 camel_imapx_store_summary_namespace_new (CamelIMAPXStoreSummary *s,
315                                          const gchar *full_name,
316                                          gchar dir_sep)
317 {
318         CamelIMAPXStoreNamespace *ns;
319         gchar *p, *o, c;
320         gint len;
321
322         ns = g_malloc0 (sizeof (*ns));
323         ns->full_name = g_strdup (full_name);
324         len = strlen (ns->full_name) - 1;
325         if (len >= 0 && ns->full_name[len] == dir_sep)
326                 ns->full_name[len] = 0;
327         ns->sep = dir_sep;
328
329         o = p = ns->path = camel_imapx_store_summary_full_to_path (s, ns->full_name, dir_sep);
330         while ((c = *p++)) {
331                 if (c != '#') {
332                         if (c == '/')
333                                 c = '.';
334                         *o++ = c;
335                 }
336         }
337         *o = 0;
338
339         return ns;
340 }
341
342 void camel_imapx_store_summary_namespace_set (CamelIMAPXStoreSummary *s, CamelIMAPXStoreNamespace *ns)
343 {
344         d("Setting namesapce to '%s' '%c' -> '%s'\n", ns->full_name, ns->sep, ns->path);
345
346         /* CHEN not needed  */
347         camel_store_summary_touch ((CamelStoreSummary *) s);
348 }
349
350 CamelIMAPXStoreNamespace *
351 camel_imapx_store_summary_namespace_find_path (CamelIMAPXStoreSummary *s,
352                                                const gchar *path)
353 {
354         gint len;
355         CamelIMAPXStoreNamespace *ns;
356
357         /* NB: this currently only compares against 1 namespace, in future compare against others */
358         /* CHEN TODO */
359         ns = s->namespaces->personal;
360         while (ns) {
361                 len = strlen (ns->path);
362                 if (len == 0
363                     || (strncmp (ns->path, path, len) == 0
364                         && (path[len] == '/' || path[len] == 0)))
365                         break;
366                 ns = NULL;
367         }
368
369         /* have a default? */
370         return ns;
371 }
372
373 CamelIMAPXStoreNamespace *
374 camel_imapx_store_summary_namespace_find_full (CamelIMAPXStoreSummary *s,
375                                                const gchar *full)
376 {
377         gint len = 0;
378         CamelIMAPXStoreNamespace *ns;
379
380         /* NB: this currently only compares against 1 namespace, in future compare against others */
381         /* CHEN TODO */
382         ns = s->namespaces->personal;
383         while (ns) {
384                 if (ns->full_name)
385                         len = strlen (ns->full_name);
386                 d("find_full: comparing namespace '%s' to name '%s'\n", ns->full_name, full);
387                 if (len == 0
388                     || (strncmp (ns->full_name, full, len) == 0
389                         && (full[len] == ns->sep || full[len] == 0)))
390                         break;
391                 ns = NULL;
392         }
393
394         /* have a default? */
395         return ns;
396 }
397
398 static CamelIMAPXNamespaceList *
399 namespace_load (CamelStoreSummary *s,
400                 FILE *in)
401 {
402         CamelIMAPXStoreNamespace *ns, *tail;
403         CamelIMAPXNamespaceList *nsl;
404         guint32 i, j;
405         gint32 n;
406
407         nsl = g_malloc0 (sizeof (CamelIMAPXNamespaceList));
408         nsl->personal = NULL;
409         nsl->shared = NULL;
410         nsl->other = NULL;
411
412         for (j = 0; j < 3; j++) {
413                 switch (j) {
414                 case 0:
415                         tail = (CamelIMAPXStoreNamespace *) &nsl->personal;
416                         break;
417                 case 1:
418                         tail = (CamelIMAPXStoreNamespace *) &nsl->shared;
419                         break;
420                 case 2:
421                         tail = (CamelIMAPXStoreNamespace *) &nsl->other;
422                         break;
423                 }
424
425                 if (camel_file_util_decode_fixed_int32 (in, &n) == -1)
426                         goto exception;
427
428                 for (i = 0; i < n; i++) {
429                         guint32 sep;
430                         gchar *path;
431                         gchar *full_name;
432
433                         if (camel_file_util_decode_string (in, &path) == -1)
434                                 goto exception;
435
436                         if (camel_file_util_decode_string (in, &full_name) == -1) {
437                                 g_free (path);
438                                 goto exception;
439                         }
440
441                         if (camel_file_util_decode_uint32 (in, &sep) == -1) {
442                                 g_free (path);
443                                 g_free (full_name);
444                                 goto exception;
445                         }
446
447                         tail->next = ns = g_malloc (sizeof (CamelIMAPXStoreNamespace));
448                         ns->sep = sep;
449                         ns->path = path;
450                         ns->full_name = full_name;
451                         ns->next = NULL;
452                         tail = ns;
453                 }
454         }
455
456         return nsl;
457 exception:
458         camel_imapx_namespace_list_clear (nsl);
459
460         return NULL;
461 }
462
463 static gint
464 namespace_save (CamelStoreSummary *s,
465                 FILE *out,
466                 CamelIMAPXNamespaceList *nsl)
467 {
468         CamelIMAPXStoreNamespace *ns, *cur = NULL;
469         guint32 i, n;
470
471         for (i = 0; i < 3; i++) {
472                 switch (i) {
473                 case 0:
474                         cur = nsl->personal;
475                         break;
476                 case 1:
477                         cur = nsl->shared;
478                         break;
479                 case 2:
480                         cur = nsl->other;
481                         break;
482                 }
483
484                 for (ns = cur, n = 0; ns; n++)
485                         ns = ns->next;
486
487                 if (camel_file_util_encode_fixed_int32 (out, n) == -1)
488                         return -1;
489
490                 ns = cur;
491                 while (ns != NULL) {
492                         if (camel_file_util_encode_string (out, ns->path) == -1)
493                                 return -1;
494
495                         if (camel_file_util_encode_string (out, ns->full_name) == -1)
496                                 return -1;
497
498                         if (camel_file_util_encode_uint32 (out, ns->sep) == -1)
499                                 return -1;
500
501                         ns = ns->next;
502                 }
503         }
504
505         return 0;
506 }
507
508 static gint
509 summary_header_load (CamelStoreSummary *s,
510                      FILE *in)
511 {
512         CamelIMAPXStoreSummary *is = (CamelIMAPXStoreSummary *) s;
513         CamelStoreSummaryClass *store_summary_class;
514         gint32 version, capabilities;
515
516         camel_imapx_namespace_list_clear (is->namespaces);
517
518         store_summary_class = CAMEL_STORE_SUMMARY_CLASS (camel_imapx_store_summary_parent_class);
519         if (store_summary_class->summary_header_load ((CamelStoreSummary *) s, in) == -1
520             || camel_file_util_decode_fixed_int32 (in, &version) == -1)
521                 return -1;
522
523         is->version = version;
524
525         if (version < CAMEL_IMAPX_STORE_SUMMARY_VERSION_0) {
526                 g_warning("Store summary header version too low");
527                 return -1;
528         }
529
530         /* note file format can be expanded to contain more namespaces, but only 1 at the moment */
531         if (camel_file_util_decode_fixed_int32 (in, &capabilities) == -1)
532                 return -1;
533
534         is->capabilities = capabilities;
535
536         /* namespaces */
537         if ((is->namespaces = namespace_load (s, in)) == NULL)
538                 return -1;
539
540         return 0;
541 }
542
543 static gint
544 summary_header_save (CamelStoreSummary *s,
545                      FILE *out)
546 {
547         CamelIMAPXStoreSummary *is = (CamelIMAPXStoreSummary *) s;
548         CamelStoreSummaryClass *store_summary_class;
549
550         /* always write as latest version */
551         store_summary_class = CAMEL_STORE_SUMMARY_CLASS (camel_imapx_store_summary_parent_class);
552         if (store_summary_class->summary_header_save ((CamelStoreSummary *) s, out) == -1
553             || camel_file_util_encode_fixed_int32 (out, CAMEL_IMAPX_STORE_SUMMARY_VERSION) == -1
554             || camel_file_util_encode_fixed_int32 (out, is->capabilities) == -1)
555                 return -1;
556
557         if (is->namespaces && namespace_save (s, out, is->namespaces) == -1)
558                 return -1;
559
560         return 0;
561 }
562
563 static CamelStoreInfo *
564 store_info_load (CamelStoreSummary *s,
565                  FILE *in)
566 {
567         CamelIMAPXStoreInfo *mi;
568         CamelStoreSummaryClass *store_summary_class;
569
570         store_summary_class = CAMEL_STORE_SUMMARY_CLASS (camel_imapx_store_summary_parent_class);
571         mi = (CamelIMAPXStoreInfo *) store_summary_class->store_info_load (s, in);
572         if (mi) {
573                 if (camel_file_util_decode_string (in, &mi->full_name) == -1) {
574                         camel_store_summary_info_free (s, (CamelStoreInfo *) mi);
575                         mi = NULL;
576                 } else {
577                         /* NB: this is done again for compatability */
578                         if (g_ascii_strcasecmp(mi->full_name, "inbox") == 0)
579                                 mi->info.flags |= CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_INBOX;
580                 }
581         }
582
583         return (CamelStoreInfo *) mi;
584 }
585
586 static gint
587 store_info_save (CamelStoreSummary *s,
588                  FILE *out,
589                  CamelStoreInfo *mi)
590 {
591         CamelIMAPXStoreInfo *isi = (CamelIMAPXStoreInfo *) mi;
592         CamelStoreSummaryClass *store_summary_class;
593
594         store_summary_class = CAMEL_STORE_SUMMARY_CLASS (camel_imapx_store_summary_parent_class);
595         if (store_summary_class->store_info_save (s, out, mi) == -1
596             || camel_file_util_encode_string (out, isi->full_name) == -1)
597                 return -1;
598
599         return 0;
600 }
601
602 static void
603 store_info_free (CamelStoreSummary *s,
604                  CamelStoreInfo *mi)
605 {
606         CamelIMAPXStoreInfo *isi = (CamelIMAPXStoreInfo *) mi;
607         CamelStoreSummaryClass *store_summary_class;
608
609         g_free (isi->full_name);
610
611         store_summary_class = CAMEL_STORE_SUMMARY_CLASS (camel_imapx_store_summary_parent_class);
612         store_summary_class->store_info_free (s, mi);
613 }
614
615 static const gchar *
616 store_info_string (CamelStoreSummary *s,
617                    const CamelStoreInfo *mi,
618                    gint type)
619 {
620         CamelIMAPXStoreInfo *isi = (CamelIMAPXStoreInfo *) mi;
621         CamelStoreSummaryClass *store_summary_class;
622
623         /* FIXME: Locks? */
624
625         g_assert (mi != NULL);
626
627         store_summary_class = CAMEL_STORE_SUMMARY_CLASS (camel_imapx_store_summary_parent_class);
628
629         switch (type) {
630         case CAMEL_IMAPX_STORE_INFO_FULL_NAME:
631                 return isi->full_name;
632         default:
633                 return store_summary_class->store_info_string (s, mi, type);
634         }
635 }
636
637 static void
638 store_info_set_string (CamelStoreSummary *s,
639                        CamelStoreInfo *mi,
640                        gint type,
641                        const gchar *str)
642 {
643         CamelIMAPXStoreInfo *isi = (CamelIMAPXStoreInfo *) mi;
644         CamelStoreSummaryClass *store_summary_class;
645
646         g_assert (mi != NULL);
647
648         store_summary_class = CAMEL_STORE_SUMMARY_CLASS (camel_imapx_store_summary_parent_class);
649
650         switch (type) {
651         case CAMEL_IMAPX_STORE_INFO_FULL_NAME:
652                 d("Set full name %s -> %s\n", isi->full_name, str);
653                 camel_store_summary_lock (s, CAMEL_STORE_SUMMARY_SUMMARY_LOCK);
654                 g_free (isi->full_name);
655                 isi->full_name = g_strdup (str);
656                 camel_store_summary_unlock (s, CAMEL_STORE_SUMMARY_SUMMARY_LOCK);
657                 break;
658         default:
659                 store_summary_class->store_info_set_string (s, mi, type, str);
660                 break;
661         }
662 }
663
664 void
665 camel_imapx_store_summary_set_namespaces (CamelIMAPXStoreSummary *summary,
666                                           const CamelIMAPXNamespaceList *nsl)
667 {
668         if (summary->namespaces)
669                 camel_imapx_namespace_list_clear (summary->namespaces);
670         summary->namespaces = camel_imapx_namespace_list_copy (nsl);
671 }