Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / providers / groupwise / camel-groupwise-store-summary.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2002 Ximian Inc.
4  *
5  * Authors: Parthasarathi Susarla <sparthasarathi@novell.com>  
6  *
7  * Description: Based on the imap implementaion of camelstoresummary
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of version 2 of the GNU Lesser General Public
11  * License as published by the Free Software Foundation.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this program; if not, write to the
20  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21  * Boston, MA 02110-1301, USA.
22  */
23
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <ctype.h>
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <unistd.h>
34
35 #include <libedataserver/e-memory.h>
36 #include <libedataserver/md5-utils.h>
37
38 #include "camel-file-utils.h"
39 #include "camel-private.h"
40 #include "camel-utf8.h"
41
42 #include "camel-groupwise-store-summary.h"
43
44 #define CAMEL_GW_STORE_SUMMARY_VERSION (0)
45
46 #define d(x)
47
48 static void namespace_clear(CamelStoreSummary *s);
49
50 static int summary_header_load(CamelStoreSummary *, FILE *);
51 static int summary_header_save(CamelStoreSummary *, FILE *);
52
53 static CamelStoreInfo *store_info_load(CamelStoreSummary *s, FILE *in) ;
54 static int store_info_save(CamelStoreSummary *s, FILE *out, CamelStoreInfo *mi) ;
55 static void store_info_free(CamelStoreSummary *s, CamelStoreInfo *mi) ;
56 static void store_info_set_string(CamelStoreSummary *s, CamelStoreInfo *mi, int type, const char *str) ;
57
58 static const char *store_info_string(CamelStoreSummary *s, const CamelStoreInfo *mi, int type) ;
59 CamelGroupwiseStoreNamespace *camel_groupwise_store_summary_namespace_find_full(CamelGroupwiseStoreSummary *s, const char *full) ;
60
61 static void camel_groupwise_store_summary_class_init (CamelGroupwiseStoreSummaryClass *klass);
62 static void camel_groupwise_store_summary_init       (CamelGroupwiseStoreSummary *obj);
63 static void camel_groupwise_store_summary_finalise   (CamelObject *obj);
64
65 static CamelStoreSummaryClass *camel_groupwise_store_summary_parent;
66
67
68 static void
69 camel_groupwise_store_summary_class_init (CamelGroupwiseStoreSummaryClass *klass)
70 {
71         CamelStoreSummaryClass *ssklass = (CamelStoreSummaryClass *)klass;
72
73         ssklass->summary_header_load = summary_header_load;
74         ssklass->summary_header_save = summary_header_save;
75         
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 }
85
86 static void
87 camel_groupwise_store_summary_init (CamelGroupwiseStoreSummary *s)
88 {
89
90         ((CamelStoreSummary *)s)->store_info_size = sizeof(CamelGroupwiseStoreInfo);
91         s->version = CAMEL_GW_STORE_SUMMARY_VERSION;
92 }
93
94
95 static void
96 camel_groupwise_store_summary_finalise (CamelObject *obj)
97 {
98 }
99
100
101 CamelType
102 camel_groupwise_store_summary_get_type (void)
103 {
104         static CamelType type = CAMEL_INVALID_TYPE;
105
106         if (type == CAMEL_INVALID_TYPE) {
107                 camel_groupwise_store_summary_parent = (CamelStoreSummaryClass *)camel_store_summary_get_type();
108                 type = camel_type_register((CamelType)camel_groupwise_store_summary_parent, "CamelGroupwiseStoreSummary",
109                                 sizeof (CamelGroupwiseStoreSummary),
110                                 sizeof (CamelGroupwiseStoreSummaryClass),
111                                 (CamelObjectClassInitFunc) camel_groupwise_store_summary_class_init,
112                                 NULL,
113                                 (CamelObjectInitFunc) camel_groupwise_store_summary_init,
114                                 (CamelObjectFinalizeFunc) camel_groupwise_store_summary_finalise);
115         }
116
117         return type;
118 }
119
120
121 CamelGroupwiseStoreSummary *
122 camel_groupwise_store_summary_new (void)
123 {
124         CamelGroupwiseStoreSummary *new = CAMEL_GW_STORE_SUMMARY ( camel_object_new (camel_groupwise_store_summary_get_type ()));
125
126         return new;
127 }
128
129
130 CamelGroupwiseStoreInfo *
131 camel_groupwise_store_summary_full_name(CamelGroupwiseStoreSummary *s, const char *full_name)
132 {
133         int count, i;
134         CamelGroupwiseStoreInfo *info;
135
136         count = camel_store_summary_count((CamelStoreSummary *)s);
137         for (i=0;i<count;i++) {
138                 info = (CamelGroupwiseStoreInfo *)camel_store_summary_index((CamelStoreSummary *)s, i);
139                 if (info) {
140                         if (strcmp(info->full_name, full_name) == 0)
141                                 return info;
142                         camel_store_summary_info_free((CamelStoreSummary *)s, (CamelStoreInfo *)info);
143                 }
144         }
145
146         return NULL;
147 }
148
149 char *
150 camel_groupwise_store_summary_full_to_path(CamelGroupwiseStoreSummary *s, const char *full_name, char dir_sep)
151 {
152         char *path, *p;
153         int c;
154         const char *f;
155
156         if (dir_sep != '/') {
157                 p = path = alloca(strlen(full_name)*3+1);
158                 f = full_name;
159                 while ( (c = *f++ & 0xff) ) {
160                         if (c == dir_sep)
161                                 *p++ = '/';
162                         else if (c == '/' || c == '%')
163                                 p += sprintf(p, "%%%02X", c);
164                         else
165                                 *p++ = c;
166                 }
167                 *p = 0;
168         } else
169                 path = (char *)full_name;
170
171         return g_strdup (path);
172 }
173 static guint32 hexnib(guint32 c)
174 {
175         if (c >= '0' && c <= '9')
176                 return c-'0';
177         else if (c>='A' && c <= 'Z')
178                 return c-'A'+10;
179         else
180                 return 0;
181 }
182
183 static int
184 namespace_save(CamelStoreSummary *s, FILE *in, CamelGroupwiseStoreNamespace *ns)
185 {
186         if (camel_file_util_encode_string(in, ns->path) == -1
187                         || camel_file_util_encode_string(in, ns->full_name) == -1
188                         || camel_file_util_encode_uint32(in, (guint32)ns->sep) == -1)
189                 return -1;
190
191         return 0;
192 }
193
194 static void
195 namespace_free(CamelStoreSummary *s, CamelGroupwiseStoreNamespace *ns)
196 {
197         g_free(ns->path);
198         g_free(ns->full_name);
199         g_free(ns);
200 }
201
202 static void
203 namespace_clear(CamelStoreSummary *s)
204 {
205         CamelGroupwiseStoreSummary *is = (CamelGroupwiseStoreSummary *)s;
206
207         if (is->namespace)
208                 namespace_free(s, is->namespace);
209         is->namespace = NULL;
210 }
211
212 static CamelGroupwiseStoreNamespace *
213 namespace_load(CamelStoreSummary *s, FILE *in)
214 {
215         CamelGroupwiseStoreNamespace *ns;
216         guint32 sep = '/';
217
218         ns = g_malloc0(sizeof(*ns));
219         if (camel_file_util_decode_string(in, &ns->path) == -1
220                         || camel_file_util_decode_string(in, &ns->full_name) == -1
221                         || camel_file_util_decode_uint32(in, &sep) == -1) {
222                 namespace_free(s, ns);
223                 ns = NULL;
224         } else {
225                 ns->sep = sep;
226         }
227
228         return ns;
229 }
230
231 char *
232 camel_groupwise_store_summary_path_to_full(CamelGroupwiseStoreSummary *s, const char *path, char dir_sep)
233 {
234         unsigned char *full, *f;
235         guint32 c, v = 0;
236         const char *p;
237         int state=0;
238         char *subpath, *last = NULL;
239         CamelStoreInfo *si;
240         CamelGroupwiseStoreNamespace *ns;
241
242         /* check to see if we have a subpath of path already defined */
243         subpath = alloca(strlen(path)+1);
244         strcpy(subpath, path);
245         do {
246                 si = camel_store_summary_path((CamelStoreSummary *)s, subpath);
247                 if (si == NULL) {
248                         last = strrchr(subpath, '/');
249                         if (last)
250                                 *last = 0;
251                 }
252         } while (si == NULL && last);
253
254         /* path is already present, use the raw version we have */
255         if (si && strlen(subpath) == strlen(path)) {
256                 f = g_strdup(camel_groupwise_store_info_full_name(s, si));
257                 camel_store_summary_info_free((CamelStoreSummary *)s, si);
258                 return f;
259         }
260
261         ns = camel_groupwise_store_summary_namespace_find_path(s, path);
262
263         f = full = alloca(strlen(path)*2+1);
264         if (si)
265                 p = path + strlen(subpath);
266         else if (ns)
267                 p = path + strlen(ns->path);
268         else
269                 p = path;
270
271         while ( (c = camel_utf8_getc((const unsigned char **)&p)) ) {
272                 switch(state) {
273                         case 0:
274                                 if (c == '%')
275                                         state = 1;
276                                 else {
277                                         if (c == '/')
278                                                 c = dir_sep;
279                                         camel_utf8_putc(&f, c);
280                                 }
281                                 break;
282                         case 1:
283                                 state = 2;
284                                 v = hexnib(c)<<4;
285                                 break;
286                         case 2:
287                                 state = 0;
288                                 v |= hexnib(c);
289                                 camel_utf8_putc(&f, v);
290                                 break;
291                 }
292         }
293         camel_utf8_putc(&f, c);
294
295         /* merge old path part if required */
296         f = g_strdup (full);
297         if (si) {
298                 full = g_strdup_printf("%s%s", camel_groupwise_store_info_full_name(s, si), f);
299                 g_free(f);
300                 camel_store_summary_info_free((CamelStoreSummary *)s, si);
301                 f = full;
302         } else if (ns) {
303                 full = g_strdup_printf("%s%s", ns->full_name, f);
304                 g_free(f);
305                 f = full;
306         }
307         return f ;
308 }
309
310 CamelGroupwiseStoreNamespace *
311 camel_groupwise_store_summary_namespace_find_full(CamelGroupwiseStoreSummary *s, const char *full)
312 {
313         int len;
314         CamelGroupwiseStoreNamespace *ns;
315
316         /* NB: this currently only compares against 1 namespace, in future compare against others */
317         ns = s->namespace;
318         while (ns) {
319                 len = strlen(ns->full_name);
320                 d(printf("find_full: comparing namespace '%s' to name '%s'\n", ns->full_name, full));
321                 if (len == 0
322                                 || (strncmp(ns->full_name, full, len) == 0
323                                         && (full[len] == ns->sep || full[len] == 0)))
324                         break;
325                 ns = NULL;
326         }
327
328         /* have a default? */
329         return ns;
330 }
331
332 CamelGroupwiseStoreInfo *
333 camel_groupwise_store_summary_add_from_full(CamelGroupwiseStoreSummary *s, const char *full, char dir_sep)
334 {
335         CamelGroupwiseStoreInfo *info;
336         char *pathu8, *prefix;
337         int len;
338         char *full_name;
339         CamelGroupwiseStoreNamespace *ns;
340
341         d(printf("adding full name '%s' '%c'\n", full, dir_sep));
342
343         len = strlen(full);
344         full_name = alloca(len+1);
345         strcpy(full_name, full);
346         if (full_name[len-1] == dir_sep)
347                 full_name[len-1] = 0;
348
349         info = camel_groupwise_store_summary_full_name(s, full_name);
350         if (info) {
351                 camel_store_summary_info_free((CamelStoreSummary *)s, (CamelStoreInfo *)info);
352                 d(printf("  already there\n"));
353                 return info;
354         }
355
356         ns = camel_groupwise_store_summary_namespace_find_full(s, full_name);
357         if (ns) {
358                 d(printf("(found namespace for '%s' ns '%s') ", full_name, ns->path));
359                 len = strlen(ns->full_name);
360                 if (len >= strlen(full_name)) {
361                         pathu8 = g_strdup(ns->path);
362                 } else {
363                         if (full_name[len] == ns->sep)
364                                 len++;
365
366                         prefix = camel_groupwise_store_summary_full_to_path(s, full_name+len, ns->sep);
367                         if (*ns->path) {
368                                 pathu8 = g_strdup_printf ("%s/%s", ns->path, prefix);
369                                 g_free (prefix);
370                         } else {
371                                 pathu8 = prefix;
372                         }
373                 }
374                 d(printf(" (pathu8 = '%s')", pathu8));
375         } else {
376                 d(printf("(Cannot find namespace for '%s')\n", full_name));
377                 pathu8 = camel_groupwise_store_summary_full_to_path(s, full_name, dir_sep);
378         }
379
380         info = (CamelGroupwiseStoreInfo *)camel_store_summary_add_from_path((CamelStoreSummary *)s, pathu8);
381         if (info) {
382                 d(printf("  '%s' -> '%s'\n", pathu8, full_name));
383                 camel_store_info_set_string((CamelStoreSummary *)s, (CamelStoreInfo *)info, CAMEL_STORE_INFO_LAST, full_name);
384         } else
385                 d(printf("  failed\n"));
386
387         return info;
388 }
389
390 char *
391 camel_groupwise_store_summary_full_from_path(CamelGroupwiseStoreSummary *s, const char *path)
392 {
393         CamelGroupwiseStoreNamespace *ns;
394         char *name = NULL;
395
396         ns = camel_groupwise_store_summary_namespace_find_path(s, path);
397         if (ns)
398                 name = camel_groupwise_store_summary_path_to_full(s, path, ns->sep);
399
400         d(printf("looking up path %s -> %s\n", path, name?name:"not found"));
401
402         return name;
403 }
404
405 CamelGroupwiseStoreNamespace *
406 camel_groupwise_store_summary_namespace_new(CamelGroupwiseStoreSummary *s, const char *full_name, char dir_sep)
407 {
408         CamelGroupwiseStoreNamespace *ns;
409         char *p, *o, c;
410         int len;
411
412         ns = g_malloc0(sizeof(*ns));
413         ns->full_name = g_strdup(full_name);
414         len = strlen(ns->full_name)-1;
415         if (len >= 0 && ns->full_name[len] == dir_sep)
416                 ns->full_name[len] = 0;
417         ns->sep = dir_sep;
418
419         o = p = ns->path = camel_groupwise_store_summary_full_to_path(s, ns->full_name, dir_sep);
420         while ((c = *p++)) {
421                 if (c != '#') {
422                         if (c == '/')
423                                 c = '.';
424                         *o++ = c;
425                 }
426         }
427         *o = 0;
428
429         return ns;
430 }
431
432 void 
433 camel_groupwise_store_summary_namespace_set(CamelGroupwiseStoreSummary *s, CamelGroupwiseStoreNamespace *ns)
434 {
435         d(printf("Setting namesapce to '%s' '%c' -> '%s'\n", ns->full_name, ns->sep, ns->path));
436         namespace_clear((CamelStoreSummary *)s);
437         s->namespace = ns;
438         camel_store_summary_touch((CamelStoreSummary *)s);
439 }
440
441 CamelGroupwiseStoreNamespace *
442 camel_groupwise_store_summary_namespace_find_path(CamelGroupwiseStoreSummary *s, const char *path)
443 {
444         int len;
445         CamelGroupwiseStoreNamespace *ns;
446
447         /* NB: this currently only compares against 1 namespace, in future compare against others */
448         ns = s->namespace;
449         while (ns) {
450                 len = strlen(ns->path);
451                 if (len == 0
452                                 || (strncmp(ns->path, path, len) == 0
453                                         && (path[len] == '/' || path[len] == 0)))
454                         break;
455                 ns = NULL;
456         }
457
458         /* have a default? */
459         return ns;
460 }
461
462
463
464 static int
465 summary_header_load(CamelStoreSummary *s, FILE *in)
466 {
467         CamelGroupwiseStoreSummary *summary = (CamelGroupwiseStoreSummary *)s ;
468          gint32 version, capabilities, count ;
469
470         namespace_clear (s) ;
471         
472         if (camel_groupwise_store_summary_parent->summary_header_load ((CamelStoreSummary *)s, in) == -1
473                         || camel_file_util_decode_fixed_int32(in, &version) == -1)
474                 return -1 ;
475
476         summary->version = version ;
477
478         if (camel_file_util_decode_fixed_int32(in, &capabilities) == -1
479                         || camel_file_util_decode_fixed_int32(in, &count) == -1
480                         || count > 1)
481                 return -1;
482
483         summary->capabilities = capabilities ;
484         if (count == 1) {
485                 if ((summary->namespace = namespace_load (s, in)) == NULL)      
486                         return -1 ;
487         }
488         return 0 ;
489 }
490
491
492 static int
493 summary_header_save(CamelStoreSummary *s, FILE *out)
494 {
495         CamelGroupwiseStoreSummary *summary = (CamelGroupwiseStoreSummary *) s ;
496         guint32 count ;
497
498         count = summary->namespace?1:0 ;
499         if (camel_groupwise_store_summary_parent->summary_header_save((CamelStoreSummary *)s, out) == -1
500                         || camel_file_util_encode_fixed_int32(out, 0) == -1
501                         || camel_file_util_encode_fixed_int32(out, summary->capabilities) == -1
502                         || camel_file_util_encode_fixed_int32(out, count) == -1)
503                 return -1;
504
505         if (summary->namespace && namespace_save(s, out, summary->namespace) == -1)
506                 return -1;
507
508
509         return 0 ;
510 }
511
512 static CamelStoreInfo *
513 store_info_load(CamelStoreSummary *s, FILE *in)
514 {
515         CamelGroupwiseStoreInfo *si;
516
517         si = (CamelGroupwiseStoreInfo *)camel_groupwise_store_summary_parent->store_info_load(s, in);
518         if (si) {
519                 if (camel_file_util_decode_string(in, &si->full_name) == -1) {
520                         camel_store_summary_info_free(s, (CamelStoreInfo *)si);
521                         si = NULL;
522                 }
523         }
524
525         return (CamelStoreInfo *)si;
526 }
527
528 static int
529 store_info_save(CamelStoreSummary *s, FILE *out, CamelStoreInfo *mi)
530 {
531         CamelGroupwiseStoreInfo *summary = (CamelGroupwiseStoreInfo *)mi;
532
533         if (camel_groupwise_store_summary_parent->store_info_save(s, out, mi) == -1
534                         || camel_file_util_encode_string(out, summary->full_name) == -1)
535                 return -1;
536
537         return 0;
538 }
539
540
541 static void
542 store_info_free(CamelStoreSummary *s, CamelStoreInfo *mi)
543 {
544         CamelGroupwiseStoreInfo *si = (CamelGroupwiseStoreInfo *)mi;
545
546         g_free(si->full_name);
547         camel_groupwise_store_summary_parent->store_info_free(s, mi);
548 }
549
550
551
552
553
554 static const char *
555 store_info_string(CamelStoreSummary *s, const CamelStoreInfo *mi, int type)
556 {
557         CamelGroupwiseStoreInfo *isi = (CamelGroupwiseStoreInfo *)mi;
558
559         /* FIXME: Locks? */
560
561         g_assert (mi != NULL);
562
563         switch (type) {
564                 case CAMEL_STORE_INFO_LAST:
565                         return isi->full_name;
566                 default:
567                         return camel_groupwise_store_summary_parent->store_info_string(s, mi, type);
568         }
569 }
570
571 static void
572 store_info_set_string(CamelStoreSummary *s, CamelStoreInfo *mi, int type, const char *str)
573 {
574         CamelGroupwiseStoreInfo *isi = (CamelGroupwiseStoreInfo *)mi;
575
576         g_assert(mi != NULL);
577
578         switch(type) {
579                 case CAMEL_STORE_INFO_LAST:
580                         d(printf("Set full name %s -> %s\n", isi->full_name, str));
581                         CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
582                         g_free(isi->full_name);
583                         isi->full_name = g_strdup(str);
584                         CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
585                         break;
586                 default:
587                         camel_groupwise_store_summary_parent->store_info_set_string(s, mi, type, str);
588                         break;
589         }
590 }
591