1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8; fill-column: 160 -*- */
2 /*
3  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
4  *
5  * This library is free software: you can redistribute it and/or modify it
6  * under the terms of the GNU Lesser General Public License as published by
7  * the Free Software Foundation.
8  *
9  * This library is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12  * for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this library. If not, see <http://www.gnu.org/licenses/>.
16  *
17  * Authors: Michael Zucchi <notzed@ximian.com>
18  */
19 
20 #include "evolution-data-server-config.h"
21 
22 #include <ctype.h>
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/stat.h>
27 #include <unistd.h>
28 
29 #include <glib/gi18n-lib.h>
30 #include <glib/gstdio.h>
31 
32 #include "camel-local-summary.h"
33 
34 #define w(x)
35 #define io(x)
36 #define d(x) /*(printf("%s(%d): ", __FILE__, __LINE__),(x))*/
37 
38 #define CAMEL_LOCAL_SUMMARY_VERSION (1)
39 
40 static CamelFIRecord *
41 		summary_header_save		(CamelFolderSummary *,
42 						 GError **error);
43 static gboolean	summary_header_load		(CamelFolderSummary *,
44 						 CamelFIRecord *);
45 
46 static CamelMessageInfo *
47 		message_info_new_from_headers	(CamelFolderSummary *,
48 						 const CamelNameValueArray *);
49 
50 static gint	local_summary_decode_x_evolution
51 						(CamelLocalSummary *cls,
52 						 const gchar *xev,
53 						 CamelMessageInfo *mi);
54 static gchar *	local_summary_encode_x_evolution
55 						(CamelLocalSummary *cls,
56 						 const CamelMessageInfo *mi);
57 
58 static gint	local_summary_load		(CamelLocalSummary *cls,
59 						 gint forceindex,
60 						 GError **error);
61 static gint	local_summary_check		(CamelLocalSummary *cls,
62 						 CamelFolderChangeInfo *changeinfo,
63 						 GCancellable *cancellable,
64 						 GError **error);
65 static gint	local_summary_sync		(CamelLocalSummary *cls,
66 						 gboolean expunge,
67 						 CamelFolderChangeInfo *changeinfo,
68 						 GCancellable *cancellable,
69 						 GError **error);
70 static CamelMessageInfo *
71 		local_summary_add		(CamelLocalSummary *cls,
72 						 CamelMimeMessage *msg,
73 						 const CamelMessageInfo *info,
74 						 CamelFolderChangeInfo *,
75 						 GError **error);
76 static gint	local_summary_need_index	(void);
77 
G_DEFINE_TYPE(CamelLocalSummary,camel_local_summary,CAMEL_TYPE_FOLDER_SUMMARY)78 G_DEFINE_TYPE (CamelLocalSummary, camel_local_summary, CAMEL_TYPE_FOLDER_SUMMARY)
79 
80 static void
81 local_summary_dispose (GObject *object)
82 {
83 	CamelLocalSummary *local_summary;
84 
85 	local_summary = CAMEL_LOCAL_SUMMARY (object);
86 	g_clear_object (&local_summary->index);
87 
88 	/* Chain up to parent's dispose() method. */
89 	G_OBJECT_CLASS (camel_local_summary_parent_class)->dispose (object);
90 }
91 
92 static void
local_summary_finalize(GObject * object)93 local_summary_finalize (GObject *object)
94 {
95 	CamelLocalSummary *local_summary;
96 
97 	local_summary = CAMEL_LOCAL_SUMMARY (object);
98 
99 	g_free (local_summary->folder_path);
100 
101 	/* Chain up to parent's finalize() method. */
102 	G_OBJECT_CLASS (camel_local_summary_parent_class)->finalize (object);
103 }
104 
105 static void
camel_local_summary_class_init(CamelLocalSummaryClass * class)106 camel_local_summary_class_init (CamelLocalSummaryClass *class)
107 {
108 	GObjectClass *object_class;
109 	CamelFolderSummaryClass *folder_summary_class;
110 
111 	object_class = G_OBJECT_CLASS (class);
112 	object_class->dispose = local_summary_dispose;
113 	object_class->finalize = local_summary_finalize;
114 
115 	folder_summary_class = CAMEL_FOLDER_SUMMARY_CLASS (class);
116 	folder_summary_class->summary_header_load = summary_header_load;
117 	folder_summary_class->summary_header_save = summary_header_save;
118 	folder_summary_class->message_info_new_from_headers = message_info_new_from_headers;
119 
120 	class->load = local_summary_load;
121 	class->check = local_summary_check;
122 	class->sync = local_summary_sync;
123 	class->add = local_summary_add;
124 	class->encode_x_evolution = local_summary_encode_x_evolution;
125 	class->decode_x_evolution = local_summary_decode_x_evolution;
126 	class->need_index = local_summary_need_index;
127 }
128 
129 static void
camel_local_summary_init(CamelLocalSummary * local_summary)130 camel_local_summary_init (CamelLocalSummary *local_summary)
131 {
132 	CamelFolderSummary *folder_summary;
133 
134 	folder_summary = CAMEL_FOLDER_SUMMARY (local_summary);
135 
136 	/* and a unique file version */
137 	camel_folder_summary_set_version (folder_summary, camel_folder_summary_get_version (folder_summary) + CAMEL_LOCAL_SUMMARY_VERSION);
138 }
139 
140 void
camel_local_summary_construct(CamelLocalSummary * new,const gchar * local_name,CamelIndex * index)141 camel_local_summary_construct (CamelLocalSummary *new,
142                                const gchar *local_name,
143                                CamelIndex *index)
144 {
145 	new->folder_path = g_strdup (local_name);
146 	new->index = index;
147 	if (index)
148 		g_object_ref (index);
149 }
150 
151 static gboolean
local_summary_load(CamelLocalSummary * cls,gint forceindex,GError ** error)152 local_summary_load (CamelLocalSummary *cls,
153                     gint forceindex,
154                     GError **error)
155 {
156 	d (g_print ("\nlocal_summary_load called \n"));
157 	return camel_folder_summary_load ((CamelFolderSummary *) cls, error);
158 }
159 
160 /* load/check the summary */
161 gboolean
camel_local_summary_load(CamelLocalSummary * cls,gint forceindex,GError ** error)162 camel_local_summary_load (CamelLocalSummary *cls,
163                           gint forceindex,
164                           GError **error)
165 {
166 	CamelLocalSummaryClass *class;
167 
168 	d (printf ("Loading summary ...\n"));
169 
170 	class = CAMEL_LOCAL_SUMMARY_GET_CLASS (cls);
171 
172 	if ((forceindex && class->need_index ())
173 	    || !class->load (cls, forceindex, error)) {
174 		w (g_warning ("Could not load summary: flags may be reset"));
175 		camel_folder_summary_clear ((CamelFolderSummary *) cls, NULL);
176 		return FALSE;
177 	}
178 
179 	return TRUE;
180 }
181 
camel_local_summary_check_force(CamelLocalSummary * cls)182 void camel_local_summary_check_force (CamelLocalSummary *cls)
183 {
184 	cls->check_force = 1;
185 }
186 
187 gchar *
camel_local_summary_encode_x_evolution(CamelLocalSummary * cls,const CamelMessageInfo * info)188 camel_local_summary_encode_x_evolution (CamelLocalSummary *cls,
189                                         const CamelMessageInfo *info)
190 {
191 	return CAMEL_LOCAL_SUMMARY_GET_CLASS (cls)->encode_x_evolution (cls, info);
192 }
193 
194 gint
camel_local_summary_decode_x_evolution(CamelLocalSummary * cls,const gchar * xev,CamelMessageInfo * info)195 camel_local_summary_decode_x_evolution (CamelLocalSummary *cls,
196                                         const gchar *xev,
197                                         CamelMessageInfo *info)
198 {
199 	return CAMEL_LOCAL_SUMMARY_GET_CLASS (cls)->decode_x_evolution (cls, xev, info);
200 }
201 
202 /*#define DOSTATS*/
203 #ifdef DOSTATS
204 struct _stat_info {
205 	gint mitotal;
206 	gint micount;
207 	gint citotal;
208 	gint cicount;
209 	gint msgid;
210 	gint msgcount;
211 };
212 
213 static void
do_stat_ci(CamelLocalSummary * cls,struct _stat_info * info,CamelMessageContentInfo * ci)214 do_stat_ci (CamelLocalSummary *cls,
215             struct _stat_info *info,
216             CamelMessageContentInfo *ci)
217 {
218 	info->cicount++;
219 	info->citotal += ((CamelFolderSummary *) cls)->content_info_size /*+ 4 memchunks are 1/4 byte overhead per mi */;
220 	if (ci->id)
221 		info->citotal += strlen (ci->id) + 4;
222 	if (ci->description)
223 		info->citotal += strlen (ci->description) + 4;
224 	if (ci->encoding)
225 		info->citotal += strlen (ci->encoding) + 4;
226 	if (ci->type) {
227 		CamelContentType *ct = ci->type;
228 		struct _camel_header_param *param;
229 
230 		info->citotal += sizeof (*ct) + 4;
231 		if (ct->type)
232 			info->citotal += strlen (ct->type) + 4;
233 		if (ct->subtype)
234 			info->citotal += strlen (ct->subtype) + 4;
235 		param = ct->params;
236 		while (param) {
237 			info->citotal += sizeof (*param) + 4;
238 			if (param->name)
239 				info->citotal += strlen (param->name) + 4;
240 			if (param->value)
241 				info->citotal += strlen (param->value) + 4;
242 			param = param->next;
243 		}
244 	}
245 	ci = ci->childs;
246 	while (ci) {
247 		do_stat_ci (cls, info, ci);
248 		ci = ci->next;
249 	}
250 }
251 
252 static void
do_stat_mi(CamelLocalSummary * cls,struct _stat_info * info,CamelMessageInfo * mi)253 do_stat_mi (CamelLocalSummary *cls,
254             struct _stat_info *info,
255             CamelMessageInfo *mi)
256 {
257 	info->micount++;
258 	info->mitotal += ((CamelFolderSummary *) cls)->content_info_size /*+ 4 */;
259 
260 	if (mi->subject)
261 		info->mitotal += strlen (mi->subject) + 4;
262 	if (mi->to)
263 		info->mitotal += strlen (mi->to) + 4;
264 	if (mi->from)
265 		info->mitotal += strlen (mi->from) + 4;
266 	if (mi->cc)
267 		info->mitotal += strlen (mi->cc) + 4;
268 	if (mi->uid)
269 		info->mitotal += strlen (mi->uid) + 4;
270 
271 	if (mi->references) {
272 		info->mitotal += (mi->references->size - 1) * sizeof (CamelSummaryMessageID) + sizeof (CamelSummaryReferences) + 4;
273 		info->msgid += (mi->references->size) * sizeof (CamelSummaryMessageID);
274 		info->msgcount += mi->references->size;
275 	}
276 
277 	/* dont have any user flags yet */
278 
279 	if (mi->content) {
280 		do_stat_ci (cls, info, mi->content);
281 	}
282 }
283 
284 #endif
285 
286 gint
camel_local_summary_check(CamelLocalSummary * cls,CamelFolderChangeInfo * changeinfo,GCancellable * cancellable,GError ** error)287 camel_local_summary_check (CamelLocalSummary *cls,
288                            CamelFolderChangeInfo *changeinfo,
289                            GCancellable *cancellable,
290                            GError **error)
291 {
292 	CamelLocalSummaryClass *local_summary_class;
293 	gint ret;
294 
295 	local_summary_class = CAMEL_LOCAL_SUMMARY_GET_CLASS (cls);
296 	ret = local_summary_class->check (cls, changeinfo, cancellable, error);
297 
298 #ifdef DOSTATS
299 	if (ret != -1) {
300 		gint i;
301 		CamelFolderSummary *s = (CamelFolderSummary *) cls;
302 		GPtrArray *known_uids;
303 		struct _stat_info stats = { 0 };
304 
305 		known_uids = camel_folder_summary_get_array (s);
306 		for (i = 0; i < camel_folder_summary_count (s); i++) {
307 			CamelMessageInfo *info = camel_folder_summary_get (s, g_ptr_array_index (known_uids, i));
308 			do_stat_mi (cls, &stats, info);
309 			g_clear_object (&info);
310 		}
311 		camel_folder_summary_free_array (known_uids);
312 
313 		printf ("\nMemory used by summary:\n\n");
314 		printf (
315 			"Total of %d messages\n",
316 			camel_folder_summary_count (s));
317 		printf (
318 			"Total: %d bytes (ave %f)\n",
319 			stats.citotal + stats.mitotal,
320 			(gdouble) (stats.citotal + stats.mitotal) /
321 			(gdouble) camel_folder_summary_count (s));
322 		printf (
323 			"Message Info: %d (ave %f)\n",
324 			stats.mitotal,
325 			(gdouble) stats.mitotal / (gdouble) stats.micount);
326 		printf (
327 			"Content Info; %d (ave %f) count %d\n",
328 			stats.citotal,
329 			(gdouble) stats.citotal / (gdouble) stats.cicount,
330 			stats.cicount);
331 		printf (
332 			"message id's: %d (ave %f) count %d\n",
333 			stats.msgid,
334 			(gdouble) stats.msgid / (gdouble) stats.msgcount,
335 			stats.msgcount);
336 	}
337 #endif
338 	return ret;
339 }
340 
341 gint
camel_local_summary_sync(CamelLocalSummary * cls,gboolean expunge,CamelFolderChangeInfo * changeinfo,GCancellable * cancellable,GError ** error)342 camel_local_summary_sync (CamelLocalSummary *cls,
343                           gboolean expunge,
344                           CamelFolderChangeInfo *changeinfo,
345                           GCancellable *cancellable,
346                           GError **error)
347 {
348 	CamelLocalSummaryClass *local_summary_class;
349 
350 	local_summary_class = CAMEL_LOCAL_SUMMARY_GET_CLASS (cls);
351 
352 	return local_summary_class->sync (cls, expunge, changeinfo, cancellable, error);
353 }
354 
355 CamelMessageInfo *
camel_local_summary_add(CamelLocalSummary * cls,CamelMimeMessage * msg,const CamelMessageInfo * info,CamelFolderChangeInfo * ci,GError ** error)356 camel_local_summary_add (CamelLocalSummary *cls,
357                          CamelMimeMessage *msg,
358                          const CamelMessageInfo *info,
359                          CamelFolderChangeInfo *ci,
360                          GError **error)
361 {
362 	CamelLocalSummaryClass *local_summary_class;
363 
364 	local_summary_class = CAMEL_LOCAL_SUMMARY_GET_CLASS (cls);
365 
366 	return local_summary_class->add (cls, msg, info, ci, error);
367 }
368 
369 /**
370  * camel_local_summary_write_headers:
371  * @fd: a file descriptor to write to
372  * @headers: a #CamelNameValueArray with headers to write
373  * @xevline: (nullable): an optional value of X-Evolution header to add
374  * @status: (nullable): an optional value of Status header to add
375  * @xstatus: (nullable): an optional value of X-Status header to add
376  *
377  * Write a bunch of headers to the file @fd.  If xevline is non NULL, then
378  * an X-Evolution header line is created at the end of all of the headers.
379  * If @status is non NULL, then a Status header line is also written.
380  * The headers written are termianted with a blank line.
381  *
382  * Returns: -1 on error, otherwise the number of bytes written.
383  **/
384 gint
camel_local_summary_write_headers(gint fd,CamelNameValueArray * headers,const gchar * xevline,const gchar * status,const gchar * xstatus)385 camel_local_summary_write_headers (gint fd,
386                                    CamelNameValueArray *headers,
387                                    const gchar *xevline,
388                                    const gchar *status,
389                                    const gchar *xstatus)
390 {
391 	gint outlen = 0, len;
392 	gint newfd;
393 	guint ii;
394 	const gchar *header_name = NULL, *header_value = NULL;
395 	FILE *out;
396 
397 	/* dum de dum, maybe the whole sync function should just use stdio for output */
398 	newfd = dup (fd);
399 	if (newfd == -1)
400 		return -1;
401 
402 	out = fdopen (newfd, "w");
403 	if (out == NULL) {
404 		close (newfd);
405 		errno = EINVAL;
406 		return -1;
407 	}
408 
409 	for (ii = 0; camel_name_value_array_get (headers, ii, &header_name, &header_value); ii++) {
410 		if (strcmp (header_name, "X-Evolution") != 0
411 		    && (status == NULL || strcmp (header_name, "Status") != 0)
412 		    && (xstatus == NULL || strcmp (header_name, "X-Status") != 0)) {
413 			len = fprintf (out, "%s:%s\n", header_name, header_value);
414 			if (len == -1) {
415 				fclose (out);
416 				return -1;
417 			}
418 			outlen += len;
419 		}
420 	}
421 
422 	if (status) {
423 		len = fprintf (out, "Status: %s\n", status);
424 		if (len == -1) {
425 			fclose (out);
426 			return -1;
427 		}
428 		outlen += len;
429 	}
430 
431 	if (xstatus) {
432 		len = fprintf (out, "X-Status: %s\n", xstatus);
433 		if (len == -1) {
434 			fclose (out);
435 			return -1;
436 		}
437 		outlen += len;
438 	}
439 
440 	if (xevline) {
441 		len = fprintf (out, "X-Evolution: %s\n", xevline);
442 		if (len == -1) {
443 			fclose (out);
444 			return -1;
445 		}
446 		outlen += len;
447 	}
448 
449 	len = fprintf (out, "\n");
450 	if (len == -1) {
451 		fclose (out);
452 		return -1;
453 	}
454 	outlen += len;
455 
456 	if (fclose (out) == -1)
457 		return -1;
458 
459 	return outlen;
460 }
461 
462 static gint
local_summary_check(CamelLocalSummary * cls,CamelFolderChangeInfo * changeinfo,GCancellable * cancellable,GError ** error)463 local_summary_check (CamelLocalSummary *cls,
464                      CamelFolderChangeInfo *changeinfo,
465                      GCancellable *cancellable,
466                      GError **error)
467 {
468 	/* FIXME: sync index here ? */
469 	return 0;
470 }
471 
472 static gint
local_summary_sync(CamelLocalSummary * cls,gboolean expunge,CamelFolderChangeInfo * changeinfo,GCancellable * cancellable,GError ** error)473 local_summary_sync (CamelLocalSummary *cls,
474                     gboolean expunge,
475                     CamelFolderChangeInfo *changeinfo,
476                     GCancellable *cancellable,
477                     GError **error)
478 {
479 	CamelFolderSummary *folder_summary;
480 	GError *local_error = NULL;
481 
482 	folder_summary = CAMEL_FOLDER_SUMMARY (cls);
483 
484 	if (!camel_folder_summary_save (folder_summary, &local_error)) {
485 		CamelFolder *folder = camel_folder_summary_get_folder (folder_summary);
486 		g_warning ("Could not save summary for local providers folder '%s': %s",
487 			folder ? camel_folder_get_full_name (folder) : "???",
488 			local_error ? local_error->message : "Unknown error");
489 		if (local_error)
490 			g_propagate_error (error, local_error);
491 		return -1;
492 	}
493 
494 	if (cls->index && camel_index_sync (cls->index) == -1) {
495 		g_warning ("Could not sync index for %s: %s", cls->folder_path, g_strerror (errno));
496 		return -1;
497 	}
498 
499 	return 0;
500 }
501 
502 static gint
local_summary_need_index(void)503 local_summary_need_index (void)
504 {
505 	return 1;
506 }
507 
508 static CamelMessageInfo *
local_summary_add(CamelLocalSummary * cls,CamelMimeMessage * msg,const CamelMessageInfo * info,CamelFolderChangeInfo * ci,GError ** error)509 local_summary_add (CamelLocalSummary *cls,
510                    CamelMimeMessage *msg,
511                    const CamelMessageInfo *info,
512                    CamelFolderChangeInfo *ci,
513                    GError **error)
514 {
515 	CamelFolderSummary *summary;
516 	CamelMessageInfo *mi;
517 	gchar *xev;
518 
519 	d (printf ("Adding message to summary\n"));
520 
521 	summary = CAMEL_FOLDER_SUMMARY (cls);
522 
523 	mi = camel_folder_summary_info_new_from_message (summary, msg);
524 	camel_message_info_set_abort_notifications (mi, TRUE);
525 
526 	if (info) {
527 		camel_message_info_take_user_flags (mi, camel_message_info_dup_user_flags (info));
528 		camel_message_info_take_user_tags (mi, camel_message_info_dup_user_tags (info));
529 		camel_message_info_set_flags (mi, ~0, camel_message_info_get_flags (info));
530 		camel_message_info_set_size (mi, camel_message_info_get_size (info));
531 	}
532 
533 	/* we need to calculate the size ourselves */
534 	if (camel_message_info_get_size (mi) == 0) {
535 		camel_message_info_set_size (mi, camel_data_wrapper_calculate_size_sync (CAMEL_DATA_WRAPPER (msg), NULL, NULL));
536 	}
537 
538 	camel_message_info_set_flags (mi, CAMEL_MESSAGE_FOLDER_NOXEV, 0);
539 	xev = camel_local_summary_encode_x_evolution (cls, mi);
540 	camel_medium_set_header ((CamelMedium *) msg, "X-Evolution", xev);
541 	g_free (xev);
542 
543 	camel_message_info_set_abort_notifications (mi, FALSE);
544 	camel_folder_summary_add (summary, mi, FALSE);
545 	camel_folder_change_info_add_uid (ci, camel_message_info_get_uid (mi));
546 
547 	return mi;
548 }
549 
550 static gchar *
local_summary_encode_x_evolution(CamelLocalSummary * cls,const CamelMessageInfo * mi)551 local_summary_encode_x_evolution (CamelLocalSummary *cls,
552                                   const CamelMessageInfo *mi)
553 {
554 	GString *out = g_string_new ("");
555 	struct _camel_header_param *params = NULL;
556 	guint32 flags;
557 	const CamelNamedFlags *user_flags;
558 	const CamelNameValueArray *user_tags;
559 	gchar *ret;
560 	const gchar *p, *uidstr;
561 	guint32 uid;
562 
563 	camel_message_info_property_lock (mi);
564 
565 	/* FIXME: work out what to do with uid's that aren't stored here? */
566 	/* FIXME: perhaps make that a mbox folder only issue?? */
567 	p = uidstr = camel_message_info_get_uid (mi);
568 	flags = camel_message_info_get_flags (mi);
569 	while (*p && isdigit (*p))
570 		p++;
571 	if (*p == 0 && sscanf (uidstr, "%u", &uid) == 1) {
572 		g_string_printf (out, "%08x-%04x", uid, flags & 0xffff);
573 	} else {
574 		g_string_printf (out, "%s-%04x", uidstr, flags & 0xffff);
575 	}
576 
577 	user_flags = camel_message_info_get_user_flags (mi);
578 	user_tags = camel_message_info_get_user_tags (mi);
579 
580 	if (user_flags || user_tags) {
581 		GString *val = g_string_new ("");
582 		const gchar *name, *value;
583 		guint ii, len;
584 
585 		len = camel_named_flags_get_length (user_flags);
586 		if (len) {
587 			for (ii = 0; ii < len; ii++) {
588 				name = camel_named_flags_get (user_flags, ii);
589 				if (!name)
590 					continue;
591 
592 				if (val->len)
593 					g_string_append_c (val, ',');
594 				g_string_append (val, name);
595 			}
596 			camel_header_set_param (&params, "flags", val->str);
597 			g_string_truncate (val, 0);
598 		}
599 
600 		len = camel_name_value_array_get_length (user_tags);
601 		if (len) {
602 			for (ii = 0; ii < len; ii++) {
603 				if (!camel_name_value_array_get (user_tags, ii, &name, &value))
604 					continue;
605 
606 				if (val->len)
607 					g_string_append_c (val, ',');
608 
609 				g_string_append (val, name);
610 				g_string_append_c (val, '=');
611 				g_string_append (val, value);
612 			}
613 			camel_header_set_param (&params, "tags", val->str);
614 		}
615 		g_string_free (val, TRUE);
616 
617 		camel_header_param_list_format_append (out, params);
618 		camel_header_param_list_free (params);
619 	}
620 	ret = out->str;
621 	g_string_free (out, FALSE);
622 
623 	camel_message_info_property_unlock (mi);
624 
625 	return ret;
626 }
627 
628 static gint
local_summary_decode_x_evolution(CamelLocalSummary * cls,const gchar * xev,CamelMessageInfo * mi)629 local_summary_decode_x_evolution (CamelLocalSummary *cls,
630                                   const gchar *xev,
631                                   CamelMessageInfo *mi)
632 {
633 	struct _camel_header_param *params, *scan;
634 	guint32 uid, flags;
635 	gchar *header;
636 	gint i;
637 	gchar uidstr[20];
638 
639 	uidstr[0] = 0;
640 
641 	/* check for uid/flags */
642 	header = camel_header_token_decode (xev);
643 	if (header && strlen (header) == strlen ("00000000-0000")
644 	    && sscanf (header, "%08x-%04x", &uid, &flags) == 2) {
645 		if (mi)
646 			g_snprintf (uidstr, sizeof (uidstr), "%u", uid);
647 	} else {
648 		g_free (header);
649 		return -1;
650 	}
651 	g_free (header);
652 
653 	if (mi == NULL)
654 		return 0;
655 
656 	/* check for additional data */
657 	header = strchr (xev, ';');
658 	if (header) {
659 		params = camel_header_param_list_decode (header + 1);
660 		scan = params;
661 		while (scan) {
662 			if (!g_ascii_strcasecmp (scan->name, "flags")) {
663 				gchar **flagv = g_strsplit (scan->value, ",", 1000);
664 
665 				for (i = 0; flagv[i]; i++)
666 					camel_message_info_set_user_flag (mi, flagv[i], TRUE);
667 				g_strfreev (flagv);
668 			} else if (!g_ascii_strcasecmp (scan->name, "tags")) {
669 				gchar **tagv = g_strsplit (scan->value, ",", 10000);
670 				gchar *val;
671 
672 				for (i = 0; tagv[i]; i++) {
673 					val = strchr (tagv[i], '=');
674 					if (val) {
675 						*val++ = 0;
676 						camel_message_info_set_user_tag (mi, tagv[i], val);
677 						val[-1]='=';
678 					}
679 				}
680 				g_strfreev (tagv);
681 			}
682 			scan = scan->next;
683 		}
684 		camel_header_param_list_free (params);
685 	}
686 
687 	camel_message_info_set_uid (mi, uidstr);
688 	camel_message_info_set_flags (mi, ~0, flags);
689 
690 	return 0;
691 }
692 
693 static gboolean
summary_header_load(CamelFolderSummary * s,CamelFIRecord * fir)694 summary_header_load (CamelFolderSummary *s,
695 		     CamelFIRecord *fir)
696 {
697 	CamelLocalSummary *cls = (CamelLocalSummary *) s;
698 	gchar *part, *tmp;
699 
700 	/* We dont actually add our own headers, but version that we don't anyway */
701 
702 	if (!CAMEL_FOLDER_SUMMARY_CLASS (camel_local_summary_parent_class)->summary_header_load (s, fir))
703 		return FALSE;
704 
705 	part = fir->bdata;
706 	if (part) {
707 		cls->version = camel_util_bdata_get_number (&part, 0);
708 	}
709 
710 	/* keep only the rest of the bdata there (strip our version digit) */
711 	tmp = g_strdup (part);
712 	g_free (fir->bdata);
713 	fir->bdata = tmp;
714 
715 	return TRUE;
716 }
717 
718 static struct _CamelFIRecord *
summary_header_save(CamelFolderSummary * s,GError ** error)719 summary_header_save (CamelFolderSummary *s,
720 		     GError **error)
721 {
722 	CamelFolderSummaryClass *folder_summary_class;
723 	struct _CamelFIRecord *fir;
724 
725 	/* Chain up to parent's summary_header_save() method. */
726 	folder_summary_class = CAMEL_FOLDER_SUMMARY_CLASS (camel_local_summary_parent_class);
727 	fir = folder_summary_class->summary_header_save (s, NULL);
728 	if (fir)
729 		fir->bdata = g_strdup_printf ("%d", CAMEL_LOCAL_SUMMARY_VERSION);
730 
731 	return fir;
732 }
733 
734 static CamelMessageInfo *
message_info_new_from_headers(CamelFolderSummary * summary,const CamelNameValueArray * headers)735 message_info_new_from_headers (CamelFolderSummary *summary,
736 			       const CamelNameValueArray *headers)
737 {
738 	CamelMessageInfo *mi;
739 	CamelLocalSummary *cls = (CamelLocalSummary *) summary;
740 
741 	mi = CAMEL_FOLDER_SUMMARY_CLASS (camel_local_summary_parent_class)->message_info_new_from_headers (summary, headers);
742 	if (mi) {
743 		const gchar *xev;
744 		gint doindex = FALSE;
745 
746 		xev = camel_name_value_array_get_named (headers, CAMEL_COMPARE_CASE_INSENSITIVE, "X-Evolution");
747 		if (xev == NULL || camel_local_summary_decode_x_evolution (cls, xev, mi) == -1) {
748 			gchar *uid;
749 
750 			uid = camel_folder_summary_next_uid_string (summary);
751 
752 			/* to indicate it has no xev header */
753 			camel_message_info_set_flags (mi, CAMEL_MESSAGE_FOLDER_FLAGGED | CAMEL_MESSAGE_FOLDER_NOXEV, CAMEL_MESSAGE_FOLDER_FLAGGED | CAMEL_MESSAGE_FOLDER_NOXEV);
754 			camel_message_info_set_uid (mi, uid);
755 
756 			g_free (uid);
757 
758 			/* shortcut, no need to look it up in the index library */
759 			doindex = TRUE;
760 		}
761 
762 		if (cls->index
763 		    && (doindex
764 			|| cls->index_force
765 			|| !camel_index_has_name (cls->index, camel_message_info_get_uid (mi)))) {
766 			d (printf ("Am indexing message %s\n", camel_message_info_get_uid (mi)));
767 			camel_folder_summary_set_index (summary, cls->index);
768 		} else {
769 			d (printf ("Not indexing message %s\n", camel_message_info_get_uid (mi)));
770 			camel_folder_summary_set_index (summary, NULL);
771 		}
772 	}
773 
774 	return mi;
775 }
776