1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Copyright (C) 2002 Ximian Inc.
5 * Authors: Michael Zucchi <notzed@ximian.com>
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.
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.
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.
32 #include <libedataserver/md5-utils.h>
33 #include <libedataserver/e-memory.h>
35 #include "camel-file-utils.h"
36 #include "camel-private.h"
37 #include "camel-store.h"
38 #include "camel-utf8.h"
40 #include "camel-imap-store-summary.h"
43 #define io(x) /* io debug */
45 #define CAMEL_IMAP_STORE_SUMMARY_VERSION_0 (0)
47 #define CAMEL_IMAP_STORE_SUMMARY_VERSION (0)
49 #define _PRIVATE(o) (((CamelImapStoreSummary *)(o))->priv)
51 static void namespace_clear(CamelStoreSummary *s);
53 static int summary_header_load(CamelStoreSummary *, FILE *);
54 static int summary_header_save(CamelStoreSummary *, FILE *);
56 /*static CamelStoreInfo * store_info_new(CamelStoreSummary *, const char *);*/
57 static CamelStoreInfo * store_info_load(CamelStoreSummary *, FILE *);
58 static int store_info_save(CamelStoreSummary *, FILE *, CamelStoreInfo *);
59 static void store_info_free(CamelStoreSummary *, CamelStoreInfo *);
61 static const char *store_info_string(CamelStoreSummary *, const CamelStoreInfo *, int);
62 static void store_info_set_string(CamelStoreSummary *, CamelStoreInfo *, int, const char *);
64 static void camel_imap_store_summary_class_init (CamelImapStoreSummaryClass *klass);
65 static void camel_imap_store_summary_init (CamelImapStoreSummary *obj);
66 static void camel_imap_store_summary_finalise (CamelObject *obj);
68 static CamelStoreSummaryClass *camel_imap_store_summary_parent;
71 camel_imap_store_summary_class_init (CamelImapStoreSummaryClass *klass)
73 CamelStoreSummaryClass *ssklass = (CamelStoreSummaryClass *)klass;
75 ssklass->summary_header_load = summary_header_load;
76 ssklass->summary_header_save = summary_header_save;
78 /*ssklass->store_info_new = store_info_new;*/
79 ssklass->store_info_load = store_info_load;
80 ssklass->store_info_save = store_info_save;
81 ssklass->store_info_free = store_info_free;
83 ssklass->store_info_string = store_info_string;
84 ssklass->store_info_set_string = store_info_set_string;
88 camel_imap_store_summary_init (CamelImapStoreSummary *s)
90 /*struct _CamelImapStoreSummaryPrivate *p;
92 p = _PRIVATE(s) = g_malloc0(sizeof(*p));*/
94 ((CamelStoreSummary *)s)->store_info_size = sizeof(CamelImapStoreInfo);
95 s->version = CAMEL_IMAP_STORE_SUMMARY_VERSION;
99 camel_imap_store_summary_finalise (CamelObject *obj)
101 /*struct _CamelImapStoreSummaryPrivate *p;*/
102 /*CamelImapStoreSummary *s = (CamelImapStoreSummary *)obj;*/
109 camel_imap_store_summary_get_type (void)
111 static CamelType type = CAMEL_INVALID_TYPE;
113 if (type == CAMEL_INVALID_TYPE) {
114 camel_imap_store_summary_parent = (CamelStoreSummaryClass *)camel_store_summary_get_type();
115 type = camel_type_register((CamelType)camel_imap_store_summary_parent, "CamelImapStoreSummary",
116 sizeof (CamelImapStoreSummary),
117 sizeof (CamelImapStoreSummaryClass),
118 (CamelObjectClassInitFunc) camel_imap_store_summary_class_init,
120 (CamelObjectInitFunc) camel_imap_store_summary_init,
121 (CamelObjectFinalizeFunc) camel_imap_store_summary_finalise);
128 * camel_imap_store_summary_new:
130 * Create a new CamelImapStoreSummary object.
132 * Return value: A new CamelImapStoreSummary widget.
134 CamelImapStoreSummary *
135 camel_imap_store_summary_new (void)
137 CamelImapStoreSummary *new = CAMEL_IMAP_STORE_SUMMARY ( camel_object_new (camel_imap_store_summary_get_type ()));
143 * camel_imap_store_summary_full_name:
147 * Retrieve a summary item by full name.
149 * A referenced to the summary item is returned, which may be
150 * ref'd or free'd as appropriate.
152 * Return value: The summary item, or NULL if the @full_name name
154 * It must be freed using camel_store_summary_info_free().
157 camel_imap_store_summary_full_name(CamelImapStoreSummary *s, const char *full_name)
160 CamelImapStoreInfo *info;
162 count = camel_store_summary_count((CamelStoreSummary *)s);
163 for (i=0;i<count;i++) {
164 info = (CamelImapStoreInfo *)camel_store_summary_index((CamelStoreSummary *)s, i);
166 if (strcmp(info->full_name, full_name) == 0)
168 camel_store_summary_info_free((CamelStoreSummary *)s, (CamelStoreInfo *)info);
176 camel_imap_store_summary_full_to_path(CamelImapStoreSummary *s, const char *full_name, char dir_sep)
182 if (dir_sep != '/') {
183 p = path = alloca(strlen(full_name)*3+1);
185 while ( (c = *f++ & 0xff) ) {
188 else if (c == '/' || c == '%')
189 p += sprintf(p, "%%%02X", c);
195 path = (char *)full_name;
197 return g_strdup(path);
200 static guint32 hexnib(guint32 c)
202 if (c >= '0' && c <= '9')
204 else if (c>='A' && c <= 'Z')
211 camel_imap_store_summary_path_to_full(CamelImapStoreSummary *s, const char *path, char dir_sep)
217 char *subpath, *last = NULL;
219 CamelImapStoreNamespace *ns;
221 /* check to see if we have a subpath of path already defined */
222 subpath = alloca(strlen(path)+1);
223 strcpy(subpath, path);
225 si = camel_store_summary_path((CamelStoreSummary *)s, subpath);
227 last = strrchr(subpath, '/');
231 } while (si == NULL && last);
233 /* path is already present, use the raw version we have */
234 if (si && strlen(subpath) == strlen(path)) {
235 f = g_strdup(camel_imap_store_info_full_name(s, si));
236 camel_store_summary_info_free((CamelStoreSummary *)s, si);
240 ns = camel_imap_store_summary_namespace_find_path(s, path);
242 f = full = alloca(strlen(path)*2+1);
244 p = path + strlen(subpath);
246 p = path + strlen(ns->path);
250 while ((c = camel_utf8_getc((const unsigned char **)&p))) {
258 camel_utf8_putc((unsigned char **) &f, c);
268 camel_utf8_putc((unsigned char **) &f, v);
272 camel_utf8_putc((unsigned char **) &f, c);
274 /* merge old path part if required */
277 full = g_strdup_printf("%s%s", camel_imap_store_info_full_name(s, si), f);
279 camel_store_summary_info_free((CamelStoreSummary *)s, si);
282 full = g_strdup_printf("%s%s", ns->full_name, f);
291 camel_imap_store_summary_add_from_full(CamelImapStoreSummary *s, const char *full, char dir_sep)
293 CamelImapStoreInfo *info;
294 char *pathu8, *prefix;
297 CamelImapStoreNamespace *ns;
299 d(printf("adding full name '%s' '%c'\n", full, dir_sep));
302 full_name = alloca(len+1);
303 strcpy(full_name, full);
304 if (full_name[len-1] == dir_sep)
305 full_name[len-1] = 0;
307 info = camel_imap_store_summary_full_name(s, full_name);
309 camel_store_summary_info_free((CamelStoreSummary *)s, (CamelStoreInfo *)info);
310 d(printf(" already there\n"));
314 ns = camel_imap_store_summary_namespace_find_full(s, full_name);
316 d(printf("(found namespace for '%s' ns '%s') ", full_name, ns->path));
317 len = strlen(ns->full_name);
318 if (len >= strlen(full_name)) {
319 pathu8 = g_strdup(ns->path);
321 if (full_name[len] == ns->sep)
324 prefix = camel_imap_store_summary_full_to_path(s, full_name+len, ns->sep);
326 pathu8 = g_strdup_printf ("%s/%s", ns->path, prefix);
332 d(printf(" (pathu8 = '%s')", pathu8));
334 d(printf("(Cannot find namespace for '%s')\n", full_name));
335 pathu8 = camel_imap_store_summary_full_to_path(s, full_name, dir_sep);
338 info = (CamelImapStoreInfo *)camel_store_summary_add_from_path((CamelStoreSummary *)s, pathu8);
340 d(printf(" '%s' -> '%s'\n", pathu8, full_name));
341 camel_store_info_set_string((CamelStoreSummary *)s, (CamelStoreInfo *)info, CAMEL_IMAP_STORE_INFO_FULL_NAME, full_name);
343 if (g_ascii_strcasecmp(full_name, "inbox"))
344 info->info.flags |= CAMEL_FOLDER_SYSTEM|CAMEL_FOLDER_TYPE_INBOX;
346 d(printf(" failed\n"));
351 /* should this be const? */
352 /* TODO: deprecate/merge this function with path_to_full */
354 camel_imap_store_summary_full_from_path(CamelImapStoreSummary *s, const char *path)
356 CamelImapStoreNamespace *ns;
359 ns = camel_imap_store_summary_namespace_find_path(s, path);
361 name = camel_imap_store_summary_path_to_full(s, path, ns->sep);
363 d(printf("looking up path %s -> %s\n", path, name?name:"not found"));
368 /* TODO: this api needs some more work */
369 CamelImapStoreNamespace *camel_imap_store_summary_namespace_new(CamelImapStoreSummary *s, const char *full_name, char dir_sep)
371 CamelImapStoreNamespace *ns;
375 ns = g_malloc0(sizeof(*ns));
376 ns->full_name = g_strdup(full_name);
377 len = strlen(ns->full_name)-1;
378 if (len >= 0 && ns->full_name[len] == dir_sep)
379 ns->full_name[len] = 0;
382 o = p = ns->path = camel_imap_store_summary_full_to_path(s, ns->full_name, dir_sep);
395 void camel_imap_store_summary_namespace_set(CamelImapStoreSummary *s, CamelImapStoreNamespace *ns)
397 d(printf("Setting namesapce to '%s' '%c' -> '%s'\n", ns->full_name, ns->sep, ns->path));
398 namespace_clear((CamelStoreSummary *)s);
400 camel_store_summary_touch((CamelStoreSummary *)s);
403 CamelImapStoreNamespace *
404 camel_imap_store_summary_namespace_find_path(CamelImapStoreSummary *s, const char *path)
407 CamelImapStoreNamespace *ns;
409 /* NB: this currently only compares against 1 namespace, in future compare against others */
412 len = strlen(ns->path);
414 || (strncmp(ns->path, path, len) == 0
415 && (path[len] == '/' || path[len] == 0)))
420 /* have a default? */
424 CamelImapStoreNamespace *
425 camel_imap_store_summary_namespace_find_full(CamelImapStoreSummary *s, const char *full)
428 CamelImapStoreNamespace *ns;
430 /* NB: this currently only compares against 1 namespace, in future compare against others */
433 len = strlen(ns->full_name);
434 d(printf("find_full: comparing namespace '%s' to name '%s'\n", ns->full_name, full));
436 || (strncmp(ns->full_name, full, len) == 0
437 && (full[len] == ns->sep || full[len] == 0)))
442 /* have a default? */
447 namespace_free(CamelStoreSummary *s, CamelImapStoreNamespace *ns)
450 g_free(ns->full_name);
455 namespace_clear(CamelStoreSummary *s)
457 CamelImapStoreSummary *is = (CamelImapStoreSummary *)s;
460 namespace_free(s, is->namespace);
461 is->namespace = NULL;
464 static CamelImapStoreNamespace *
465 namespace_load(CamelStoreSummary *s, FILE *in)
467 CamelImapStoreNamespace *ns;
470 ns = g_malloc0(sizeof(*ns));
471 if (camel_file_util_decode_string(in, &ns->path) == -1
472 || camel_file_util_decode_string(in, &ns->full_name) == -1
473 || camel_file_util_decode_uint32(in, &sep) == -1) {
474 namespace_free(s, ns);
484 namespace_save(CamelStoreSummary *s, FILE *in, CamelImapStoreNamespace *ns)
486 if (camel_file_util_encode_string(in, ns->path) == -1
487 || camel_file_util_encode_string(in, ns->full_name) == -1
488 || camel_file_util_encode_uint32(in, (guint32)ns->sep) == -1)
495 summary_header_load(CamelStoreSummary *s, FILE *in)
497 CamelImapStoreSummary *is = (CamelImapStoreSummary *)s;
498 gint32 version, capabilities, count;
502 if (camel_imap_store_summary_parent->summary_header_load((CamelStoreSummary *)s, in) == -1
503 || camel_file_util_decode_fixed_int32(in, &version) == -1)
506 is->version = version;
508 if (version < CAMEL_IMAP_STORE_SUMMARY_VERSION_0) {
509 g_warning("Store summary header version too low");
513 /* note file format can be expanded to contain more namespaces, but only 1 at the moment */
514 if (camel_file_util_decode_fixed_int32(in, &capabilities) == -1
515 || camel_file_util_decode_fixed_int32(in, &count) == -1
519 is->capabilities = capabilities;
521 if ((is->namespace = namespace_load(s, in)) == NULL)
529 summary_header_save(CamelStoreSummary *s, FILE *out)
531 CamelImapStoreSummary *is = (CamelImapStoreSummary *)s;
534 count = is->namespace?1:0;
536 /* always write as latest version */
537 if (camel_imap_store_summary_parent->summary_header_save((CamelStoreSummary *)s, out) == -1
538 || camel_file_util_encode_fixed_int32(out, CAMEL_IMAP_STORE_SUMMARY_VERSION) == -1
539 || camel_file_util_encode_fixed_int32(out, is->capabilities) == -1
540 || camel_file_util_encode_fixed_int32(out, count) == -1)
543 if (is->namespace && namespace_save(s, out, is->namespace) == -1)
549 static CamelStoreInfo *
550 store_info_load(CamelStoreSummary *s, FILE *in)
552 CamelImapStoreInfo *mi;
554 mi = (CamelImapStoreInfo *)camel_imap_store_summary_parent->store_info_load(s, in);
556 if (camel_file_util_decode_string(in, &mi->full_name) == -1) {
557 camel_store_summary_info_free(s, (CamelStoreInfo *)mi);
560 /* NB: this is done again for compatability */
561 if (g_ascii_strcasecmp(mi->full_name, "inbox") == 0)
562 mi->info.flags |= CAMEL_FOLDER_SYSTEM|CAMEL_FOLDER_TYPE_INBOX;
566 return (CamelStoreInfo *)mi;
570 store_info_save(CamelStoreSummary *s, FILE *out, CamelStoreInfo *mi)
572 CamelImapStoreInfo *isi = (CamelImapStoreInfo *)mi;
574 if (camel_imap_store_summary_parent->store_info_save(s, out, mi) == -1
575 || camel_file_util_encode_string(out, isi->full_name) == -1)
582 store_info_free(CamelStoreSummary *s, CamelStoreInfo *mi)
584 CamelImapStoreInfo *isi = (CamelImapStoreInfo *)mi;
586 g_free(isi->full_name);
587 camel_imap_store_summary_parent->store_info_free(s, mi);
591 store_info_string(CamelStoreSummary *s, const CamelStoreInfo *mi, int type)
593 CamelImapStoreInfo *isi = (CamelImapStoreInfo *)mi;
597 g_assert (mi != NULL);
600 case CAMEL_IMAP_STORE_INFO_FULL_NAME:
601 return isi->full_name;
603 return camel_imap_store_summary_parent->store_info_string(s, mi, type);
608 store_info_set_string(CamelStoreSummary *s, CamelStoreInfo *mi, int type, const char *str)
610 CamelImapStoreInfo *isi = (CamelImapStoreInfo *)mi;
612 g_assert(mi != NULL);
615 case CAMEL_IMAP_STORE_INFO_FULL_NAME:
616 d(printf("Set full name %s -> %s\n", isi->full_name, str));
617 CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
618 g_free(isi->full_name);
619 isi->full_name = g_strdup(str);
620 CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
623 camel_imap_store_summary_parent->store_info_set_string(s, mi, type, str);