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