Extending test-client-custom-summary to try e_book_client_get_contacts_uids()
[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         gboolean is_inbox = g_ascii_strcasecmp (full_name, "INBOX") == 0;
128
129         count = camel_store_summary_count ((CamelStoreSummary *) s);
130         for (i = 0; i < count; i++) {
131                 info = (CamelIMAPXStoreInfo *) camel_store_summary_index ((CamelStoreSummary *) s, i);
132                 if (info) {
133                         if (strcmp (info->full_name, full_name) == 0 ||
134                             (is_inbox && g_ascii_strcasecmp (info->full_name, full_name) == 0))
135                                 return info;
136                         camel_store_summary_info_free ((CamelStoreSummary *) s, (CamelStoreInfo *) info);
137                 }
138         }
139
140         return NULL;
141 }
142
143 gchar *
144 camel_imapx_store_summary_full_to_path (CamelIMAPXStoreSummary *s,
145                                         const gchar *full_name,
146                                         gchar dir_sep)
147 {
148         gchar *path, *p;
149
150         p = path = g_strdup (full_name);
151
152         if (dir_sep && dir_sep != '/') {
153                 while (*p) {
154                         if (*p == '/')
155                                 *p = dir_sep;
156                         else if (*p == dir_sep)
157                                 *p = '/';
158                         p++;
159                 }
160         }
161         return path;
162 }
163
164 gchar *
165 camel_imapx_store_summary_path_to_full (CamelIMAPXStoreSummary *s,
166                                         const gchar *path,
167                                         gchar dir_sep)
168 {
169         gchar *full, *f;
170         const gchar *p;
171         gchar *subpath, *last = NULL;
172         CamelStoreInfo *si;
173         CamelIMAPXStoreNamespace *ns;
174
175         /* check to see if we have a subpath of path already defined */
176         subpath = alloca (strlen (path) + 1);
177         strcpy (subpath, path);
178         do {
179                 si = camel_store_summary_path ((CamelStoreSummary *) s, subpath);
180                 if (si == NULL) {
181                         last = strrchr (subpath, '/');
182                         if (last)
183                                 *last = 0;
184                 }
185         } while (si == NULL && last);
186
187         /* path is already present, use the raw version we have */
188         if (si && strlen (subpath) == strlen (path)) {
189                 f = g_strdup (camel_imapx_store_info_full_name (s, si));
190                 camel_store_summary_info_free ((CamelStoreSummary *) s, si);
191                 return f;
192         }
193
194         ns = camel_imapx_store_summary_namespace_find_path (s, path);
195
196         if (si)
197                 p = path + strlen (subpath);
198         else if (ns)
199                 p = path + strlen (ns->path);
200         else
201                 p = path;
202
203         f = full = g_strdup (p);
204         if (dir_sep != '/') {
205                 while (*f) {
206                         if (*f == '/')
207                                 *f = dir_sep;
208                         else if (*f == dir_sep)
209                                 *f = '/';
210                         f++;
211                 }
212         }
213
214         /* merge old path part if required */
215         f = full;
216         if (si) {
217                 full = g_strdup_printf ("%s%s", camel_imapx_store_info_full_name (s, si), f);
218                 g_free (f);
219                 camel_store_summary_info_free ((CamelStoreSummary *) s, si);
220                 f = full;
221         } else if (ns) {
222                 full = g_strdup_printf ("%s%s", ns->full_name, f);
223                 g_free (f);
224                 f = full;
225         }
226
227         return f;
228 }
229
230 CamelIMAPXStoreInfo *
231 camel_imapx_store_summary_add_from_full (CamelIMAPXStoreSummary *s,
232                                          const gchar *full,
233                                          gchar dir_sep)
234 {
235         CamelIMAPXStoreInfo *info;
236         gchar *pathu8, *prefix;
237         gint len;
238         gchar *full_name;
239         CamelIMAPXStoreNamespace *ns;
240
241         d ("adding full name '%s' '%c'\n", full, dir_sep);
242
243         len = strlen (full);
244         full_name = alloca (len + 1);
245         strcpy (full_name, full);
246         if (full_name[len - 1] == dir_sep)
247                 full_name[len - 1] = 0;
248
249         info = camel_imapx_store_summary_full_name (s, full_name);
250         if (info) {
251                 camel_store_summary_info_free ((CamelStoreSummary *) s, (CamelStoreInfo *) info);
252                 d ("  already there\n");
253                 return info;
254         }
255
256         ns = camel_imapx_store_summary_namespace_find_full (s, full_name);
257         if (ns) {
258                 d ("(found namespace for '%s' ns '%s') ", full_name, ns->path);
259                 len = strlen (ns->full_name);
260                 if (len >= strlen (full_name)) {
261                         pathu8 = g_strdup (ns->path);
262                 } else {
263                         if (full_name[len] == ns->sep)
264                                 len++;
265
266                         prefix = camel_imapx_store_summary_full_to_path (s, full_name + len, ns->sep);
267                         if (*ns->path) {
268                                 pathu8 = g_strdup_printf ("%s/%s", ns->path, prefix);
269                                 g_free (prefix);
270                         } else {
271                                 pathu8 = prefix;
272                         }
273                 }
274                 d (" (pathu8 = '%s')", pathu8);
275         } else {
276                 d ("(Cannot find namespace for '%s')\n", full_name);
277                 pathu8 = camel_imapx_store_summary_full_to_path (s, full_name, dir_sep);
278         }
279
280         info = (CamelIMAPXStoreInfo *) camel_store_summary_add_from_path ((CamelStoreSummary *) s, pathu8);
281         if (info) {
282                 d ("  '%s' -> '%s'\n", pathu8, full_name);
283                 camel_store_info_set_string ((CamelStoreSummary *) s, (CamelStoreInfo *) info, CAMEL_IMAPX_STORE_INFO_FULL_NAME, full_name);
284
285                 if (!g_ascii_strcasecmp (full_name, "inbox"))
286                         info->info.flags |= CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_INBOX;
287         } else {
288                 d ("  failed\n");
289         }
290
291         g_free (pathu8);
292
293         return info;
294 }
295
296 /* should this be const? */
297 /* TODO: deprecate/merge this function with path_to_full */
298 gchar *
299 camel_imapx_store_summary_full_from_path (CamelIMAPXStoreSummary *s,
300                                           const gchar *path)
301 {
302         CamelIMAPXStoreNamespace *ns;
303         gchar *name = NULL;
304
305         ns = camel_imapx_store_summary_namespace_find_path (s, path);
306         if (ns)
307                 name = camel_imapx_store_summary_path_to_full (s, path, ns->sep);
308
309         d ("looking up path %s -> %s\n", path, name ? name:"not found");
310
311         return name;
312 }
313
314 /* TODO: this api needs some more work */
315 CamelIMAPXStoreNamespace *
316 camel_imapx_store_summary_namespace_new (CamelIMAPXStoreSummary *s,
317                                          const gchar *full_name,
318                                          gchar dir_sep)
319 {
320         CamelIMAPXStoreNamespace *ns;
321         gchar *p, *o, c;
322         gint len;
323
324         ns = g_malloc0 (sizeof (*ns));
325         ns->full_name = g_strdup (full_name);
326         len = strlen (ns->full_name) - 1;
327         if (len >= 0 && ns->full_name[len] == dir_sep)
328                 ns->full_name[len] = 0;
329         ns->sep = dir_sep;
330
331         o = p = ns->path = camel_imapx_store_summary_full_to_path (s, ns->full_name, dir_sep);
332         while ((c = *p++)) {
333                 if (c != '#') {
334                         if (c == '/')
335                                 c = '.';
336                         *o++ = c;
337                 }
338         }
339         *o = 0;
340
341         return ns;
342 }
343
344 void camel_imapx_store_summary_namespace_set (CamelIMAPXStoreSummary *s, CamelIMAPXStoreNamespace *ns)
345 {
346         d ("Setting namesapce to '%s' '%c' -> '%s'\n", ns->full_name, ns->sep, ns->path);
347
348         /* CHEN not needed  */
349         camel_store_summary_touch ((CamelStoreSummary *) s);
350 }
351
352 CamelIMAPXStoreNamespace *
353 camel_imapx_store_summary_namespace_find_path (CamelIMAPXStoreSummary *s,
354                                                const gchar *path)
355 {
356         gint len;
357         CamelIMAPXStoreNamespace *ns;
358
359         /* NB: this currently only compares against 1 namespace, in future compare against others */
360         /* CHEN TODO */
361         ns = s->namespaces->personal;
362         while (ns) {
363                 len = strlen (ns->path);
364                 if (len == 0
365                     || (strncmp (ns->path, path, len) == 0
366                         && (path[len] == '/' || path[len] == 0)))
367                         break;
368                 ns = NULL;
369         }
370
371         /* have a default? */
372         return ns;
373 }
374
375 CamelIMAPXStoreNamespace *
376 camel_imapx_store_summary_namespace_find_full (CamelIMAPXStoreSummary *s,
377                                                const gchar *full)
378 {
379         gint len = 0;
380         CamelIMAPXStoreNamespace *ns;
381
382         /* NB: this currently only compares against 1 namespace, in future compare against others */
383         /* CHEN TODO */
384         ns = s->namespaces->personal;
385         while (ns) {
386                 if (ns->full_name)
387                         len = strlen (ns->full_name);
388                 d ("find_full: comparing namespace '%s' to name '%s'\n", ns->full_name, full);
389                 if (len == 0
390                     || (strncmp (ns->full_name, full, len) == 0
391                         && (full[len] == ns->sep || full[len] == 0)))
392                         break;
393                 ns = NULL;
394         }
395
396         /* have a default? */
397         return ns;
398 }
399
400 static CamelIMAPXNamespaceList *
401 namespace_load (CamelStoreSummary *s,
402                 FILE *in)
403 {
404         CamelIMAPXStoreNamespace *ns, *tail;
405         CamelIMAPXNamespaceList *nsl;
406         guint32 i, j;
407         gint32 n;
408
409         nsl = g_malloc0 (sizeof (CamelIMAPXNamespaceList));
410         nsl->personal = NULL;
411         nsl->shared = NULL;
412         nsl->other = NULL;
413
414         for (j = 0; j < 3; j++) {
415                 switch (j) {
416                 case 0:
417                         tail = (CamelIMAPXStoreNamespace *) &nsl->personal;
418                         break;
419                 case 1:
420                         tail = (CamelIMAPXStoreNamespace *) &nsl->shared;
421                         break;
422                 case 2:
423                         tail = (CamelIMAPXStoreNamespace *) &nsl->other;
424                         break;
425                 }
426
427                 if (camel_file_util_decode_fixed_int32 (in, &n) == -1)
428                         goto exception;
429
430                 for (i = 0; i < n; i++) {
431                         guint32 sep;
432                         gchar *path;
433                         gchar *full_name;
434
435                         if (camel_file_util_decode_string (in, &path) == -1)
436                                 goto exception;
437
438                         if (camel_file_util_decode_string (in, &full_name) == -1) {
439                                 g_free (path);
440                                 goto exception;
441                         }
442
443                         if (camel_file_util_decode_uint32 (in, &sep) == -1) {
444                                 g_free (path);
445                                 g_free (full_name);
446                                 goto exception;
447                         }
448
449                         tail->next = ns = g_malloc (sizeof (CamelIMAPXStoreNamespace));
450                         ns->sep = sep;
451                         ns->path = path;
452                         ns->full_name = full_name;
453                         ns->next = NULL;
454                         tail = ns;
455                 }
456         }
457
458         return nsl;
459 exception:
460         camel_imapx_namespace_list_clear (nsl);
461
462         return NULL;
463 }
464
465 static gint
466 namespace_save (CamelStoreSummary *s,
467                 FILE *out,
468                 CamelIMAPXNamespaceList *nsl)
469 {
470         CamelIMAPXStoreNamespace *ns, *cur = NULL;
471         guint32 i, n;
472
473         for (i = 0; i < 3; i++) {
474                 switch (i) {
475                 case 0:
476                         cur = nsl->personal;
477                         break;
478                 case 1:
479                         cur = nsl->shared;
480                         break;
481                 case 2:
482                         cur = nsl->other;
483                         break;
484                 }
485
486                 for (ns = cur, n = 0; ns; n++)
487                         ns = ns->next;
488
489                 if (camel_file_util_encode_fixed_int32 (out, n) == -1)
490                         return -1;
491
492                 ns = cur;
493                 while (ns != NULL) {
494                         if (camel_file_util_encode_string (out, ns->path) == -1)
495                                 return -1;
496
497                         if (camel_file_util_encode_string (out, ns->full_name) == -1)
498                                 return -1;
499
500                         if (camel_file_util_encode_uint32 (out, ns->sep) == -1)
501                                 return -1;
502
503                         ns = ns->next;
504                 }
505         }
506
507         return 0;
508 }
509
510 static gint
511 summary_header_load (CamelStoreSummary *s,
512                      FILE *in)
513 {
514         CamelIMAPXStoreSummary *is = (CamelIMAPXStoreSummary *) s;
515         CamelStoreSummaryClass *store_summary_class;
516         gint32 version, capabilities;
517
518         camel_imapx_namespace_list_clear (is->namespaces);
519
520         store_summary_class = CAMEL_STORE_SUMMARY_CLASS (camel_imapx_store_summary_parent_class);
521         if (store_summary_class->summary_header_load ((CamelStoreSummary *) s, in) == -1
522             || camel_file_util_decode_fixed_int32 (in, &version) == -1)
523                 return -1;
524
525         is->version = version;
526
527         if (version < CAMEL_IMAPX_STORE_SUMMARY_VERSION_0) {
528                 g_warning ("Store summary header version too low");
529                 return -1;
530         }
531
532         /* note file format can be expanded to contain more namespaces, but only 1 at the moment */
533         if (camel_file_util_decode_fixed_int32 (in, &capabilities) == -1)
534                 return -1;
535
536         is->capabilities = capabilities;
537
538         /* namespaces */
539         if ((is->namespaces = namespace_load (s, in)) == NULL)
540                 return -1;
541
542         return 0;
543 }
544
545 static gint
546 summary_header_save (CamelStoreSummary *s,
547                      FILE *out)
548 {
549         CamelIMAPXStoreSummary *is = (CamelIMAPXStoreSummary *) s;
550         CamelStoreSummaryClass *store_summary_class;
551
552         /* always write as latest version */
553         store_summary_class = CAMEL_STORE_SUMMARY_CLASS (camel_imapx_store_summary_parent_class);
554         if (store_summary_class->summary_header_save ((CamelStoreSummary *) s, out) == -1
555             || camel_file_util_encode_fixed_int32 (out, CAMEL_IMAPX_STORE_SUMMARY_VERSION) == -1
556             || camel_file_util_encode_fixed_int32 (out, is->capabilities) == -1)
557                 return -1;
558
559         if (is->namespaces && namespace_save (s, out, is->namespaces) == -1)
560                 return -1;
561
562         return 0;
563 }
564
565 static CamelStoreInfo *
566 store_info_load (CamelStoreSummary *s,
567                  FILE *in)
568 {
569         CamelIMAPXStoreInfo *mi;
570         CamelStoreSummaryClass *store_summary_class;
571
572         store_summary_class = CAMEL_STORE_SUMMARY_CLASS (camel_imapx_store_summary_parent_class);
573         mi = (CamelIMAPXStoreInfo *) store_summary_class->store_info_load (s, in);
574         if (mi) {
575                 if (camel_file_util_decode_string (in, &mi->full_name) == -1) {
576                         camel_store_summary_info_free (s, (CamelStoreInfo *) mi);
577                         mi = NULL;
578                 } else {
579                         /* NB: this is done again for compatability */
580                         if (g_ascii_strcasecmp (mi->full_name, "inbox") == 0)
581                                 mi->info.flags |= CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_INBOX;
582                 }
583         }
584
585         return (CamelStoreInfo *) mi;
586 }
587
588 static gint
589 store_info_save (CamelStoreSummary *s,
590                  FILE *out,
591                  CamelStoreInfo *mi)
592 {
593         CamelIMAPXStoreInfo *isi = (CamelIMAPXStoreInfo *) mi;
594         CamelStoreSummaryClass *store_summary_class;
595
596         store_summary_class = CAMEL_STORE_SUMMARY_CLASS (camel_imapx_store_summary_parent_class);
597         if (store_summary_class->store_info_save (s, out, mi) == -1
598             || camel_file_util_encode_string (out, isi->full_name) == -1)
599                 return -1;
600
601         return 0;
602 }
603
604 static void
605 store_info_free (CamelStoreSummary *s,
606                  CamelStoreInfo *mi)
607 {
608         CamelIMAPXStoreInfo *isi = (CamelIMAPXStoreInfo *) mi;
609         CamelStoreSummaryClass *store_summary_class;
610
611         g_free (isi->full_name);
612
613         store_summary_class = CAMEL_STORE_SUMMARY_CLASS (camel_imapx_store_summary_parent_class);
614         store_summary_class->store_info_free (s, mi);
615 }
616
617 static const gchar *
618 store_info_string (CamelStoreSummary *s,
619                    const CamelStoreInfo *mi,
620                    gint type)
621 {
622         CamelIMAPXStoreInfo *isi = (CamelIMAPXStoreInfo *) mi;
623         CamelStoreSummaryClass *store_summary_class;
624
625         /* FIXME: Locks? */
626
627         g_assert (mi != NULL);
628
629         store_summary_class = CAMEL_STORE_SUMMARY_CLASS (camel_imapx_store_summary_parent_class);
630
631         switch (type) {
632         case CAMEL_IMAPX_STORE_INFO_FULL_NAME:
633                 return isi->full_name;
634         default:
635                 return store_summary_class->store_info_string (s, mi, type);
636         }
637 }
638
639 static void
640 store_info_set_string (CamelStoreSummary *s,
641                        CamelStoreInfo *mi,
642                        gint type,
643                        const gchar *str)
644 {
645         CamelIMAPXStoreInfo *isi = (CamelIMAPXStoreInfo *) mi;
646         CamelStoreSummaryClass *store_summary_class;
647
648         g_assert (mi != NULL);
649
650         store_summary_class = CAMEL_STORE_SUMMARY_CLASS (camel_imapx_store_summary_parent_class);
651
652         switch (type) {
653         case CAMEL_IMAPX_STORE_INFO_FULL_NAME:
654                 d ("Set full name %s -> %s\n", isi->full_name, str);
655                 camel_store_summary_lock (s, CAMEL_STORE_SUMMARY_SUMMARY_LOCK);
656                 g_free (isi->full_name);
657                 isi->full_name = g_strdup (str);
658                 camel_store_summary_unlock (s, CAMEL_STORE_SUMMARY_SUMMARY_LOCK);
659                 break;
660         default:
661                 store_summary_class->store_info_set_string (s, mi, type, str);
662                 break;
663         }
664 }
665
666 void
667 camel_imapx_store_summary_set_namespaces (CamelIMAPXStoreSummary *summary,
668                                           const CamelIMAPXNamespaceList *nsl)
669 {
670         if (summary->namespaces)
671                 camel_imapx_namespace_list_clear (summary->namespaces);
672         summary->namespaces = camel_imapx_namespace_list_copy (nsl);
673 }