Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / providers / imapp / camel-imapp-store-summary.c
1 /*
2  * Copyright (C) 2002 Ximian Inc.
3  *
4  * Authors: Michael Zucchi <notzed@ximian.com>
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 <errno.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include <libedataserver/md5-utils.h>
32 #include <libedataserver/e-memory.h>
33
34 #include "camel-file-utils.h"
35 #include "camel-private.h"
36 #include "camel-string-utils.h"
37 #include "camel-utf8.h"
38
39 #include "camel-imapp-store-summary.h"
40
41 #define d(x)
42 #define io(x)                   /* io debug */
43
44 #define CAMEL_IMAPP_STORE_SUMMARY_VERSION_0 (0)
45
46 #define CAMEL_IMAPP_STORE_SUMMARY_VERSION (0)
47
48 #define _PRIVATE(o) (((CamelIMAPPStoreSummary *)(o))->priv)
49
50 static int summary_header_load(CamelStoreSummary *, FILE *);
51 static int summary_header_save(CamelStoreSummary *, FILE *);
52
53 /*static CamelStoreInfo * store_info_new(CamelStoreSummary *, const char *);*/
54 static CamelStoreInfo * store_info_load(CamelStoreSummary *, FILE *);
55 static int               store_info_save(CamelStoreSummary *, FILE *, CamelStoreInfo *);
56 static void              store_info_free(CamelStoreSummary *, CamelStoreInfo *);
57
58 static const char *store_info_string(CamelStoreSummary *, const CamelStoreInfo *, int);
59 static void store_info_set_string(CamelStoreSummary *, CamelStoreInfo *, int, const char *);
60
61 static void camel_imapp_store_summary_class_init (CamelIMAPPStoreSummaryClass *klass);
62 static void camel_imapp_store_summary_init       (CamelIMAPPStoreSummary *obj);
63 static void camel_imapp_store_summary_finalise   (CamelObject *obj);
64
65 static CamelStoreSummaryClass *camel_imapp_store_summary_parent;
66
67 static void
68 camel_imapp_store_summary_class_init (CamelIMAPPStoreSummaryClass *klass)
69 {
70         CamelStoreSummaryClass *ssklass = (CamelStoreSummaryClass *)klass;
71
72         ssklass->summary_header_load = summary_header_load;
73         ssklass->summary_header_save = summary_header_save;
74
75         /*ssklass->store_info_new  = store_info_new;*/
76         ssklass->store_info_load = store_info_load;
77         ssklass->store_info_save = store_info_save;
78         ssklass->store_info_free = store_info_free;
79
80         ssklass->store_info_string = store_info_string;
81         ssklass->store_info_set_string = store_info_set_string;
82 }
83
84 static void
85 camel_imapp_store_summary_init (CamelIMAPPStoreSummary *s)
86 {
87         /*struct _CamelIMAPPStoreSummaryPrivate *p;
88
89           p = _PRIVATE(s) = g_malloc0(sizeof(*p));*/
90
91         ((CamelStoreSummary *)s)->store_info_size = sizeof(CamelIMAPPStoreInfo);
92         s->version = CAMEL_IMAPP_STORE_SUMMARY_VERSION;
93 }
94
95 static void
96 camel_imapp_store_summary_finalise (CamelObject *obj)
97 {
98         /*struct _CamelIMAPPStoreSummaryPrivate *p;*/
99         /*CamelIMAPPStoreSummary *s = (CamelIMAPPStoreSummary *)obj;*/
100
101         /*p = _PRIVATE(obj);
102           g_free(p);*/
103 }
104
105 CamelType
106 camel_imapp_store_summary_get_type (void)
107 {
108         static CamelType type = CAMEL_INVALID_TYPE;
109         
110         if (type == CAMEL_INVALID_TYPE) {
111                 camel_imapp_store_summary_parent = (CamelStoreSummaryClass *)camel_store_summary_get_type();
112                 type = camel_type_register((CamelType)camel_imapp_store_summary_parent, "CamelIMAPPStoreSummary",
113                                            sizeof (CamelIMAPPStoreSummary),
114                                            sizeof (CamelIMAPPStoreSummaryClass),
115                                            (CamelObjectClassInitFunc) camel_imapp_store_summary_class_init,
116                                            NULL,
117                                            (CamelObjectInitFunc) camel_imapp_store_summary_init,
118                                            (CamelObjectFinalizeFunc) camel_imapp_store_summary_finalise);
119         }
120         
121         return type;
122 }
123
124 /**
125  * camel_imapp_store_summary_new:
126  *
127  * Create a new CamelIMAPPStoreSummary object.
128  * 
129  * Return value: A new CamelIMAPPStoreSummary widget.
130  **/
131 CamelIMAPPStoreSummary *
132 camel_imapp_store_summary_new (void)
133 {
134         CamelIMAPPStoreSummary *new = CAMEL_IMAPP_STORE_SUMMARY ( camel_object_new (camel_imapp_store_summary_get_type ()));
135
136         return new;
137 }
138
139 /**
140  * camel_imapp_store_summary_full_name:
141  * @s: 
142  * @path: 
143  * 
144  * Retrieve a summary item by full name.
145  *
146  * A referenced to the summary item is returned, which may be
147  * ref'd or free'd as appropriate.
148  * 
149  * Return value: The summary item, or NULL if the @full_name name
150  * is not available.
151  * It must be freed using camel_store_summary_info_free().
152  **/
153 CamelIMAPPStoreInfo *
154 camel_imapp_store_summary_full_name(CamelIMAPPStoreSummary *s, const char *full_name)
155 {
156         int count, i;
157         CamelIMAPPStoreInfo *info;
158
159         count = camel_store_summary_count((CamelStoreSummary *)s);
160         for (i=0;i<count;i++) {
161                 info = (CamelIMAPPStoreInfo *)camel_store_summary_index((CamelStoreSummary *)s, i);
162                 if (info) {
163                         if (strcmp(info->full_name, full_name) == 0)
164                                 return info;
165                         camel_store_summary_info_free((CamelStoreSummary *)s, (CamelStoreInfo *)info);
166                 }
167         }
168
169         return NULL;
170 }
171
172 char *
173 camel_imapp_store_summary_full_to_path(CamelIMAPPStoreSummary *s, const char *full_name, char dir_sep)
174 {
175         char *path, *p;
176         int c;
177         const char *f;
178
179         if (dir_sep != '/') {
180                 p = path = alloca(strlen(full_name)*3+1);
181                 f = full_name;
182                 while ( (c = *f++ & 0xff) ) {
183                         if (c == dir_sep)
184                                 *p++ = '/';
185                         else if (c == '/' || c == '%')
186                                 p += sprintf(p, "%%%02X", c);
187                         else
188                                 *p++ = c;
189                 }
190                 *p = 0;
191         } else
192                 path = (char *)full_name;
193
194         return camel_utf7_utf8(path);
195 }
196
197 static guint32 hexnib(guint32 c)
198 {
199         if (c >= '0' && c <= '9')
200                 return c-'0';
201         else if (c>='A' && c <= 'Z')
202                 return c-'A'+10;
203         else
204                 return 0;
205 }
206
207 char *
208 camel_imapp_store_summary_path_to_full(CamelIMAPPStoreSummary *s, const char *path, char dir_sep)
209 {
210         unsigned char *full, *f;
211         guint32 c, v = 0;
212         const char *p;
213         int state=0;
214         char *subpath, *last = NULL;
215         CamelStoreInfo *si;
216         CamelIMAPPStoreNamespace *ns;
217
218         /* check to see if we have a subpath of path already defined */
219         subpath = alloca(strlen(path)+1);
220         strcpy(subpath, path);
221         do {
222                 si = camel_store_summary_path((CamelStoreSummary *)s, subpath);
223                 if (si == NULL) {
224                         last = strrchr(subpath, '/');
225                         if (last)
226                                 *last = 0;
227                 }
228         } while (si == NULL && last);
229
230         /* path is already present, use the raw version we have */
231         if (si && strlen(subpath) == strlen(path)) {
232                 f = g_strdup(camel_imapp_store_info_full_name(s, si));
233                 camel_store_summary_info_free((CamelStoreSummary *)s, si);
234                 return f;
235         }
236
237         ns = camel_imapp_store_summary_namespace_find_path(s, path);
238
239         f = full = alloca(strlen(path)*2+1);
240         if (si)
241                 p = path + strlen(subpath);
242         else if (ns)
243                 p = path + strlen(ns->path);
244         else
245                 p = path;
246
247         while ( (c = camel_utf8_getc((const unsigned char **)&p)) ) {
248                 switch(state) {
249                 case 0:
250                         if (c == '%')
251                                 state = 1;
252                         else {
253                                 if (c == '/')
254                                         c = dir_sep;
255                                 camel_utf8_putc(&f, c);
256                         }
257                         break;
258                 case 1:
259                         state = 2;
260                         v = hexnib(c)<<4;
261                         break;
262                 case 2:
263                         state = 0;
264                         v |= hexnib(c);
265                         camel_utf8_putc(&f, v);
266                         break;
267                 }
268         }
269         camel_utf8_putc(&f, c);
270
271         /* merge old path part if required */
272         f = camel_utf8_utf7(full);
273         if (si) {
274                 full = g_strdup_printf("%s%s", camel_imapp_store_info_full_name(s, si), f);
275                 g_free(f);
276                 camel_store_summary_info_free((CamelStoreSummary *)s, si);
277                 f = full;
278         } else if (ns) {
279                 full = g_strdup_printf("%s%s", ns->full_name, f);
280                 g_free(f);
281                 f = full;
282         }
283
284         return f;
285 }
286
287 CamelIMAPPStoreInfo *
288 camel_imapp_store_summary_add_from_full(CamelIMAPPStoreSummary *s, const char *full, char dir_sep)
289 {
290         CamelIMAPPStoreInfo *info;
291         char *pathu8, *prefix;
292         int len;
293         char *full_name;
294         CamelIMAPPStoreNamespace *ns;
295
296         d(printf("adding full name '%s' '%c'\n", full, dir_sep));
297
298         len = strlen(full);
299         full_name = alloca(len+1);
300         strcpy(full_name, full);
301         if (full_name[len-1] == dir_sep)
302                 full_name[len-1] = 0;
303
304         info = camel_imapp_store_summary_full_name(s, full_name);
305         if (info) {
306                 camel_store_summary_info_free((CamelStoreSummary *)s, (CamelStoreInfo *)info);
307                 d(printf("  already there\n"));
308                 return info;
309         }
310
311         ns = camel_imapp_store_summary_namespace_find_full(s, full_name);
312         if (ns) {
313                 d(printf("(found namespace for '%s' ns '%s') ", full_name, ns->path));
314                 len = strlen(ns->full_name);
315                 if (len >= strlen(full_name)) {
316                         pathu8 = g_strdup(ns->path);
317                 } else {
318                         if (full_name[len] == ns->sep)
319                                 len++;
320                         
321                         prefix = camel_imapp_store_summary_full_to_path(s, full_name+len, ns->sep);
322                         if (*ns->path) {
323                                 pathu8 = g_strdup_printf ("%s/%s", ns->path, prefix);
324                                 g_free (prefix);
325                         } else {
326                                 pathu8 = prefix;
327                         }
328                 }
329                 d(printf(" (pathu8 = '%s')", pathu8));
330         } else {
331                 d(printf("(Cannot find namespace for '%s')\n", full_name));
332                 pathu8 = camel_imapp_store_summary_full_to_path(s, full_name, dir_sep);
333         }
334
335         info = (CamelIMAPPStoreInfo *)camel_store_summary_add_from_path((CamelStoreSummary *)s, pathu8);
336         if (info) {
337                 d(printf("  '%s' -> '%s'\n", pathu8, full_name));
338                 camel_store_info_set_string((CamelStoreSummary *)s, (CamelStoreInfo *)info, CAMEL_IMAPP_STORE_INFO_FULL_NAME, full_name);
339         } else
340                 d(printf("  failed\n"));
341
342         return info;
343 }
344
345 /* should this be const? */
346 /* TODO: deprecate/merge this function with path_to_full */
347 char *
348 camel_imapp_store_summary_full_from_path(CamelIMAPPStoreSummary *s, const char *path)
349 {
350         CamelIMAPPStoreNamespace *ns;
351         char *name = NULL;
352
353         ns = camel_imapp_store_summary_namespace_find_path(s, path);
354         if (ns)
355                 name = camel_imapp_store_summary_path_to_full(s, path, ns->sep);
356
357         d(printf("looking up path %s -> %s\n", path, name?name:"not found"));
358
359         return name;
360 }
361
362 /* TODO: this api needs some more work */
363 CamelIMAPPStoreNamespace *camel_imapp_store_summary_namespace_new(CamelIMAPPStoreSummary *s, const char *full_name, char dir_sep)
364 {
365         CamelIMAPPStoreNamespace *ns;
366         char *p;
367         int len;
368
369         ns = g_malloc0(sizeof(*ns));
370         ns->full_name = g_strdup(full_name);
371         len = strlen(ns->full_name)-1;
372         if (len >= 0 && ns->full_name[len] == dir_sep)
373                 ns->full_name[len] = 0;
374         ns->sep = dir_sep;
375
376         p = ns->path = camel_imapp_store_summary_full_to_path(s, ns->full_name, dir_sep);
377         while (*p) {
378                 if (*p == '/')
379                         *p = '.';
380                 p++;
381         }
382
383         return ns;
384 }
385
386 void camel_imapp_store_summary_namespace_set(CamelIMAPPStoreSummary *s, CamelIMAPPStoreNamespace *ns)
387 {
388         static void namespace_clear(CamelStoreSummary *s);
389
390         d(printf("Setting namesapce to '%s' '%c' -> '%s'\n", ns->full_name, ns->sep, ns->path));
391         namespace_clear((CamelStoreSummary *)s);
392         s->namespace = ns;
393         camel_store_summary_touch((CamelStoreSummary *)s);
394 }
395
396 CamelIMAPPStoreNamespace *
397 camel_imapp_store_summary_namespace_find_path(CamelIMAPPStoreSummary *s, const char *path)
398 {
399         int len;
400         CamelIMAPPStoreNamespace *ns;
401
402         /* NB: this currently only compares against 1 namespace, in future compare against others */
403         ns = s->namespace;
404         while (ns) {
405                 len = strlen(ns->path);
406                 if (len == 0
407                     || (strncmp(ns->path, path, len) == 0
408                         && (path[len] == '/' || path[len] == 0)))
409                         break;
410                 ns = NULL;
411         }
412
413         /* have a default? */
414         return ns;
415 }
416
417 CamelIMAPPStoreNamespace *
418 camel_imapp_store_summary_namespace_find_full(CamelIMAPPStoreSummary *s, const char *full)
419 {
420         int len;
421         CamelIMAPPStoreNamespace *ns;
422
423         /* NB: this currently only compares against 1 namespace, in future compare against others */
424         ns = s->namespace;
425         while (ns) {
426                 len = strlen(ns->full_name);
427                 d(printf("find_full: comparing namespace '%s' to name '%s'\n", ns->full_name, full));
428                 if (len == 0
429                     || (strncmp(ns->full_name, full, len) == 0
430                         && (full[len] == ns->sep || full[len] == 0)))
431                         break;
432                 ns = NULL;
433         }
434
435         /* have a default? */
436         return ns;
437 }
438
439 static void
440 namespace_free(CamelStoreSummary *s, CamelIMAPPStoreNamespace *ns)
441 {
442         g_free(ns->path);
443         g_free(ns->full_name);
444         g_free(ns);
445 }
446
447 static void
448 namespace_clear(CamelStoreSummary *s)
449 {
450         CamelIMAPPStoreSummary *is = (CamelIMAPPStoreSummary *)s;
451
452         if (is->namespace)
453                 namespace_free(s, is->namespace);
454         is->namespace = NULL;
455 }
456
457 static CamelIMAPPStoreNamespace *
458 namespace_load(CamelStoreSummary *s, FILE *in)
459 {
460         CamelIMAPPStoreNamespace *ns;
461         guint32 sep = '/';
462
463         ns = g_malloc0(sizeof(*ns));
464         if (camel_file_util_decode_string(in, &ns->path) == -1
465             || camel_file_util_decode_string(in, &ns->full_name) == -1
466             || camel_file_util_decode_uint32(in, &sep) == -1) {
467                 namespace_free(s, ns);
468                 ns = NULL;
469         } else {
470                 ns->sep = sep;
471         }
472
473         return ns;
474 }
475
476 static int
477 namespace_save(CamelStoreSummary *s, FILE *in, CamelIMAPPStoreNamespace *ns)
478 {
479         if (camel_file_util_encode_string(in, ns->path) == -1
480             || camel_file_util_encode_string(in, ns->full_name) == -1
481             || camel_file_util_encode_uint32(in, (guint32)ns->sep) == -1)
482                 return -1;
483
484         return 0;
485 }
486
487 static int
488 summary_header_load(CamelStoreSummary *s, FILE *in)
489 {
490         CamelIMAPPStoreSummary *is = (CamelIMAPPStoreSummary *)s;
491         gint32 version, capabilities, count;
492
493         namespace_clear(s);
494
495         if (camel_imapp_store_summary_parent->summary_header_load((CamelStoreSummary *)s, in) == -1
496             || camel_file_util_decode_fixed_int32(in, &version) == -1)
497                 return -1;
498
499         is->version = version;
500
501         if (version < CAMEL_IMAPP_STORE_SUMMARY_VERSION_0) {
502                 g_warning("Store summary header version too low");
503                 return -1;
504         }
505
506         /* note file format can be expanded to contain more namespaces, but only 1 at the moment */
507         if (camel_file_util_decode_fixed_int32(in, &capabilities) == -1
508             || camel_file_util_decode_fixed_int32(in, &count) == -1
509             || count > 1)
510                 return -1;
511
512         is->capabilities = capabilities;
513         if (count == 1) {
514                 if ((is->namespace = namespace_load(s, in)) == NULL)
515                         return -1;
516         }
517
518         return 0;
519 }
520
521 static int
522 summary_header_save(CamelStoreSummary *s, FILE *out)
523 {
524         CamelIMAPPStoreSummary *is = (CamelIMAPPStoreSummary *)s;
525         guint32 count;
526
527         count = is->namespace?1:0;
528
529         /* always write as latest version */
530         if (camel_imapp_store_summary_parent->summary_header_save((CamelStoreSummary *)s, out) == -1
531             || camel_file_util_encode_fixed_int32(out, CAMEL_IMAPP_STORE_SUMMARY_VERSION) == -1
532             || camel_file_util_encode_fixed_int32(out, is->capabilities) == -1
533             || camel_file_util_encode_fixed_int32(out, count) == -1)        
534                 return -1;
535
536         if (is->namespace && namespace_save(s, out, is->namespace) == -1)
537                 return -1;
538
539         return 0;
540 }
541
542 static CamelStoreInfo *
543 store_info_load(CamelStoreSummary *s, FILE *in)
544 {
545         CamelIMAPPStoreInfo *mi;
546
547         mi = (CamelIMAPPStoreInfo *)camel_imapp_store_summary_parent->store_info_load(s, in);
548         if (mi) {
549                 if (camel_file_util_decode_string(in, &mi->full_name) == -1) {
550                         camel_store_summary_info_free(s, (CamelStoreInfo *)mi);
551                         mi = NULL;
552                 }
553         }
554
555         return (CamelStoreInfo *)mi;
556 }
557
558 static int
559 store_info_save(CamelStoreSummary *s, FILE *out, CamelStoreInfo *mi)
560 {
561         CamelIMAPPStoreInfo *isi = (CamelIMAPPStoreInfo *)mi;
562
563         if (camel_imapp_store_summary_parent->store_info_save(s, out, mi) == -1
564             || camel_file_util_encode_string(out, isi->full_name) == -1)
565                 return -1;
566
567         return 0;
568 }
569
570 static void
571 store_info_free(CamelStoreSummary *s, CamelStoreInfo *mi)
572 {
573         CamelIMAPPStoreInfo *isi = (CamelIMAPPStoreInfo *)mi;
574
575         g_free(isi->full_name);
576         camel_imapp_store_summary_parent->store_info_free(s, mi);
577 }
578
579 static const char *
580 store_info_string(CamelStoreSummary *s, const CamelStoreInfo *mi, int type)
581 {
582         CamelIMAPPStoreInfo *isi = (CamelIMAPPStoreInfo *)mi;
583
584         /* FIXME: Locks? */
585
586         g_assert (mi != NULL);
587
588         switch (type) {
589         case CAMEL_IMAPP_STORE_INFO_FULL_NAME:
590                 return isi->full_name;
591         default:
592                 return camel_imapp_store_summary_parent->store_info_string(s, mi, type);
593         }
594 }
595
596 static void
597 store_info_set_string(CamelStoreSummary *s, CamelStoreInfo *mi, int type, const char *str)
598 {
599         CamelIMAPPStoreInfo *isi = (CamelIMAPPStoreInfo *)mi;
600
601         g_assert(mi != NULL);
602
603         switch(type) {
604         case CAMEL_IMAPP_STORE_INFO_FULL_NAME:
605                 d(printf("Set full name %s -> %s\n", isi->full_name, str));
606                 CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
607                 g_free(isi->full_name);
608                 isi->full_name = g_strdup(str);
609                 CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
610                 break;
611         default:
612                 camel_imapp_store_summary_parent->store_info_set_string(s, mi, type, str);
613                 break;
614         }
615 }