Fix FSF address (Tobias Mueller, #470445)
[platform/upstream/evolution-data-server.git] / servers / exchange / storage / e-storage.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* e-storage.c
3  *
4  * Copyright (C) 2000, 2001 Ximian, Inc.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of version 2 of the GNU Lesser General Public
8  * License as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  * Author: Ettore Perazzoli
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include "e-storage.h"
28
29 #include "e-folder-tree.h"
30
31 #include <glib/gi18n-lib.h>
32 #include <libedataserver/e-data-server-util.h>
33
34 #include <string.h>
35
36 #define PARENT_TYPE G_TYPE_OBJECT
37 static GObjectClass *parent_class = NULL;
38
39 struct EStoragePrivate {
40         /* The set of folders we have in this storage.  */
41         EFolderTree *folder_tree;
42
43         /* Internal name of the storage */
44         char *name;
45 };
46
47 enum {
48         NEW_FOLDER,
49         UPDATED_FOLDER,
50         REMOVED_FOLDER,
51         LAST_SIGNAL
52 };
53
54 static guint signals[LAST_SIGNAL] = { 0 };
55
56 /* Destroy notification function for the folders in the tree.  */
57
58 static void
59 folder_destroy_notify (EFolderTree *tree,
60                        const char *path,
61                        void *data,
62                        void *closure)
63 {
64         EFolder *e_folder;
65
66         if (data == NULL) {
67                 /* The root folder has no EFolder associated to it.  */
68                 return;
69         }
70
71         e_folder = E_FOLDER (data);
72         g_object_unref (e_folder);
73 }
74
75 /* Signal callbacks for the EFolders.  */
76
77 static void
78 folder_changed_cb (EFolder *folder,
79                    void *data)
80 {
81         EStorage *storage;
82         EStoragePrivate *priv;
83         const char *path, *p;
84         gboolean highlight;
85
86         g_assert (E_IS_STORAGE (data));
87
88         storage = E_STORAGE (data);
89         priv = storage->priv;
90
91         path = e_folder_tree_get_path_for_data (priv->folder_tree, folder);
92         g_assert (path != NULL);
93
94         g_signal_emit (storage, signals[UPDATED_FOLDER], 0, path);
95
96         highlight = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (folder), "last_highlight"));
97         if (highlight != e_folder_get_highlighted (folder)) {
98                 highlight = !highlight;
99                 g_object_set_data (G_OBJECT (folder), "last_highlight", GINT_TO_POINTER (highlight));
100                 p = strrchr (path, '/');
101                 if (p && p != path) {
102                         char *name;
103                         
104                         name = g_strndup (path, p - path);
105                         folder = e_folder_tree_get_folder (priv->folder_tree, name);
106                         g_free (name);
107                         if (folder)
108                                 e_folder_set_child_highlight (folder, highlight);
109                 }
110         }
111 }
112
113 /* GObject methods.  */
114
115 static void
116 impl_finalize (GObject *object)
117 {
118         EStorage *storage;
119         EStoragePrivate *priv;
120
121         storage = E_STORAGE (object);
122         priv = storage->priv;
123
124         if (priv->folder_tree != NULL)
125                 e_folder_tree_destroy (priv->folder_tree);
126
127         g_free (priv->name);
128
129         g_free (priv);
130
131         (* G_OBJECT_CLASS (parent_class)->finalize) (object);
132 }
133
134 /* EStorage methods.  */
135
136 static GList *
137 impl_get_subfolder_paths (EStorage *storage,
138                           const char *path)
139 {
140         EStoragePrivate *priv;
141
142         priv = storage->priv;
143
144         return e_folder_tree_get_subfolders (priv->folder_tree, path);
145 }
146
147 static EFolder *
148 impl_get_folder (EStorage *storage,
149                  const char *path)
150 {
151         EStoragePrivate *priv;
152         EFolder *e_folder;
153
154         priv = storage->priv;
155
156         e_folder = (EFolder *) e_folder_tree_get_folder (priv->folder_tree, path);
157
158         return e_folder;
159 }
160
161 static const char *
162 impl_get_name (EStorage *storage)
163 {
164         return storage->priv->name;
165 }
166
167 static void
168 impl_async_create_folder (EStorage *storage,
169                           const char *path,
170                           const char *type,
171                           EStorageResultCallback callback,
172                           void *data)
173 {
174         (* callback) (storage, E_STORAGE_NOTIMPLEMENTED, data);
175 }
176
177 static void
178 impl_async_remove_folder (EStorage *storage,
179                           const char *path,
180                           EStorageResultCallback callback,
181                           void *data)
182 {
183         (* callback) (storage, E_STORAGE_NOTIMPLEMENTED, data);
184 }
185
186 static void
187 impl_async_xfer_folder (EStorage *storage,
188                         const char *source_path,
189                         const char *destination_path,
190                         gboolean remove_source,
191                         EStorageResultCallback callback,
192                         void *data)
193 {
194         (* callback) (storage, E_STORAGE_NOTIMPLEMENTED, data);
195 }
196
197 static void
198 impl_async_open_folder (EStorage *storage,
199                         const char *path,
200                         EStorageDiscoveryCallback callback,
201                         void *data)
202 {
203         (* callback) (storage, E_STORAGE_NOTIMPLEMENTED, NULL, data);
204 }
205
206 static gboolean
207 impl_will_accept_folder (EStorage *storage,
208                          EFolder *new_parent,
209                          EFolder *source)
210 {
211         EStoragePrivate *priv = storage->priv;
212         const char *parent_path, *source_path;
213         int source_len;
214
215         /* By default, we only disallow dragging a folder into
216          * a subfolder of itself.
217          */
218
219         if (new_parent == source)
220                 return FALSE;
221
222         parent_path = e_folder_tree_get_path_for_data (priv->folder_tree,
223                                                        new_parent);
224         source_path = e_folder_tree_get_path_for_data (priv->folder_tree,
225                                                        source);
226         if (!parent_path || !source_path)
227                 return FALSE;
228
229         source_len = strlen (source_path);
230         if (!strncmp (parent_path, source_path, source_len) &&
231             parent_path[source_len] == '/')
232                 return FALSE;
233
234         return TRUE;
235 }
236
237 static void
238 impl_async_discover_shared_folder (EStorage *storage,
239                                    const char *owner,
240                                    const char *folder_name,
241                                    EStorageDiscoveryCallback callback,
242                                    void *data)
243 {
244         (* callback) (storage, E_STORAGE_NOTIMPLEMENTED, NULL, data);
245 }
246
247 static void
248 impl_async_remove_shared_folder (EStorage *storage,
249                                  const char *path,
250                                  EStorageResultCallback callback,
251                                  void *data)
252 {
253         (* callback) (storage, E_STORAGE_NOTIMPLEMENTED, data);
254 }
255
256 /* Initialization.  */
257
258 static void
259 e_storage_class_init (EStorageClass *class)
260 {
261         GObjectClass *object_class;
262
263         object_class = G_OBJECT_CLASS (class);
264         parent_class = g_type_class_ref (PARENT_TYPE);
265
266         object_class->finalize = impl_finalize;
267
268         class->get_subfolder_paths = impl_get_subfolder_paths;
269         class->get_folder          = impl_get_folder;
270         class->get_name            = impl_get_name;
271         class->async_create_folder = impl_async_create_folder;
272         class->async_remove_folder = impl_async_remove_folder;
273         class->async_xfer_folder   = impl_async_xfer_folder;
274         class->async_open_folder   = impl_async_open_folder;
275         class->will_accept_folder  = impl_will_accept_folder;
276
277         class->async_discover_shared_folder  = impl_async_discover_shared_folder;
278         class->async_remove_shared_folder    = impl_async_remove_shared_folder;
279         signals[NEW_FOLDER] =
280                 g_signal_new ("new_folder",
281                               G_OBJECT_CLASS_TYPE (object_class),
282                               G_SIGNAL_RUN_FIRST,
283                               G_STRUCT_OFFSET (EStorageClass, new_folder),
284                               NULL, NULL,
285                               g_cclosure_marshal_VOID__STRING,
286                               G_TYPE_NONE, 1,
287                               G_TYPE_STRING);
288         signals[UPDATED_FOLDER] =
289                 g_signal_new ("updated_folder",
290                               G_OBJECT_CLASS_TYPE (object_class),
291                               G_SIGNAL_RUN_FIRST,
292                               G_STRUCT_OFFSET (EStorageClass, updated_folder),
293                               NULL, NULL,
294                               g_cclosure_marshal_VOID__STRING,
295                               G_TYPE_NONE, 1,
296                               G_TYPE_STRING);
297         signals[REMOVED_FOLDER] =
298                 g_signal_new ("removed_folder",
299                               G_OBJECT_CLASS_TYPE (object_class),
300                               G_SIGNAL_RUN_FIRST,
301                               G_STRUCT_OFFSET (EStorageClass, removed_folder),
302                               NULL, NULL,
303                               g_cclosure_marshal_VOID__STRING,
304                               G_TYPE_NONE, 1,
305                               G_TYPE_STRING);
306 }
307
308 static void
309 e_storage_init (EStorage *storage)
310 {
311         EStoragePrivate *priv;
312
313         priv = g_new0 (EStoragePrivate, 1);
314
315         priv->folder_tree   = e_folder_tree_new (folder_destroy_notify, NULL);
316
317         storage->priv = priv;
318 }
319
320 /* Creation.  */
321
322 void
323 e_storage_construct (EStorage *storage,
324                      const char *name,
325                      EFolder *root_folder)
326 {
327         EStoragePrivate *priv;
328
329         g_return_if_fail (E_IS_STORAGE (storage));
330
331         priv = storage->priv;
332
333         priv->name = g_strdup (name);
334
335         e_storage_new_folder (storage, "/", root_folder);
336 }
337
338 EStorage *
339 e_storage_new (const char *name,
340                EFolder *root_folder)
341 {
342         EStorage *new;
343
344         new = g_object_new (e_storage_get_type (), NULL);
345
346         e_storage_construct (new, name, root_folder);
347
348         return new;
349 }
350
351 gboolean
352 e_storage_path_is_absolute (const char *path)
353 {
354         g_return_val_if_fail (path != NULL, FALSE);
355
356         return *path == '/';
357 }
358
359 gboolean
360 e_storage_path_is_relative (const char *path)
361 {
362         g_return_val_if_fail (path != NULL, FALSE);
363
364         return *path != '/';
365 }
366
367 GList *
368 e_storage_get_subfolder_paths (EStorage *storage,
369                                const char *path)
370 {
371         g_return_val_if_fail (E_IS_STORAGE (storage), NULL);
372         g_return_val_if_fail (path != NULL, NULL);
373         g_return_val_if_fail (g_path_is_absolute (path), NULL);
374
375         return (* E_STORAGE_GET_CLASS (storage)->get_subfolder_paths) (storage, path);
376 }
377
378 EFolder *
379 e_storage_get_folder (EStorage *storage,
380                       const char *path)
381 {
382         g_return_val_if_fail (E_IS_STORAGE (storage), NULL);
383         g_return_val_if_fail (path != NULL, NULL);
384         g_return_val_if_fail (e_storage_path_is_absolute (path), NULL);
385
386         return (* E_STORAGE_GET_CLASS (storage)->get_folder) (storage, path);
387 }
388
389 const char *
390 e_storage_get_name (EStorage *storage)
391 {
392         g_return_val_if_fail (E_IS_STORAGE (storage), NULL);
393
394         return (* E_STORAGE_GET_CLASS (storage)->get_name) (storage);
395 }
396
397 /* Folder operations.  */
398
399 void
400 e_storage_async_create_folder (EStorage *storage,
401                                const char *path,
402                                const char *type,
403                                EStorageResultCallback callback,
404                                void *data)
405 {
406         g_return_if_fail (E_IS_STORAGE (storage));
407         g_return_if_fail (path != NULL);
408         g_return_if_fail (g_path_is_absolute (path));
409         g_return_if_fail (type != NULL);
410         g_return_if_fail (callback != NULL);
411
412         (* E_STORAGE_GET_CLASS (storage)->async_create_folder) (storage, path, type, callback, data);
413 }
414
415 void
416 e_storage_async_remove_folder (EStorage              *storage,
417                                const char            *path,
418                                EStorageResultCallback callback,
419                                void                  *data)
420 {
421         g_return_if_fail (E_IS_STORAGE (storage));
422         g_return_if_fail (path != NULL);
423         g_return_if_fail (g_path_is_absolute (path));
424         g_return_if_fail (callback != NULL);
425
426         (* E_STORAGE_GET_CLASS (storage)->async_remove_folder) (storage, path, callback, data);
427 }
428
429 void
430 e_storage_async_xfer_folder (EStorage *storage,
431                              const char *source_path,
432                              const char *destination_path,
433                              const gboolean remove_source,
434                              EStorageResultCallback callback,
435                              void *data)
436 {
437         g_return_if_fail (E_IS_STORAGE (storage));
438         g_return_if_fail (source_path != NULL);
439         g_return_if_fail (g_path_is_absolute (source_path));
440         g_return_if_fail (destination_path != NULL);
441         g_return_if_fail (g_path_is_absolute (destination_path));
442
443         if (strcmp (source_path, destination_path) == 0) {
444                 (* callback) (storage, E_STORAGE_OK, data);
445                 return;
446         }
447
448         if (remove_source) {
449                 int destination_len;
450                 int source_len;
451
452                 source_len = strlen (source_path);
453                 destination_len = strlen (destination_path);
454
455                 if (source_len < destination_len
456                     && destination_path[source_len] == '/'
457                     && strncmp (destination_path, source_path, source_len) == 0) {
458                         (* callback) (storage, E_STORAGE_CANTMOVETODESCENDANT, data);
459                         return;
460                 }
461         }
462
463         (* E_STORAGE_GET_CLASS (storage)->async_xfer_folder) (storage, source_path, destination_path, remove_source, callback, data);
464 }
465
466 void
467 e_storage_async_open_folder (EStorage *storage,
468                              const char *path,
469                              EStorageDiscoveryCallback callback,
470                              void *data)
471 {
472         EStoragePrivate *priv;
473         EFolder *folder;
474
475         g_return_if_fail (E_IS_STORAGE (storage));
476         g_return_if_fail (path != NULL);
477         g_return_if_fail (g_path_is_absolute (path));
478
479         priv = storage->priv;
480
481         folder = e_folder_tree_get_folder (priv->folder_tree, path);
482         if (folder == NULL) {
483                 (* callback) (storage, E_STORAGE_NOTFOUND, path, data);
484                 return;
485         }
486
487         if (! e_folder_get_has_subfolders (folder)) {
488                 (* callback) (storage, E_STORAGE_OK, path, data);
489                 return;
490         }
491
492         (* E_STORAGE_GET_CLASS (storage)->async_open_folder) (storage, path, callback, data);
493 }
494
495 gboolean
496 e_storage_will_accept_folder (EStorage *storage,
497                               EFolder *new_parent, EFolder *source)
498 {
499         g_return_val_if_fail (E_IS_STORAGE (storage), FALSE);
500         g_return_val_if_fail (E_IS_FOLDER (new_parent), FALSE);
501         g_return_val_if_fail (E_IS_FOLDER (source), FALSE);
502
503         return (* E_STORAGE_GET_CLASS (storage)->will_accept_folder) (storage, new_parent, source);
504 }
505
506 /* Shared folders.  */
507
508 void
509 e_storage_async_discover_shared_folder (EStorage *storage,
510                                         const char *owner,
511                                         const char *folder_name,
512                                         EStorageDiscoveryCallback callback,
513                                         void *data)
514 {
515         g_return_if_fail (E_IS_STORAGE (storage));
516         g_return_if_fail (owner != NULL);
517         g_return_if_fail (folder_name != NULL);
518
519         (* E_STORAGE_GET_CLASS (storage)->async_discover_shared_folder) (storage, owner, folder_name, callback, data);
520 }
521
522 void
523 e_storage_cancel_discover_shared_folder (EStorage *storage,
524                                          const char *owner,
525                                          const char *folder_name)
526 {
527         g_return_if_fail (E_IS_STORAGE (storage));
528         g_return_if_fail (owner != NULL);
529         g_return_if_fail (folder_name != NULL);
530         g_return_if_fail (E_STORAGE_GET_CLASS (storage)->cancel_discover_shared_folder != NULL);
531
532         (* E_STORAGE_GET_CLASS (storage)->cancel_discover_shared_folder) (storage, owner, folder_name);
533 }
534
535 void
536 e_storage_async_remove_shared_folder (EStorage *storage,
537                                       const char *path,
538                                       EStorageResultCallback callback,
539                                       void *data)
540 {
541         g_return_if_fail (E_IS_STORAGE (storage));
542         g_return_if_fail (path != NULL);
543         g_return_if_fail (g_path_is_absolute (path));
544
545         (* E_STORAGE_GET_CLASS (storage)->async_remove_shared_folder) (storage, path, callback, data);
546 }
547
548 const char *
549 e_storage_result_to_string (EStorageResult result)
550 {
551         switch (result) {
552         case E_STORAGE_OK:
553                 return _("No error");
554         case E_STORAGE_GENERICERROR:
555                 return _("Generic error");
556         case E_STORAGE_EXISTS:
557                 return _("A folder with the same name already exists");
558         case E_STORAGE_INVALIDTYPE:
559                 return _("The specified folder type is not valid");
560         case E_STORAGE_IOERROR:
561                 return _("I/O error");
562         case E_STORAGE_NOSPACE:
563                 return _("Not enough space to create the folder");
564         case E_STORAGE_NOTEMPTY:
565                 return _("The folder is not empty");
566         case E_STORAGE_NOTFOUND:
567                 return _("The specified folder was not found");
568         case E_STORAGE_NOTIMPLEMENTED:
569                 return _("Function not implemented in this storage");
570         case E_STORAGE_PERMISSIONDENIED:
571                 return _("Permission denied");
572         case E_STORAGE_UNSUPPORTEDOPERATION:
573                 return _("Operation not supported");
574         case E_STORAGE_UNSUPPORTEDTYPE:
575                 return _("The specified type is not supported in this storage");
576         case E_STORAGE_CANTCHANGESTOCKFOLDER:
577                 return _("The specified folder cannot be modified or removed");
578         case E_STORAGE_CANTMOVETODESCENDANT:
579                 return _("Cannot make a folder a child of one of its descendants");
580         case E_STORAGE_INVALIDNAME:
581                 return _("Cannot create a folder with that name");
582         case E_STORAGE_NOTONLINE:
583                 return _("This operation cannot be performed in off-line mode");
584         default:
585                 return _("Unknown error");
586         }
587 }
588
589 /* Public utility functions.  */
590
591 typedef struct {
592         const char *physical_uri;
593         char *retval;
594 } GetPathForPhysicalUriForeachData;
595
596 static void
597 get_path_for_physical_uri_foreach (EFolderTree *folder_tree,
598                                    const char *path,
599                                    void *path_data,
600                                    void *user_data)
601 {
602         GetPathForPhysicalUriForeachData *foreach_data;
603         const char *physical_uri;
604         EFolder *e_folder;
605
606         foreach_data = (GetPathForPhysicalUriForeachData *) user_data;
607         if (foreach_data->retval != NULL)
608                 return;
609
610         e_folder = (EFolder *) path_data;
611         if (e_folder == NULL)
612                 return;
613
614         physical_uri = e_folder_get_physical_uri (e_folder);
615         if (physical_uri == NULL)
616                 return;
617
618         if (strcmp (foreach_data->physical_uri, physical_uri) == 0)
619                 foreach_data->retval = g_strdup (path);
620 }
621
622 /**
623  * e_storage_get_path_for_physical_uri:
624  * @storage: A storage
625  * @physical_uri: A physical URI
626  * 
627  * Look for the folder having the specified @physical_uri.
628  * 
629  * Return value: The path of the folder having the specified @physical_uri in
630  * @storage.  If such a folder does not exist, just return NULL.  The return
631  * value must be freed by the caller.
632  **/
633 char *
634 e_storage_get_path_for_physical_uri (EStorage *storage,
635                                      const char *physical_uri)
636 {
637         GetPathForPhysicalUriForeachData foreach_data;
638         EStoragePrivate *priv;
639
640         g_return_val_if_fail (E_IS_STORAGE (storage), NULL);
641         g_return_val_if_fail (physical_uri != NULL, NULL);
642
643         priv = storage->priv;
644
645         foreach_data.physical_uri = physical_uri;
646         foreach_data.retval       = NULL;
647
648         e_folder_tree_foreach (priv->folder_tree, get_path_for_physical_uri_foreach, &foreach_data);
649
650         return foreach_data.retval;
651 }
652
653 /* Protected functions.  */
654
655 /* These functions are used by subclasses to add and remove folders from the
656    state stored in the storage object.  */
657
658 static void
659 remove_subfolders_except (EStorage *storage, const char *path, const char *except)
660 {
661         EStoragePrivate *priv;
662         GList *subfolders, *f;
663         const char *folder_path;
664
665         priv = storage->priv;
666
667         subfolders = e_folder_tree_get_subfolders (priv->folder_tree, path);
668         for (f = subfolders; f; f = f->next) {
669                 folder_path = f->data;
670                 if (!except || strcmp (folder_path, except) != 0)
671                         e_storage_removed_folder (storage, folder_path);
672         }
673         for (f = subfolders; f != NULL; f = f->next)
674                 g_free (f->data);
675
676         g_list_free (subfolders);
677 }
678
679 gboolean
680 e_storage_new_folder (EStorage *storage,
681                       const char *path,
682                       EFolder *e_folder)
683 {
684         EStoragePrivate *priv;
685         char *parent_path, *p;
686         EFolder *parent;
687
688         g_return_val_if_fail (E_IS_STORAGE (storage), FALSE);
689         g_return_val_if_fail (path != NULL, FALSE);
690         g_return_val_if_fail (g_path_is_absolute (path), FALSE);
691         g_return_val_if_fail (E_IS_FOLDER (e_folder), FALSE);
692
693         priv = storage->priv;
694
695         if (! e_folder_tree_add (priv->folder_tree, path, e_folder))
696                 return FALSE;
697
698         /* If this is the child of a folder that has a pseudo child,
699          * remove the pseudo child now.
700          */
701         p = strrchr (path, '/');
702         if (p && p != path)
703                 parent_path = g_strndup (path, p - path);
704         else
705                 parent_path = g_strdup ("/");
706         parent = e_folder_tree_get_folder (priv->folder_tree, parent_path);
707         if (parent && e_folder_get_has_subfolders (parent)) {
708                 remove_subfolders_except (storage, parent_path, path);
709                 e_folder_set_has_subfolders (parent, FALSE);
710         }
711         g_free (parent_path);
712
713         g_signal_connect_object (e_folder, "changed", G_CALLBACK (folder_changed_cb), storage, 0);
714
715         g_signal_emit (storage, signals[NEW_FOLDER], 0, path);
716
717         folder_changed_cb (e_folder, storage);
718
719         return TRUE;
720 }
721
722 /* This really should be called e_storage_set_has_subfolders, but then
723  * it would look like it was an EStorageSet function. (Fact o' the
724  * day: The word "set" has more distinct meanings than any other word
725  * in the English language.) Anyway, we now return you to your
726  * regularly scheduled source code, already in progress.
727  */
728 gboolean
729 e_storage_declare_has_subfolders (EStorage *storage,
730                                   const char *path,
731                                   const char *message)
732 {
733         EStoragePrivate *priv;
734         EFolder *parent, *pseudofolder;
735         char *pseudofolder_path;
736         gboolean ok;
737
738         g_return_val_if_fail (E_IS_STORAGE (storage), FALSE);
739         g_return_val_if_fail (path != NULL, FALSE);
740         g_return_val_if_fail (g_path_is_absolute (path), FALSE);
741         g_return_val_if_fail (message != NULL, FALSE);
742
743         priv = storage->priv;
744
745         parent = e_folder_tree_get_folder (priv->folder_tree, path);
746         if (parent == NULL)
747                 return FALSE;
748         if (e_folder_get_has_subfolders (parent))
749                 return TRUE;
750
751         remove_subfolders_except (storage, path, NULL);
752
753         pseudofolder = e_folder_new (message, "working", "");
754         if (strcmp (path, "/") == 0)
755                 pseudofolder_path = g_strdup_printf ("/%s", message);
756         else
757                 pseudofolder_path = g_strdup_printf ("%s/%s", path, message);
758         e_folder_set_physical_uri (pseudofolder, pseudofolder_path);
759
760         ok = e_storage_new_folder (storage, pseudofolder_path, pseudofolder);
761         g_free (pseudofolder_path);
762         if (!ok) {
763                 g_object_unref (pseudofolder);
764                 return FALSE;
765         }
766
767         e_folder_set_has_subfolders (parent, TRUE);
768         return TRUE;
769 }
770
771 gboolean
772 e_storage_get_has_subfolders (EStorage *storage,
773                               const char *path)
774 {
775         EStoragePrivate *priv;
776         EFolder *folder;
777
778         g_return_val_if_fail (E_IS_STORAGE (storage), FALSE);
779         g_return_val_if_fail (path != NULL, FALSE);
780         g_return_val_if_fail (g_path_is_absolute (path), FALSE);
781
782         priv = storage->priv;
783
784         folder = e_folder_tree_get_folder (priv->folder_tree, path);
785
786         return folder && e_folder_get_has_subfolders (folder);
787 }
788
789 gboolean
790 e_storage_removed_folder (EStorage *storage,
791                           const char *path)
792 {
793         EStoragePrivate *priv;
794         EFolder *folder;
795         const char *p;
796
797         g_return_val_if_fail (E_IS_STORAGE (storage), FALSE);
798         g_return_val_if_fail (path != NULL, FALSE);
799         g_return_val_if_fail (g_path_is_absolute (path), FALSE);
800
801         priv = storage->priv;
802
803         folder = e_folder_tree_get_folder (priv->folder_tree, path);
804         if (folder == NULL)
805                 return FALSE;
806
807         p = strrchr (path, '/');
808         if (p != NULL && p != path) {
809                 EFolder *parent_folder;
810                 char *parent_path;
811
812                 parent_path = g_strndup (path, p - path);
813                 parent_folder = e_folder_tree_get_folder (priv->folder_tree, parent_path);
814
815                 if (e_folder_get_highlighted (folder))
816                         e_folder_set_child_highlight (parent_folder, FALSE);
817
818                 g_free (parent_path);
819         }
820
821         g_signal_emit (storage, signals[REMOVED_FOLDER], 0, path);
822
823         e_folder_tree_remove (priv->folder_tree, path);
824
825         return TRUE;
826 }
827
828 G_DEFINE_TYPE (EStorage, e_storage, G_TYPE_OBJECT)