/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ /* * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * * This library is free software: you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library. If not, see . * * Authors: Michael Zucchi */ #include "evolution-data-server-config.h" #include #include #include #include #include #include #include #include #include "camel-mbox-folder.h" #include "camel-mbox-store.h" #define d(x) G_DEFINE_TYPE (CamelMboxStore, camel_mbox_store, CAMEL_TYPE_LOCAL_STORE) static const gchar *extensions[] = { ".msf", ".ev-summary", ".ev-summary-meta", ".ibex.index", ".ibex.index.data", ".cmeta", ".lock", ".db", ".journal" }; /* used to find out where we've visited already */ struct _inode { dev_t dnode; ino_t inode; }; static guint inode_hash (gconstpointer d) { const struct _inode *v = d; return v->inode ^ v->dnode; } static gboolean inode_equal (gconstpointer a, gconstpointer b) { const struct _inode *v1 = a, *v2 = b; return v1->inode == v2->inode && v1->dnode == v2->dnode; } static struct _inode * inode_new (const struct _inode *src) { struct _inode *inode; inode = g_slice_new0 (struct _inode); if (src) { inode->dnode = src->dnode; inode->inode = src->inode; } return inode; } static void inode_free (gpointer ptr) { g_slice_free (struct _inode, ptr); } static gboolean ignore_file (const gchar *filename, gboolean sbd) { gint flen, len, i; /* TODO: Should probably just be 1 regex */ flen = strlen (filename); if (flen > 0 && filename[flen - 1] == '~') return TRUE; for (i = 0; i < G_N_ELEMENTS (extensions); i++) { len = strlen (extensions[i]); if (len < flen && !strcmp (filename + flen - len, extensions[i])) return TRUE; } if (sbd && flen > 4 && !strcmp (filename + flen - 4, ".sbd")) return TRUE; return FALSE; } /* NB: duplicated in maildir store */ static void fill_fi (CamelStore *store, CamelFolderInfo *fi, guint32 flags) { CamelFolder *folder; fi->unread = -1; fi->total = -1; folder = camel_object_bag_peek (camel_store_get_folders_bag (store), fi->full_name); if (folder) { if ((flags & CAMEL_STORE_FOLDER_INFO_FAST) == 0) camel_folder_refresh_info_sync (folder, NULL, NULL); fi->unread = camel_folder_get_unread_message_count (folder); fi->total = camel_folder_get_message_count (folder); g_object_unref (folder); } else { CamelLocalStore *local_store; gchar *folderpath; CamelMboxSummary *mbs; local_store = CAMEL_LOCAL_STORE (store); /* This should be fast enough not * to have to test for INFO_FAST. */ folderpath = camel_local_store_get_full_path ( local_store, fi->full_name); mbs = (CamelMboxSummary *) camel_mbox_summary_new ( NULL, folderpath, NULL); /* FIXME[disk-summary] track exception */ if (camel_folder_summary_header_load ((CamelFolderSummary *) mbs, store, fi->full_name, NULL)) { fi->unread = camel_folder_summary_get_unread_count ( (CamelFolderSummary *) mbs); fi->total = camel_folder_summary_get_saved_count ( (CamelFolderSummary *) mbs); } g_object_unref (mbs); g_free (folderpath); } if (camel_local_store_is_main_store (CAMEL_LOCAL_STORE (store)) && fi->full_name && (fi->flags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_NORMAL) fi->flags = (fi->flags & ~CAMEL_FOLDER_TYPE_MASK) | camel_local_store_get_folder_type_by_full_name ( CAMEL_LOCAL_STORE (store), fi->full_name); } static CamelFolderInfo * scan_dir (CamelStore *store, GHashTable *visited, CamelFolderInfo *parent, const gchar *root, const gchar *name, guint32 flags, GError **error) { CamelFolderInfo *folders, *tail, *fi; GHashTable *folder_hash; const gchar *dent; GDir *dir; tail = folders = NULL; if (!(dir = g_dir_open (root, 0, NULL))) return NULL; folder_hash = g_hash_table_new (g_str_hash, g_str_equal); /* FIXME: it would be better if we queue'd up the recursive * scans till the end so that we can limit the number of * directory descriptors open at any given time... */ while ((dent = g_dir_read_name (dir))) { gchar *short_name, *full_name, *path, *ext; struct stat st; if (dent[0] == '.') continue; if (ignore_file (dent, FALSE)) continue; path = g_strdup_printf ("%s/%s", root, dent); if (g_stat (path, &st) == -1) { g_free (path); continue; } #ifndef G_OS_WIN32 if (S_ISDIR (st.st_mode)) { struct _inode in = { st.st_dev, st.st_ino }; if (g_hash_table_lookup (visited, &in)) { g_free (path); continue; } } #endif short_name = g_strdup (dent); if ((ext = strrchr (short_name, '.')) && !strcmp (ext, ".sbd")) *ext = '\0'; if (name != NULL) full_name = g_strdup_printf ("%s/%s", name, short_name); else full_name = g_strdup (short_name); if ((fi = g_hash_table_lookup (folder_hash, short_name)) != NULL) { g_free (short_name); g_free (full_name); if (S_ISDIR (st.st_mode)) { fi->flags = (fi->flags & ~CAMEL_FOLDER_NOCHILDREN) | CAMEL_FOLDER_CHILDREN; } else { fi->flags &= ~CAMEL_FOLDER_NOSELECT; } } else { fi = camel_folder_info_new (); fi->parent = parent; fi->full_name = full_name; fi->display_name = short_name; fi->unread = -1; fi->total = -1; if (S_ISDIR (st.st_mode)) fi->flags = CAMEL_FOLDER_NOSELECT; else fi->flags = CAMEL_FOLDER_NOCHILDREN; if (tail == NULL) folders = fi; else tail->next = fi; tail = fi; g_hash_table_insert (folder_hash, fi->display_name, fi); } if (!S_ISDIR (st.st_mode)) { fill_fi (store, fi, flags); } else if ((flags & CAMEL_STORE_FOLDER_INFO_RECURSIVE)) { struct _inode in = { st.st_dev, st.st_ino }; if (g_hash_table_lookup (visited, &in) == NULL) { #ifndef G_OS_WIN32 struct _inode *inew = inode_new (&in); g_hash_table_insert (visited, inew, inew); #endif if ((fi->child = scan_dir (store, visited, fi, path, fi->full_name, flags, error))) fi->flags |= CAMEL_FOLDER_CHILDREN; else fi->flags = (fi->flags & ~CAMEL_FOLDER_CHILDREN) | CAMEL_FOLDER_NOCHILDREN; } } g_free (path); } g_dir_close (dir); g_hash_table_destroy (folder_hash); return folders; } static gint xrename (CamelStore *store, const gchar *old_name, const gchar *new_name, const gchar *ext, gboolean missingok) { CamelLocalStore *ls = (CamelLocalStore *) store; gchar *oldpath, *newpath; struct stat st; gint ret = -1; if (ext != NULL) { oldpath = camel_local_store_get_meta_path (ls, old_name, ext); newpath = camel_local_store_get_meta_path (ls, new_name, ext); } else { oldpath = camel_local_store_get_full_path (ls, old_name); newpath = camel_local_store_get_full_path (ls, new_name); } if (g_stat (oldpath, &st) == -1) { if (missingok && errno == ENOENT) { ret = 0; } else { ret = -1; } #ifndef G_OS_WIN32 } else if (S_ISDIR (st.st_mode)) { /* use rename for dirs */ if (g_rename (oldpath, newpath) == 0 || g_stat (newpath, &st) == 0) { ret = 0; } else { ret = -1; } } else if (link (oldpath, newpath) == 0 /* and link for files */ ||(g_stat (newpath, &st) == 0 && st.st_nlink == 2)) { if (unlink (oldpath) == 0) { ret = 0; } else { unlink (newpath); ret = -1; } } else { ret = -1; #else } else if ((!g_file_test (newpath, G_FILE_TEST_EXISTS) || g_remove (newpath) == 0) && g_rename (oldpath, newpath) == 0) { ret = 0; } else { ret = -1; #endif } g_free (oldpath); g_free (newpath); return ret; } static CamelFolder * mbox_store_get_folder_sync (CamelStore *store, const gchar *folder_name, CamelStoreGetFolderFlags flags, GCancellable *cancellable, GError **error) { CamelStoreClass *store_class; CamelLocalStore *local_store; struct stat st; gchar *name; /* Chain up to parent's get_folder_sync() method. */ store_class = CAMEL_STORE_CLASS (camel_mbox_store_parent_class); if (!store_class->get_folder_sync (store, folder_name, flags, cancellable, error)) return NULL; local_store = CAMEL_LOCAL_STORE (store); name = camel_local_store_get_full_path (local_store, folder_name); if (g_stat (name, &st) == -1) { gchar *basename; gchar *dirname; gint fd; if (errno != ENOENT) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), _("Cannot get folder “%s”: %s"), folder_name, g_strerror (errno)); g_free (name); return NULL; } if ((flags & CAMEL_STORE_FOLDER_CREATE) == 0) { g_set_error ( error, CAMEL_STORE_ERROR, CAMEL_STORE_ERROR_NO_FOLDER, _("Cannot get folder “%s”: folder does not exist."), folder_name); g_free (name); return NULL; } /* sanity check the folder name */ basename = g_path_get_basename (folder_name); if (basename[0] == '.' || ignore_file (basename, TRUE)) { g_set_error ( error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, _("Cannot create a folder by this name.")); g_free (name); g_free (basename); return NULL; } g_free (basename); dirname = g_path_get_dirname (name); if (g_mkdir_with_parents (dirname, 0700) == -1 && errno != EEXIST) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), _("Cannot create folder “%s”: %s"), folder_name, g_strerror (errno)); g_free (dirname); g_free (name); return NULL; } g_free (dirname); fd = g_open ( name, O_LARGEFILE | O_WRONLY | O_CREAT | O_APPEND | O_BINARY, 0666); if (fd == -1) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), _("Cannot create folder “%s”: %s"), folder_name, g_strerror (errno)); g_free (name); return NULL; } g_free (name); close (fd); } else if (!S_ISREG (st.st_mode)) { g_set_error ( error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, _("Cannot get folder “%s”: not a regular file."), folder_name); g_free (name); return NULL; } else g_free (name); return camel_mbox_folder_new (store, folder_name, flags, cancellable, error); } static CamelFolderInfo * mbox_store_get_folder_info_sync (CamelStore *store, const gchar *top, CamelStoreGetFolderInfoFlags flags, GCancellable *cancellable, GError **error) { CamelLocalStore *local_store; GHashTable *visited; #ifndef G_OS_WIN32 struct _inode *inode; #endif gchar *path, *subdir; CamelFolderInfo *fi; gchar *basename; struct stat st; if (top == NULL) top = ""; local_store = CAMEL_LOCAL_STORE (store); path = camel_local_store_get_full_path (local_store, top); if (*top == '\0') { /* requesting root dir scan */ if (g_stat (path, &st) == -1 || !S_ISDIR (st.st_mode)) { g_free (path); return NULL; } visited = g_hash_table_new_full (inode_hash, inode_equal, inode_free, NULL); #ifndef G_OS_WIN32 inode = inode_new (NULL); inode->dnode = st.st_dev; inode->inode = st.st_ino; g_hash_table_insert (visited, inode, inode); #endif fi = scan_dir (store, visited, NULL, path, NULL, flags, error); g_hash_table_destroy (visited); g_free (path); return fi; } /* requesting scan of specific folder */ if (g_stat (path, &st) == -1 || !S_ISREG (st.st_mode)) { gchar *test_if_subdir = g_strdup_printf ("%s.sbd", path); if (g_stat (test_if_subdir, &st) == -1) { g_free (path); g_free (test_if_subdir); return NULL; } g_free (test_if_subdir); } visited = g_hash_table_new_full (inode_hash, inode_equal, inode_free, NULL); basename = g_path_get_basename (top); fi = camel_folder_info_new (); fi->parent = NULL; fi->full_name = g_strdup (top); fi->display_name = basename; fi->unread = -1; fi->total = -1; fill_fi (store, fi, flags); subdir = g_strdup_printf ("%s.sbd", path); if (g_stat (subdir, &st) == 0) { if (S_ISDIR (st.st_mode)) fi->child = scan_dir (store, visited, fi, subdir, top, flags, error); } if (fi->child) fi->flags |= CAMEL_FOLDER_CHILDREN; else fi->flags |= CAMEL_FOLDER_NOCHILDREN; g_free (subdir); g_hash_table_destroy (visited); g_free (path); return fi; } static CamelFolderInfo * mbox_store_create_folder_sync (CamelStore *store, const gchar *parent_name, const gchar *folder_name, GCancellable *cancellable, GError **error) { /* FIXME This is almost an exact copy of * CamelLocalStore::create_folder() except that we use * different path schemes - need to find a way to share * parent's code? */ CamelLocalSettings *local_settings; CamelLocalStore *local_store; CamelFolderInfo *info = NULL; CamelSettings *settings; CamelService *service; gchar *root_path = NULL; gchar *name = NULL; gchar *path = NULL; gchar *dir; CamelFolder *folder; struct stat st; service = CAMEL_SERVICE (store); settings = camel_service_ref_settings (service); local_settings = CAMEL_LOCAL_SETTINGS (settings); root_path = camel_local_settings_dup_path (local_settings); g_object_unref (settings); local_store = CAMEL_LOCAL_STORE (store); if (!g_path_is_absolute (root_path)) { g_set_error ( error, CAMEL_STORE_ERROR, CAMEL_STORE_ERROR_NO_FOLDER, _("Store root %s is not an absolute path"), root_path); goto exit; } if (folder_name[0] == '.' || ignore_file (folder_name, TRUE)) { g_set_error ( error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, _("Cannot create a folder by this name.")); goto exit; } if (parent_name && *parent_name) name = g_strdup_printf ("%s/%s", parent_name, folder_name); else name = g_strdup (folder_name); path = camel_local_store_get_full_path (local_store, name); dir = g_path_get_dirname (path); if (g_mkdir_with_parents (dir, 0777) == -1 && errno != EEXIST) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), _("Cannot create directory “%s”: %s."), dir, g_strerror (errno)); g_free (dir); goto exit; } g_free (dir); if (g_stat (path, &st) == 0 || errno != ENOENT) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), _("Cannot create folder: %s: %s"), path, errno ? g_strerror (errno) : _("Folder already exists")); goto exit; } folder = CAMEL_STORE_GET_CLASS (store)->get_folder_sync ( store, name, CAMEL_STORE_FOLDER_CREATE, cancellable, error); if (folder) { g_object_unref (folder); info = CAMEL_STORE_GET_CLASS (store)->get_folder_info_sync ( store, name, 0, cancellable, error); } exit: g_free (root_path); g_free (name); g_free (path); return info; } static gboolean mbox_store_delete_folder_sync (CamelStore *store, const gchar *folder_name, GCancellable *cancellable, GError **error) { CamelLocalStore *local_store; CamelFolderInfo *fi; CamelFolder *lf; gchar *name, *path; struct stat st; local_store = CAMEL_LOCAL_STORE (store); name = camel_local_store_get_full_path (local_store, folder_name); path = g_strdup_printf ("%s.sbd", name); if (g_rmdir (path) == -1 && errno != ENOENT) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), _("Could not delete folder “%s”:\n%s"), folder_name, g_strerror (errno)); g_free (path); g_free (name); return FALSE; } g_free (path); if (g_stat (name, &st) == -1) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), _("Could not delete folder “%s”:\n%s"), folder_name, g_strerror (errno)); g_free (name); return FALSE; } if (!S_ISREG (st.st_mode)) { g_set_error ( error, CAMEL_STORE_ERROR, CAMEL_STORE_ERROR_NO_FOLDER, _("“%s” is not a regular file."), name); g_free (name); return FALSE; } if (st.st_size != 0) { g_set_error ( error, CAMEL_FOLDER_ERROR, CAMEL_FOLDER_ERROR_NON_EMPTY, _("Folder “%s” is not empty. Not deleted."), folder_name); g_free (name); return FALSE; } if (g_unlink (name) == -1 && errno != ENOENT) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), _("Could not delete folder “%s”:\n%s"), name, g_strerror (errno)); g_free (name); return FALSE; } /* FIXME: we have to do our own meta cleanup here rather than * calling our parent class' delete_folder() method since our * naming convention is different. Need to find a way for * CamelLocalStore to be able to construct the folder & meta * paths itself */ path = camel_local_store_get_meta_path ( local_store, folder_name, ".ev-summary"); if (g_unlink (path) == -1 && errno != ENOENT) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), _("Could not delete folder summary file “%s”: %s"), path, g_strerror (errno)); g_free (path); g_free (name); return FALSE; } g_free (path); path = camel_local_store_get_meta_path ( local_store, folder_name, ".ev-summary-meta"); if (g_unlink (path) == -1 && errno != ENOENT) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), _("Could not delete folder summary file “%s”: %s"), path, g_strerror (errno)); g_free (path); g_free (name); return FALSE; } g_free (path); path = camel_local_store_get_meta_path ( local_store, folder_name, ".ibex"); if (camel_text_index_remove (path) == -1 && errno != ENOENT) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), _("Could not delete folder index file “%s”: %s"), path, g_strerror (errno)); g_free (path); g_free (name); return FALSE; } g_free (path); path = NULL; if ((lf = camel_store_get_folder_sync (store, folder_name, 0, cancellable, NULL))) { CamelObject *object = CAMEL_OBJECT (lf); const gchar *state_filename; state_filename = camel_object_get_state_filename (object); path = g_strdup (state_filename); camel_object_set_state_filename (object, NULL); g_object_unref (lf); } if (path == NULL) path = camel_local_store_get_meta_path ( local_store, folder_name, ".cmeta"); if (g_unlink (path) == -1 && errno != ENOENT) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), _("Could not delete folder meta file “%s”: %s"), path, g_strerror (errno)); g_free (path); g_free (name); return FALSE; } g_free (path); g_free (name); fi = camel_folder_info_new (); fi->full_name = g_strdup (folder_name); fi->display_name = g_path_get_basename (folder_name); fi->unread = -1; camel_store_folder_deleted (store, fi); camel_folder_info_free (fi); return TRUE; } static gboolean mbox_store_rename_folder_sync (CamelStore *store, const gchar *old, const gchar *new, GCancellable *cancellable, GError **error) { CamelLocalStore *local_store; CamelLocalFolder *folder = NULL; gchar *oldibex, *newibex, *newdir; gint errnosav; if (new[0] == '.' || ignore_file (new, TRUE)) { g_set_error ( error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, _("The new folder name is illegal.")); return FALSE; } /* try to rollback failures, has obvious races */ local_store = CAMEL_LOCAL_STORE (store); oldibex = camel_local_store_get_meta_path (local_store, old, ".ibex"); newibex = camel_local_store_get_meta_path (local_store, new, ".ibex"); newdir = g_path_get_dirname (newibex); if (g_mkdir_with_parents (newdir, 0700) == -1) { if (errno != EEXIST) { g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errno), _("Could not rename “%s”: “%s”: %s"), old, new, g_strerror (errno)); g_free (oldibex); g_free (newibex); g_free (newdir); return FALSE; } g_free (newdir); newdir = NULL; } folder = camel_object_bag_get (camel_store_get_folders_bag (store), old); if (folder && folder->index) { if (camel_index_rename (folder->index, newibex) == -1 && errno != ENOENT) { errnosav = errno; goto ibex_failed; } } else { /* TODO camel_text_index_rename should find out * if we have an active index itself? */ if (camel_text_index_rename (oldibex, newibex) == -1 && errno != ENOENT) { errnosav = errno; goto ibex_failed; } } if (xrename (store, old, new, ".ev-summary", TRUE) == -1) { errnosav = errno; goto summary_failed; } if (xrename (store, old, new, ".ev-summary-meta", TRUE) == -1) { errnosav = errno; goto summary_failed; } if (xrename (store, old, new, ".cmeta", TRUE) == -1) { errnosav = errno; goto cmeta_failed; } if (xrename (store, old, new, ".sbd", TRUE) == -1) { errnosav = errno; goto subdir_failed; } if (xrename (store, old, new, NULL, FALSE) == -1) { errnosav = errno; goto base_failed; } g_free (oldibex); g_free (newibex); if (folder) g_object_unref (folder); return TRUE; base_failed: xrename (store, new, old, ".sbd", TRUE); subdir_failed: xrename (store, new, old, ".cmeta", TRUE); cmeta_failed: xrename (store, new, old, ".ev-summary", TRUE); xrename (store, new, old, ".ev-summary-meta", TRUE); summary_failed: if (folder) { if (folder->index) camel_index_rename (folder->index, oldibex); } else camel_text_index_rename (newibex, oldibex); ibex_failed: if (newdir) { /* newdir is only non-NULL if we needed to mkdir */ g_rmdir (newdir); g_free (newdir); } g_set_error ( error, G_IO_ERROR, g_io_error_from_errno (errnosav), _("Could not rename “%s” to %s: %s"), old, new, g_strerror (errnosav)); g_free (newibex); g_free (oldibex); if (folder) g_object_unref (folder); return FALSE; } static gchar * mbox_store_get_full_path (CamelLocalStore *ls, const gchar *full_name) { CamelLocalSettings *local_settings; CamelSettings *settings; CamelService *service; GString *full_path; gchar *root_path; const gchar *cp; service = CAMEL_SERVICE (ls); settings = camel_service_ref_settings (service); local_settings = CAMEL_LOCAL_SETTINGS (settings); root_path = camel_local_settings_dup_path (local_settings); g_object_unref (settings); g_return_val_if_fail (root_path != NULL, NULL); full_path = g_string_new (root_path); /* Root path may or may not have a trailing separator. */ if (!g_str_has_suffix (root_path, G_DIR_SEPARATOR_S)) g_string_append_c (full_path, G_DIR_SEPARATOR); cp = full_name; while (*cp != '\0') { if (G_IS_DIR_SEPARATOR (*cp)) { g_string_append (full_path, ".sbd"); g_string_append_c (full_path, *cp++); /* Skip extranaeous separators. */ while (G_IS_DIR_SEPARATOR (*cp)) cp++; } else { g_string_append_c (full_path, *cp++); } } g_free (root_path); return g_string_free (full_path, FALSE); } static gchar * mbox_store_get_meta_path (CamelLocalStore *ls, const gchar *full_name, const gchar *ext) { /*#define USE_HIDDEN_META_FILES*/ #ifdef USE_HIDDEN_META_FILES gchar *name, *slash; gsize name_len; name_len = strlen (full_name) + strlen (ext) + 2; name = g_alloca (name_len); if ((slash = strrchr (full_name, '/'))) g_snprintf ( name, name_len, "%.*s.%s%s", slash - full_name + 1, full_name, slash + 1, ext); else g_snprintf (name, name_len, ".%s%s", full_name, ext); return mbox_store_get_full_path (ls, name); #else gchar *full_path, *path; full_path = mbox_store_get_full_path (ls, full_name); path = g_strdup_printf ("%s%s", full_path, ext); g_free (full_path); return path; #endif } static void camel_mbox_store_class_init (CamelMboxStoreClass *class) { CamelStoreClass *store_class; CamelLocalStoreClass *local_store_class; store_class = CAMEL_STORE_CLASS (class); store_class->get_folder_sync = mbox_store_get_folder_sync; store_class->get_folder_info_sync = mbox_store_get_folder_info_sync; store_class->create_folder_sync = mbox_store_create_folder_sync; store_class->delete_folder_sync = mbox_store_delete_folder_sync; store_class->rename_folder_sync = mbox_store_rename_folder_sync; local_store_class = CAMEL_LOCAL_STORE_CLASS (class); local_store_class->get_full_path = mbox_store_get_full_path; local_store_class->get_meta_path = mbox_store_get_meta_path; } static void camel_mbox_store_init (CamelMboxStore *mbox_store) { }