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