Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / camel / providers / nntp / camel-nntp-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: 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 <libedataserver/md5-utils.h>
33 #include <libedataserver/e-memory.h>
34
35 #include "camel-file-utils.h"
36 #include "camel-private.h"
37 #include "camel-utf8.h"
38
39 #include "camel-nntp-store-summary.h"
40
41 #define d(x)
42 #define io(x)                   /* io debug */
43
44 #define CAMEL_NNTP_STORE_SUMMARY_VERSION_0 (0)
45 #define CAMEL_NNTP_STORE_SUMMARY_VERSION_1 (1)
46
47 #define CAMEL_NNTP_STORE_SUMMARY_VERSION (1)
48
49 #define _PRIVATE(o) (((CamelNNTPStoreSummary *)(o))->priv)
50
51 static int summary_header_load(CamelStoreSummary *, FILE *);
52 static int summary_header_save(CamelStoreSummary *, FILE *);
53
54 /*static CamelStoreInfo * store_info_new(CamelStoreSummary *, const char *);*/
55 static CamelStoreInfo * store_info_load(CamelStoreSummary *, FILE *);
56 static int               store_info_save(CamelStoreSummary *, FILE *, CamelStoreInfo *);
57 static void              store_info_free(CamelStoreSummary *, CamelStoreInfo *);
58
59 static const char *store_info_string(CamelStoreSummary *, const CamelStoreInfo *, int);
60 static void store_info_set_string(CamelStoreSummary *, CamelStoreInfo *, int, const char *);
61
62 static void camel_nntp_store_summary_class_init (CamelNNTPStoreSummaryClass *klass);
63 static void camel_nntp_store_summary_init       (CamelNNTPStoreSummary *obj);
64 static void camel_nntp_store_summary_finalise   (CamelObject *obj);
65
66 static CamelStoreSummaryClass *camel_nntp_store_summary_parent;
67
68 static void
69 camel_nntp_store_summary_class_init (CamelNNTPStoreSummaryClass *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_new  = store_info_new;*/
77         ssklass->store_info_load = store_info_load;
78         ssklass->store_info_save = store_info_save;
79         ssklass->store_info_free = store_info_free;
80
81         ssklass->store_info_string = store_info_string;
82         ssklass->store_info_set_string = store_info_set_string;
83 }
84
85 static void
86 camel_nntp_store_summary_init (CamelNNTPStoreSummary *s)
87 {
88         /*struct _CamelNNTPStoreSummaryPrivate *p;
89
90           p = _PRIVATE(s) = g_malloc0(sizeof(*p));*/
91
92         ((CamelStoreSummary *) s)->store_info_size = sizeof (CamelNNTPStoreInfo);
93         s->version = CAMEL_NNTP_STORE_SUMMARY_VERSION;
94         memset (&s->last_newslist, 0, sizeof (s->last_newslist));
95 }
96
97 static void
98 camel_nntp_store_summary_finalise (CamelObject *obj)
99 {
100         /*struct _CamelNNTPStoreSummaryPrivate *p;*/
101         /*CamelNNTPStoreSummary *s = (CamelNNTPStoreSummary *)obj;*/
102
103         /*p = _PRIVATE(obj);
104           g_free(p);*/
105 }
106
107 CamelType
108 camel_nntp_store_summary_get_type (void)
109 {
110         static CamelType type = CAMEL_INVALID_TYPE;
111         
112         if (type == CAMEL_INVALID_TYPE) {
113                 camel_nntp_store_summary_parent = (CamelStoreSummaryClass *)camel_store_summary_get_type();
114                 type = camel_type_register((CamelType)camel_nntp_store_summary_parent, "CamelNNTPStoreSummary",
115                                            sizeof (CamelNNTPStoreSummary),
116                                            sizeof (CamelNNTPStoreSummaryClass),
117                                            (CamelObjectClassInitFunc) camel_nntp_store_summary_class_init,
118                                            NULL,
119                                            (CamelObjectInitFunc) camel_nntp_store_summary_init,
120                                            (CamelObjectFinalizeFunc) camel_nntp_store_summary_finalise);
121         }
122         
123         return type;
124 }
125
126 /**
127  * camel_nntp_store_summary_new:
128  *
129  * Create a new CamelNNTPStoreSummary object.
130  *
131  * Return value: A new CamelNNTPStoreSummary widget.
132  **/
133 CamelNNTPStoreSummary *
134 camel_nntp_store_summary_new (void)
135 {
136         return (CamelNNTPStoreSummary *) camel_object_new (camel_nntp_store_summary_get_type ());
137 }
138
139 /**
140  * camel_nntp_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 CamelNNTPStoreInfo *
154 camel_nntp_store_summary_full_name(CamelNNTPStoreSummary *s, const char *full_name)
155 {
156         int count, i;
157         CamelNNTPStoreInfo *info;
158         
159         count = camel_store_summary_count ((CamelStoreSummary *) s);
160         for (i = 0; i < count; i++) {
161                 info = (CamelNNTPStoreInfo *)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_nntp_store_summary_full_to_path (CamelNNTPStoreSummary *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 = g_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
198 hexnib (guint32 c)
199 {
200         if (c >= '0' && c <= '9')
201                 return c-'0';
202         else if (c >= 'A' && c <= 'Z')
203                 return c - 'A' + 10;
204         else
205                 return 0;
206 }
207
208 char *
209 camel_nntp_store_summary_path_to_full (CamelNNTPStoreSummary *s, const char *path, char dir_sep)
210 {
211         char *full, *f;
212         guint32 c, v = 0;
213         const char *p;
214         int state=0;
215         char *subpath, *last = NULL;
216         CamelStoreInfo *si;
217         
218         /* check to see if we have a subpath of path already defined */
219         subpath = g_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_nntp_store_info_full_name (s, si));
233                 camel_store_summary_info_free ((CamelStoreSummary *) s, si);
234                 return f;
235         }
236         
237         f = full = g_alloca (strlen (path)*2+1);
238         if (si)
239                 p = path + strlen (subpath);
240         else
241                 p = path;
242         
243         while ((c = camel_utf8_getc ((const unsigned char **) &p))) {
244                 switch (state) {
245                 case 0:
246                         if (c == '%') {
247                                 state = 1;
248                         } else {
249                                 if (c == '/')
250                                         c = dir_sep;
251                                 camel_utf8_putc((unsigned char **) &f, c);
252                         }
253                         break;
254                 case 1:
255                         state = 2;
256                         v = hexnib (c) << 4;
257                         break;
258                 case 2:
259                         state = 0;
260                         v |= hexnib (c);
261                         camel_utf8_putc ((unsigned char **) &f, v);
262                         break;
263                 }
264         }
265         camel_utf8_putc ((unsigned char **) &f, c);
266         
267         /* merge old path part if required */
268         f = camel_utf8_utf7 (full);
269         if (si) {
270                 full = g_strdup_printf ("%s%s", camel_nntp_store_info_full_name (s, si), f);
271                 g_free (f);
272                 camel_store_summary_info_free ((CamelStoreSummary *) s, si);
273                 f = full;
274         }
275         
276         return f;
277 }
278
279 CamelNNTPStoreInfo *
280 camel_nntp_store_summary_add_from_full (CamelNNTPStoreSummary *s, const char *full, char dir_sep)
281 {
282         CamelNNTPStoreInfo *info;
283         char *pathu8;
284         int len;
285         char *full_name;
286         
287         d(printf("adding full name '%s' '%c'\n", full, dir_sep));
288         
289         len = strlen (full);
290         full_name = g_alloca (len+1);
291         strcpy(full_name, full);
292         if (full_name[len-1] == dir_sep)
293                 full_name[len-1] = 0;
294         
295         info = camel_nntp_store_summary_full_name (s, full_name);
296         if (info) {
297                 camel_store_summary_info_free ((CamelStoreSummary *) s, (CamelStoreInfo *) info);
298                 d(printf("  already there\n"));
299                 return info;
300         }
301         
302         pathu8 = camel_nntp_store_summary_full_to_path (s, full_name, dir_sep);
303         
304         info = (CamelNNTPStoreInfo *) camel_store_summary_add_from_path ((CamelStoreSummary *) s, pathu8);
305         if (info) {
306                 d(printf("  '%s' -> '%s'\n", pathu8, full_name));
307                 camel_store_info_set_string((CamelStoreSummary *)s, (CamelStoreInfo *)info, CAMEL_NNTP_STORE_INFO_FULL_NAME, full_name);
308         } else
309                 d(printf("  failed\n"));
310         
311         return info;
312 }
313
314 static int
315 summary_header_load (CamelStoreSummary *s, FILE *in)
316 {
317         CamelNNTPStoreSummary *is = (CamelNNTPStoreSummary *) s;
318         gint32 version, nil;
319         
320         if (camel_nntp_store_summary_parent->summary_header_load ((CamelStoreSummary *) s, in) == -1
321             || camel_file_util_decode_fixed_int32 (in, &version) == -1)
322                 return -1;
323         
324         is->version = version;
325         
326         if (version < CAMEL_NNTP_STORE_SUMMARY_VERSION_0) {
327                 g_warning("Store summary header version too low");
328                 return -1;
329         }
330         
331         if (fread (is->last_newslist, 1, NNTP_DATE_SIZE, in) < NNTP_DATE_SIZE)
332                 return -1;
333         
334         camel_file_util_decode_fixed_int32 (in, &nil);
335
336         return 0;
337 }
338
339 static int
340 summary_header_save (CamelStoreSummary *s, FILE *out)
341 {
342         CamelNNTPStoreSummary *is = (CamelNNTPStoreSummary *) s;
343         
344         /* always write as latest version */
345         if (camel_nntp_store_summary_parent->summary_header_save ((CamelStoreSummary *) s, out) == -1
346             || camel_file_util_encode_fixed_int32 (out, CAMEL_NNTP_STORE_SUMMARY_VERSION) == -1
347             || fwrite (is->last_newslist, 1, NNTP_DATE_SIZE, out) < NNTP_DATE_SIZE
348             || camel_file_util_encode_fixed_int32 (out, 0) == -1)
349                 return -1;
350         
351         return 0;
352 }
353
354 static CamelStoreInfo *
355 store_info_load (CamelStoreSummary *s, FILE *in)
356 {
357         CamelNNTPStoreInfo *ni;
358         
359         ni = (CamelNNTPStoreInfo *) camel_nntp_store_summary_parent->store_info_load (s, in);
360         if (ni) {
361                 if (camel_file_util_decode_string (in, &ni->full_name) == -1) {
362                         camel_store_summary_info_free (s, (CamelStoreInfo *) ni);
363                         return NULL;
364                 }
365                 if (((CamelNNTPStoreSummary *)s)->version >= CAMEL_NNTP_STORE_SUMMARY_VERSION_1) {
366                         if (camel_file_util_decode_uint32(in, &ni->first) == -1
367                             || camel_file_util_decode_uint32(in, &ni->last) == -1) {
368                                 camel_store_summary_info_free (s, (CamelStoreInfo *) ni);
369                                 return NULL;
370                         }
371                 }
372                 /* set the URL */
373         }
374         
375         return (CamelStoreInfo *) ni;
376 }
377
378 static int
379 store_info_save (CamelStoreSummary *s, FILE *out, CamelStoreInfo *mi)
380 {
381         CamelNNTPStoreInfo *isi = (CamelNNTPStoreInfo *)mi;
382         
383         if (camel_nntp_store_summary_parent->store_info_save (s, out, mi) == -1
384             || camel_file_util_encode_string (out, isi->full_name) == -1
385             || camel_file_util_encode_uint32(out, isi->first) == -1
386             || camel_file_util_encode_uint32(out, isi->last) == -1)
387                 return -1;
388         
389         return 0;
390 }
391
392 static void
393 store_info_free (CamelStoreSummary *s, CamelStoreInfo *mi)
394 {
395         CamelNNTPStoreInfo *nsi = (CamelNNTPStoreInfo *) mi;
396         
397         g_free (nsi->full_name);
398         camel_nntp_store_summary_parent->store_info_free (s, mi);
399 }
400
401 static const char *
402 store_info_string(CamelStoreSummary *s, const CamelStoreInfo *mi, int type)
403 {
404         CamelNNTPStoreInfo *nsi = (CamelNNTPStoreInfo *)mi;
405         
406         /* FIXME: Locks? */
407         
408         g_assert (mi != NULL);
409         
410         switch (type) {
411         case CAMEL_NNTP_STORE_INFO_FULL_NAME:
412                 return nsi->full_name;
413         default:
414                 return camel_nntp_store_summary_parent->store_info_string(s, mi, type);
415         }
416 }
417
418 static void
419 store_info_set_string(CamelStoreSummary *s, CamelStoreInfo *mi, int type, const char *str)
420 {
421         CamelNNTPStoreInfo *nsi = (CamelNNTPStoreInfo *)mi;
422         
423         g_assert(mi != NULL);
424         
425         switch (type) {
426         case CAMEL_NNTP_STORE_INFO_FULL_NAME:
427                 d(printf("Set full name %s -> %s\n", nsi->full_name, str));
428                 CAMEL_STORE_SUMMARY_LOCK(s, summary_lock);
429                 g_free (nsi->full_name);
430                 nsi->full_name = g_strdup (str);
431                 CAMEL_STORE_SUMMARY_UNLOCK(s, summary_lock);
432                 break;
433         default:
434                 camel_nntp_store_summary_parent->store_info_set_string (s, mi, type, str);
435                 break;
436         }
437 }