source: trunk/third/evolution/mail/component-factory.c @ 16865

Revision 16865, 38.1 KB checked in by ghudson, 23 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r16864, which included commits to RCS files with non-trunk default branches.
Line 
1/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2/* component-factory.c
3 *
4 * Authors: Ettore Perazzoli <ettore@ximian.com>
5 *
6 * Copyright (C) 2000  Ximian, Inc.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of version 2 of the GNU General Public
10 * License as published by the Free Software Foundation.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public
18 * License along with this program; if not, write to the
19 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20 * Boston, MA 02111-1307, USA.
21 */
22
23#ifdef HAVE_CONFIG_H
24#include <config.h>
25#endif
26
27#include <bonobo/bonobo-generic-factory.h>
28#include <gal/widgets/e-gui-utils.h>
29
30#include "camel.h"
31
32#include "Evolution.h"
33#include "evolution-storage.h"
34#include "evolution-wizard.h"
35
36#include "folder-browser-factory.h"
37#include "evolution-shell-component.h"
38#include "evolution-shell-component-dnd.h"
39#include "folder-browser.h"
40#include "mail.h"               /* YUCK FIXME */
41#include "mail-config.h"
42#include "mail-tools.h"
43#include "mail-ops.h"
44#include "mail-offline-handler.h"
45#include "mail-local.h"
46#include "mail-session.h"
47#include "mail-mt.h"
48#include "mail-importer.h"
49#include "mail-folder-cache.h"
50
51#include "component-factory.h"
52
53#include "mail-send-recv.h"
54
55#include "mail-vfolder.h"
56#include "mail-autofilter.h"
57
58#define d(x)
59
60char *default_drafts_folder_uri;
61CamelFolder *drafts_folder = NULL;
62char *default_sent_folder_uri;
63CamelFolder *sent_folder = NULL;
64char *default_outbox_folder_uri;
65CamelFolder *outbox_folder = NULL;
66char *evolution_dir;
67
68EvolutionShellClient *global_shell_client = NULL;
69
70RuleContext *search_context = NULL;
71
72static MailAsyncEvent *async_event = NULL;
73
74static GHashTable *storages_hash;
75static EvolutionShellComponent *shell_component;
76
77enum {
78        ACCEPTED_DND_TYPE_MESSAGE_RFC822,
79        ACCEPTED_DND_TYPE_X_EVOLUTION_MESSAGE,
80        ACCEPTED_DND_TYPE_TEXT_URI_LIST,
81};
82
83static char *accepted_dnd_types[] = {
84        "message/rfc822",
85        "x-evolution-message",    /* ...from an evolution message list... */
86        "text/uri-list",          /* ...from nautilus... */
87        NULL
88};
89
90enum {
91        EXPORTED_DND_TYPE_TEXT_URI_LIST,
92};
93
94static char *exported_dnd_types[] = {
95        "text/uri-list",          /* we have to export to nautilus as text/uri-list */
96        NULL
97};
98
99static const EvolutionShellComponentFolderType folder_types[] = {
100        { "mail", "evolution-inbox.png", N_("Mail"), N_("Folder containing mail"), TRUE, accepted_dnd_types, exported_dnd_types },
101        { "mailstorage", "evolution-inbox.png", "Mailstorage", N_("Mail storage folder (internal)"), FALSE, NULL, NULL },
102        { "vtrash", "evolution-trash.png", N_("Virtual Trash"), N_("Virtual Trash folder"), FALSE, accepted_dnd_types, exported_dnd_types },
103        { NULL, NULL, NULL, NULL, FALSE, NULL, NULL }
104};
105
106static const char *schema_types[] = {
107        "mailto",
108        NULL
109};
110
111/* EvolutionShellComponent methods and signals.  */
112
113static void
114storage_activate (BonoboControl *control, gboolean activate,
115                  const char *physical_uri)
116{
117        CamelService *store;
118        EvolutionStorage *storage;
119        CamelException ex;
120
121        if (!activate)
122                return;
123
124        camel_exception_init (&ex);
125        store = camel_session_get_service (session, physical_uri,
126                                           CAMEL_PROVIDER_STORE, &ex);
127        if (!store) {
128                e_notice (NULL, GNOME_MESSAGE_BOX_ERROR,
129                          _("Cannot connect to store: %s"),
130                          camel_exception_get_description (&ex));
131                camel_exception_clear (&ex);
132                return;
133        }
134        camel_exception_clear (&ex);
135
136        storage = g_hash_table_lookup (storages_hash, store);
137        if (storage && !gtk_object_get_data (GTK_OBJECT (storage), "connected"))
138                mail_note_store (CAMEL_STORE(store), storage, CORBA_OBJECT_NIL, NULL, NULL);
139        camel_object_unref (CAMEL_OBJECT (store));
140}       
141
142static BonoboControl *
143create_noselect_control (void)
144{
145        GtkWidget *label;
146
147        label = gtk_label_new (_("This folder cannot contain messages."));
148        gtk_widget_show (label);
149        return bonobo_control_new (label);
150}
151
152static EvolutionShellComponentResult
153create_view (EvolutionShellComponent *shell_component,
154             const char *physical_uri,
155             const char *folder_type,
156             BonoboControl **control_return,
157             void *closure)
158{
159        EvolutionShellClient *shell_client;
160        GNOME_Evolution_Shell corba_shell;
161        BonoboControl *control;
162       
163        shell_client = evolution_shell_component_get_owner (shell_component);
164        corba_shell = bonobo_object_corba_objref (BONOBO_OBJECT (shell_client));
165       
166        if (!g_strcasecmp (folder_type, "mail")) {
167                const char *noselect;
168                CamelURL *url;
169               
170                url = camel_url_new (physical_uri, NULL);
171                noselect = url ? camel_url_get_param (url, "noselect") : NULL;
172                if (noselect && !g_strcasecmp (noselect, "yes"))
173                        control = create_noselect_control ();
174                else
175                        control = folder_browser_factory_new_control (physical_uri,
176                                                                      corba_shell);
177                camel_url_free (url);
178        } else if (!g_strcasecmp (folder_type, "mailstorage")) {
179                char *uri_dup = g_strdup (physical_uri);
180
181                control = create_noselect_control ();
182                gtk_object_set_data_full (GTK_OBJECT (control), "physical_uri",
183                                          uri_dup, g_free);
184                gtk_signal_connect (GTK_OBJECT (control), "activate",
185                                    storage_activate, uri_dup);
186        } else if (!g_strcasecmp (folder_type, "vtrash")) {
187                if (!g_strncasecmp (physical_uri, "file:", 5))
188                        control = folder_browser_factory_new_control ("vtrash:file:/", corba_shell);
189                else
190                        control = folder_browser_factory_new_control (physical_uri, corba_shell);
191        } else
192                return EVOLUTION_SHELL_COMPONENT_UNSUPPORTEDTYPE;
193       
194        if (!control)
195                return EVOLUTION_SHELL_COMPONENT_NOTFOUND;
196       
197        *control_return = control;
198        return EVOLUTION_SHELL_COMPONENT_OK;
199}
200
201static void
202create_folder_done (char *uri, CamelFolder *folder, void *data)
203{
204        GNOME_Evolution_ShellComponentListener listener = data;
205        GNOME_Evolution_ShellComponentListener_Result result;
206        CORBA_Environment ev;
207
208        if (folder) {
209                result = GNOME_Evolution_ShellComponentListener_OK;
210        } else {
211                result = GNOME_Evolution_ShellComponentListener_INVALID_URI;
212        }
213
214        CORBA_exception_init (&ev);
215        GNOME_Evolution_ShellComponentListener_notifyResult (listener, result, &ev);
216        CORBA_Object_release (listener, &ev);
217        CORBA_exception_free (&ev);
218}
219
220static void
221create_folder (EvolutionShellComponent *shell_component,
222               const char *physical_uri,
223               const char *type,
224               const GNOME_Evolution_ShellComponentListener listener,
225               void *closure)
226{
227        CORBA_Environment ev;
228
229        CORBA_exception_init (&ev);
230       
231        if (!strcmp (type, "mail")) {
232                mail_get_folder (physical_uri, CAMEL_STORE_FOLDER_CREATE, create_folder_done,
233                                 CORBA_Object_duplicate (listener, &ev), mail_thread_new);
234        } else {
235                GNOME_Evolution_ShellComponentListener_notifyResult (
236                        listener, GNOME_Evolution_ShellComponentListener_UNSUPPORTED_TYPE, &ev);
237        }
238
239        CORBA_exception_free (&ev);
240}
241
242static void
243remove_folder_done (char *uri, gboolean removed, void *data)
244{
245        GNOME_Evolution_ShellComponentListener listener = data;
246        GNOME_Evolution_ShellComponentListener_Result result;
247        CORBA_Environment ev;
248       
249        if (removed)
250                result = GNOME_Evolution_ShellComponentListener_OK;
251        else
252                result = GNOME_Evolution_ShellComponentListener_INVALID_URI;
253       
254        CORBA_exception_init (&ev);
255        GNOME_Evolution_ShellComponentListener_notifyResult (listener, result, &ev);
256        CORBA_Object_release (listener, &ev);
257        CORBA_exception_free (&ev);
258}
259
260static void
261remove_folder (EvolutionShellComponent *shell_component,
262               const char *physical_uri,
263               const char *type,
264               const GNOME_Evolution_ShellComponentListener listener,
265               void *closure)
266{
267        CORBA_Environment ev;
268       
269        CORBA_exception_init (&ev);
270       
271        if (strcmp (type, "mail") != 0) {
272                GNOME_Evolution_ShellComponentListener_notifyResult (listener,
273                                                                     GNOME_Evolution_ShellComponentListener_UNSUPPORTED_TYPE, &ev);
274                CORBA_exception_free (&ev);
275                return;
276        }
277       
278        mail_remove_folder (physical_uri, remove_folder_done, CORBA_Object_duplicate (listener, &ev));
279        CORBA_exception_free (&ev);
280}
281
282typedef struct _xfer_folder_data {
283        GNOME_Evolution_ShellComponentListener listener;
284        gboolean remove_source;
285        char *source_uri;
286} xfer_folder_data;
287
288static void
289xfer_folder_done (gboolean ok, void *data)
290{
291        xfer_folder_data *xfd = (xfer_folder_data *)data;
292        GNOME_Evolution_ShellComponentListener listener = xfd->listener;
293        GNOME_Evolution_ShellComponentListener_Result result;
294        CORBA_Environment ev;
295
296        if (xfd->remove_source && ok) {
297                mail_remove_folder (xfd->source_uri, remove_folder_done, xfd->listener);
298        } else {
299                if (ok)
300                        result = GNOME_Evolution_ShellComponentListener_OK;
301                else
302                        result = GNOME_Evolution_ShellComponentListener_INVALID_URI;
303       
304                CORBA_exception_init (&ev);
305                GNOME_Evolution_ShellComponentListener_notifyResult (listener, result, &ev);
306                CORBA_Object_release (listener, &ev);
307                CORBA_exception_free (&ev);
308        }
309
310        g_free (xfd->source_uri);
311        g_free (xfd);
312}
313
314static void
315xfer_folder (EvolutionShellComponent *shell_component,
316             const char *source_physical_uri,
317             const char *destination_physical_uri,
318             const char *type,
319             gboolean remove_source,
320             const GNOME_Evolution_ShellComponentListener listener,
321             void *closure)
322{
323        CORBA_Environment ev;
324        CamelFolder *source;
325        CamelException ex;
326        GPtrArray *uids;
327        CamelURL *src, *dst;
328
329        d(printf("Renaming folder '%s' to dest '%s' type '%s'\n", source_physical_uri, destination_physical_uri, type));
330
331        CORBA_exception_init (&ev);
332
333        if (strcmp (type, "mail") != 0) {
334                GNOME_Evolution_ShellComponentListener_notifyResult (listener,
335                                                                     GNOME_Evolution_ShellComponentListener_UNSUPPORTED_TYPE, &ev);
336                return;
337        }
338
339        src = camel_url_new(source_physical_uri, NULL);
340        if (src == NULL) {
341                GNOME_Evolution_ShellComponentListener_notifyResult (listener, GNOME_Evolution_ShellComponentListener_INVALID_URI, &ev);
342                return;
343        }
344
345        dst = camel_url_new(destination_physical_uri, NULL);
346        if (dst == NULL) {
347                camel_url_free(src);
348                GNOME_Evolution_ShellComponentListener_notifyResult (listener, GNOME_Evolution_ShellComponentListener_INVALID_URI, &ev);
349                return;
350        }
351
352        if (camel_url_get_param(dst, "noselect") != NULL) {
353                camel_url_free(src);
354                camel_url_free(dst);
355                GNOME_Evolution_ShellComponentListener_notifyResult (listener,
356                                                                     GNOME_Evolution_ShellComponentListener_UNSUPPORTED_OPERATION, &ev);
357                return;
358        }
359       
360        camel_exception_init (&ex);
361
362        /* If we are really doing a rename, implement it as a rename */
363        if (remove && strcmp(src->protocol, dst->protocol) == 0) {
364                char *sname, *dname;
365                CamelStore *store;
366
367                if (src->fragment)
368                        sname = src->fragment;
369                else {
370                        if (src->path && *src->path)
371                                sname = src->path+1;
372                        else
373                                sname = "";
374                }
375
376                if (dst->fragment)
377                        dname = dst->fragment;
378                else {
379                        if (dst->path && *dst->path)
380                                dname = dst->path+1;
381                        else
382                                dname = "";
383                }
384
385                store = camel_session_get_store(session, source_physical_uri, &ex);
386                if (store != NULL)
387                        camel_store_rename_folder(store, sname, dname, &ex);
388
389                if (camel_exception_is_set(&ex))
390                        GNOME_Evolution_ShellComponentListener_notifyResult (listener, GNOME_Evolution_ShellComponentListener_INVALID_URI, &ev);
391                else {
392                        /* Since the shell doesn't play nice with local folders, we have to do this manually */
393                        mail_vfolder_rename_uri(store, source_physical_uri, destination_physical_uri);
394                        mail_filter_rename_uri(store, source_physical_uri, destination_physical_uri);
395                        GNOME_Evolution_ShellComponentListener_notifyResult (listener, GNOME_Evolution_ShellComponentListener_OK, &ev);
396                }
397                camel_object_unref((CamelObject *)store);
398        } else {
399                source = mail_tool_uri_to_folder (source_physical_uri, 0, &ex);
400       
401                if (source) {
402                        xfer_folder_data *xfd;
403                       
404                        xfd = g_new0 (xfer_folder_data, 1);
405                        xfd->remove_source = remove_source;
406                        xfd->source_uri = g_strdup (source_physical_uri);
407                        xfd->listener = CORBA_Object_duplicate (listener, &ev);
408               
409                        uids = camel_folder_get_uids (source);
410                        mail_transfer_messages (source, uids, remove_source, destination_physical_uri, CAMEL_STORE_FOLDER_CREATE, xfer_folder_done, xfd);
411                        camel_object_unref (CAMEL_OBJECT (source));
412                } else
413                        GNOME_Evolution_ShellComponentListener_notifyResult (listener, GNOME_Evolution_ShellComponentListener_INVALID_URI, &ev);
414        }
415
416        CORBA_exception_free (&ev);
417        camel_exception_clear (&ex);
418
419        camel_url_free(src);
420        camel_url_free(dst);
421}
422
423#if 0
424static void
425populate_folder_context_menu (EvolutionShellComponent *shell_component,
426                              BonoboUIComponent *uic,
427                              const char *physical_uri,
428                              const char *type,
429                              void *closure)
430{
431#ifdef TRANSLATORS_ONLY
432        static char popup_xml_i18n[] = {N_("Properties..."), N_("Change this folder's properties")};
433#endif
434        static char popup_xml[] =
435                "<menuitem name=\"ChangeFolderProperties\" verb=\"ChangeFolderProperties\""
436                "          _label=\"Properties...\" _tip=\"Change this folder's properties\"/>";
437       
438        if (strcmp (type, "mail") != 0)
439                return;
440       
441        bonobo_ui_component_set_translate (uic, EVOLUTION_SHELL_COMPONENT_POPUP_PLACEHOLDER,
442                                           popup_xml, NULL);
443}
444#endif
445
446static char *
447get_dnd_selection (EvolutionShellComponent *shell_component,
448                   const char *physical_uri,
449                   int type,
450                   int *format_return,
451                   const char **selection_return,
452                   int *selection_length_return,
453                   void *closure)
454{
455        g_print ("should get dnd selection for %s\n", physical_uri);
456       
457        return NULL;
458}
459
460/* Destination side DnD */
461static CORBA_boolean
462destination_folder_handle_motion (EvolutionShellComponentDndDestinationFolder *folder,
463                                  const char *physical_uri,
464                                  const char *folder_type,
465                                  const GNOME_Evolution_ShellComponentDnd_DestinationFolder_Context *destination_context,
466                                  GNOME_Evolution_ShellComponentDnd_Action *suggested_action_return,
467                                  gpointer user_data)
468{
469        const char *noselect;
470        CamelURL *url;
471       
472        url = camel_url_new (physical_uri, NULL);
473        noselect = camel_url_get_param (url, "noselect");
474       
475        if (noselect && !g_strcasecmp (noselect, "yes"))
476                /* uh, no way to say "illegal" */
477                *suggested_action_return = GNOME_Evolution_ShellComponentDnd_ACTION_DEFAULT;
478        else
479                *suggested_action_return = GNOME_Evolution_ShellComponentDnd_ACTION_MOVE;
480       
481        camel_url_free (url);
482       
483        return TRUE;
484}
485
486static void
487message_rfc822_dnd (CamelFolder *dest, CamelStream *stream, CamelException *ex)
488{
489        CamelMimeParser *mp;
490       
491        mp = camel_mime_parser_new ();
492        camel_mime_parser_scan_from (mp, TRUE);
493        camel_mime_parser_init_with_stream (mp, stream);
494       
495        while (camel_mime_parser_step (mp, 0, 0) == HSCAN_FROM) {
496                CamelMessageInfo *info;
497                CamelMimeMessage *msg;
498               
499                msg = camel_mime_message_new ();
500                if (camel_mime_part_construct_from_parser (CAMEL_MIME_PART (msg), mp) == -1) {
501                        camel_object_unref (CAMEL_OBJECT (msg));
502                        break;
503                }
504               
505                /* append the message to the folder... */
506                info = g_new0 (CamelMessageInfo, 1);
507                camel_folder_append_message (dest, msg, info, ex);
508                camel_object_unref (CAMEL_OBJECT (msg));
509               
510                if (camel_exception_is_set (ex))
511                        break;
512               
513                /* skip over the FROM_END state */
514                camel_mime_parser_step (mp, 0, 0);
515        }
516       
517        camel_object_unref (CAMEL_OBJECT (mp));
518}
519
520static CORBA_boolean
521destination_folder_handle_drop (EvolutionShellComponentDndDestinationFolder *dest_folder,
522                                const char *physical_uri,
523                                const char *folder_type,
524                                const GNOME_Evolution_ShellComponentDnd_DestinationFolder_Context *destination_context,
525                                const GNOME_Evolution_ShellComponentDnd_Action action,
526                                const GNOME_Evolution_ShellComponentDnd_Data *data,
527                                gpointer user_data)
528{
529        char *tmp, *url, **urls, *in, *inptr, *inend;
530        gboolean retval = FALSE;
531        const char *noselect;
532        CamelFolder *folder;
533        CamelStream *stream;
534        CamelException ex;
535        GPtrArray *uids;
536        CamelURL *uri;
537        int i, type, fd;
538       
539        if (action == GNOME_Evolution_ShellComponentDnd_ACTION_LINK)
540                return FALSE; /* we can't create links */
541       
542        /* this means the drag was cancelled */
543        if (!data->bytes._buffer || data->bytes._length == -1)
544                return FALSE;
545       
546        uri = camel_url_new (physical_uri, NULL);
547        noselect = uri ? camel_url_get_param (uri, "noselect") : NULL;
548        if (noselect && !g_strcasecmp (noselect, "yes")) {
549                camel_url_free (uri);
550                return FALSE;
551        }
552        camel_url_free (uri);
553       
554        g_print ("in destination_folder_handle_drop (%s)\n", physical_uri);
555       
556        for (type = 0; accepted_dnd_types[type]; type++)
557                if (!strcmp (destination_context->dndType, accepted_dnd_types[type]))
558                        break;
559       
560        camel_exception_init (&ex);
561       
562        /* if this is a local vtrash folder, then it's uri is vtrash:file:/ */
563        if (!strcmp (folder_type, "vtrash") && !strncmp (physical_uri, "file:", 5))
564                physical_uri = "vtrash:file:/";
565       
566        switch (type) {
567        case ACCEPTED_DND_TYPE_TEXT_URI_LIST:
568                folder = mail_tool_uri_to_folder (physical_uri, 0, NULL);
569                if (!folder)
570                        return FALSE;
571               
572                tmp = g_strndup (data->bytes._buffer, data->bytes._length);
573                urls = g_strsplit (tmp, "\n", 0);
574                g_free (tmp);
575               
576                retval = TRUE;
577                for (i = 0; urls[i] != NULL && retval; i++) {
578                        /* get the path component */
579                        url = g_strstrip (urls[i]);
580                       
581                        uri = camel_url_new (url, NULL);
582                        g_free (url);
583                        url = uri->path;
584                        uri->path = NULL;
585                        camel_url_free (uri);
586                       
587                        fd = open (url, O_RDONLY);
588                        if (fd == -1) {
589                                g_free (url);
590                                /* FIXME: okay, so what do we do in this case? */
591                                continue;
592                        }
593                       
594                        stream = camel_stream_fs_new_with_fd (fd);
595                        message_rfc822_dnd (folder, stream, &ex);
596                        camel_object_unref (CAMEL_OBJECT (stream));
597                        camel_object_unref (CAMEL_OBJECT (folder));
598                       
599                        retval = !camel_exception_is_set (&ex);
600                       
601                        if (action == GNOME_Evolution_ShellComponentDnd_ACTION_MOVE && retval)
602                                unlink (url);
603                       
604                        g_free (url);
605                }
606               
607                g_free (urls);
608                break;
609        case ACCEPTED_DND_TYPE_MESSAGE_RFC822:
610                folder = mail_tool_uri_to_folder (physical_uri, 0, &ex);
611                if (!folder) {
612                        camel_exception_clear (&ex);
613                        return FALSE;
614                }
615               
616                /* write the message(s) out to a CamelStream so we can use it */
617                stream = camel_stream_mem_new ();
618                camel_stream_write (stream, data->bytes._buffer, data->bytes._length);
619                camel_stream_reset (stream);
620               
621                message_rfc822_dnd (folder, stream, &ex);
622                camel_object_unref (CAMEL_OBJECT (stream));
623                camel_object_unref (CAMEL_OBJECT (folder));
624                break;
625        case ACCEPTED_DND_TYPE_X_EVOLUTION_MESSAGE:
626                folder = mail_tools_x_evolution_message_parse (data->bytes._buffer,
627                                                               data->bytes._length,
628                                                               &uids);
629               
630                if (!folder)
631                        return FALSE;
632               
633                mail_transfer_messages (folder, uids,
634                                        action == GNOME_Evolution_ShellComponentDnd_ACTION_MOVE,
635                                        physical_uri, 0, NULL, NULL);
636               
637                camel_object_unref (CAMEL_OBJECT (folder));
638                break;
639        default:
640                break;
641        }
642       
643        camel_exception_clear (&ex);
644       
645        return retval;
646}
647
648
649static struct {
650        char *name, **uri;
651        CamelFolder **folder;
652} standard_folders[] = {
653        { "Drafts", &default_drafts_folder_uri, &drafts_folder },
654        { "Outbox", &default_outbox_folder_uri, &outbox_folder },
655        { "Sent", &default_sent_folder_uri, &sent_folder },
656};
657
658static void
659unref_standard_folders (void)
660{
661        int i;
662       
663        for (i = 0; i < sizeof (standard_folders) / sizeof (standard_folders[0]); i++) {
664                if (standard_folders[i].folder) {
665                        CamelFolder *folder = *standard_folders[i].folder;
666                       
667                        *standard_folders[i].folder = NULL;
668                       
669                        if (CAMEL_OBJECT (folder)->ref_count == 1)
670                                d(printf ("About to finalise folder %s\n", folder->full_name));
671                        else
672                                d(printf ("Folder %s still has %d extra ref%s on it\n", folder->full_name,
673                                          CAMEL_OBJECT (folder)->ref_count - 1,
674                                          CAMEL_OBJECT (folder)->ref_count - 1 == 1 ? "" : "s"));
675                       
676                        camel_object_unref (CAMEL_OBJECT (folder));
677                }
678        }
679}
680
681static void
682got_folder (char *uri, CamelFolder *folder, void *data)
683{
684        CamelFolder **fp = data;
685       
686        if (folder) {
687                *fp = folder;
688               
689                camel_object_ref (CAMEL_OBJECT (folder));
690               
691                /* emit a changed event, this is a little hack so that the folderinfo cache
692                   will update knowing whether this is the outbox_folder or not, etc */
693                if (folder == outbox_folder) {
694                        CamelFolderChangeInfo *changes = camel_folder_change_info_new();
695                       
696                        camel_object_trigger_event((CamelObject *)folder, "folder_changed", changes);
697                        camel_folder_change_info_free(changes);
698                }
699        }
700}
701
702static void
703shell_client_destroy (GtkObject *object)
704{
705        global_shell_client = NULL;
706}
707
708static void
709warning_clicked (GtkWidget *dialog, gpointer user_data)
710{
711        gtk_widget_destroy (dialog);
712}
713
714static void
715owner_set_cb (EvolutionShellComponent *shell_component,
716              EvolutionShellClient *shell_client,
717              const char *evolution_homedir,
718              gpointer user_data)
719{
720        GNOME_Evolution_Shell corba_shell;
721        const GSList *accounts;
722#ifdef ENABLE_NNTP
723        const GSList *news;
724#endif
725        int i;
726       
727        /* FIXME: should we ref this? */
728        global_shell_client = shell_client;
729        gtk_signal_connect (GTK_OBJECT (shell_client), "destroy",
730                            shell_client_destroy, NULL);
731       
732        evolution_dir = g_strdup (evolution_homedir);
733        mail_session_init ();
734
735        async_event = mail_async_event_new();
736
737        storages_hash = g_hash_table_new (NULL, NULL);
738       
739        corba_shell = bonobo_object_corba_objref (BONOBO_OBJECT (shell_client));
740       
741        vfolder_load_storage(corba_shell);
742
743        accounts = mail_config_get_accounts ();
744        mail_load_storages (corba_shell, accounts, TRUE);
745       
746#ifdef ENABLE_NNTP
747        news = mail_config_get_news ();
748        mail_load_storages (corba_shell, news, FALSE);
749#endif
750       
751        mail_local_storage_startup (shell_client, evolution_dir);
752        mail_importer_init (shell_client);
753       
754        for (i = 0; i < sizeof (standard_folders) / sizeof (standard_folders[0]); i++) {
755                *standard_folders[i].uri = g_strdup_printf ("file://%s/local/%s", evolution_dir, standard_folders[i].name);
756                mail_msg_wait (mail_get_folder (*standard_folders[i].uri, CAMEL_STORE_FOLDER_CREATE,
757                                                got_folder, standard_folders[i].folder, mail_thread_new));
758        }
759       
760        mail_autoreceive_setup ();
761
762        {
763                /* setup the global quick-search context */
764                char *user = g_strdup_printf ("%s/searches.xml", evolution_dir);
765                char *system = g_strdup (EVOLUTION_DATADIR "/evolution/vfoldertypes.xml");
766               
767                search_context = rule_context_new ();
768                gtk_object_set_data_full (GTK_OBJECT (search_context), "user", user, g_free);
769                gtk_object_set_data_full (GTK_OBJECT (search_context), "system", system, g_free);
770               
771                rule_context_add_part_set (search_context, "partset", filter_part_get_type (),
772                                           rule_context_add_part, rule_context_next_part);
773               
774                rule_context_add_rule_set (search_context, "ruleset", filter_rule_get_type (),
775                                           rule_context_add_rule, rule_context_next_rule);
776               
777                rule_context_load (search_context, system, user);
778        }
779       
780        if (mail_config_is_corrupt ()) {
781                GtkWidget *dialog;
782               
783                dialog = gnome_warning_dialog (_("Some of your mail settings seem corrupt, "
784                                                 "please check that everything is in order."));
785                gtk_signal_connect (GTK_OBJECT (dialog), "clicked", warning_clicked, NULL);
786                gtk_window_set_modal (GTK_WINDOW (dialog), TRUE);
787                gtk_widget_show (dialog);
788        }
789}
790
791static void
792free_storage (gpointer service, gpointer storage, gpointer data)
793{
794        if (service) {
795                mail_note_store_remove((CamelStore *)service);
796                camel_service_disconnect (CAMEL_SERVICE (service), TRUE, NULL);
797                camel_object_unref (CAMEL_OBJECT (service));
798        }
799       
800        if (storage)
801                bonobo_object_unref (BONOBO_OBJECT (storage));
802}
803
804static void
805debug_cb (EvolutionShellComponent *shell_component, gpointer user_data)
806{
807        extern gboolean camel_verbose_debug;
808       
809        camel_verbose_debug = 1;
810}
811
812static void
813interactive_cb (EvolutionShellComponent *shell_component, gboolean on, gpointer user_data)
814{
815        mail_session_enable_interaction(on);
816}
817
818static void
819handle_external_uri_cb (EvolutionShellComponent *shell_component,
820                        const char *uri,
821                        void *data)
822{
823        if (strncmp (uri, "mailto:", 7) != 0) {
824                /* FIXME: Exception?  The EvolutionShellComponent object should
825                   give me a chance to do so, but currently it doesn't.  */
826                g_warning ("Invalid URI requested to mail component -- %s", uri);
827                return;
828        }
829               
830        /* FIXME: Sigh.  This shouldn't be here.  But the code is messy, so
831           I'll just put it here anyway.  */
832        send_to_url (uri);
833}
834
835static void
836user_create_new_item_cb (EvolutionShellComponent *shell_component,
837                         const char *id,
838                         const char *parent_folder_physical_uri,
839                         const char *parent_folder_type,
840                         gpointer data)
841{
842        if (!strcmp (id, "message")) {
843                send_to_url (NULL);
844                return;
845        }
846
847        g_warning ("Don't know how to create item of type \"%s\"", id);
848}
849
850static gboolean
851idle_quit (gpointer user_data)
852{
853        static int shutdown_vfolder = FALSE;
854        static int shutdown_shutdown = FALSE;
855
856        if (!shutdown_shutdown) {
857                if (mail_msg_active(-1)) {
858                        /* short sleep? */
859                        return TRUE;
860                }
861
862                if (!shutdown_vfolder) {
863                        shutdown_vfolder = TRUE;
864                        mail_vfolder_shutdown();
865                        return TRUE;
866                }
867
868                if (mail_async_event_destroy(async_event) == -1)
869                        return TRUE;
870
871                shutdown_shutdown = TRUE;
872                g_hash_table_foreach (storages_hash, free_storage, NULL);
873                g_hash_table_destroy (storages_hash);
874                storages_hash = NULL;
875        }
876       
877        if (e_list_length (folder_browser_factory_get_control_list ()))
878                return TRUE;
879
880        gtk_main_quit ();
881
882        return FALSE;
883}       
884
885static void owner_unset_cb (EvolutionShellComponent *shell_component, gpointer user_data);
886
887/* Table for signal handler setup/cleanup */
888static struct {
889        char *sig;
890        GtkSignalFunc func;
891        int hand;
892} shell_component_handlers[] = {
893        { "owner_set", owner_set_cb, },
894        { "owner_unset", owner_unset_cb, },
895        { "debug", debug_cb, },
896        { "interactive", interactive_cb },
897        { "destroy", owner_unset_cb, },
898        { "handle_external_uri", handle_external_uri_cb, },
899        { "user_create_new_item", user_create_new_item_cb }
900};
901
902static void
903owner_unset_cb (EvolutionShellComponent *shell_component, gpointer user_data)
904{
905        int i;
906       
907        for (i=0;i<sizeof(shell_component_handlers)/sizeof(shell_component_handlers[0]);i++)
908                gtk_signal_disconnect((GtkObject *)shell_component, shell_component_handlers[i].hand);
909       
910        if (mail_config_get_empty_trash_on_exit ())
911                empty_trash (NULL, NULL, NULL);
912       
913        unref_standard_folders ();
914        mail_importer_uninit ();
915       
916        global_shell_client = NULL;
917        mail_session_enable_interaction (FALSE);
918       
919        gtk_object_unref (GTK_OBJECT (search_context));
920        search_context = NULL;
921       
922        g_timeout_add(100, idle_quit, NULL);
923}
924
925static BonoboObject *
926create_component (void)
927{
928        EvolutionShellComponentDndDestinationFolder *destination_interface;
929        MailOfflineHandler *offline_handler;
930        int i;
931       
932        shell_component = evolution_shell_component_new (folder_types,
933                                                         schema_types,
934                                                         create_view,
935                                                         create_folder,
936                                                         remove_folder,
937                                                         xfer_folder,
938                                                         /*populate_folder_context_menu*/NULL,
939                                                         get_dnd_selection,
940                                                         NULL);
941       
942        destination_interface = evolution_shell_component_dnd_destination_folder_new (destination_folder_handle_motion,
943                                                                                      destination_folder_handle_drop,
944                                                                                      shell_component);
945       
946        bonobo_object_add_interface (BONOBO_OBJECT (shell_component),
947                                     BONOBO_OBJECT (destination_interface));
948       
949        evolution_mail_config_wizard_init ();
950       
951        evolution_shell_component_add_user_creatable_item (shell_component, "message", _("New Mail Message"), _("New _Mail Message"), 'm');
952
953        for (i=0;i<sizeof(shell_component_handlers)/sizeof(shell_component_handlers[0]);i++) {
954                shell_component_handlers[i].hand = gtk_signal_connect(GTK_OBJECT(shell_component),
955                                                                      shell_component_handlers[i].sig,
956                                                                      shell_component_handlers[i].func, NULL);
957        }
958       
959        offline_handler = mail_offline_handler_new ();
960        bonobo_object_add_interface (BONOBO_OBJECT (shell_component), BONOBO_OBJECT (offline_handler));
961       
962        return BONOBO_OBJECT (shell_component);
963}
964
965void
966component_factory_init (void)
967{
968        BonoboObject *shell_component;
969        int result;
970
971        shell_component = create_component ();
972        result = oaf_active_server_register (COMPONENT_ID, bonobo_object_corba_objref (shell_component));
973        if (result == OAF_REG_ERROR) {
974                e_notice (NULL, GNOME_MESSAGE_BOX_ERROR,
975                          _("Cannot initialize Evolution's mail component."));
976                exit (1);
977        } else if (result == OAF_REG_ALREADY_ACTIVE) {
978                g_warning ("evolution-mail is already running");
979                exit (1);
980        }
981
982        if (evolution_mail_config_factory_init () == FALSE) {
983                e_notice (NULL, GNOME_MESSAGE_BOX_ERROR,
984                          _("Cannot initialize Evolution's mail config component."));
985                exit (1);
986        }
987
988        if (evolution_folder_info_factory_init () == FALSE) {
989                e_notice (NULL, GNOME_MESSAGE_BOX_ERROR,
990                          _("Cannot initialize Evolution's folder info component."));
991                exit (1);
992        }
993}
994
995static void
996notify_listener (const Bonobo_Listener listener,
997                 GNOME_Evolution_Storage_Result corba_result)
998{
999        CORBA_any any;
1000        CORBA_Environment ev;
1001
1002        CORBA_exception_init (&ev);
1003
1004        any._type = TC_GNOME_Evolution_Storage_Result;
1005        any._value = &corba_result;
1006
1007        Bonobo_Listener_event (listener, "result", &any, &ev);
1008
1009        CORBA_exception_free (&ev);
1010}
1011
1012static void
1013storage_create_folder (EvolutionStorage *storage,
1014                       const Bonobo_Listener listener,
1015                       const char *path,
1016                       const char *type,
1017                       const char *description,
1018                       const char *parent_physical_uri,
1019                       gpointer user_data)
1020{
1021        CamelStore *store = user_data;
1022        CamelFolderInfo *root, *fi;
1023        char *name;
1024        CamelURL *url;
1025        CamelException ex;
1026
1027        /* We could just use 'path' always here? */
1028
1029        if (strcmp (type, "mail") != 0) {
1030                notify_listener (listener, GNOME_Evolution_Storage_UNSUPPORTED_TYPE);
1031                return;
1032        }
1033       
1034        name = strrchr (path, '/');
1035        if (!name) {
1036                notify_listener (listener, GNOME_Evolution_Storage_INVALID_URI);
1037                return;
1038        }
1039        name++;
1040       
1041        camel_exception_init (&ex);
1042        if (*parent_physical_uri) {
1043                url = camel_url_new (parent_physical_uri, NULL);
1044                if (!url) {
1045                        notify_listener (listener, GNOME_Evolution_Storage_INVALID_URI);
1046                        return;
1047                }
1048               
1049                root = camel_store_create_folder (store, url->fragment?url->fragment:url->path + 1, name, &ex);
1050                camel_url_free (url);
1051        } else
1052                root = camel_store_create_folder (store, NULL, name, &ex);
1053       
1054        if (camel_exception_is_set (&ex)) {
1055                /* FIXME: do better than this */
1056                camel_exception_clear (&ex);
1057                notify_listener (listener, GNOME_Evolution_Storage_INVALID_URI);
1058                return;
1059        }
1060       
1061        if (camel_store_supports_subscriptions (store)) {
1062                for (fi = root; fi; fi = fi->child)
1063                        camel_store_subscribe_folder (store, fi->full_name, NULL);
1064        }
1065
1066        camel_store_free_folder_info (store, root);
1067       
1068        notify_listener (listener, GNOME_Evolution_Storage_OK);
1069}
1070
1071static void
1072storage_remove_folder (EvolutionStorage *storage,
1073                       const Bonobo_Listener listener,
1074                       const char *path,
1075                       const char *physical_uri,
1076                       gpointer user_data)
1077{
1078        CamelStore *store = user_data;
1079        CamelURL *url = NULL;
1080        char *name;
1081        CamelException ex;
1082       
1083        g_warning ("storage_remove_folder: path=\"%s\"; uri=\"%s\"", path, physical_uri);
1084       
1085        if (!path || !physical_uri || !strncmp (physical_uri, "vtrash:", 7)) {
1086                notify_listener (listener, GNOME_Evolution_Storage_INVALID_URI);
1087                return;
1088        }
1089       
1090        url = camel_url_new (physical_uri, NULL);
1091        if (!url) {
1092                notify_listener (listener, GNOME_Evolution_Storage_INVALID_URI);
1093                return;
1094        }
1095       
1096        if (!*path) {
1097                camel_url_free (url);
1098                notify_listener (listener, GNOME_Evolution_Storage_INVALID_URI);
1099                return;
1100        }
1101       
1102        camel_exception_init (&ex);
1103
1104        if (url->fragment)
1105                name = url->fragment;
1106        else if (url->path && url->path[0])
1107                name = url->path+1;
1108        else
1109                name = "";
1110
1111        if (camel_store_supports_subscriptions (store))
1112                camel_store_unsubscribe_folder (store, name, NULL);
1113       
1114        camel_store_delete_folder (store, name, &ex);
1115       
1116        camel_url_free (url);
1117        if (camel_exception_is_set (&ex))
1118                goto exception;
1119       
1120        evolution_storage_removed_folder (storage, path);
1121       
1122        notify_listener (listener, GNOME_Evolution_Storage_OK);
1123        return;
1124       
1125 exception:
1126        /* FIXME: do better than this... */
1127        camel_exception_clear (&ex);
1128        notify_listener (listener, GNOME_Evolution_Storage_INVALID_URI);
1129}
1130
1131static void
1132storage_xfer_folder (EvolutionStorage *storage,
1133                     const Bonobo_Listener listener,
1134                     const char *source_path,
1135                     const char *destination_path,
1136                     gboolean remove_source,
1137                     CamelStore *store)
1138{
1139        CamelException ex;
1140        char *src, *dst;
1141        char *p, c, sep;
1142
1143        d(printf("Transfer folder on store source = '%s' dest = '%s'\n", source_path, destination_path));
1144
1145        /* Remap the 'path' to the camel friendly name based on the store dir separator */
1146        sep = store->dir_sep;
1147        src = g_strdup(source_path[0]=='/'?source_path+1:source_path);
1148        dst = g_strdup(destination_path[0]=='/'?destination_path+1:destination_path);
1149        if (sep != '/') {
1150                p = src;
1151                while ((c = *p++))
1152                        if (c == '/')
1153                                p[-1] = sep;
1154               
1155                p = dst;
1156                while ((c = *p++))
1157                        if (c == '/')
1158                                p[-1] = sep;
1159        }
1160
1161        camel_exception_init (&ex);
1162        if (remove_source) {
1163                d(printf("trying to rename\n"));
1164                camel_store_rename_folder(store, src, dst, &ex);
1165                if (camel_exception_is_set(&ex))
1166                        notify_listener (listener, GNOME_Evolution_Storage_GENERIC_ERROR);
1167                else
1168                        notify_listener (listener, GNOME_Evolution_Storage_OK);
1169        } else {
1170                d(printf("No remove, can't rename\n"));
1171                /* FIXME: Implement folder 'copy' for remote stores */
1172                /* This exception never goes anywhere, so it doesn't need translating or using */
1173                notify_listener (listener, GNOME_Evolution_Storage_UNSUPPORTED_OPERATION);
1174        }
1175
1176        g_free(src);
1177        g_free(dst);
1178
1179        camel_exception_clear (&ex);
1180}
1181
1182static void
1183add_storage (const char *name, const char *uri, CamelService *store,
1184             GNOME_Evolution_Shell corba_shell, CamelException *ex)
1185{
1186        EvolutionStorage *storage;
1187        EvolutionStorageResult res;
1188       
1189        storage = evolution_storage_new (name, uri, "mailstorage");
1190        gtk_signal_connect (GTK_OBJECT (storage), "create_folder", storage_create_folder, store);
1191        gtk_signal_connect (GTK_OBJECT (storage), "remove_folder", storage_remove_folder, store);
1192        gtk_signal_connect ((GtkObject *)storage, "xfer_folder", storage_xfer_folder, store);
1193       
1194        res = evolution_storage_register_on_shell (storage, corba_shell);
1195       
1196        switch (res) {
1197        case EVOLUTION_STORAGE_OK:
1198                mail_hash_storage (store, storage);
1199                mail_note_store((CamelStore *)store, storage, CORBA_OBJECT_NIL, NULL, NULL);
1200                /* falllll */
1201        case EVOLUTION_STORAGE_ERROR_ALREADYREGISTERED:
1202        case EVOLUTION_STORAGE_ERROR_EXISTS:
1203                return;
1204        default:
1205                camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM,
1206                                     _("Cannot register storage with shell"));
1207                break;
1208        }
1209}
1210
1211void
1212mail_load_storage_by_uri (GNOME_Evolution_Shell shell, const char *uri, const char *name)
1213{
1214        CamelException ex;
1215        CamelService *store;
1216        CamelProvider *prov;
1217       
1218        camel_exception_init (&ex);
1219       
1220        /* Load the service (don't connect!). Check its provider and
1221         * see if this belongs in the shell's folder list. If so, add
1222         * it.
1223         */
1224
1225        prov = camel_session_get_provider (session, uri, &ex);
1226        if (prov == NULL) {
1227                /* FIXME: real error dialog */
1228                g_warning ("couldn't get service %s: %s\n", uri,
1229                           camel_exception_get_description (&ex));
1230                camel_exception_clear (&ex);
1231                return;
1232        }
1233
1234        if (!(prov->flags & CAMEL_PROVIDER_IS_STORAGE) ||
1235            (prov->flags & CAMEL_PROVIDER_IS_EXTERNAL))
1236                return;
1237
1238        store = camel_session_get_service (session, uri, CAMEL_PROVIDER_STORE, &ex);
1239        if (store == NULL) {
1240                /* FIXME: real error dialog */
1241                g_warning ("couldn't get service %s: %s\n", uri,
1242                           camel_exception_get_description (&ex));
1243                camel_exception_clear (&ex);
1244                return;
1245        }
1246       
1247        if (name == NULL) {
1248                char *service_name;
1249               
1250                service_name = camel_service_get_name (store, TRUE);
1251                add_storage (service_name, uri, store, shell, &ex);
1252                g_free (service_name);
1253        } else
1254                add_storage (name, uri, store, shell, &ex);
1255       
1256        if (camel_exception_is_set (&ex)) {
1257                /* FIXME: real error dialog */
1258                g_warning ("Cannot load storage: %s",
1259                           camel_exception_get_description (&ex));
1260                camel_exception_clear (&ex);
1261        }
1262       
1263        camel_object_unref (CAMEL_OBJECT (store));
1264}
1265
1266/* FIXME: 'is_account_data' is an ugly hack, if we remove support for NNTP we can take it out -- fejj */
1267void
1268mail_load_storages (GNOME_Evolution_Shell shell, const GSList *sources, gboolean is_account_data)
1269{
1270        CamelException ex;
1271        const GSList *iter;
1272       
1273        camel_exception_init (&ex);
1274       
1275        /* Load each service (don't connect!). Check its provider and
1276         * see if this belongs in the shell's folder list. If so, add
1277         * it.
1278         */
1279       
1280        for (iter = sources; iter; iter = iter->next) {
1281                const MailConfigAccount *account = NULL;
1282                const MailConfigService *service = NULL;
1283                char *name;
1284               
1285                if (is_account_data) {
1286                        account = iter->data;
1287                        service = account->source;
1288                        name = account->name;
1289                } else {
1290                        service = iter->data;
1291                        name = NULL;
1292                }
1293               
1294                if (service == NULL || service->url == NULL || service->url[0] == '\0' || !service->enabled)
1295                        continue;
1296               
1297                mail_load_storage_by_uri (shell, service->url, name);
1298        }
1299}
1300
1301void
1302mail_hash_storage (CamelService *store, EvolutionStorage *storage)
1303{
1304        camel_object_ref (CAMEL_OBJECT (store));
1305        g_hash_table_insert (storages_hash, store, storage);
1306}
1307
1308EvolutionStorage*
1309mail_lookup_storage (CamelStore *store)
1310{
1311        EvolutionStorage *storage;
1312       
1313        /* Because the storages_hash holds a reference to each store
1314         * used as a key in it, none of them will ever be gc'ed, meaning
1315         * any call to camel_session_get_{service,store} with the same
1316         * URL will always return the same object. So this works.
1317         */
1318       
1319        storage = g_hash_table_lookup (storages_hash, store);
1320        if (storage)
1321                bonobo_object_ref (BONOBO_OBJECT (storage));
1322       
1323        return storage;
1324}
1325
1326static void
1327store_disconnect(CamelStore *store, void *event_data, void *data)
1328{
1329        camel_service_disconnect (CAMEL_SERVICE (store), TRUE, NULL);
1330        camel_object_unref (CAMEL_OBJECT (store));
1331}
1332
1333void
1334mail_remove_storage (CamelStore *store)
1335{
1336        EvolutionStorage *storage;
1337        EvolutionShellClient *shell_client;
1338        GNOME_Evolution_Shell corba_shell;
1339       
1340        /* Because the storages_hash holds a reference to each store
1341         * used as a key in it, none of them will ever be gc'ed, meaning
1342         * any call to camel_session_get_{service,store} with the same
1343         * URL will always return the same object. So this works.
1344         */
1345       
1346        storage = g_hash_table_lookup (storages_hash, store);
1347        g_hash_table_remove (storages_hash, store);
1348
1349        /* so i guess potentially we could have a race, add a store while one
1350           being removed.  ?? */
1351        mail_note_store_remove(store);
1352       
1353        shell_client = evolution_shell_component_get_owner (shell_component);
1354        corba_shell = bonobo_object_corba_objref (BONOBO_OBJECT (shell_client));
1355       
1356        evolution_storage_deregister_on_shell (storage, corba_shell);
1357
1358        mail_async_event_emit(async_event, MAIL_ASYNC_THREAD, (MailAsyncFunc)store_disconnect, store, NULL, NULL);
1359}
1360
1361void
1362mail_remove_storage_by_uri (const char *uri)
1363{
1364        CamelProvider *prov;
1365        CamelService *store;
1366
1367        prov = camel_session_get_provider (session, uri, NULL);
1368        if (!prov)
1369                return;
1370        if (!(prov->flags & CAMEL_PROVIDER_IS_STORAGE) ||
1371            (prov->flags & CAMEL_PROVIDER_IS_EXTERNAL))
1372                return;
1373
1374        store = camel_session_get_service (session, uri, CAMEL_PROVIDER_STORE, NULL);
1375        if (store != NULL) {
1376                mail_remove_storage (CAMEL_STORE (store));
1377                camel_object_unref (CAMEL_OBJECT (store));
1378        }
1379}
1380
1381int
1382mail_storages_count (void)
1383{
1384        return g_hash_table_size (storages_hash);
1385}
1386
1387void
1388mail_storages_foreach (GHFunc func, gpointer data)
1389{
1390        g_hash_table_foreach (storages_hash, func, data);
1391}
Note: See TracBrowser for help on using the repository browser.