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  * 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 <dirent.h>
23 #include <errno.h>
24 #include <string.h>
25 #include <unistd.h>
26 #include <sys/stat.h>
27 
28 #include <glib/gstdio.h>
29 #include <glib/gi18n-lib.h>
30 
31 #include "camel-mh-folder.h"
32 #include "camel-mh-settings.h"
33 #include "camel-mh-store.h"
34 #include "camel-mh-summary.h"
35 
36 #define d(x)
37 
38 G_DEFINE_TYPE (CamelMhStore, camel_mh_store, CAMEL_TYPE_LOCAL_STORE)
39 
40 enum {
41 	UPDATE_NONE,
42 	UPDATE_ADD,
43 	UPDATE_REMOVE,
44 	UPDATE_RENAME
45 };
46 
47 /* update the .folders file if it exists, or create it if it doesn't */
48 static void
folders_update(const gchar * root,gint mode,const gchar * folder,const gchar * new,GCancellable * cancellable)49 folders_update (const gchar *root,
50                 gint mode,
51                 const gchar *folder,
52                 const gchar *new,
53                 GCancellable *cancellable)
54 {
55 	gchar *tmp, *tmpnew, *line = NULL;
56 	gsize tmpnew_len = 0;
57 	CamelStream *stream, *in = NULL, *out = NULL;
58 	gchar *folder_newline;
59 	gint flen = strlen (folder);
60 
61 	folder_newline = g_strdup_printf ("%s\n", folder);
62 
63 	tmpnew_len = strlen (root) + 16;
64 	tmpnew = g_alloca (tmpnew_len);
65 	g_snprintf (
66 		tmpnew, tmpnew_len,
67 		"%s" G_DIR_SEPARATOR_S ".folders~", root);
68 
69 	out = camel_stream_fs_new_with_name (
70 		tmpnew, O_WRONLY | O_CREAT | O_TRUNC, 0666, NULL);
71 	if (out == NULL)
72 		goto fail;
73 
74 	tmp = g_alloca (tmpnew_len);
75 	g_snprintf (
76 		tmp, tmpnew_len,
77 		"%s" G_DIR_SEPARATOR_S ".folders", root);
78 	stream = camel_stream_fs_new_with_name (tmp, O_RDONLY, 0, NULL);
79 	if (stream) {
80 		in = camel_stream_buffer_new (stream, CAMEL_STREAM_BUFFER_READ);
81 		g_object_unref (stream);
82 	}
83 	if (in == NULL || stream == NULL) {
84 		if (mode == UPDATE_ADD) {
85 			gint ret;
86 
87 			ret = camel_stream_write_string (
88 				out, folder_newline, cancellable, NULL);
89 
90 			if (ret == -1)
91 				goto fail;
92 		}
93 		goto done;
94 	}
95 
96 	while ((line = camel_stream_buffer_read_line ((CamelStreamBuffer *) in, cancellable, NULL))) {
97 		gint copy = TRUE;
98 
99 		switch (mode) {
100 		case UPDATE_REMOVE:
101 			if (strcmp (line, folder) == 0)
102 				copy = FALSE;
103 			break;
104 		case UPDATE_RENAME:
105 			if (strncmp (line, folder, flen) == 0
106 			    && (line[flen] == 0 || line[flen] == '/')) {
107 				if (camel_stream_write (out, new, strlen (new), cancellable, NULL) == -1
108 				    || camel_stream_write (out, line + flen, strlen (line) - flen, cancellable, NULL) == -1
109 				    || camel_stream_write (out, "\n", 1, cancellable, NULL) == -1)
110 					goto fail;
111 				copy = FALSE;
112 			}
113 			break;
114 		case UPDATE_ADD: {
115 			gint cmp = strcmp (line, folder);
116 
117 			if (cmp > 0) {
118 				gint ret;
119 
120 				/* found insertion point */
121 				ret = camel_stream_write_string (
122 					out, folder_newline, cancellable, NULL);
123 
124 				if (ret == -1)
125 					goto fail;
126 				mode = UPDATE_NONE;
127 			} else if (cmp == 0) {
128 				/* already there */
129 				mode = UPDATE_NONE;
130 			}
131 			break; }
132 		case UPDATE_NONE:
133 			break;
134 		}
135 
136 		if (copy) {
137 			gchar *string;
138 			gint ret;
139 
140 			string = g_strdup_printf ("%s\n", line);
141 			ret = camel_stream_write_string (
142 				out, string, cancellable, NULL);
143 			g_free (string);
144 
145 			if (ret == -1)
146 				goto fail;
147 		}
148 
149 		g_free (line);
150 		line = NULL;
151 	}
152 
153 	/* add to end? */
154 	if (mode == UPDATE_ADD) {
155 		gint ret;
156 
157 		ret = camel_stream_write_string (
158 			out, folder_newline, cancellable, NULL);
159 
160 		if (ret == -1)
161 			goto fail;
162 	}
163 
164 	if (camel_stream_close (out, cancellable, NULL) == -1)
165 		goto fail;
166 
167 done:
168 	if (g_rename (tmpnew, tmp) == -1) {
169 		g_warning ("%s: Failed to rename '%s' to '%s': %s", G_STRFUNC, tmpnew, tmp, g_strerror (errno));
170 	}
171 fail:
172 	unlink (tmpnew);		/* remove it if its there */
173 	g_free (line);
174 	if (in)
175 		g_object_unref (in);
176 	if (out)
177 		g_object_unref (out);
178 
179 	g_free (folder_newline);
180 }
181 
182 static void
fill_fi(CamelStore * store,CamelFolderInfo * fi,guint32 flags,GCancellable * cancellable)183 fill_fi (CamelStore *store,
184          CamelFolderInfo *fi,
185          guint32 flags,
186          GCancellable *cancellable)
187 {
188 	CamelLocalStore *local_store;
189 	CamelFolder *folder;
190 
191 	local_store = CAMEL_LOCAL_STORE (store);
192 	folder = camel_object_bag_peek (camel_store_get_folders_bag (store), fi->full_name);
193 
194 	if (folder != NULL) {
195 		fi->unread = camel_folder_get_unread_message_count (folder);
196 		fi->total = camel_folder_get_message_count (folder);
197 		g_object_unref (folder);
198 	} else {
199 		CamelLocalSettings *local_settings;
200 		CamelSettings *settings;
201 		CamelService *service;
202 		CamelFolderSummary *s;
203 		gchar *folderpath;
204 		gchar *path;
205 
206 		service = CAMEL_SERVICE (store);
207 
208 		settings = camel_service_ref_settings (service);
209 
210 		local_settings = CAMEL_LOCAL_SETTINGS (settings);
211 		path = camel_local_settings_dup_path (local_settings);
212 
213 		g_object_unref (settings);
214 
215 		/* This should be fast enough not to have to test for INFO_FAST */
216 
217 		/* We could: if we have no folder, and FAST isn't specified,
218 		 * perform a full scan of all messages for their status flags.
219 		 * But its probably not worth it as we need to read the top of
220 		 * every file, i.e. very very slow */
221 
222 		folderpath = g_strdup_printf ("%s/%s", path, fi->full_name);
223 		s = (CamelFolderSummary *) camel_mh_summary_new (NULL, folderpath, NULL);
224 		if (camel_folder_summary_header_load (s, store, fi->full_name, NULL)) {
225 			fi->unread = camel_folder_summary_get_unread_count (s);
226 			fi->total = camel_folder_summary_get_saved_count (s);
227 		}
228 		g_object_unref (s);
229 		g_free (folderpath);
230 
231 		g_free (path);
232 	}
233 
234 	if (camel_local_store_is_main_store (local_store) && fi->full_name
235 	    && (fi->flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_NORMAL)
236 		fi->flags =
237 			(fi->flags & ~CAMEL_FOLDER_TYPE_MASK) |
238 			camel_local_store_get_folder_type_by_full_name (
239 				local_store, fi->full_name);
240 }
241 
242 static CamelFolderInfo *
folder_info_new(CamelStore * store,const gchar * root,const gchar * path,guint32 flags,GCancellable * cancellable)243 folder_info_new (CamelStore *store,
244                  const gchar *root,
245                  const gchar *path,
246                  guint32 flags,
247                  GCancellable *cancellable)
248 {
249 	/* FIXME Need to set fi->flags = CAMEL_FOLDER_NOSELECT
250 	 *       (and possibly others) when appropriate. */
251 	CamelFolderInfo *fi;
252 	gchar *base;
253 
254 	base = strrchr (path, '/');
255 
256 	/* Build the folder info structure. */
257 	fi = camel_folder_info_new ();
258 	fi->full_name = g_strdup (path);
259 	fi->display_name = g_strdup (base ? base + 1 : path);
260 	fill_fi (store, fi, flags, cancellable);
261 
262 	return fi;
263 }
264 
265 /* used to find out where we've visited already */
266 struct _inode {
267 	dev_t dnode;
268 	ino_t inode;
269 };
270 
271 /* Scan path, under root, for directories to add folders for.  Both
272  * root and path should have a trailing "/" if they aren't empty. */
273 static void
recursive_scan(CamelStore * store,CamelFolderInfo ** fip,CamelFolderInfo * parent,GHashTable * visited,const gchar * root,const gchar * path,guint32 flags,GCancellable * cancellable)274 recursive_scan (CamelStore *store,
275                 CamelFolderInfo **fip,
276                 CamelFolderInfo *parent,
277                 GHashTable *visited,
278                 const gchar *root,
279                 const gchar *path,
280                 guint32 flags,
281                 GCancellable *cancellable)
282 {
283 	gchar *fullpath, *tmp;
284 	gsize fullpath_len;
285 	DIR *dp;
286 	struct dirent *d;
287 	struct stat st;
288 	CamelFolderInfo *fi;
289 	struct _inode in, *inew;
290 
291 	/* Open the specified directory. */
292 	if (path[0]) {
293 		fullpath_len = strlen (root) + strlen (path) + 2;
294 		fullpath = alloca (fullpath_len);
295 		g_snprintf (fullpath, fullpath_len, "%s/%s", root, path);
296 	} else
297 		fullpath = (gchar *) root;
298 
299 	if (g_stat (fullpath, &st) == -1 || !S_ISDIR (st.st_mode))
300 		return;
301 
302 	in.dnode = st.st_dev;
303 	in.inode = st.st_ino;
304 
305 	/* see if we've visited already */
306 	if (g_hash_table_lookup (visited, &in) != NULL)
307 		return;
308 
309 	inew = g_malloc (sizeof (*inew));
310 	*inew = in;
311 	g_hash_table_insert (visited, inew, inew);
312 
313 	/* link in ... */
314 	fi = folder_info_new (store, root, path, flags, cancellable);
315 	fi->parent = parent;
316 	fi->next = *fip;
317 	*fip = fi;
318 
319 	if (((flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE) || parent == NULL)) {
320 		/* now check content for possible other directories */
321 		dp = opendir (fullpath);
322 		if (dp == NULL)
323 			return;
324 
325 		/* Look for subdirectories to add and scan. */
326 		while ((d = readdir (dp)) != NULL) {
327 			/* Skip current and parent directory. */
328 			if (strcmp (d->d_name, ".") == 0
329 			    || strcmp (d->d_name, "..") == 0)
330 				continue;
331 
332 			/* skip fully-numerical entries (i.e. mh messages) */
333 			strtoul (d->d_name, &tmp, 10);
334 			if (*tmp == 0)
335 				continue;
336 
337 			/* Otherwise, treat at potential node, and recurse,
338 			 * a bit more expensive than needed, but tough! */
339 			if (path[0]) {
340 				tmp = g_strdup_printf ("%s/%s", path, d->d_name);
341 				recursive_scan (
342 					store, &fi->child, fi, visited,
343 					root, tmp, flags, cancellable);
344 				g_free (tmp);
345 			} else {
346 				recursive_scan (
347 					store, &fi->child, fi, visited,
348 					root, d->d_name, flags, cancellable);
349 			}
350 		}
351 
352 		closedir (dp);
353 	}
354 }
355 
356 /* scan a .folders file */
357 static void
folders_scan(CamelStore * store,const gchar * root,const gchar * top,CamelFolderInfo ** fip,guint32 flags,GCancellable * cancellable)358 folders_scan (CamelStore *store,
359               const gchar *root,
360               const gchar *top,
361               CamelFolderInfo **fip,
362               guint32 flags,
363               GCancellable *cancellable)
364 {
365 	CamelFolderInfo *fi;
366 	gchar  line[512], *path, *tmp;
367 	gsize tmp_len;
368 	CamelStream *stream, *in;
369 	struct stat st;
370 	GPtrArray *folders;
371 	GHashTable *visited;
372 	gint len;
373 
374 	tmp_len = strlen (root) + 16;
375 	tmp = g_alloca (tmp_len);
376 	g_snprintf (tmp, tmp_len, "%s/.folders", root);
377 	stream = camel_stream_fs_new_with_name (tmp, 0, O_RDONLY, NULL);
378 	if (stream == NULL)
379 		return;
380 
381 	in = camel_stream_buffer_new (stream, CAMEL_STREAM_BUFFER_READ);
382 	g_object_unref (stream);
383 	if (in == NULL)
384 		return;
385 
386 	visited = g_hash_table_new (g_str_hash, g_str_equal);
387 	folders = g_ptr_array_new ();
388 
389 	while ((len = camel_stream_buffer_gets (
390 		(CamelStreamBuffer *) in, line,
391 		sizeof (line), cancellable, NULL)) > 0) {
392 
393 		/* ignore blank lines */
394 		if (len <= 1)
395 			continue;
396 
397 		/* Check for invalidly long lines,
398 		 * we abort everything and fallback. */
399 		if (line[len - 1] != '\n') {
400 			gint i;
401 
402 			for (i = 0; i < folders->len; i++)
403 				camel_folder_info_free (folders->pdata[i]);
404 			g_ptr_array_set_size (folders, 0);
405 			break;
406 		}
407 		line[len - 1] = 0;
408 
409 		/* check for \r ? */
410 
411 		if (top && top[0]) {
412 			gint toplen = strlen (top);
413 
414 			/* check is dir or subdir */
415 			if (strncmp (top, line, toplen) != 0
416 			    || (line[toplen] != 0 && line[toplen] != '/'))
417 				continue;
418 
419 			/* check is not sub-subdir if not recursive */
420 			if ((flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE) == 0
421 			    && (tmp = strrchr (line, '/'))
422 			    && tmp > line + toplen)
423 				continue;
424 		}
425 
426 		if (g_hash_table_lookup (visited, line) != NULL)
427 			continue;
428 
429 		tmp = g_strdup (line);
430 		g_hash_table_insert (visited, tmp, tmp);
431 
432 		path = g_strdup_printf ("%s/%s", root, line);
433 		if (g_stat (path, &st) == 0 && S_ISDIR (st.st_mode)) {
434 			fi = folder_info_new (
435 				store, root, line, flags, cancellable);
436 			g_ptr_array_add (folders, fi);
437 		}
438 		g_free (path);
439 	}
440 
441 	if (folders->len)
442 		*fip = camel_folder_info_build(folders, top, '/', TRUE);
443 	g_ptr_array_free (folders, TRUE);
444 
445 	g_hash_table_foreach (visited, (GHFunc) g_free, NULL);
446 	g_hash_table_destroy (visited);
447 
448 	g_object_unref (in);
449 }
450 
451 /* FIXME: move to camel-local, this is shared with maildir code */
452 static guint
inode_hash(gconstpointer d)453 inode_hash (gconstpointer d)
454 {
455 	const struct _inode *v = d;
456 
457 	return v->inode ^ v->dnode;
458 }
459 
460 static gboolean
inode_equal(gconstpointer a,gconstpointer b)461 inode_equal (gconstpointer a,
462              gconstpointer b)
463 {
464 	const struct _inode *v1 = a, *v2 = b;
465 
466 	return v1->inode == v2->inode && v1->dnode == v2->dnode;
467 }
468 
469 static void
inode_free(gpointer k,gpointer v,gpointer d)470 inode_free (gpointer k,
471             gpointer v,
472             gpointer d)
473 {
474 	g_free (k);
475 }
476 
477 static CamelFolder *
mh_store_get_folder_sync(CamelStore * store,const gchar * folder_name,CamelStoreGetFolderFlags flags,GCancellable * cancellable,GError ** error)478 mh_store_get_folder_sync (CamelStore *store,
479                           const gchar *folder_name,
480                           CamelStoreGetFolderFlags flags,
481                           GCancellable *cancellable,
482                           GError **error)
483 {
484 	CamelStoreClass *store_class;
485 	CamelLocalSettings *local_settings;
486 	CamelSettings *settings;
487 	CamelService *service;
488 	CamelFolder *folder = NULL;
489 	gboolean use_dot_folders;
490 	struct stat st;
491 	gchar *name;
492 	gchar *path;
493 
494 	/* Chain up to parent's get_folder() method. */
495 	store_class = CAMEL_STORE_CLASS (camel_mh_store_parent_class);
496 	if (store_class->get_folder_sync (
497 		store, folder_name, flags, cancellable, error) == NULL)
498 		return NULL;
499 
500 	service = CAMEL_SERVICE (store);
501 
502 	settings = camel_service_ref_settings (service);
503 
504 	local_settings = CAMEL_LOCAL_SETTINGS (settings);
505 	path = camel_local_settings_dup_path (local_settings);
506 
507 	use_dot_folders = camel_mh_settings_get_use_dot_folders (
508 		CAMEL_MH_SETTINGS (settings));
509 
510 	g_object_unref (settings);
511 
512 	name = g_build_filename (path, folder_name, NULL);
513 
514 	if (g_stat (name, &st) == -1) {
515 		if (errno != ENOENT) {
516 			g_set_error (
517 				error, G_IO_ERROR,
518 				g_io_error_from_errno (errno),
519 				_("Cannot get folder “%s”: %s"),
520 				folder_name, g_strerror (errno));
521 			goto exit;
522 		}
523 
524 		if ((flags & CAMEL_STORE_FOLDER_CREATE) == 0) {
525 			g_set_error (
526 				error, CAMEL_STORE_ERROR,
527 				CAMEL_STORE_ERROR_NO_FOLDER,
528 				_("Cannot get folder “%s”: "
529 				"folder does not exist."),
530 				folder_name);
531 			goto exit;
532 		}
533 
534 		if (g_mkdir (name, 0777) != 0) {
535 			g_set_error (
536 				error, G_IO_ERROR,
537 				g_io_error_from_errno (errno),
538 				_("Could not create folder “%s”: %s"),
539 				folder_name, g_strerror (errno));
540 			goto exit;
541 		}
542 
543 		/* add to .folders if we are supposed to */
544 		/* FIXME: throw exception on error */
545 		if (use_dot_folders)
546 			folders_update (
547 				path, UPDATE_ADD, folder_name,
548 				NULL, cancellable);
549 
550 	} else if (!S_ISDIR (st.st_mode)) {
551 		g_set_error (
552 			error, CAMEL_STORE_ERROR,
553 			CAMEL_STORE_ERROR_NO_FOLDER,
554 			_("Cannot get folder “%s”: not a directory."),
555 			folder_name);
556 		goto exit;
557 	}
558 
559 	folder = camel_mh_folder_new (
560 		store, folder_name, flags, cancellable, error);
561 
562 exit:
563 	g_free (name);
564 	g_free (path);
565 
566 	return folder;
567 }
568 
569 static CamelFolderInfo *
mh_store_get_folder_info_sync(CamelStore * store,const gchar * top,CamelStoreGetFolderInfoFlags flags,GCancellable * cancellable,GError ** error)570 mh_store_get_folder_info_sync (CamelStore *store,
571                                const gchar *top,
572                                CamelStoreGetFolderInfoFlags flags,
573                                GCancellable *cancellable,
574                                GError **error)
575 {
576 	CamelLocalSettings *local_settings;
577 	CamelService *service;
578 	CamelSettings *settings;
579 	CamelFolderInfo *fi = NULL;
580 	gboolean use_dot_folders;
581 	gchar *path;
582 
583 	service = CAMEL_SERVICE (store);
584 
585 	settings = camel_service_ref_settings (service);
586 
587 	local_settings = CAMEL_LOCAL_SETTINGS (settings);
588 	path = camel_local_settings_dup_path (local_settings);
589 
590 	use_dot_folders = camel_mh_settings_get_use_dot_folders (
591 		CAMEL_MH_SETTINGS (settings));
592 
593 	g_object_unref (settings);
594 
595 	/* use .folders if we are supposed to */
596 	if (use_dot_folders) {
597 		folders_scan (
598 			store, path, top, &fi, flags, cancellable);
599 	} else {
600 		GHashTable *visited;
601 
602 		visited = g_hash_table_new (inode_hash, inode_equal);
603 
604 		if (top == NULL)
605 			top = "";
606 
607 		recursive_scan (
608 			store, &fi, NULL, visited,
609 			path, top, flags, cancellable);
610 
611 		/* If we actually scanned from root,
612 		 * we have a "" root node we dont want. */
613 		if (fi != NULL && top[0] == 0) {
614 			CamelFolderInfo *rfi;
615 
616 			rfi = fi;
617 			fi = rfi->child;
618 			rfi->child = NULL;
619 			camel_folder_info_free (rfi);
620 		}
621 
622 		g_hash_table_foreach (visited, inode_free, NULL);
623 		g_hash_table_destroy (visited);
624 	}
625 
626 	g_free (path);
627 
628 	return fi;
629 }
630 
631 static CamelFolder *
mh_store_get_inbox_sync(CamelStore * store,GCancellable * cancellable,GError ** error)632 mh_store_get_inbox_sync (CamelStore *store,
633                          GCancellable *cancellable,
634                          GError **error)
635 {
636 	return mh_store_get_folder_sync (
637 		store, "inbox", 0, cancellable, error);
638 }
639 
640 static gboolean
mh_store_delete_folder_sync(CamelStore * store,const gchar * folder_name,GCancellable * cancellable,GError ** error)641 mh_store_delete_folder_sync (CamelStore *store,
642                              const gchar *folder_name,
643                              GCancellable *cancellable,
644                              GError **error)
645 {
646 	CamelStoreClass *store_class;
647 	CamelLocalSettings *local_settings;
648 	CamelSettings *settings;
649 	CamelService *service;
650 	gboolean use_dot_folders;
651 	gchar *name;
652 	gchar *path;
653 
654 	service = CAMEL_SERVICE (store);
655 
656 	settings = camel_service_ref_settings (service);
657 
658 	local_settings = CAMEL_LOCAL_SETTINGS (settings);
659 	path = camel_local_settings_dup_path (local_settings);
660 
661 	use_dot_folders = camel_mh_settings_get_use_dot_folders (
662 		CAMEL_MH_SETTINGS (settings));
663 
664 	g_object_unref (settings);
665 
666 	/* remove folder directory - will fail if not empty */
667 	name = g_build_filename (path, folder_name, NULL);
668 	if (rmdir (name) == -1) {
669 		g_set_error (
670 			error, G_IO_ERROR,
671 			g_io_error_from_errno (errno),
672 			_("Could not delete folder “%s”: %s"),
673 			folder_name, g_strerror (errno));
674 		g_free (name);
675 		g_free (path);
676 		return FALSE;
677 	}
678 	g_free (name);
679 
680 	/* remove from .folders if we are supposed to */
681 	if (use_dot_folders)
682 		folders_update (
683 			path, UPDATE_REMOVE, folder_name,
684 			NULL, cancellable);
685 
686 	g_free (path);
687 
688 	/* Chain up to parent's delete_folder() method. */
689 	store_class = CAMEL_STORE_CLASS (camel_mh_store_parent_class);
690 	return store_class->delete_folder_sync (
691 		store, folder_name, cancellable, error);
692 }
693 
694 static gboolean
mh_store_rename_folder_sync(CamelStore * store,const gchar * old,const gchar * new,GCancellable * cancellable,GError ** error)695 mh_store_rename_folder_sync (CamelStore *store,
696                              const gchar *old,
697                              const gchar *new,
698                              GCancellable *cancellable,
699                              GError **error)
700 {
701 	CamelStoreClass *store_class;
702 	CamelLocalSettings *local_settings;
703 	CamelSettings *settings;
704 	CamelService *service;
705 	gboolean use_dot_folders;
706 	gboolean success;
707 	gchar *path;
708 
709 	service = CAMEL_SERVICE (store);
710 
711 	settings = camel_service_ref_settings (service);
712 
713 	local_settings = CAMEL_LOCAL_SETTINGS (settings);
714 	path = camel_local_settings_dup_path (local_settings);
715 
716 	use_dot_folders = camel_mh_settings_get_use_dot_folders (
717 		CAMEL_MH_SETTINGS (settings));
718 
719 	g_object_unref (settings);
720 
721 	/* Chain up to parent's rename_folder() method. */
722 	store_class = CAMEL_STORE_CLASS (camel_mh_store_parent_class);
723 	success = store_class->rename_folder_sync (
724 		store, old, new, cancellable, error);
725 
726 	if (success && use_dot_folders) {
727 		/* yeah this is messy, but so is mh! */
728 		folders_update (
729 			path, UPDATE_RENAME, old, new, cancellable);
730 	}
731 
732 	g_free (path);
733 
734 	return success;
735 }
736 
737 static void
camel_mh_store_class_init(CamelMhStoreClass * class)738 camel_mh_store_class_init (CamelMhStoreClass *class)
739 {
740 	CamelServiceClass *service_class;
741 	CamelStoreClass *store_class;
742 
743 	service_class = CAMEL_SERVICE_CLASS (class);
744 	service_class->settings_type = CAMEL_TYPE_MH_SETTINGS;
745 
746 	store_class = CAMEL_STORE_CLASS (class);
747 	store_class->get_folder_sync = mh_store_get_folder_sync;
748 	store_class->get_folder_info_sync = mh_store_get_folder_info_sync;
749 	store_class->get_inbox_folder_sync = mh_store_get_inbox_sync;
750 	store_class->delete_folder_sync = mh_store_delete_folder_sync;
751 	store_class->rename_folder_sync = mh_store_rename_folder_sync;
752 }
753 
754 static void
camel_mh_store_init(CamelMhStore * mh_store)755 camel_mh_store_init (CamelMhStore *mh_store)
756 {
757 }
758 
759