1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
4 * Copyright (C) 2003 Ximian Inc.
6 * Authors: Ettore Perazzoli <ettore@ximian.com>
7 * Michael Zucchi <notzed@ximian.com>
8 * Jeffrey Stedfast <fejj@ximian.com>
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of version 2 of the GNU General Public
12 * License as published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
19 * You should have received a copy of the GNU General Public
20 * License along with this program; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
32 #include <sys/types.h>
38 #include <libedataserver/e-data-server-util.h>
40 #include "em-composer-utils.h"
41 #include "em-format.h"
42 #include "em-folder-tree.h"
43 #include "em-folder-browser.h"
44 #include "em-message-browser.h"
45 #include "em-folder-selector.h"
46 #include "em-folder-selection.h"
47 #include "em-folder-utils.h"
48 #include "em-migrate.h"
50 #include "misc/e-info-label.h"
51 #include "e-util/e-error.h"
52 #include "e-util/e-util-private.h"
54 #include "em-search-context.h"
55 #include "mail-config.h"
56 #include "mail-component.h"
57 #include "mail-folder-cache.h"
58 #include "mail-vfolder.h"
61 #include "mail-tools.h"
62 #include "mail-send-recv.h"
63 #include "mail-session.h"
64 #include "message-list.h"
66 #include "e-activity-handler.h"
67 #include "shell/e-user-creatable-items-handler.h"
68 #include "shell/e-component-view.h"
70 #include "composer/e-msg-composer.h"
72 #include "e-task-bar.h"
74 #include <gtk/gtklabel.h>
76 #include <e-util/e-mktemp.h>
77 #include <Evolution.h>
79 #include <table/e-tree.h>
80 #include <table/e-tree-memory.h>
81 #include <libgnome/gnome-i18n.h>
83 #include <camel/camel-file-utils.h>
84 #include <camel/camel-vtrash-folder.h>
85 #include <camel/camel-disco-store.h>
86 #include <camel/camel-offline-store.h>
88 #include <bonobo/bonobo-control.h>
89 #include <bonobo/bonobo-widget.h>
93 static void create_local_item_cb(EUserCreatableItemsHandler
*handler
, const char *item_type_name
, void *data
);
94 static void view_changed_timeout_remove (EComponentView
*component_view
);
96 #define MAIL_COMPONENT_DEFAULT(mc) if (mc == NULL) mc = mail_component_peek();
98 #define PARENT_TYPE evolution_component_get_type ()
99 static BonoboObjectClass
*parent_class
= NULL
;
108 /* we keep a reference to these so they remain around for the session */
113 void (*done
)(CamelStore
*store
, CamelFolderInfo
*info
, void *data
);
120 struct _MailComponentPrivate
{
123 /* states/data used during shutdown */
124 enum { MC_QUIT_START
, MC_QUIT_SYNC
, MC_QUIT_THREADS
} quit_state
;
126 int quit_expunge
; /* expunge on quit this time around? */
128 char *base_directory
;
130 EMFolderTreeModel
*model
;
132 EActivityHandler
*activity_handler
;
134 MailAsyncEvent
*async_event
;
135 GHashTable
*store_hash
; /* stores store_info objects by store */
137 RuleContext
*search_context
;
139 char *context_path
; /* current path for right-click menu */
141 CamelStore
*local_store
;
144 /* indexed by _mail_component_folder_t */
149 } mc_default_folders
[] = {
150 /* translators: standard local mailbox names */
155 { "Inbox", }, /* 'always local' inbox */
158 static struct _store_info
*
159 store_info_new(CamelStore
*store
, const char *name
)
161 struct _store_info
*si
;
163 si
= g_malloc0(sizeof(*si
));
166 si
->name
= camel_service_get_name((CamelService
*)store
, TRUE
);
168 si
->name
= g_strdup(name
);
170 camel_object_ref(store
);
171 /* If these are vfolders then they need to be opened now,
172 * otherwise they wont keep track of all folders */
173 if ((store
->flags
& CAMEL_STORE_VTRASH
) != 0)
174 si
->vtrash
= camel_store_get_trash(store
, NULL
);
175 if ((store
->flags
& CAMEL_STORE_VJUNK
) != 0)
176 si
->vjunk
= camel_store_get_junk(store
, NULL
);
182 store_info_ref(struct _store_info
*si
)
188 store_info_unref(struct _store_info
*si
)
190 if (si
->ref_count
> 1) {
196 camel_object_unref(si
->vtrash
);
198 camel_object_unref(si
->vjunk
);
199 camel_object_unref(si
->store
);
205 mc_add_store_done(CamelStore
*store
, CamelFolderInfo
*info
, void *data
)
207 struct _store_info
*si
= data
;
210 si
->done(store
, info
, si
);
213 /* let the counters know about the already opened junk/trash folders */
215 mail_note_folder(si
->vtrash
);
217 mail_note_folder(si
->vjunk
);
220 store_info_unref(si
);
223 /* Utility functions. */
225 mc_add_store(MailComponent
*component
, CamelStore
*store
, const char *name
, void (*done
)(CamelStore
*store
, CamelFolderInfo
*info
, void *data
))
227 struct _store_info
*si
;
229 MAIL_COMPONENT_DEFAULT(component
);
231 si
= store_info_new(store
, name
);
233 g_hash_table_insert(component
->priv
->store_hash
, store
, si
);
234 em_folder_tree_model_add_store(component
->priv
->model
, store
, si
->name
);
236 mail_note_store(store
, NULL
, mc_add_store_done
, si
);
240 mc_add_local_store_done(CamelStore
*store
, CamelFolderInfo
*info
, void *data
)
242 /*MailComponent *mc = data;*/
245 for (i
=0;i
<sizeof(mc_default_folders
)/sizeof(mc_default_folders
[0]);i
++) {
246 if (mc_default_folders
[i
].folder
)
247 mail_note_folder(mc_default_folders
[i
].folder
);
252 mc_add_local_store(CamelStore
*store
, const char *name
, MailComponent
*mc
)
254 mc_add_store(mc
, store
, name
, mc_add_local_store_done
);
255 camel_object_unref(store
);
260 mc_setup_local_store(MailComponent
*mc
)
262 MailComponentPrivate
*p
= mc
->priv
;
268 g_mutex_lock(p
->lock
);
269 if (p
->local_store
!= NULL
) {
270 g_mutex_unlock(p
->lock
);
274 camel_exception_init(&ex
);
276 url
= camel_url_new("mbox:", NULL
);
277 tmp
= g_strdup_printf("%s/mail/local", p
->base_directory
);
278 camel_url_set_path(url
, tmp
);
280 tmp
= camel_url_to_string(url
, 0);
281 p
->local_store
= (CamelStore
*)camel_session_get_service(session
, tmp
, CAMEL_PROVIDER_STORE
, &ex
);
283 if (p
->local_store
== NULL
)
286 for (i
=0;i
<sizeof(mc_default_folders
)/sizeof(mc_default_folders
[0]);i
++) {
287 /* FIXME: should this uri be account relative? */
288 camel_url_set_fragment(url
, mc_default_folders
[i
].name
);
289 mc_default_folders
[i
].uri
= camel_url_to_string(url
, 0);
290 mc_default_folders
[i
].folder
= camel_store_get_folder(p
->local_store
, mc_default_folders
[i
].name
,
291 CAMEL_STORE_FOLDER_CREATE
, &ex
);
292 camel_exception_clear(&ex
);
296 g_mutex_unlock(p
->lock
);
299 camel_object_ref(p
->local_store
);
300 mail_async_event_emit(p
->async_event
, MAIL_ASYNC_GUI
, (MailAsyncFunc
)mc_add_local_store
, p
->local_store
, _("On This Computer"), mc
);
304 g_mutex_unlock(p
->lock
);
306 g_warning("Could not setup local store/folder: %s", ex
.desc
);
309 camel_exception_clear(&ex
);
313 load_accounts (MailComponent
*component
, EAccountList
*accounts
)
317 /* Load each service (don't connect!). Check its provider and
318 * see if this belongs in the shell's folder list. If so, add
322 iter
= e_list_get_iterator ((EList
*) accounts
);
323 while (e_iterator_is_valid (iter
)) {
324 EAccountService
*service
;
328 account
= (EAccount
*) e_iterator_get (iter
);
329 service
= account
->source
;
330 name
= account
->name
;
332 /* HACK: mbox url's are handled by the local store setup above,
333 any that come through as account sources are really movemail sources! */
335 && service
->url
!= NULL
337 && strncmp(service
->url
, "mbox:", 5) != 0)
338 mail_component_load_store_by_uri (component
, service
->url
, name
);
340 e_iterator_next (iter
);
343 g_object_unref (iter
);
347 setup_search_context (MailComponent
*component
)
349 MailComponentPrivate
*priv
= component
->priv
;
351 if (priv
->search_context
== NULL
) {
352 char *user
= g_build_filename(component
->priv
->base_directory
, "mail/searches.xml", NULL
);
353 char *system
= g_build_filename (EVOLUTION_PRIVDATADIR
, "searchtypes.xml", NULL
);
355 priv
->search_context
= (RuleContext
*)em_search_context_new ();
356 g_object_set_data_full (G_OBJECT (priv
->search_context
), "user", user
, g_free
);
357 g_object_set_data_full (G_OBJECT (priv
->search_context
), "system", system
, g_free
);
358 rule_context_load (priv
->search_context
, system
, user
);
363 mc_startup(MailComponent
*mc
)
365 static int started
= 0;
371 mc_setup_local_store(mc
);
372 load_accounts(mc
, mail_config_get_accounts());
373 vfolder_load_storage();
377 folder_selected_cb (EMFolderTree
*emft
, const char *path
, const char *uri
, guint32 flags
, EMFolderView
*view
)
379 EMFolderTreeModel
*model
;
381 if ((flags
& CAMEL_FOLDER_NOSELECT
) || !path
) {
382 em_folder_view_set_folder (view
, NULL
, NULL
);
384 model
= em_folder_tree_get_model (emft
);
385 em_folder_tree_model_set_selected (model
, uri
);
386 em_folder_tree_model_save_state (model
);
388 em_folder_view_set_folder_uri (view
, uri
);
393 check_autosave(void *data
)
395 e_msg_composer_check_autosave(NULL
);
401 view_control_activate_cb (BonoboControl
*control
, gboolean activate
, EMFolderView
*view
)
403 BonoboUIComponent
*uic
;
404 static int recover
= 0;
406 uic
= bonobo_control_get_ui_component (control
);
407 g_assert (uic
!= NULL
);
410 Bonobo_UIContainer container
;
412 container
= bonobo_control_get_remote_ui_container (control
, NULL
);
413 bonobo_ui_component_set_container (uic
, container
, NULL
);
414 bonobo_object_release_unref (container
, NULL
);
416 g_assert (container
== bonobo_ui_component_get_container(uic
));
417 g_return_if_fail (container
!= CORBA_OBJECT_NIL
);
419 em_folder_view_activate (view
, uic
, activate
);
420 e_user_creatable_items_handler_activate(g_object_get_data((GObject
*)view
, "e-creatable-items-handler"), uic
);
422 em_folder_view_activate (view
, uic
, activate
);
423 bonobo_ui_component_unset_container (uic
, NULL
);
426 /* This is a weird place to put it, but createControls does it too early.
427 I also think we should wait to do it until we actually visit the mailer.
428 The delay is arbitrary - without it it shows up before the main window */
431 g_timeout_add(1000, check_autosave
, NULL
);
435 /* GObject methods. */
438 impl_dispose (GObject
*object
)
440 MailComponentPrivate
*priv
= MAIL_COMPONENT (object
)->priv
;
442 view_changed_timeout_remove ((EComponentView
*)object
);
444 if (priv
->activity_handler
!= NULL
) {
445 g_object_unref (priv
->activity_handler
);
446 priv
->activity_handler
= NULL
;
449 if (priv
->search_context
!= NULL
) {
450 g_object_unref (priv
->search_context
);
451 priv
->search_context
= NULL
;
454 if (priv
->local_store
!= NULL
) {
455 camel_object_unref (priv
->local_store
);
456 priv
->local_store
= NULL
;
459 (* G_OBJECT_CLASS (parent_class
)->dispose
) (object
);
463 store_hash_free (CamelStore
*store
, struct _store_info
*si
, void *data
)
466 store_info_unref(si
);
470 impl_finalize (GObject
*object
)
472 MailComponentPrivate
*priv
= MAIL_COMPONENT (object
)->priv
;
474 g_free (priv
->base_directory
);
476 mail_async_event_destroy (priv
->async_event
);
478 g_hash_table_foreach (priv
->store_hash
, (GHFunc
)store_hash_free
, NULL
);
479 g_hash_table_destroy (priv
->store_hash
);
481 if (mail_async_event_destroy (priv
->async_event
) == -1) {
482 g_warning("Cannot destroy async event: would deadlock");
483 g_warning(" system may be unstable at exit");
486 g_free (priv
->context_path
);
487 g_mutex_free(priv
->lock
);
490 (* G_OBJECT_CLASS (parent_class
)->finalize
) (object
);
494 view_on_url (GObject
*emitter
, const char *url
, const char *nice_url
, MailComponent
*mail_component
)
496 MailComponentPrivate
*priv
= mail_component
->priv
;
498 e_activity_handler_set_message (priv
->activity_handler
, nice_url
);
502 view_changed(EMFolderView
*emfv
, EComponentView
*component_view
)
504 EInfoLabel
*el
= g_object_get_data((GObject
*)component_view
, "info-label");
505 CORBA_Environment ev
;
507 CORBA_exception_init(&ev
);
511 guint32 visible
, unread
, deleted
, junked
;
512 GString
*tmp
= g_string_new("");
514 camel_object_get(emfv
->folder
, NULL
,
515 CAMEL_FOLDER_NAME
, &name
,
516 CAMEL_FOLDER_DELETED
, &deleted
,
517 CAMEL_FOLDER_VISIBLE
, &visible
,
518 CAMEL_FOLDER_JUNKED
, &junked
,
519 CAMEL_FOLDER_UNREAD
, &unread
, NULL
);
521 if (CAMEL_IS_VTRASH_FOLDER(emfv
->folder
)) {
522 if (((CamelVTrashFolder
*)emfv
->folder
)->type
== CAMEL_VTRASH_FOLDER_TRASH
)
523 g_string_append_printf(tmp
, ngettext ("%d deleted", "%d deleted", deleted
), deleted
);
525 g_string_append_printf(tmp
, ngettext ("%d junk", "%d junk", junked
), junked
);
530 /* This is so that if any of these are
531 * shared/reused, we fallback to the standard
532 * display behaviour */
534 selected
= message_list_get_selected(emfv
->list
);
536 if (em_utils_folder_is_drafts(emfv
->folder
, emfv
->folder_uri
))
538 if (em_utils_folder_is_sent(emfv
->folder
, emfv
->folder_uri
))
540 if (em_utils_folder_is_outbox(emfv
->folder
, emfv
->folder_uri
))
542 /* HACK: hardcoded inbox or maildir '.' folder */
543 if (g_ascii_strcasecmp(emfv
->folder
->full_name
, "inbox") == 0
544 || g_ascii_strcasecmp(emfv
->folder
->full_name
, ".") == 0)
548 g_string_append_printf(tmp
, ngettext ("%d draft", "%d drafts", visible
), visible
);
550 g_string_append_printf(tmp
, ngettext ("%d sent", "%d sent", visible
), visible
);
552 g_string_append_printf(tmp
, ngettext ("%d unsent", "%d unsent", visible
), visible
);
554 if (!emfv
->hide_deleted
)
556 g_string_append_printf(tmp
, ngettext ("%d total", "%d total", visible
), visible
);
557 if (unread
&& selected
->len
<=1)
558 g_string_append_printf(tmp
, ngettext (", %d unread", ", %d unread", unread
), unread
);
561 if (selected
->len
> 1)
562 g_string_append_printf(tmp
, ngettext (", %d selected", ", %d selected", selected
->len
), selected
->len
);
563 message_list_free_uids(emfv
->list
, selected
);
566 if (emfv
->folder
->parent_store
== mail_component_peek_local_store(NULL
)
567 && (!strcmp (name
, "Drafts") || !strcmp (name
, "Inbox")
568 || !strcmp (name
, "Outbox") || !strcmp (name
, "Sent")))
569 e_info_label_set_info(el
, _(name
), tmp
->str
);
571 e_info_label_set_info(el
, name
, tmp
->str
);
573 title
= g_strdup_printf("%s (%s)", _(name
), tmp
->str
);
574 e_component_view_set_title(component_view
, title
);
577 g_string_free(tmp
, TRUE
);
578 camel_object_free(emfv
->folder
, CAMEL_FOLDER_NAME
, name
);
580 e_info_label_set_info(el
, _("Mail"), "");
581 e_component_view_set_title(component_view
, _("Mail"));
586 view_changed_timeout_remove (EComponentView
*component_view
)
592 v
= g_object_get_data((GObject
*)component_view
, "view-changed-timeout");
594 g_source_remove(GPOINTER_TO_INT(v
));
595 g_object_set_data((GObject
*)component_view
, "view-changed-timeout", NULL
);
597 el
= g_object_get_data((GObject
*)component_view
, "info-label");
598 emfv
= g_object_get_data((GObject
*)el
, "folderview");
600 g_object_unref(emfv
);
605 view_changed_timeout(void *d
)
607 EComponentView
*component_view
= d
;
608 EInfoLabel
*el
= g_object_get_data((GObject
*)component_view
, "info-label");
609 EMFolderView
*emfv
= g_object_get_data((GObject
*)el
, "folderview");
611 view_changed(emfv
, component_view
);
613 g_object_set_data((GObject
*)component_view
, "view-changed-timeout", NULL
);
616 g_object_unref(emfv
);
622 view_changed_cb(EMFolderView
*emfv
, EComponentView
*component_view
)
625 EInfoLabel
*el
= g_object_get_data((GObject
*)component_view
, "info-label");
627 /* This can get called 3 times every cursor move, so
628 we don't need to/want to run it immediately */
630 /* NB: we should have a 'view' struct/object to manage this crap, but this'll do for now */
631 v
= g_object_get_data((GObject
*)component_view
, "view-changed-timeout");
633 g_source_remove(GPOINTER_TO_INT(v
));
639 g_object_set_data((GObject
*)component_view
, "view-changed-timeout", GINT_TO_POINTER(g_timeout_add(250, view_changed_timeout
, component_view
)));
643 disable_folder_tree (gpointer
*emfb
, GtkWidget
*widget
)
645 gtk_widget_set_sensitive (widget
, FALSE
);
649 enable_folder_tree (GtkWidget
*emfb
, GtkWidget
*emft
)
652 CamelURL
*selected_curl
, *current_curl
;
655 EMFolderView
*emfv
= (EMFolderView
*)emfb
;
657 /* Currently displayed folder */
658 MessageList
*ml
= emfv
->list
;
660 uri
= mail_tools_folder_to_url (folder
);
661 current_curl
= camel_url_new (uri
, NULL
);
663 /* Selected folder in emft*/
664 uri
= em_folder_tree_get_selected_uri ((EMFolderTree
*) emft
);
665 folder
= mail_tool_uri_to_folder (uri
, 0, &ex
);
666 selected_curl
= camel_url_new (uri
, NULL
);
668 if (!camel_url_equal (selected_curl
, current_curl
))
669 g_signal_emit_by_name (emft
, "folder-selected", emft
, uri
, folder
->full_name
, uri
, folder
->folder_flags
);
671 gtk_widget_set_sensitive (emft
, TRUE
);
673 camel_url_free (current_curl
);
674 camel_url_free (selected_curl
);
679 /* Evolution::Component CORBA methods. */
681 static GNOME_Evolution_ComponentView
682 impl_createView (PortableServer_Servant servant
,
683 GNOME_Evolution_ShellView parent
,
684 CORBA_Environment
*ev
)
686 MailComponent
*mail_component
= MAIL_COMPONENT (bonobo_object_from_servant (servant
));
687 MailComponentPrivate
*priv
= mail_component
->priv
;
688 EComponentView
*component_view
;
689 GtkWidget
*tree_widget
, *vbox
, *info
;
690 GtkWidget
*view_widget
;
691 GtkWidget
*statusbar_widget
;
694 mail_session_set_interactive(TRUE
);
695 mc_startup(mail_component
);
697 view_widget
= em_folder_browser_new ();
699 tree_widget
= (GtkWidget
*) em_folder_tree_new_with_model (priv
->model
);
700 em_folder_tree_set_excluded ((EMFolderTree
*) tree_widget
, 0);
701 em_folder_tree_enable_drag_and_drop ((EMFolderTree
*) tree_widget
);
703 if ((uri
= em_folder_tree_model_get_selected (priv
->model
))) {
704 em_folder_tree_set_selected ((EMFolderTree
*) tree_widget
, uri
);
708 em_format_set_session ((EMFormat
*) ((EMFolderView
*) view_widget
)->preview
, session
);
710 g_signal_connect (view_widget
, "on-url", G_CALLBACK (view_on_url
), mail_component
);
711 em_folder_view_set_statusbar ((EMFolderView
*)view_widget
, FALSE
);
713 statusbar_widget
= e_task_bar_new ();
714 e_activity_handler_attach_task_bar (priv
->activity_handler
, E_TASK_BAR (statusbar_widget
));
716 gtk_widget_show (tree_widget
);
717 gtk_widget_show (view_widget
);
718 gtk_widget_show (statusbar_widget
);
720 vbox
= gtk_vbox_new(FALSE
, 0);
721 info
= e_info_label_new("stock_mail");
722 e_info_label_set_info((EInfoLabel
*)info
, _("Mail"), "");
723 gtk_box_pack_start((GtkBox
*)vbox
, info
, FALSE
, TRUE
, 0);
724 gtk_box_pack_start((GtkBox
*)vbox
, tree_widget
, TRUE
, TRUE
, 0);
726 gtk_widget_show(info
);
727 gtk_widget_show(vbox
);
729 component_view
= e_component_view_new(parent
, "mail", vbox
, view_widget
, statusbar_widget
);
731 g_object_set_data((GObject
*)component_view
, "info-label", info
);
733 g_object_set_data_full((GObject
*)view_widget
, "e-creatable-items-handler",
734 e_user_creatable_items_handler_new("mail", create_local_item_cb
, tree_widget
),
735 (GDestroyNotify
)g_object_unref
);
738 g_signal_connect (component_view
->view_control
, "activate", G_CALLBACK (view_control_activate_cb
), view_widget
);
739 g_signal_connect (tree_widget
, "folder-selected", G_CALLBACK (folder_selected_cb
), view_widget
);
741 g_signal_connect((EMFolderBrowser
*)view_widget
, "account_search_cleared", G_CALLBACK (enable_folder_tree
), tree_widget
);
742 g_signal_connect(((EMFolderBrowser
*)view_widget
), "account_search_activated", G_CALLBACK (disable_folder_tree
), tree_widget
);
743 g_signal_connect(view_widget
, "changed", G_CALLBACK(view_changed_cb
), component_view
);
744 g_signal_connect(view_widget
, "loaded", G_CALLBACK(view_changed_cb
), component_view
);
746 g_object_set_data((GObject
*)info
, "folderview", view_widget
);
748 return BONOBO_OBJREF(component_view
);
752 impl_requestQuit(PortableServer_Servant servant
, CORBA_Environment
*ev
)
754 /*MailComponent *mc = MAIL_COMPONENT(bonobo_object_from_servant(servant));*/
758 if (!e_msg_composer_request_close_all())
761 folder
= mc_default_folders
[MAIL_COMPONENT_FOLDER_OUTBOX
].folder
;
763 && camel_session_is_online(session
)
764 && camel_object_get(folder
, NULL
, CAMEL_FOLDER_VISIBLE
, &unsent
, 0) == 0
766 && e_error_run(NULL
, "mail:exit-unsaved", NULL
) != GTK_RESPONSE_YES
)
773 mc_quit_sync_done(CamelStore
*store
, void *data
)
775 MailComponent
*mc
= data
;
777 mc
->priv
->quit_count
--;
781 mc_quit_sync(CamelStore
*store
, struct _store_info
*si
, MailComponent
*mc
)
783 mc
->priv
->quit_count
++;
784 mail_sync_store(store
, mc
->priv
->quit_expunge
, mc_quit_sync_done
, mc
);
788 impl_quit(PortableServer_Servant servant
, CORBA_Environment
*ev
)
790 MailComponent
*mc
= MAIL_COMPONENT(bonobo_object_from_servant(servant
));
792 mail_config_prune_proxies ();
793 switch (mc
->priv
->quit_state
) {
794 case MC_QUIT_START
: {
795 int now
= time(NULL
)/60/60/24, days
;
796 GConfClient
*gconf
= mail_config_get_gconf_client();
798 mail_vfolder_shutdown();
800 mc
->priv
->quit_expunge
= gconf_client_get_bool(gconf
, "/apps/evolution/mail/trash/empty_on_exit", NULL
)
801 && ((days
= gconf_client_get_int(gconf
, "/apps/evolution/mail/trash/empty_on_exit_days", NULL
)) == 0
802 || (days
+ gconf_client_get_int(gconf
, "/apps/evolution/mail/trash/empty_date", NULL
)) <= now
);
804 g_hash_table_foreach(mc
->priv
->store_hash
, (GHFunc
)mc_quit_sync
, mc
);
806 if (mc
->priv
->quit_expunge
)
807 gconf_client_set_int(gconf
, "/apps/evolution/mail/trash/empty_date", now
, NULL
);
809 mc
->priv
->quit_state
= MC_QUIT_SYNC
;
813 if (mc
->priv
->quit_count
> 0)
817 mc
->priv
->quit_state
= MC_QUIT_THREADS
;
820 case MC_QUIT_THREADS
:
821 /* should we keep cancelling? */
822 return !mail_msg_active((unsigned int)-1);
828 static GNOME_Evolution_CreatableItemTypeList
*
829 impl__get_userCreatableItems (PortableServer_Servant servant
, CORBA_Environment
*ev
)
831 GNOME_Evolution_CreatableItemTypeList
*list
= GNOME_Evolution_CreatableItemTypeList__alloc ();
834 list
->_maximum
= list
->_length
;
835 list
->_buffer
= GNOME_Evolution_CreatableItemTypeList_allocbuf (list
->_length
);
837 CORBA_sequence_set_release (list
, FALSE
);
839 list
->_buffer
[0].id
= "message";
840 list
->_buffer
[0].description
= _("New Mail Message");
841 list
->_buffer
[0].menuDescription
= _("_Mail Message");
842 list
->_buffer
[0].tooltip
= _("Compose a new mail message");
843 list
->_buffer
[0].menuShortcut
= 'm';
844 list
->_buffer
[0].iconName
= "stock_mail-compose";
845 list
->_buffer
[0].type
= GNOME_Evolution_CREATABLE_OBJECT
;
847 list
->_buffer
[1].id
= "folder";
848 list
->_buffer
[1].description
= _("New Mail Folder");
849 list
->_buffer
[1].menuDescription
= _("Mail _Folder");
850 list
->_buffer
[1].tooltip
= _("Create a new mail folder");
851 list
->_buffer
[1].menuShortcut
= '\0';
852 list
->_buffer
[1].iconName
= "stock_new-dir";
853 list
->_buffer
[1].type
= GNOME_Evolution_CREATABLE_FOLDER
;
859 create_item(const char *type
, EMFolderTreeModel
*model
, const char *uri
)
861 if (strcmp(type
, "message") == 0) {
862 if (!em_utils_check_user_can_send_mail(NULL
))
865 em_utils_compose_new_message(uri
);
866 } else if (strcmp(type
, "folder") == 0) {
867 em_folder_utils_create_folder(NULL
);
875 create_local_item_cb(EUserCreatableItemsHandler
*handler
, const char *item_type_name
, void *data
)
877 EMFolderTree
*tree
= data
;
878 char *uri
= em_folder_tree_get_selected_uri(tree
);
880 create_item(item_type_name
, em_folder_tree_get_model(tree
), uri
);
885 impl_requestCreateItem (PortableServer_Servant servant
,
886 const CORBA_char
*item_type_name
,
887 CORBA_Environment
*ev
)
889 MailComponent
*mc
= MAIL_COMPONENT(bonobo_object_from_servant(servant
));
891 if (create_item(item_type_name
, mc
->priv
->model
, NULL
) == -1) {
892 CORBA_exception_set (ev
, CORBA_USER_EXCEPTION
,
893 ex_GNOME_Evolution_Component_UnknownType
, NULL
);
898 handleuri_got_folder(char *uri
, CamelFolder
*folder
, void *data
)
900 CamelURL
*url
= data
;
901 EMMessageBrowser
*emmb
;
903 if (folder
!= NULL
) {
904 const char *reply
= camel_url_get_param(url
, "reply");
905 const char *forward
= camel_url_get_param(url
, "forward");
910 if (!strcmp(reply
, "all"))
911 mode
= REPLY_MODE_ALL
;
912 else if (!strcmp(reply
, "list"))
913 mode
= REPLY_MODE_LIST
;
914 else /* if "sender" or anything else */
915 mode
= REPLY_MODE_SENDER
;
917 em_utils_reply_to_message(folder
, camel_url_get_param(url
, "uid"), NULL
, mode
, NULL
);
918 } else if (forward
) {
922 uid
= camel_url_get_param(url
, "uid");
924 g_warning("Could not forward the message. UID is NULL.");
926 uids
= g_ptr_array_new();
927 g_ptr_array_add(uids
, g_strdup(uid
));
929 if (!strcmp(forward
, "attached"))
930 em_utils_forward_attached(folder
, uids
, uri
);
931 else if (!strcmp(forward
, "inline"))
932 em_utils_forward_inline(folder
, uids
, uri
);
933 else if (!strcmp(forward
, "quoted"))
934 em_utils_forward_quoted(folder
, uids
, uri
);
935 else { /* Just the default forward */
936 em_utils_forward_messages(folder
, uids
, uri
);
940 emmb
= (EMMessageBrowser
*)em_message_browser_window_new();
941 /*message_list_set_threaded(((EMFolderView *)emmb)->list, emfv->list->threaded);*/
942 /* FIXME: session needs to be passed easier than this */
943 em_format_set_session((EMFormat
*)((EMFolderView
*)emmb
)->preview
, session
);
944 em_folder_view_set_folder((EMFolderView
*)emmb
, folder
, uri
);
945 em_folder_view_set_message((EMFolderView
*)emmb
, camel_url_get_param(url
, "uid"), FALSE
);
946 gtk_widget_show(emmb
->window
);
949 g_warning("Couldn't open folder '%s'", uri
);
955 impl_handleURI (PortableServer_Servant servant
, const char *uri
, CORBA_Environment
*ev
)
957 if (!strncmp (uri
, "mailto:", 7)) {
958 if (!em_utils_check_user_can_send_mail(NULL
))
961 em_utils_compose_new_message_with_mailto (uri
, NULL
);
962 } else if (!strncmp(uri
, "email:", 6)) {
963 CamelURL
*url
= camel_url_new(uri
, NULL
);
965 if (camel_url_get_param(url
, "uid") != NULL
) {
966 char *curi
= em_uri_to_camel(uri
);
968 mail_get_folder(curi
, 0, handleuri_got_folder
, url
, mail_thread_new
);
971 g_warning("email uri's must include a uid parameter");
978 impl_sendAndReceive (PortableServer_Servant servant
, CORBA_Environment
*ev
)
980 mail_send_receive ();
984 impl_upgradeFromVersion (PortableServer_Servant servant
, const short major
, const short minor
, const short revision
, CORBA_Environment
*ev
)
986 MailComponent
*component
;
989 component
= mail_component_peek ();
991 camel_exception_init (&ex
);
992 if (em_migrate (component
->priv
->base_directory
, major
, minor
, revision
, &ex
) == -1) {
993 GNOME_Evolution_Component_UpgradeFailed
*failedex
;
995 failedex
= GNOME_Evolution_Component_UpgradeFailed__alloc();
996 failedex
->what
= CORBA_string_dup(_("Failed upgrading Mail settings or folders."));
997 failedex
->why
= CORBA_string_dup(ex
.desc
);
998 CORBA_exception_set(ev
, CORBA_USER_EXCEPTION
, ex_GNOME_Evolution_Component_UpgradeFailed
, failedex
);
1001 camel_exception_clear (&ex
);
1004 struct _setline_data
{
1005 GNOME_Evolution_Listener listener
;
1006 CORBA_boolean status
;
1011 setline_done(CamelStore
*store
, void *data
)
1013 struct _setline_data
*sd
= data
;
1015 g_assert(sd
->pending
> 0);
1018 if (sd
->pending
== 0) {
1019 CORBA_Environment ev
= { 0 };
1021 GNOME_Evolution_Listener_complete(sd
->listener
, &ev
);
1022 CORBA_exception_free(&ev
);
1023 CORBA_Object_release(sd
->listener
, &ev
);
1024 CORBA_exception_free(&ev
);
1026 camel_session_set_online(session
, sd
->status
);
1032 setline_check(void *key
, void *value
, void *data
)
1034 CamelService
*service
= key
;
1035 struct _setline_data
*sd
= data
;
1037 if (CAMEL_IS_DISCO_STORE(service
)
1038 || CAMEL_IS_OFFLINE_STORE(service
)) {
1040 mail_store_set_offline((CamelStore
*)service
, !sd
->status
, setline_done
, sd
);
1045 status_check (GNOME_Evolution_ShellState shell_state
)
1049 switch (shell_state
)
1051 case GNOME_Evolution_USER_OFFLINE
:
1054 case GNOME_Evolution_FORCED_OFFLINE
:
1055 /*Network is down so change network state on the camel session*/
1057 /* Cancel all operations as they wont happen anyway cos Network is down*/
1059 camel_session_set_network_state (session
, FALSE
);
1061 case GNOME_Evolution_USER_ONLINE
:
1062 camel_session_set_network_state (session
, TRUE
);
1070 impl_setLineStatus(PortableServer_Servant servant
, GNOME_Evolution_ShellState shell_state
, GNOME_Evolution_Listener listener
, CORBA_Environment
*ev
)
1072 struct _setline_data
*sd
;
1073 int status
= status_check(shell_state
);
1075 /* This will dis/enable further auto-mail-check action. */
1076 /* FIXME: If send/receive active, wait for it to finish? */
1078 camel_session_set_online(session
, status
);
1080 sd
= g_malloc0(sizeof(*sd
));
1081 sd
->status
= status
;
1082 sd
->listener
= CORBA_Object_duplicate(listener
, ev
);
1083 if (ev
->_major
== CORBA_NO_EXCEPTION
)
1084 mail_component_stores_foreach(mail_component_peek(), setline_check
, sd
);
1086 CORBA_exception_free(ev
);
1088 if (sd
->pending
== 0) {
1090 CORBA_Object_release(sd
->listener
, ev
);
1091 CORBA_exception_free(ev
);
1097 camel_session_set_online(session
, status
);
1098 GNOME_Evolution_Listener_complete(listener
, ev
);
1103 impl_mail_test(PortableServer_Servant servant
, CORBA_Environment
*ev
)
1105 printf("*** Testing mail interface!! ***\n");
1108 /* Initialization. */
1111 mail_component_class_init (MailComponentClass
*class)
1113 POA_GNOME_Evolution_Component__epv
*epv
= &((EvolutionComponentClass
*)class)->epv
;
1114 POA_GNOME_Evolution_MailComponent__epv
*mepv
= &class->epv
;
1115 GObjectClass
*object_class
= G_OBJECT_CLASS (class);
1117 parent_class
= g_type_class_peek_parent (class);
1119 object_class
->dispose
= impl_dispose
;
1120 object_class
->finalize
= impl_finalize
;
1122 epv
->createView
= impl_createView
;
1123 epv
->requestQuit
= impl_requestQuit
;
1124 epv
->quit
= impl_quit
;
1125 epv
->_get_userCreatableItems
= impl__get_userCreatableItems
;
1126 epv
->requestCreateItem
= impl_requestCreateItem
;
1127 epv
->handleURI
= impl_handleURI
;
1128 epv
->sendAndReceive
= impl_sendAndReceive
;
1129 epv
->upgradeFromVersion
= impl_upgradeFromVersion
;
1130 epv
->setLineStatus
= impl_setLineStatus
;
1132 mepv
->test
= impl_mail_test
;
1136 mail_component_init (MailComponent
*component
)
1138 MailComponentPrivate
*priv
;
1140 priv
= g_new0 (MailComponentPrivate
, 1);
1141 component
->priv
= priv
;
1143 priv
->lock
= g_mutex_new();
1145 priv
->base_directory
= g_build_filename (g_get_home_dir (), ".evolution", NULL
);
1148 char *p
= priv
->base_directory
;
1149 while ((p
= strchr(p
, '\\')))
1153 if (e_util_mkdir_hier (priv
->base_directory
, 0777) == -1 && errno
!= EEXIST
)
1156 priv
->model
= em_folder_tree_model_new (priv
->base_directory
);
1158 priv
->activity_handler
= e_activity_handler_new ();
1160 mail_session_init (priv
->base_directory
);
1162 priv
->async_event
= mail_async_event_new();
1163 priv
->store_hash
= g_hash_table_new (NULL
, NULL
);
1165 mail_autoreceive_init();
1170 mail_component_peek (void)
1172 static MailComponent
*component
= NULL
;
1174 if (component
== NULL
)
1175 component
= g_object_new(mail_component_get_type(), NULL
);
1181 mail_component_peek_base_directory (MailComponent
*component
)
1183 MAIL_COMPONENT_DEFAULT(component
);
1185 return component
->priv
->base_directory
;
1189 mail_component_peek_search_context (MailComponent
*component
)
1191 MAIL_COMPONENT_DEFAULT(component
);
1193 setup_search_context(component
);
1195 return component
->priv
->search_context
;
1199 mail_component_peek_activity_handler (MailComponent
*component
)
1201 MAIL_COMPONENT_DEFAULT(component
);
1203 return component
->priv
->activity_handler
;
1206 struct _CamelSession
*mail_component_peek_session(MailComponent
*component
)
1208 MAIL_COMPONENT_DEFAULT(component
);
1214 mail_component_add_store (MailComponent
*component
, CamelStore
*store
, const char *name
)
1216 mc_add_store(component
, store
, name
, NULL
);
1220 * mail_component_load_store_by_uri:
1221 * @component: mail component
1222 * @uri: uri of store
1223 * @name: name of store (used for display purposes)
1225 * Return value: Pointer to the newly added CamelStore. The caller is supposed
1226 * to ref the object if it wants to store it.
1229 mail_component_load_store_by_uri (MailComponent
*component
, const char *uri
, const char *name
)
1233 CamelProvider
*prov
;
1235 MAIL_COMPONENT_DEFAULT(component
);
1237 camel_exception_init (&ex
);
1239 /* Load the service (don't connect!). Check its provider and
1240 * see if this belongs in the shell's folder list. If so, add
1244 prov
= camel_provider_get(uri
, &ex
);
1246 /* EPFIXME: real error dialog */
1247 g_warning ("couldn't get service %s: %s\n", uri
,
1248 camel_exception_get_description (&ex
));
1249 camel_exception_clear (&ex
);
1253 if (!(prov
->flags
& CAMEL_PROVIDER_IS_STORAGE
))
1256 store
= (CamelStore
*) camel_session_get_service (session
, uri
, CAMEL_PROVIDER_STORE
, &ex
);
1257 if (store
== NULL
) {
1258 /* EPFIXME: real error dialog */
1259 g_warning ("couldn't get service %s: %s\n", uri
,
1260 camel_exception_get_description (&ex
));
1261 camel_exception_clear (&ex
);
1265 mail_component_add_store(component
, store
, name
);
1266 camel_object_unref (store
);
1272 store_disconnect (CamelStore
*store
, void *event_data
, void *user_data
)
1274 camel_service_disconnect (CAMEL_SERVICE (store
), TRUE
, NULL
);
1275 camel_object_unref (store
);
1279 mail_component_remove_store (MailComponent
*component
, CamelStore
*store
)
1281 MailComponentPrivate
*priv
;
1282 struct _store_info
*si
;
1284 MAIL_COMPONENT_DEFAULT(component
);
1286 priv
= component
->priv
;
1288 /* Because the store_hash holds a reference to each store
1289 * used as a key in it, none of them will ever be gc'ed, meaning
1290 * any call to camel_session_get_{service,store} with the same
1291 * URL will always return the same object. So this works.
1294 if (!(si
= g_hash_table_lookup (priv
->store_hash
, store
)))
1297 camel_object_ref (store
);
1298 g_hash_table_remove (priv
->store_hash
, store
);
1300 store_info_unref(si
);
1302 /* so i guess potentially we could have a race, add a store while one
1303 being removed. ?? */
1304 mail_note_store_remove (store
);
1306 em_folder_tree_model_remove_store (priv
->model
, store
);
1308 mail_async_event_emit (priv
->async_event
, MAIL_ASYNC_THREAD
, (MailAsyncFunc
) store_disconnect
, store
, NULL
, NULL
);
1312 mail_component_remove_store_by_uri (MailComponent
*component
, const char *uri
)
1314 CamelProvider
*prov
;
1317 MAIL_COMPONENT_DEFAULT(component
);
1319 if (!(prov
= camel_provider_get(uri
, NULL
)))
1322 if (!(prov
->flags
& CAMEL_PROVIDER_IS_STORAGE
))
1325 store
= (CamelStore
*) camel_session_get_service (session
, uri
, CAMEL_PROVIDER_STORE
, NULL
);
1326 if (store
!= NULL
) {
1327 mail_component_remove_store (component
, store
);
1328 camel_object_unref (store
);
1333 mail_component_get_store_count (MailComponent
*component
)
1335 MAIL_COMPONENT_DEFAULT(component
);
1337 return g_hash_table_size (component
->priv
->store_hash
);
1340 /* need to map from internal struct to external api */
1341 struct _store_foreach_data
{
1347 mc_stores_foreach(CamelStore
*store
, struct _store_info
*si
, struct _store_foreach_data
*data
)
1349 data
->func((void *)store
, (void *)si
->name
, data
->data
);
1353 mail_component_stores_foreach (MailComponent
*component
, GHFunc func
, void *user_data
)
1355 struct _store_foreach_data data
= { func
, user_data
};
1357 MAIL_COMPONENT_DEFAULT(component
);
1359 g_hash_table_foreach (component
->priv
->store_hash
, (GHFunc
)mc_stores_foreach
, &data
);
1363 mail_component_remove_folder (MailComponent
*component
, CamelStore
*store
, const char *path
)
1365 MAIL_COMPONENT_DEFAULT(component
);
1367 /* FIXME: implement me. but first, am I really even needed? */
1371 mail_component_peek_tree_model (MailComponent
*component
)
1373 MAIL_COMPONENT_DEFAULT(component
);
1375 return component
->priv
->model
;
1379 mail_component_peek_local_store (MailComponent
*mc
)
1381 MAIL_COMPONENT_DEFAULT (mc
);
1382 mc_setup_local_store (mc
);
1384 return mc
->priv
->local_store
;
1388 * mail_component_get_folder:
1392 * Get a standard/default folder by id. This call is thread-safe.
1396 struct _CamelFolder
*
1397 mail_component_get_folder(MailComponent
*mc
, enum _mail_component_folder_t id
)
1399 g_assert(id
<= MAIL_COMPONENT_FOLDER_LOCAL_INBOX
);
1401 MAIL_COMPONENT_DEFAULT(mc
);
1402 mc_setup_local_store(mc
);
1404 return mc_default_folders
[id
].folder
;
1408 * mail_component_get_folder_uri:
1412 * Get a standard/default folder's uri. This call is thread-safe.
1417 mail_component_get_folder_uri(MailComponent
*mc
, enum _mail_component_folder_t id
)
1419 g_assert(id
<= MAIL_COMPONENT_FOLDER_LOCAL_INBOX
);
1421 MAIL_COMPONENT_DEFAULT(mc
);
1422 mc_setup_local_store(mc
);
1424 return mc_default_folders
[id
].uri
;
1427 BONOBO_TYPE_FUNC_FULL (MailComponent
, GNOME_Evolution_MailComponent
, PARENT_TYPE
, mail_component
)