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