diff options
Diffstat (limited to 'mail')
-rw-r--r-- | mail/ChangeLog | 645 | ||||
-rw-r--r-- | mail/Makefile.am | 270 | ||||
-rw-r--r-- | mail/component-factory.c | 1668 | ||||
-rw-r--r-- | mail/em-folder-view.c | 101 | ||||
-rw-r--r-- | mail/em-junk-filter.c | 327 | ||||
-rw-r--r-- | mail/em-junk-filter.h | 30 | ||||
-rw-r--r-- | mail/em-junk-plugin.c | 24 | ||||
-rw-r--r-- | mail/em-junk-plugin.h | 47 | ||||
-rw-r--r-- | mail/em-popup.c | 50 | ||||
-rw-r--r-- | mail/em-popup.h | 15 | ||||
-rw-r--r-- | mail/mail-local.c | 1571 | ||||
-rw-r--r-- | mail/mail-ops.c | 159 | ||||
-rw-r--r-- | mail/mail-ops.h | 4 | ||||
-rw-r--r-- | mail/mail-session.c | 18 | ||||
-rw-r--r-- | mail/mail-tools.c | 29 | ||||
-rw-r--r-- | mail/mail-vfolder.c | 45 | ||||
-rw-r--r-- | mail/message-list.c | 129 | ||||
-rw-r--r-- | mail/message-list.h | 3 |
18 files changed, 4146 insertions, 989 deletions
diff --git a/mail/ChangeLog b/mail/ChangeLog index 14c0682cd2..b4c757d5b8 100644 --- a/mail/ChangeLog +++ b/mail/ChangeLog @@ -1,366 +1,13 @@ -2003-11-10 Not Zed <NotZed@Ximian.com> +2003-10-23 Radek Doulik <rodo@ximian.com> - * em-format-html.c (em_format_html_multipart_signed_sign): changed - for cipher context api changes. - * em-format.c (emf_multipart_signed): ditto. - -2003-11-07 Not Zed <NotZed@Ximian.com> - - * mail-config.glade: added security_information_dialog (probably - temporarily in this file). - - * em-format.c (em_format_is_attachment): also treat pkcs7-mime as - non-attachment. - (emf_application_xpkcs7mime): no longer need to worry about - scanning the part. - -2003-11-07 Dan Winship <danw@ximian.com> - - * message-list.c: Don't #include e-name-western.h, since the code - that uses it is commented out, and we want to remove the local - copy, but we don't want to make the mailer depend on the e-d-s - copy if it's not even going to be using it. - -2003-11-07 JP Rosevear <jpr@ximian.com> - - * Makefile.am: build the marshall files right at the beginning - -2003-11-07 JP Rosevear <jpr@ximian.com> - - * Makefile.am: Make sure the marshal files are listed above other - sources - -2003-11-06 Jeffrey Stedfast <fejj@ximian.com> - - * em-migrate.c (em_migrate_filter_file): Call em_migrate_uri() - instead of em_uri_from_camel(). - (em_migrate_uri): Special-case file: uri's by converting them into - email://local@local/ uri's since these folders will have been - migrated to the newer mbox tree structure. - -2003-11-06 Jeffrey Stedfast <fejj@ximian.com> - - * mail-vfolder.c (mail_vfolder_delete_uri): User vfolder rules - should be in ${evolution_dir}/mail/vfolders.xml rather than - ${evolution_dir}/vfolders.xml - (mail_vfolder_rename_uri): Same. - (store_folder_deleted): Here too. - (store_folder_renamed): And here. - (vfolder_load_storage): Again here. - (vfolder_editor_response): Same. - (edit_rule_response): And here. - (new_rule_clicked): Here too. - - * mail-session.c (main_get_filter_driver): User filter rules - should be in ${evolution_dir}/mail/filters.xml rather than - ${evolution_dir}/filters.xml - - * mail-autofilter.c (filter_gui_add_from_message): Same. - (mail_filter_rename_uri): And here. - (mail_filter_delete_uri): Here too. - - * em-utils.c (filter_editor_response): Again here. - (em_utils_edit_filters): Same. - - * em-migrate.c (em_migrate_filter_file): Same (also for - vfolders.xml) - -2003-11-05 Jeffrey Stedfast <fejj@ximian.com> - - * em-migrate.[c,h]: New source files to migrate from the old mail - directory to the new mail directory. - - * mail-component.c (mail_component_init): Changed to use - ~/.evolution and added code to migrate the old mail folders over - if ~/.evolution/mail does not yet exist. - -2003-11-03 Ettore Perazzoli <ettore@ximian.com> - - * GNOME_Evolution_Mail.server.in.in: Use "evolution2:config_item" - properties for the config item instead of "evolution:config_item" - ones. - -2003-11-01 Jeffrey Stedfast <fejj@ximian.com> - - * em-folder-view.c: setup filter-on-thread and vfolder-on-thread. - - * mail-autofilter.c (rule_from_message): handle AUTO_THREAD - (rule_match_thread): new function to setup a filter on a msgid - -2003-10-31 Not Zed <NotZed@Ximian.com> - - * mail-config.glade: Rearranged smime config slightly, and added - encrypt key preference. - - * mail-account-gui.c (mail_account_gui_save, - mail_account_gui_new): enable smime always if have_nss. Added new - options in glade file and handle changed names. - (smime_changed, smime_sign_key_select, smime_sign_key_clear) - (smime_encrypt_key_select, smime_encrypt_key_clear): Add a bunch - of mostly dummy UI behaviour management stuff. - -2003-10-30 Not Zed <NotZed@Ximian.com> - - * em-format.c (em_format_is_inline): show application/x-pkcs7-mime - inline always by default. - -2003-10-30 Not Zed <NotZed@Ximian.com> - - * em-format.c (emf_multipart_signed): handle - application/x-pkcs7-signature. - -2003-10-29 Not Zed <NotZed@Ximian.com> - - * em-format-html.c (em_format_html_multipart_signed_sign): handle - application/x-pkcs7-signature. - - * em-format.c (emf_application_xpkcs7mime): Handle - application/x-pkcs7-mime. - -2003-10-29 Jeffrey Stedfast <fejj@ximian.com> - - * em-account-prefs.h: Changed the OAFIID. - - * em-composer-prefs.h: Changed the OAFIID. - - * em-mailer-prefs.h: Changed the OAFIID. - - * GNOME_Evolution_Mail.server.in.in: Changed OAFIIDs for the prefs - controls. - -2003-10-29 Jeffrey Stedfast <fejj@ximian.com> - - * GNOME_Evolution_Mail.server.in.in: Nuked the MailConfig - interface stuff. - - * Mailer.idl: Removed. None of the interfaces are needed/used - anymore. - - * folder-info.[c,h]: Removed. The summary was the only thing that - needed/used this code and it has been nuked into oblivion, so - these interfaces are no longer needed. - - * mail-config.c: Removed old crufty CORBA interface snot that is - no longer needed or used. - -2003-10-27 Jeffrey Stedfast <fejj@ximian.com> - - * Fixes bug #49816 - - * em-popup.c (emp_part_popup_reply_sender): No need to pass a - parent window arg anymore. - (emp_part_popup_reply_list): Same. - (emp_part_popup_reply_all): Here too. - (emp_part_popup_forward): Same. - (emp_uri_popup_address_send): Here too. - - * em-folder-browser.c (emfb_mail_compose): No need to pass a - parent window arg anymore. - (emfb_mail_post): Same. - - * em-folder-view.c (emfv_message_reply): Don't pass a parent - window argument anymore. - (emfv_popup_forward): Same. - (emfv_popup_resend): Same here. - (em_folder_view_open_selected): Same. - (emfv_message_forward_attached): Here too. - (emfv_message_forward_inline): And here. - (emfv_message_forward_quoted): Same. - (emfv_message_redirect): Here too. - (emfv_message_post_reply): And here. - (emfv_format_link_clicked): ANd finally here. - - * em-utils.c (create_new_composer): Don't set_transient_for() - anymore. - (em_utils_compose_new_message): No longer takes a parent window - argument. - (em_utils_forward_attached): No longer takes a parent window arg. - (em_utils_forward_inline): Same. - (em_utils_forward_quoted): Same. - (em_utils_forward_message): Same. - (em_utils_forward_messages): Here too. - (redirect_get_composer): Don't set_transient_for() here either. - (em_utils_redirect_message): No longer takes a parent window arg. - (em_utils_redirect_message_by_uid): Same. - (reply_get_composer): Don't set_transient_for() here. - (em_utils_reply_to_message): No longer takes a parent window arg. - (em_utils_reply_to_message_by_uid): Same. - (post_reply_to_message): Don't set_transient_for() here. - (em_utils_post_reply_to_message_by_uid): No longer takes a parent - window arg. - (em_utils_compose_new_message_with_mailto): Don't - set_transient_for() here. - (em_utils_post_to_url): Same. - (em_utils_edit_message): No longer takes a parent window arg. - (em_utils_edit_messages): Same. - -2003-10-24 Jeffrey Stedfast <fejj@ximian.com> - - * em-account-prefs.[c,h]: Re-Namespaced mail-accounts.c - - * em-composer-prefs.[c,h]: Re-Namespaced mail-composer-prefs.c - - * em-mailer-prefs.[c,h]: Re-Namespaced mail-preferences.c - - * mail-accounts.[c,h]: Removed. - - * mail-composer-prefs.[c,h]: Removed. - - * mail-preferences.[c,h]: Removed. - - * mail-account-editor.c: Updated. - - * mail-account-gui.c: Updated. - - * mail-config-factory.c: Updated. - - * mail-component-factory.c: Updated. - -2003-10-22 Ettore Perazzoli <ettore@ximian.com> - - * GNOME_Evolution_Mail.server.in.in: Add an - "evolution:button_icon" attribute. - -2003-10-22 Ettore Perazzoli <ettore@ximian.com> - - * GNOME_Evolution_Mail.server.in.in: Add an - "evolution:button_sort_order" attribute. - -2003-10-22 Jeffrey Stedfast <fejj@ximian.com> - - * mail-component.c (parse_uid_list): Removed, use - em_utils_selection_get_uidlist() instead. - (drop_uid_list): Use em_utils_selection_get_uidlist() to parse the - x-uid-list selection data and use mail_tool_uri_to_folder() - directly since we have the uri (originally we expected the first - component of the selection data to be the e-storage-set-view - folder path rather than the uri). - (folder_receive_drop_cb): Call gtk_drag_finish() here. - - * message-list.c: s/x-evolution-message/x-uid-list/ - - * em-utils.c: Same. - -2003-10-22 Ettore Perazzoli <ettore@ximian.com> - - * mail-component.c (impl_createControls): Use - e_storage_browser_peek_widget_scrolled() to retrieve the scrolled - window that the EStorageSetView is contained in. - -2003-10-22 Ettore Perazzoli <ettore@ximian.com> - - * GNOME_Evolution_Mail.server.in.in: Add an - "evolution:button_label" attribute to the Component for use in the - shell. - -2003-10-22 Not Zed <NotZed@Ximian.com> - - * mail-component.c (em_copy_folders): handle destination of "" - properly. - (emc_popup_copy_folder_selected): handle url->path properly, strip - leading /. - (em_copy_folders): if we just created a new folder on a - subscribable store, subscribe to it. - (em_copy_folders): handle nonselectable folders specially. - -2003-10-21 Jeffrey Stedfast <fejj@ximian.com> - - * mail-component.c (drag_text_uri_list, folder_dragged_cb) - (drop_uid_list, drop_folder, import_message_rfc822) - (drop_message_rfc822, drop_text_uri_list, folder_receive_drop_cb): - New functions to handle drag & drop to/from the folder tree. - (impl_createControls): Setup drag & drop support. - - * em-format.c (emf_multipart_mixed): Put an <hr> between parts of - a multipart. - -2003-10-21 Not Zed <NotZed@Ximian.com> - - * mail-component.c (emc_popup_properties): implement. - (emc_popup_properties_got_folder): builds dynamic - folder-properties dialogue. - (emc_popup_properties_response): set the properties on the folder - on an ok response. - (emc_popup_properties_free): free the properties working data. - - * mail-folder-cache.c (unset_folder_info): unhook from the right - function for message_changed. - -2003-10-20 Not Zed <NotZed@Ximian.com> - - * mail-component.c (emc_popup_new_folder): pass the right object - to set_selected(). Fixes a new real bug. Undid reformatting. - -2003-10-17 Jeffrey Stedfast <fejj@ximian.com> - - * mail-component.c: General compile fixes. - (emc_popup_new_folder): Fixed to not shadow a parameter. Fixes a - real bug. - - * mail-component.h: Added some prototypes. - -2003-10-13 Not Zed <NotZed@Ximian.com> - - * em-popup.c (em_popup_create_menu_once): only hookup target free - if we have a target set. - - * mail-component.c (load_accounts): removed debug i accidentally - left in. - (emc_tree_right_click): handle right-click context menu, using an - EMPopup table. - (emc_popup_*): setup empty popup handlers. - -2003-10-13 Not Zed <NotZed@Ximian.com> - - * em-folder-selection.c (em_select_folder): asynchornous folder - selection call. - (emfs_folder_selected): callback for folder selected. - - * em-folder-view.c (emfv_popup_move): implement. - (emfv_popup_copy): " - (emfv_popup_move_cb): async folder select callback to run it. - -2003-10-10 Not Zed <NotZed@Ximian.com> - - * mail-account-gui.c - (mail_account_gui_folder_selector_button_new): use - em_folder_selection_button. - (mail_account_gui_new): " - (folder_selected): " - - * em-folder-selection-button.c: Make this use camel uri's rather - than camelfolders. - (set_selection): removed, redundant. - (impl_dispose): removed, not needed. - - * em-folder-selection-button.h: change the selected signal not to - actually return the selection, which must get retrieved later. - - * mail-component.c (em_uri_from_camel): create an evo mail uri - from a camel one. - (em_uri_to_camel): the reverse. - - * mail-signature-editor.c (mail_signature_editor): up the version - of the gtkhtml editor. - -2003-10-09 Not Zed <NotZed@Ximian.com> - - * em-folder-selection-button.c (set_selection): always set - selected_folder, otherwise we don't unset it properly. - - * em-folder-selection.c (em_folder_selection_run_dialog): fix a - small memleak. - (em_folder_selection_run_dialog_uri): do the same as run_dialog - but take, and return physical uri's. - - * mail-component-factory.c (factory): removed some fixme's, and - re-hookedup the composer. + * em-spam-filter.c (em_spam_sa_check_spam): lock spamd test + between threads 2003-10-09 Frederic Crozat <fcrozat@mandrakesoft.com> - * em-icon-stream.c (emis_sync_close): Use - gnome-thumbnail_scale_down_pixbuf if available, for better - performance. + * em-icon-stream.c: (emis_sync_close): + Use gnome-thumbnail_scale_down_pixbuf if available, + for better performance. 2003-10-08 Jeffrey Stedfast <fejj@ximian.com> @@ -368,38 +15,6 @@ create-rule-from-message bars so that we don't segfault when we right click with a multi-selection. -2003-10-08 Chris Toshok <toshok@ximian.com> - - * em-utils.c (em_utils_camel_address_to_destination): EDestination - => EABDestination, and e_destination => eab_destination. - (reply_get_composer): same. - (post_reply_to_message): same. - - * em-composer-utils.c (ask_confirm_for_unwanted_html_mail) - EDestination => EABDestination, and e_destination => - eab_destination. - (composer_get_message):same. - -2003-10-08 Not Zed <NotZed@Ximian.com> - - * mail-component.c (mail_component_peek): setup vfolders once we - hve the component, since its setup will call mail_component_peek, - fun recursion. - -2003-10-08 Not Zed <NotZed@Ximian.com> - - * mail-component.c (setup_local_folder): removed. - (setup_local_store): setup various needed globals properly. - (setup_account_storages): renamed to load_accounts. - (go_online): turn on interactivity as well as onlinedness. - - * GNOME_Evolution_Mail.server.in.in: point the preferences pages - to the right factory. - -2003-10-07 Not Zed <NotZed@Ximian.com> - - * mail-component.[ch]: Fix copyrights. - 2003-10-06 Jeffrey Stedfast <fejj@ximian.com> * mail-config-druid.c (identity_prepare): Fixed. @@ -418,22 +33,6 @@ the first unread message for now. This is actually annoying the fuck out of me, Radek, and a few other people. -2003-10-02 Not Zed <NotZed@Ximian.com> - - * mail-component.c (add_storage): Add the storage to the hash - after we've initialised it. - (mail_component_evomail_uri_from_folder): hardcode "local" account - pseudo-id for local folders. - (mail_component_get_folder_from_evomail_uri): handle the "local" - account case. - -2003-10-02 Not Zed <NotZed@Ximian.com> - - * mail-component.c (setup_local_store): use mbox:/path rather than - mbox:///path - the mbox code is 'wrong', but this is easier to - fix. fixes local unread counts. maybe the provider url-compare - should address this too. - 2003-10-02 Suresh Chandrasekharan <suresh.chandrasekharan@sun.com> * mail-config-druid.c: Fix for 40917 "Backspace shouldn't @@ -507,27 +106,6 @@ * em-format.c (emf_init): Oops, put the arguments in the right order. -2003-09-29 Ettore Perazzoli <ettore@ximian.com> - - * mail-component.c: New member local_store in - MailComponentPrivate. - (impl_dispose): Unref. - (mail_component_load_storage_by_uri): Return the CamelStore. - (setup_local_folder): New. - (setup_local_store): New. - (mail_component_init): Call it. - (mail_component_peek_storage_set): New. - (mail_component_get_folder_from_evomail_uri): New. - (mail_component_evomail_uri_from_folder): New. - - * em-folder-selection-button.c: New. - * em-folder-selection-button.h: New. - - * em-folder-selection.c: New. - * em-folder-selection.h: New. - - * em-marshal.list: Add NONE:POINTER. - 2003-09-25 Jeffrey Stedfast <fejj@ximian.com> * mail-account-gui.c (mail_account_gui_save): Allow any file: uri @@ -547,33 +125,6 @@ charset string is empty, default the charset to the user's locale charset. Partial fix for bug #47638. -2003-09-23 Ettore Perazzoli <ettore@ximian.com> - - * mail-component.c (add_storage): Remove unused arg "uri". - (mail_component_add_store): Likewise. - (add_storage): Don't set the "Connecting..." node. - (mail_component_init): Set up local store at - ~/.evolution/mail/local. - - * evolution-mbox-upgrade.c (get_local_store): Remove a double - xmlFree() that was causing it to crash. - - -2003-09-23 Ettore Perazzoli <ettore@ximian.com> - - * mail-component.c (add_storage): Note the new store. - - * mail-component-factory.c: Don't include "mail-callbacks.h" - anymore. - - * em-format-html.c (em_format_html_get_type): Get the base - directory with mail_component_peek_base_directory(). - * em-utils.c (filter_editor_response): Likewise. - (em_utils_edit_filters): Likewise. - - * em-folder-browser.c (emfb_init): Get the search context through - mail_component_peek_search_context(). - 2003-09-23 Jeffrey Stedfast <fejj@ximian.com> * evolution-mbox-upgrade.c (get_local_store): Don't xmlFree (name) @@ -727,14 +278,6 @@ * evolution-mbox-upgrade.c: New source file to migrate from the old mbox structure to the new mbox structure. -2003-09-08 Ettore Perazzoli <ettore@ximian.com> - - * mail-folder-cache.c (mail_note_store): Allow NULL storage in - precondition. - - * mail-component.c (mail_component_init): Remove debugging - message. - 2003-08-22 Not Zed <NotZed@Ximian.com> * mail-format.c (write_date): translate the local time format. @@ -746,13 +289,14 @@ day names, and the autoconf magic which made Not Zed dislike the inclusion of the timezone name. -2003-08-18 Ettore Perazzoli <ettore@ximian.com> +2003-08-14 Jeffrey Stedfast <fejj@ximian.com> - * GNOME_Evolution_Mail.server.in.in: Rename - GNOME_Evolution_Mail_Component2 to - GNOME_Evolution_Mail_Component_2 and GNOME_Evolution_Mail_Factory2 - to GNOME_Evolution_Mail_Factory_2. - * mail-component-factory.c: Update accordingly. + * mail-ops.c (mail_send_message): Don't abort at the first failure + after sending (filtering, appending to Sent, syncing). Instead, + keep a running tab of exceptions and then set a culmulative + exception at the end to report to our caller. Also, if we fail to + append to the account Sent folder, try again with the local Sent + folder. Fixes bug #46512. 2003-08-18 Jeffrey Stedfast <fejj@ximian.com> @@ -797,15 +341,6 @@ * mail-display.c (mail_display_render): Change "%P" to "%p" so that strftime() can work under solaris. -2003-08-14 Jeffrey Stedfast <fejj@ximian.com> - - * mail-ops.c (mail_send_message): Don't abort at the first failure - after sending (filtering, appending to Sent, syncing). Instead, - keep a running tab of exceptions and then set a culmulative - exception at the end to report to our caller. Also, if we fail to - append to the account Sent folder, try again with the local Sent - folder. Fixes bug #46512. - 2003-08-13 Suresh Chandrasekharan <suresh.chandrasekharan@sun.com> * e-searching-tokenizer.c (searcher_next_token): Fix for 45818 ( @@ -823,12 +358,6 @@ * mail-session.c (remove_timeout): Removed. (register_timeout): Removed. -2003-08-09 Ettore Perazzoli <ettore@ximian.com> - - * mail-component.c (storage_go_online): Pass NULL for the - operation pointer to mail_note_store(), to sync with Michael's - changes. - 2003-08-05 Jeffrey Stedfast <fejj@ximian.com> * mail-format.c (handle_multipart_encrypted): Updated for @@ -1784,154 +1313,6 @@ * message-browser.c (message_browser_new): Handle our own Delete key presses. Fixes bug #45597. -2003-07-25 Ettore Perazzoli <ettore@ximian.com> - - * mail-callbacks.c (do_view_message): No need to pass a shell - argument to message_browser_new() anymore. - - * message-browser.c (message_browser_new): Removed arg shell. No - need to pass it to folder_browser_new() either. - - * mail-component.c (create_view_callback): No need to pass a shell - arg to folder_browser_factory_new_control() anymore. - - * folder-browser-factory.c (folder_browser_factory_new_control): - Removed arg shell; folder_browser_browser_new() doesn't need it - anymore. - - * folder-browser.c (folder_browser_destroy): No need to unref - ->shell anymore. - (folder_browser_new): Removed shell arg. - (folder_browser_gui_init): Removed a const qualifier that was not - supposed to be there. - - * folder-browser.h: Removed member shell from struct - FolderBrowser. - -2003-07-25 Ettore Perazzoli <ettore@ximian.com> - - * folder-browser.c (folder_browser_gui_init): Get the search - context through mail_component_peek_search_context(), since it's - no longer a global variable. - (folder_browser_gui_init): Cleaned up an extra unneeded if() - statement. - - * mail-component.c: New member search_context in struct - MailComponentPrivate. - (mail_component_peek_search_context): New. - (setup_search_context): New function to initialize the - search_context, based on the old code in component-factory.c. - (mail_component_init): Call it here. - (impl_dispose): Unref the rule_context. - - * mail-component-factory.c: Removed global variable - search_context. - -2003-07-25 Ettore Perazzoli <ettore@ximian.com> - - * mail-component.c (browser_page_switched_callback): New callback - for the "page_switched" signal on EStorageBrowser; deactivate the - previous page, activate the new one. - (impl_createControls): Connect. - -2003-07-24 Ettore Perazzoli <ettore@ximian.com> - - * mail-mt.c (do_op_status): Pass "evolution-mail" as the ID to - evolution_activity_client_new(). [This is just a temporary thing - to avoid the fact that we don't have component-factory.h anymore. - Eventually we'll just get rid of the activity client stuff.] - - * mail-component-factory.c: Added to the build. Also, finished - implementing and moving the factory over from component-factory.c. - - * component-factory.c: Removed from the build. - * component-factory.h: Removed from the build. - - * mail-component.c: Removed some debugging messages. - -2003-07-23 Ettore Perazzoli <ettore@ximian.com> - - * subscribe-dialog.c: Converted to use EStorages instead of - EvolutionStorages and the new MailComponent object. - - * mail.h: Nuked a bunch of stuff. This will go away when I am - done refactoring. - - * mail-offline-handler.c: Use the new MailComponent object. - - * mail-folder-cache.c, mail-folder-cache.h: Converted to use - EStorages instead of EvolutionStorages. - - * mail-display.c: Use g_timeout and g_source functions instead of - gtk_timeout functions. - - * mail-send-recv.c: Use g_timeout and g_source functions instead - of gtk_timeout functions. - (receive_update_got_store): Updated for the new mail_note_store(). - - * mail-session.c: Use g_timeout and g_source functions instead of - gtk_timeout functions. - - * mail-config-factory.c (factory): Removed. - - * folder-browser.c (folder_browser_destroy): Use GLib - timeout/source functions instead of the deprecated GTK ones. - (done_message_selected): Likewise. - (folder_browser_gui_init): Protect against fb->search being NULL. - - * mail-account-gui.c (add_new_store): Use new MailComponent object - and EStorages instead of EvolutionStorages. - (mail_account_gui_save): Likewise. - - * mail-accounts.c (account_delete_clicked): Use new MailComponent - object and EStorages instead of EvolutionStorages. - (account_able_clicked): Likewise. - (account_able_toggled): Likewise. - - * mail-autofilter.c: Use mail_component_peek_base_directory() - instead of the evolution_dir global. - * mail-callbacks.c: Likewise. - * mail-config.c (uri_to_evname): Likewise. - (mail_config_get_signature_list): Likewise. - (delete_unused_signature_file): Likewise. - * mail-display.c (mail_display_class_init): Likewise. - * mail-importer.c (mail_importer_make_local_folder): Likewise. - * mail-local.c (mlf_getv): Likewise. - * mail-ops.c (uid_cachename_hack): Likewise. - * mail-summary.c (generate_folder_summaries): Likewise. - * mail-tools.c (mail_tool_get_local_inbox): Likewise. - (mail_tools_folder_to_url): Likewise. - * mail-vfolder.c (mail_vfolder_delete_uri): Likewise. - (mail_vfolder_rename_uri): Likewise. - (context_rule_removed): Likewise. - (store_folder_deleted): Likewise. - (store_folder_renamed): Likewise. - (vfolder_load_storage): Likewise. - (vfolder_editor_response): Likewise. - (edit_rule_response): Likewise. - (new_rule_clicked): Likewise. - (vfolder_gui_add_rule): Likewise. - * mail-session.c (main_get_filter_driver): Likewise. - (mail_session_forget_password): Likewise. - (mail_session_init): Get a base_directory arg. - - * component-factory.c, component-factory.h: Disabled a bunch of - stuff to get it to compile in the new configuration. These files - will eventually go away when I am done refactoring this. - - * Makefile.am: Do not build importers, compile generate - skels/stubs for Evolution. - - * GNOME_Evolution_Mail.server.in.in: Rename control factory to - OAFIID:GNOME_Evolution_Mail_Factory2. Add new component - GNOME_Evolution_Mail_Component2. - - * mail-component-factory.c: New file implementing the Bonobo - factory. - - * mail-component.c, mail-component.h: New files implementing the - new mail component, using the new Evolution::Component IDL. - 2003-07-23 Jeffrey Stedfast <fejj@ximian.com> * mail-format.c (mail_format_data_wrapper_write_to_stream): Revert diff --git a/mail/Makefile.am b/mail/Makefile.am index 41757c704f..ed63d84526 100644 --- a/mail/Makefile.am +++ b/mail/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = # importers FIXME +SUBDIRS = importers libexec_PROGRAMS = \ evolution-mbox-upgrade @@ -17,6 +17,8 @@ INCLUDES = \ -I$(top_srcdir)/shell \ -I$(top_srcdir)/shell/importer \ -I$(top_builddir)/shell/importer \ + -I$(top_srcdir)/addressbook/backend \ + -I$(top_builddir)/addressbook/backend \ $(EVOLUTION_MAIL_CFLAGS) \ -DEVOLUTION_DATADIR=\""$(datadir)"\" \ -DEVOLUTION_PRIVDATADIR=\""$(privdatadir)"\" \ @@ -37,133 +39,132 @@ INCLUDES = \ component_LTLIBRARIES = libevolution-mail.la -# Code generation for Spell.idl +EVOLUTION_MAIL_CORBA_GENERATED_H = \ + Mailer.h +EVOLUTION_MAIL_CORBA_GENERATED_C = \ + Mailer-common.c \ + Mailer-skels.c \ + Mailer-stubs.c +EVOLUTION_MAIL_CORBA_GENERATED = $(EVOLUTION_MAIL_CORBA_GENERATED_C) $(EVOLUTION_MAIL_CORBA_GENERATED_H) SPELL_IDL = Spell.idl -SPELL_IDL_GENERATED_H = \ +IDL_GENERATED_H = \ Spell.h -SPELL_IDL_GENERATED_C = \ +IDL_GENERATED_C = \ Spell-common.c \ Spell-skels.c \ Spell-stubs.c -SPELL_IDL_GENERATED = $(SPELL_IDL_GENERATED_C) $(SPELL_IDL_GENERATED_H) +IDL_GENERATED = $(IDL_GENERATED_C) $(IDL_GENERATED_H) -$(SPELL_IDL_GENERATED_H): $(SPELL_IDL) +$(IDL_GENERATED_H): $(SPELL_IDL) $(ORBIT_IDL) -I $(srcdir) -I $(datadir)/idl $(IDL_INCLUDES) $(srcdir)/Spell.idl -$(SPELL_IDL_GENERATED_C): $(SPELL_IDL_GENERATED_H) +$(IDL_GENERATED_C): $(IDL_GENERATED_H) Spell-impl.o: Spell.h - -# libevolution-mail - -libevolution_mail_la_SOURCES = \ - $(SPELL_IDL_GENERATED) \ - $(MARSHAL_GENERATED) \ - e-searching-tokenizer.c \ - e-searching-tokenizer.h \ - em-account-prefs.c \ - em-account-prefs.h \ - em-composer-prefs.c \ - em-composer-prefs.h \ - em-mailer-prefs.c \ - em-mailer-prefs.h \ - em-inline-filter.c \ - em-inline-filter.h \ - em-folder-selection.c \ - em-folder-selection.h \ - em-folder-selection-button.c \ - em-folder-selection-button.h \ - em-folder-selector.c \ - em-folder-selector.h \ - em-folder-view.c \ - em-folder-view.h \ - em-folder-browser.c \ - em-folder-browser.h \ - em-format.c \ - em-format.h \ - em-format-html.c \ - em-format-html.h \ - em-format-html-display.c \ - em-format-html-display.h \ - em-format-html-print.c \ - em-format-html-print.h \ - em-format-html-quote.c \ - em-format-html-quote.h \ - em-format-quote.c \ - em-format-quote.h \ - em-message-browser.c \ - em-message-browser.h \ - em-migrate.c \ - em-migrate.h \ - em-composer-utils.c \ - em-composer-utils.h \ - em-popup.c \ - em-popup.h \ - em-utils.c \ - em-utils.h \ - em-subscribe-editor.c \ - em-subscribe-editor.h \ - em-sync-stream.c \ - em-sync-stream.h \ - em-icon-stream.c \ - em-icon-stream.h \ - em-html-stream.c \ - em-html-stream.h \ - folder-browser-factory.c \ - folder-browser-factory.h \ - mail-account-editor.c \ - mail-account-editor.h \ - mail-account-gui.c \ - mail-account-gui.h \ - mail-autofilter.c \ - mail-autofilter.h \ - mail-component-factory.c \ - mail-component.c \ - mail-component.h \ - mail-config.c \ - mail-config.h \ - mail-config-druid.c \ - mail-config-druid.h \ - mail-crypto.c \ - mail-crypto.h \ - mail-config-factory.c \ - mail-config-factory.h \ - mail-folder-cache.c \ - mail-folder-cache.h \ - mail-importer.c \ - mail-importer.h \ - mail-mt.c \ - mail-mt.h \ - mail-offline-handler.c \ - mail-offline-handler.h \ - mail-ops.c \ - mail-ops.h \ - mail-send-recv.c \ - mail-send-recv.h \ - mail-session.c \ - mail-session.h \ - mail-signature-editor.c \ - mail-signature-editor.h \ - mail-tools.c \ - mail-tools.h \ - mail-types.h \ - mail-vfolder.c \ - mail-vfolder.h \ - message-list.c \ - message-list.h \ - message-tag-editor.c \ - message-tag-editor.h \ - message-tag-followup.c \ - message-tag-followup.h \ +libevolution_mail_la_SOURCES = \ + $(EVOLUTION_MAIL_CORBA_GENERATED) \ + $(IDL_GENERATED) \ + component-factory.c \ + component-factory.h \ + e-searching-tokenizer.c \ + e-searching-tokenizer.h \ + em-inline-filter.c \ + em-inline-filter.h \ + em-folder-view.c \ + em-folder-view.h \ + em-folder-browser.c \ + em-folder-browser.h \ + em-format.c \ + em-format.h \ + em-format-html.c \ + em-format-html.h \ + em-format-html-display.c \ + em-format-html-display.h \ + em-format-html-print.c \ + em-format-html-print.h \ + em-format-html-quote.c \ + em-format-html-quote.h \ + em-format-quote.c \ + em-format-quote.h \ + em-marshal.c \ + em-marshal.h \ + em-message-browser.c \ + em-message-browser.h \ + em-composer-utils.c \ + em-composer-utils.h \ + em-popup.c \ + em-popup.h \ + em-utils.c \ + em-utils.h \ + em-subscribe-editor.c \ + em-subscribe-editor.h \ + em-sync-stream.c \ + em-sync-stream.h \ + em-icon-stream.c \ + em-icon-stream.h \ + em-html-stream.c \ + em-html-stream.h \ + em-junk-filter.c \ + em-junk-filter.h \ + em-junk-plugin.c \ + em-junk-plugin.h \ + folder-browser-factory.c \ + folder-browser-factory.h \ + folder-info.c \ + folder-info.h \ + mail-account-editor.c \ + mail-account-editor.h \ + mail-account-gui.c \ + mail-account-gui.h \ + mail-accounts.c \ + mail-accounts.h \ + mail-autofilter.c \ + mail-autofilter.h \ + mail-composer-prefs.c \ + mail-composer-prefs.h \ + mail-config.c \ + mail-config.h \ + mail-config-druid.c \ + mail-config-druid.h \ + mail-crypto.c \ + mail-crypto.h \ + mail-config-factory.c \ + mail-config-factory.h \ + mail-preferences.c \ + mail-preferences.h \ + mail-folder-cache.c \ + mail-folder-cache.h \ + mail-importer.c \ + mail-importer.h \ + mail-local.c \ + mail-local.h \ + mail-mt.c \ + mail-mt.h \ + mail-offline-handler.c \ + mail-offline-handler.h \ + mail-ops.c \ + mail-ops.h \ + mail-send-recv.c \ + mail-send-recv.h \ + mail-session.c \ + mail-session.h \ + mail-signature-editor.c \ + mail-signature-editor.h \ + mail-tools.c \ + mail-tools.h \ + mail-types.h \ + mail-vfolder.c \ + mail-vfolder.h \ + message-list.c \ + message-list.h \ + message-tag-editor.c \ + message-tag-editor.h \ + message-tag-followup.c \ + message-tag-followup.h \ mail.h -# EPFIXME: Functionality to be brought back to life. -# -# mail-local.c -# mail-local.h - libevolution_mail_la_LIBADD = \ $(top_builddir)/shell/importer/libevolution-importer.la \ $(top_builddir)/camel/libcamel.la \ @@ -172,6 +173,7 @@ libevolution_mail_la_LIBADD = \ $(top_builddir)/composer/libcomposer.la \ $(top_builddir)/widgets/misc/libemiscwidgets.la \ $(top_builddir)/widgets/misc/libefilterbar.la \ + $(top_builddir)/addressbook/backend/ebook/libebook.la \ $(top_builddir)/filter/libfilter.la \ $(top_builddir)/widgets/menus/libmenus.la \ $(EVOLUTION_MAIL_LIBS) @@ -179,9 +181,6 @@ libevolution_mail_la_LIBADD = \ libevolution_mail_la_LDFLAGS = \ -avoid-version -module - -# .server files - evolution_mbox_upgrade_SOURCES = evolution-mbox-upgrade.c evolution_mbox_upgrade_LDADD = \ $(top_builddir)/camel/libcamel.la \ @@ -189,31 +188,39 @@ evolution_mbox_upgrade_LDADD = \ $(EVOLUTION_MAIL_LIBS) server_in_files = GNOME_Evolution_Mail.server.in.in -server_DATA = $(server_in_files:.server.in.in=_$(BASE_VERSION).server) -@EVO_SUBST_SERVER_RULE@ -@EVO_NAME_SERVER_RULE@ -@INTLTOOL_SERVER_RULE@ -# Misc data to install +server_DATA = $(server_in_files:.server.in.in=.server) +%.server.in: %.server.in.in + sed -e "s|\@COMPONENTDIR\@|$(componentdir)|" $< > $@ +@INTLTOOL_SERVER_RULE@ -glade_DATA = mail-config.glade local-config.glade subscribe-dialog.glade message-tags.glade mail-search.glade MARSHAL_GENERATED = em-marshal.c em-marshal.h @EVO_MARSHAL_RULE@ +glade_DATA = mail-config.glade local-config.glade subscribe-dialog.glade message-tags.glade mail-search.glade + etspec_DATA = message-list.etspec +schemadir = $(GCONF_SCHEMA_FILE_DIR) +schema_DATA = evolution-mail.schemas + +idl_DATA = Mailer.idl + +$(EVOLUTION_MAIL_CORBA_GENERATED_H): Mailer.idl + $(ORBIT_IDL) -I $(srcdir) -I $(datadir)/idl $(IDL_INCLUDES) $(srcdir)/Mailer.idl +$(EVOLUTION_MAIL_CORBA_GENERATED_C): $(EVOLUTION_MAIL_CORBA_GENERATED_H) + EXTRA_DIST = \ ChangeLog.pre-1-4 \ em-marshal.list \ + Mailer.idl \ $(SPELL_IDL) \ $(glade_DATA) \ $(schema_DATA) \ $(server_in_files) \ + $(server_DATA) \ $(etspec_DATA) - -# Purify support - if ENABLE_PURIFY PLINK = $(LIBTOOL) --mode=link $(PURIFY) $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ @@ -225,12 +232,6 @@ evolution-mail.pure: evolution-mail endif - -# GConf - -schemadir = $(GCONF_SCHEMA_FILE_DIR) -schema_DATA = evolution-mail.schemas - install-data-local: if test -z "$(DESTDIR)" ; then \ for p in $(schema_DATA) ; do \ @@ -238,11 +239,8 @@ install-data-local: done \ fi - -# Prologue - dist-hook: cd $(distdir); rm -f $(BUILT_SOURCES) -BUILT_SOURCES = $(SPELL_IDL_GENERATED) $(MARSHAL_GENERATED) $(server_DATA) +BUILT_SOURCES = $(EVOLUTION_MAIL_CORBA_GENERATED) $(IDL_GENERATED) $(MARSHAL_GENERATED) $(server_DATA) CLEANFILES = $(BUILT_SOURCES) diff --git a/mail/component-factory.c b/mail/component-factory.c new file mode 100644 index 0000000000..f578cd13d5 --- /dev/null +++ b/mail/component-factory.c @@ -0,0 +1,1668 @@ +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ +/* component-factory.c + * + * Authors: Ettore Perazzoli <ettore@ximian.com> + * + * Copyright (C) 2000 Ximian, Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <signal.h> + +#include <gconf/gconf.h> +#include <gconf/gconf-client.h> + +#include <camel/camel.h> +#include <camel/camel-vee-store.h> + +#include <bonobo/bonobo-generic-factory.h> +#include <bonobo/bonobo-shlib-factory.h> + +#include <gal/widgets/e-gui-utils.h> + +#include "e-util/e-dialog-utils.h" + +#include "Evolution.h" +#include "evolution-storage.h" +#include "evolution-wizard.h" +#include "evolution-composer.h" + +#include "folder-browser-factory.h" +#include "evolution-shell-component.h" +#include "evolution-shell-component-dnd.h" +#include "folder-info.h" +#include "mail.h" +#include "mail-config.h" +#include "mail-config-factory.h" +#include "mail-preferences.h" +#include "mail-composer-prefs.h" +#include "mail-tools.h" +#include "mail-ops.h" +#include "mail-offline-handler.h" +#include "mail-local.h" +#include "mail-session.h" +#include "mail-mt.h" +#include "mail-importer.h" +#include "mail-folder-cache.h" +#include "em-utils.h" +#include "component-factory.h" + +#include "mail-send-recv.h" + +#include "mail-vfolder.h" +#include "mail-autofilter.h" + +#define d(x) + +char *default_drafts_folder_uri; +CamelFolder *drafts_folder = NULL; +char *default_sent_folder_uri; +CamelFolder *sent_folder = NULL; +char *default_outbox_folder_uri; +CamelFolder *outbox_folder = NULL; +char *evolution_dir; + +EvolutionShellClient *global_shell_client = NULL; + +RuleContext *search_context = NULL; + +static MailAsyncEvent *async_event = NULL; + +static GHashTable *storages_hash; +static EvolutionShellComponent *shell_component; + +enum { + ACCEPTED_DND_TYPE_MESSAGE_RFC822, + ACCEPTED_DND_TYPE_X_EVOLUTION_MESSAGE, + ACCEPTED_DND_TYPE_TEXT_URI_LIST, +}; + +static char *accepted_dnd_types[] = { + "message/rfc822", + "x-evolution-message", /* ...from an evolution message list... */ + "text/uri-list", /* ...from nautilus... */ + NULL +}; + +enum { + EXPORTED_DND_TYPE_TEXT_URI_LIST, +}; + +static char *exported_dnd_types[] = { + "text/uri-list", /* we have to export to nautilus as text/uri-list */ + NULL +}; + +static const EvolutionShellComponentFolderType folder_types[] = { + { "mail", "evolution-inbox.png", N_("Mail"), N_("Folder containing mail"), TRUE, accepted_dnd_types, exported_dnd_types }, + { "mail/public", "evolution-inbox.png", N_("Public Mail"), N_("Public folder containing mail"), FALSE, accepted_dnd_types, exported_dnd_types }, + { "vtrash", "evolution-trash.png", N_("Virtual Trash"), N_("Virtual Trash folder"), FALSE, accepted_dnd_types, exported_dnd_types }, + { "vjunk", "evolution-junk.png", N_("Virtual Junk"), N_("Virtual Junk folder"), FALSE, accepted_dnd_types, exported_dnd_types }, + { NULL, NULL, NULL, NULL, FALSE, NULL, NULL } +}; + +static const char *schema_types[] = { + "mailto", + NULL +}; + +static inline gboolean +type_is_mail (const char *type) +{ + return !strcmp (type, "mail") || !strcmp (type, "mail/public"); +} + +static inline gboolean +type_is_vtrash (const char *type) +{ + return !strcmp (type, "vtrash"); +} + +static inline gboolean +type_is_vjunk (const char *type) +{ + return !strcmp (type, "vjunk"); +} + +/* EvolutionShellComponent methods and signals. */ + +static BonoboControl * +create_noselect_control (void) +{ + GtkWidget *label; + + label = gtk_label_new (_("This folder cannot contain messages.")); + gtk_widget_show (label); + return bonobo_control_new (label); +} + +static EvolutionShellComponentResult +create_view (EvolutionShellComponent *shell_component, + const char *physical_uri, + const char *folder_type, + const char *view_info, + BonoboControl **control_return, + void *closure) +{ + EvolutionShellClient *shell_client; + GNOME_Evolution_Shell corba_shell; + BonoboControl *control; + + shell_client = evolution_shell_component_get_owner (shell_component); + corba_shell = evolution_shell_client_corba_objref(shell_client); + + if (type_is_mail (folder_type)) { + const char *noselect; + CamelURL *url; + + url = camel_url_new (physical_uri, NULL); + noselect = url ? camel_url_get_param (url, "noselect") : NULL; + if (noselect && !strcasecmp (noselect, "yes")) + control = create_noselect_control (); + else + control = folder_browser_factory_new_control (physical_uri, + corba_shell); + camel_url_free (url); + } else if (type_is_vtrash (folder_type)) { + if (!strncasecmp (physical_uri, "file:", 5)) + control = folder_browser_factory_new_control ("vtrash:file:/", corba_shell); + else + control = folder_browser_factory_new_control (physical_uri, corba_shell); + } else if (type_is_vjunk (folder_type)) + if (!strncasecmp (physical_uri, "file:", 5)) + control = folder_browser_factory_new_control ("vjunk:file:/", corba_shell); + else + control = folder_browser_factory_new_control (physical_uri, corba_shell); + else + return EVOLUTION_SHELL_COMPONENT_UNSUPPORTEDTYPE; + + if (!control) + return EVOLUTION_SHELL_COMPONENT_NOTFOUND; + + *control_return = control; + return EVOLUTION_SHELL_COMPONENT_OK; +} + +static void +create_folder_done (char *uri, CamelFolder *folder, void *data) +{ + GNOME_Evolution_ShellComponentListener listener = data; + GNOME_Evolution_ShellComponentListener_Result result; + CORBA_Environment ev; + + if (folder) { + result = GNOME_Evolution_ShellComponentListener_OK; + } else { + result = GNOME_Evolution_ShellComponentListener_INVALID_URI; + } + + CORBA_exception_init (&ev); + GNOME_Evolution_ShellComponentListener_notifyResult (listener, result, &ev); + CORBA_Object_release (listener, &ev); + CORBA_exception_free (&ev); +} + +static void +create_folder (EvolutionShellComponent *shell_component, + const char *physical_uri, + const char *type, + const GNOME_Evolution_ShellComponentListener listener, + void *closure) +{ + CORBA_Environment ev; + + CORBA_exception_init (&ev); + + if (type_is_mail (type)) { + mail_get_folder (physical_uri, CAMEL_STORE_FOLDER_CREATE, create_folder_done, + CORBA_Object_duplicate (listener, &ev), mail_thread_new); + } else { + GNOME_Evolution_ShellComponentListener_notifyResult ( + listener, GNOME_Evolution_ShellComponentListener_UNSUPPORTED_TYPE, &ev); + } + + CORBA_exception_free (&ev); +} + +static void +remove_folder_done (char *uri, gboolean removed, void *data) +{ + GNOME_Evolution_ShellComponentListener listener = data; + GNOME_Evolution_ShellComponentListener_Result result; + CORBA_Environment ev; + + if (removed) + result = GNOME_Evolution_ShellComponentListener_OK; + else + result = GNOME_Evolution_ShellComponentListener_INVALID_URI; + + CORBA_exception_init (&ev); + GNOME_Evolution_ShellComponentListener_notifyResult (listener, result, &ev); + CORBA_Object_release (listener, &ev); + CORBA_exception_free (&ev); +} + +static void +remove_folder (EvolutionShellComponent *shell_component, + const char *physical_uri, + const char *type, + const GNOME_Evolution_ShellComponentListener listener, + void *closure) +{ + CORBA_Environment ev; + + CORBA_exception_init (&ev); + + if (!type_is_mail (type)) { + GNOME_Evolution_ShellComponentListener_notifyResult (listener, + GNOME_Evolution_ShellComponentListener_UNSUPPORTED_TYPE, &ev); + CORBA_exception_free (&ev); + return; + } + + mail_remove_folder (physical_uri, remove_folder_done, CORBA_Object_duplicate (listener, &ev)); + CORBA_exception_free (&ev); +} + +typedef struct _xfer_folder_data { + GNOME_Evolution_ShellComponentListener listener; + gboolean remove_source; + char *source_uri; +} xfer_folder_data; + +static void +xfer_folder_done (gboolean ok, void *data) +{ + xfer_folder_data *xfd = (xfer_folder_data *)data; + GNOME_Evolution_ShellComponentListener listener = xfd->listener; + GNOME_Evolution_ShellComponentListener_Result result; + CORBA_Environment ev; + + if (xfd->remove_source && ok) { + mail_remove_folder (xfd->source_uri, remove_folder_done, xfd->listener); + } else { + if (ok) + result = GNOME_Evolution_ShellComponentListener_OK; + else + result = GNOME_Evolution_ShellComponentListener_INVALID_URI; + + CORBA_exception_init (&ev); + GNOME_Evolution_ShellComponentListener_notifyResult (listener, result, &ev); + CORBA_Object_release (listener, &ev); + CORBA_exception_free (&ev); + } + + g_free (xfd->source_uri); + g_free (xfd); +} + +static void +xfer_folder (EvolutionShellComponent *shell_component, + const char *source_physical_uri, + const char *destination_physical_uri, + const char *type, + gboolean remove_source, + const GNOME_Evolution_ShellComponentListener listener, + void *closure) +{ + CORBA_Environment ev; + CamelFolder *source; + CamelException ex; + GPtrArray *uids; + CamelURL *src, *dst; + + d(printf("Renaming folder '%s' to dest '%s' type '%s'\n", source_physical_uri, destination_physical_uri, type)); + + CORBA_exception_init (&ev); + + if (!type_is_mail (type)) { + GNOME_Evolution_ShellComponentListener_notifyResult (listener, + GNOME_Evolution_ShellComponentListener_UNSUPPORTED_TYPE, &ev); + return; + } + + src = camel_url_new(source_physical_uri, NULL); + if (src == NULL) { + GNOME_Evolution_ShellComponentListener_notifyResult (listener, GNOME_Evolution_ShellComponentListener_INVALID_URI, &ev); + return; + } + + dst = camel_url_new(destination_physical_uri, NULL); + if (dst == NULL) { + camel_url_free(src); + GNOME_Evolution_ShellComponentListener_notifyResult (listener, GNOME_Evolution_ShellComponentListener_INVALID_URI, &ev); + return; + } + + if (camel_url_get_param(dst, "noselect") != NULL) { + camel_url_free(src); + camel_url_free(dst); + GNOME_Evolution_ShellComponentListener_notifyResult (listener, + GNOME_Evolution_ShellComponentListener_UNSUPPORTED_OPERATION, &ev); + return; + } + + camel_exception_init (&ex); + + /* If we are really doing a rename, implement it as a rename */ + if (remove && strcmp(src->protocol, dst->protocol) == 0) { + char *sname, *dname; + CamelStore *store; + + if (src->fragment) + sname = src->fragment; + else { + if (src->path && *src->path) + sname = src->path+1; + else + sname = ""; + } + + if (dst->fragment) + dname = dst->fragment; + else { + if (dst->path && *dst->path) + dname = dst->path+1; + else + dname = ""; + } + + store = camel_session_get_store(session, source_physical_uri, &ex); + if (store != NULL) + camel_store_rename_folder(store, sname, dname, &ex); + + if (camel_exception_is_set(&ex)) + GNOME_Evolution_ShellComponentListener_notifyResult (listener, GNOME_Evolution_ShellComponentListener_INVALID_URI, &ev); + else { + /* Since the shell doesn't play nice with local folders, we have to do this manually */ + mail_vfolder_rename_uri(store, source_physical_uri, destination_physical_uri); + mail_filter_rename_uri(store, source_physical_uri, destination_physical_uri); + GNOME_Evolution_ShellComponentListener_notifyResult (listener, GNOME_Evolution_ShellComponentListener_OK, &ev); + } + camel_object_unref((CamelObject *)store); + } else { + source = mail_tool_uri_to_folder (source_physical_uri, 0, &ex); + + if (source) { + xfer_folder_data *xfd; + + xfd = g_new0 (xfer_folder_data, 1); + xfd->remove_source = remove_source; + xfd->source_uri = g_strdup (source_physical_uri); + xfd->listener = CORBA_Object_duplicate (listener, &ev); + + uids = camel_folder_get_uids (source); + mail_transfer_messages (source, uids, remove_source, destination_physical_uri, CAMEL_STORE_FOLDER_CREATE, xfer_folder_done, xfd); + camel_object_unref (CAMEL_OBJECT (source)); + } else + GNOME_Evolution_ShellComponentListener_notifyResult (listener, GNOME_Evolution_ShellComponentListener_INVALID_URI, &ev); + } + + CORBA_exception_free (&ev); + camel_exception_clear (&ex); + + camel_url_free(src); + camel_url_free(dst); +} + +static void +configure_folder_popup(BonoboUIComponent *component, void *user_data, const char *cname) +{ + char *uri = user_data; + + /* FIXME: re-implement */ + + if (strncmp(uri, "vfolder:", 8) == 0) + vfolder_edit_rule(uri); +#if 0 + else { + struct _EMFolderBrowser *fb = folder_browser_factory_get_browser(uri); + + if (fb) + configure_folder(component, fb, cname); + else + mail_local_reconfigure_folder(uri, NULL, NULL); + } +#endif +} + +static void +populate_folder_context_menu (EvolutionShellComponent *shell_component, + BonoboUIComponent *uic, + const char *physical_uri, + const char *type, + void *closure) +{ +#ifdef TRANSLATORS_ONLY + static char popup_xml_i18n[] = {N_("Properties..."), N_("Change this folder's properties")}; +#endif + static char popup_xml[] = + "<menuitem name=\"ChangeFolderPropertiesPopUp\" verb=\"ChangeFolderPropertiesPopUp\"" + " _label=\"Properties...\" _tip=\"Change this folder's properties\"/>"; + + if (!type_is_mail (type)) + return; + + /* FIXME: handle other types */ + + /* the unmatched test is a bit of a hack but it works */ + if ((strncmp(physical_uri, "vfolder:", 8) == 0 + && strstr(physical_uri, "#" CAMEL_UNMATCHED_NAME) == NULL) + || strncmp(physical_uri, "file:", 5) == 0) { + bonobo_ui_component_add_verb_full(uic, "ChangeFolderPropertiesPopUp", + g_cclosure_new(G_CALLBACK(configure_folder_popup), + g_strdup(physical_uri), (GClosureNotify)g_free)); + bonobo_ui_component_set_translate (uic, EVOLUTION_SHELL_COMPONENT_POPUP_PLACEHOLDER, popup_xml, NULL); + } +} + +static void +unpopulate_folder_context_menu (EvolutionShellComponent *shell_component, + BonoboUIComponent *uic, + const char *physical_uri, + const char *type, + void *closure) +{ + if (!type_is_mail (type)) + return; + + /* FIXME: handle other types */ + + /* the unmatched test is a bit of a hack but it works */ + if ((strncmp(physical_uri, "vfolder:", 8) == 0 + && strstr(physical_uri, "#" CAMEL_UNMATCHED_NAME) == NULL) + || strncmp(physical_uri, "file:", 5) == 0) { + bonobo_ui_component_rm (uic, EVOLUTION_SHELL_COMPONENT_POPUP_PLACEHOLDER "/ChangeFolderPropertiesPopUp", NULL); + } +} + +static char * +get_dnd_selection (EvolutionShellComponent *shell_component, + const char *physical_uri, + int type, + int *format_return, + const char **selection_return, + int *selection_length_return, + void *closure) +{ + d(printf ("should get dnd selection for %s\n", physical_uri)); + + return NULL; +} + +/* Destination side DnD */ +static CORBA_boolean +destination_folder_handle_motion (EvolutionShellComponentDndDestinationFolder *folder, + const char *physical_uri, + const char *folder_type, + const GNOME_Evolution_ShellComponentDnd_DestinationFolder_Context *destination_context, + GNOME_Evolution_ShellComponentDnd_Action *suggested_action_return, + gpointer user_data) +{ + const char *noselect; + CamelURL *url; + + url = camel_url_new (physical_uri, NULL); + noselect = url ? camel_url_get_param (url, "noselect") : NULL; + + if (noselect && !strcasecmp (noselect, "yes")) + /* uh, no way to say "illegal" */ + *suggested_action_return = GNOME_Evolution_ShellComponentDnd_ACTION_DEFAULT; + else + *suggested_action_return = GNOME_Evolution_ShellComponentDnd_ACTION_MOVE; + + if (url) + camel_url_free (url); + + return TRUE; +} + +static gboolean +message_rfc822_dnd (CamelFolder *dest, CamelStream *stream, CamelException *ex) +{ + CamelMimeParser *mp; + gboolean handled = FALSE; + + mp = camel_mime_parser_new (); + camel_mime_parser_scan_from (mp, TRUE); + camel_mime_parser_init_with_stream (mp, stream); + + while (camel_mime_parser_step (mp, 0, 0) == CAMEL_MIME_PARSER_STATE_FROM) { + CamelMessageInfo *info; + CamelMimeMessage *msg; + + handled = TRUE; + + msg = camel_mime_message_new (); + if (camel_mime_part_construct_from_parser (CAMEL_MIME_PART (msg), mp) == -1) { + camel_object_unref (msg); + handled = FALSE; + break; + } + + /* append the message to the folder... */ + info = g_new0 (CamelMessageInfo, 1); + camel_folder_append_message (dest, msg, info, NULL, ex); + camel_object_unref (msg); + + if (camel_exception_is_set (ex)) { + handled = FALSE; + break; + } + + /* skip over the FROM_END state */ + camel_mime_parser_step (mp, 0, 0); + } + + camel_object_unref (mp); + + return handled; +} + +static CORBA_boolean +destination_folder_handle_drop (EvolutionShellComponentDndDestinationFolder *dest_folder, + const char *physical_uri, + const char *folder_type, + const GNOME_Evolution_ShellComponentDnd_DestinationFolder_Context *destination_context, + const GNOME_Evolution_ShellComponentDnd_Action action, + const GNOME_Evolution_ShellComponentDnd_Data *data, + gpointer user_data) +{ + char *tmp, *url, **urls; + gboolean retval = FALSE; + const char *noselect; + CamelFolder *folder; + CamelStream *stream; + CamelException ex; + GPtrArray *uids; + CamelURL *uri; + int i, type, fd; + + if (action == GNOME_Evolution_ShellComponentDnd_ACTION_LINK) + return FALSE; /* we can't create links */ + + /* this means the drag was cancelled */ + if (!data->bytes._buffer || data->bytes._length == -1) + return FALSE; + + uri = camel_url_new (physical_uri, NULL); + noselect = uri ? camel_url_get_param (uri, "noselect") : NULL; + if (noselect && !strcasecmp (noselect, "yes")) { + camel_url_free (uri); + return FALSE; + } + + if (uri) + camel_url_free (uri); + + d(printf ("in destination_folder_handle_drop (%s)\n", physical_uri)); + + for (type = 0; accepted_dnd_types[type]; type++) + if (!strcmp (destination_context->dndType, accepted_dnd_types[type])) + break; + + camel_exception_init (&ex); + + /* if this is a local vtrash folder, then it's uri is vtrash:file:/ */ + if (type_is_vtrash (folder_type) && !strncmp (physical_uri, "file:", 5)) + physical_uri = "vtrash:file:/"; + + /* if this is a local vjunk folder, then it's uri is vjunk:file:/ */ + if (type_is_vjunk (folder_type) && !strncmp (physical_uri, "file:", 5)) + physical_uri = "vjunk:file:/"; + + switch (type) { + case ACCEPTED_DND_TYPE_TEXT_URI_LIST: + folder = mail_tool_uri_to_folder (physical_uri, 0, &ex); + if (!folder) { + camel_exception_clear (&ex); + return FALSE; + } + + tmp = g_strndup (data->bytes._buffer, data->bytes._length); + urls = g_strsplit (tmp, "\n", 0); + g_free (tmp); + + retval = TRUE; + for (i = 0; urls[i] != NULL && retval; i++) { + /* get the path component */ + url = g_strstrip (urls[i]); + + uri = camel_url_new (url, NULL); + g_free (url); + + if (!uri) + continue; + + url = uri->path; + uri->path = NULL; + camel_url_free (uri); + + fd = open (url, O_RDONLY); + if (fd == -1) { + g_free (url); + /* FIXME: okay, so what do we do in this case? */ + continue; + } + + stream = camel_stream_fs_new_with_fd (fd); + retval = message_rfc822_dnd (folder, stream, &ex); + camel_object_unref (stream); + camel_object_unref (folder); + + if (action == GNOME_Evolution_ShellComponentDnd_ACTION_MOVE && retval) + unlink (url); + + g_free (url); + } + + g_free (urls); + break; + case ACCEPTED_DND_TYPE_MESSAGE_RFC822: + folder = mail_tool_uri_to_folder (physical_uri, 0, &ex); + if (!folder) { + camel_exception_clear (&ex); + return FALSE; + } + + /* write the message(s) out to a CamelStream so we can use it */ + stream = camel_stream_mem_new (); + camel_stream_write (stream, data->bytes._buffer, data->bytes._length); + camel_stream_reset (stream); + + retval = message_rfc822_dnd (folder, stream, &ex); + camel_object_unref (stream); + camel_object_unref (folder); + break; + case ACCEPTED_DND_TYPE_X_EVOLUTION_MESSAGE: + folder = mail_tools_x_evolution_message_parse (data->bytes._buffer, + data->bytes._length, + &uids); + + if (!folder) + return FALSE; + + mail_transfer_messages (folder, uids, + action == GNOME_Evolution_ShellComponentDnd_ACTION_MOVE, + physical_uri, 0, NULL, NULL); + + camel_object_unref (folder); + break; + default: + break; + } + + camel_exception_clear (&ex); + + return retval; +} + + +static struct { + char *name, **uri; + CamelFolder **folder; +} standard_folders[] = { + { "Drafts", &default_drafts_folder_uri, &drafts_folder }, + { "Outbox", &default_outbox_folder_uri, &outbox_folder }, + { "Sent", &default_sent_folder_uri, &sent_folder }, +}; + +static void +unref_standard_folders (void) +{ + int i; + + for (i = 0; i < sizeof (standard_folders) / sizeof (standard_folders[0]); i++) { + if (standard_folders[i].folder) { + CamelFolder *folder = *standard_folders[i].folder; + + *standard_folders[i].folder = NULL; + + if (CAMEL_OBJECT (folder)->ref_count == 1) + d(printf ("About to finalise folder %s\n", folder->full_name)); + else + d(printf ("Folder %s still has %d extra ref%s on it\n", folder->full_name, + CAMEL_OBJECT (folder)->ref_count - 1, + CAMEL_OBJECT (folder)->ref_count - 1 == 1 ? "" : "s")); + + camel_object_unref (CAMEL_OBJECT (folder)); + } + } +} + +static void +got_folder (char *uri, CamelFolder *folder, void *data) +{ + CamelFolder **fp = data; + + *fp = folder; + + if (folder) { + camel_object_ref (CAMEL_OBJECT (folder)); + + /* emit a changed event, this is a little hack so that the folderinfo cache + will update knowing whether this is the outbox_folder or not, etc */ + if (folder == outbox_folder) { + CamelFolderChangeInfo *changes = camel_folder_change_info_new(); + + camel_object_trigger_event((CamelObject *)folder, "folder_changed", changes); + camel_folder_change_info_free(changes); + } + } +} + +static void +shell_client_destroy (GtkObject *object) +{ + global_shell_client = NULL; +} + +static void +owner_set_cb (EvolutionShellComponent *shell_component, + EvolutionShellClient *shell_client, + const char *evolution_homedir, + gpointer user_data) +{ + GNOME_Evolution_Shell corba_shell; + EAccountList *accounts; + int i; + + /* FIXME: should we ref this? */ + global_shell_client = shell_client; + g_object_weak_ref ((GObject *) shell_client, (GWeakNotify) shell_client_destroy, NULL); + + evolution_dir = g_strdup (evolution_homedir); + mail_session_init (); + + async_event = mail_async_event_new(); + + storages_hash = g_hash_table_new (NULL, NULL); + + corba_shell = evolution_shell_client_corba_objref (shell_client); + + for (i = 0; i < sizeof (standard_folders) / sizeof (standard_folders[0]); i++) + *standard_folders[i].uri = g_strdup_printf ("file://%s/local/%s", evolution_dir, standard_folders[i].name); + + vfolder_load_storage(corba_shell); + + accounts = mail_config_get_accounts (); + mail_load_storages (corba_shell, accounts); + + mail_local_storage_startup (shell_client, evolution_dir); + mail_importer_init (shell_client); + + for (i = 0; i < sizeof (standard_folders) / sizeof (standard_folders[0]); i++) { + mail_msg_wait (mail_get_folder (*standard_folders[i].uri, CAMEL_STORE_FOLDER_CREATE, + got_folder, standard_folders[i].folder, mail_thread_new)); + } + + mail_autoreceive_setup (); + + { + /* setup the global quick-search context */ + char *user = g_strdup_printf ("%s/searches.xml", evolution_dir); + char *system = g_strdup (EVOLUTION_PRIVDATADIR "/searchtypes.xml"); + + search_context = rule_context_new (); + g_object_set_data_full(G_OBJECT(search_context), "user", user, g_free); + g_object_set_data_full(G_OBJECT(search_context), "system", system, g_free); + + rule_context_add_part_set (search_context, "partset", filter_part_get_type (), + rule_context_add_part, rule_context_next_part); + + rule_context_add_rule_set (search_context, "ruleset", filter_rule_get_type (), + rule_context_add_rule, rule_context_next_rule); + + rule_context_load (search_context, system, user); + } + + if (mail_config_is_corrupt ()) { + GtkWidget *dialog; + + dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE, + _("Some of your mail settings seem corrupt, " + "please check that everything is in order.")); + g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog); + gtk_widget_show (dialog); + } + + /* Everything should be ready now */ + evolution_folder_info_notify_ready (); +} + +static void +free_storage (gpointer service, gpointer storage, gpointer data) +{ + if (service) { + mail_note_store_remove((CamelStore *)service); + camel_service_disconnect (CAMEL_SERVICE (service), TRUE, NULL); + camel_object_unref (CAMEL_OBJECT (service)); + } + + if (storage) + bonobo_object_unref (BONOBO_OBJECT (storage)); +} + +static void +debug_cb (EvolutionShellComponent *shell_component, gpointer user_data) +{ + extern gboolean camel_verbose_debug; + + camel_verbose_debug = 1; +} + +static void +interactive_cb (EvolutionShellComponent *shell_component, gboolean on, + gulong new_view_xid, gpointer user_data) +{ + mail_session_set_interactive (on); + + if (on) + /* how do we get the parent window? */ + e_msg_composer_check_autosave(NULL); +} + +static void +handle_external_uri_cb (EvolutionShellComponent *shell_component, + const char *uri, + void *data) +{ + if (strncmp (uri, "mailto:", 7) != 0) { + /* FIXME: Exception? The EvolutionShellComponent object should + give me a chance to do so, but currently it doesn't. */ + g_warning ("Invalid URI requested to mail component -- %s", uri); + return; + } + + /* FIXME: Sigh. This shouldn't be here. But the code is messy, so + I'll just put it here anyway. */ + em_utils_compose_new_message_with_mailto(NULL, uri); +} + +static void +user_create_new_item_cb (EvolutionShellComponent *shell_component, + const char *id, + const char *parent_folder_physical_uri, + const char *parent_folder_type, + gpointer data) +{ + if (!strcmp (id, "message")) { + em_utils_compose_new_message_with_mailto(NULL, NULL); + /*send_to_url (NULL, parent_folder_physical_uri);*/ + return; + } else if (!strcmp (id, "post")) { + em_utils_post_to_url (NULL, parent_folder_physical_uri); + return; + } + + g_warning ("Don't know how to create item of type \"%s\"", id); +} + +static void owner_unset_cb (EvolutionShellComponent *shell_component, gpointer user_data); + +/* Table for signal handler setup/cleanup */ +static struct { + char *sig; + GCallback func; + int hand; +} shell_component_handlers[] = { + { "owner_set", G_CALLBACK(owner_set_cb), }, + { "owner_unset", G_CALLBACK(owner_unset_cb), }, + { "debug", G_CALLBACK(debug_cb), }, + { "interactive", G_CALLBACK(interactive_cb) }, + { "destroy", G_CALLBACK(owner_unset_cb), }, + { "handle_external_uri", G_CALLBACK(handle_external_uri_cb), }, + { "user_create_new_item", G_CALLBACK(user_create_new_item_cb) } +}; + +static void +owner_unset_cb (EvolutionShellComponent *shell_component, gpointer user_data) +{ + CORBA_Environment ev; + GConfClient *gconf; + int i; + EIterator *it; + + gconf = mail_config_get_gconf_client (); + + for (i=0;i<sizeof(shell_component_handlers)/sizeof(shell_component_handlers[0]);i++) + g_signal_handler_disconnect((GtkObject *)shell_component, shell_component_handlers[i].hand); + + if (gconf_client_get_bool (gconf, "/apps/evolution/mail/trash/empty_on_exit", NULL)) + em_utils_empty_trash(NULL); + + unref_standard_folders (); + mail_local_storage_shutdown (); + mail_importer_uninit (); + + global_shell_client = NULL; + mail_session_set_interactive (FALSE); + + g_object_unref (search_context); + search_context = NULL; + + /* force de-activate of all controls, tho only one should be active anyway? */ + CORBA_exception_init(&ev); + for (it = e_list_get_iterator(folder_browser_factory_get_control_list()); + e_iterator_is_valid(it); + e_iterator_next(it)) { + Bonobo_Control_activate(bonobo_object_corba_objref((BonoboObject *)e_iterator_get(it)), + FALSE, &ev); + } + CORBA_exception_free(&ev); + + for (i= 0;i<3;i++) { + /* need to flush any outstanding tasks before proceeding */ + + /* NOTE!! This may cause a deadlock situation, if we were + called from a deeper main loop than the top level + - is there a way to detect this? + - is this a very big problem? + FIXME: should use semaphores or something to wait rather than polling */ + while (e_thread_busy(NULL) || mail_msg_active(-1)) { + if (g_main_context_pending(NULL)) + g_main_context_iteration(NULL, TRUE); + else + usleep(100000); + } + + switch(i) { + case 0: + mail_vfolder_shutdown(); + break; + case 1: + if (mail_async_event_destroy(async_event) == -1) { + g_warning("Cannot destroy async event: would deadlock"); + g_warning(" system may be unstable at exit"); + } + break; + case 2: + g_hash_table_foreach (storages_hash, free_storage, NULL); + g_hash_table_destroy (storages_hash); + storages_hash = NULL; + break; + } + } +} + +static void +send_receive_cb (EvolutionShellComponent *shell_component, + gboolean show_dialog, + void *data) +{ + EAccount *account; + GtkWidget *dialog; + + /* FIXME: configure_mail() should be changed to work without a + FolderBrowser, and then we will be able to call configure_mail from + here properly. */ + if (!mail_config_is_configured () /* && !configure_mail (fb) */) + return; + + account = mail_config_get_default_account (); + if (!account || !account->transport->url) { + dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, + _("You have not set a mail transport method")); + g_signal_connect (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog); + gtk_widget_show (dialog); + } else { + dialog = mail_send_receive (); + e_dialog_set_transient_for_xid((GtkWindow *)dialog, evolution_shell_component_get_parent_view_xid(shell_component)); + } +} + +static gboolean +request_quit (EvolutionShellComponent *shell_component, + void *closure) +{ + GtkWidget *dialog; + int resp; + + if (!e_msg_composer_request_close_all ()) + return FALSE; + + if (!outbox_folder || !camel_folder_get_message_count (outbox_folder)) + return TRUE; + + dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_INFO, GTK_BUTTONS_YES_NO, + _("You have unsent messages, do you wish to quit anyway?")); + gtk_dialog_set_default_response((GtkDialog *)dialog, GTK_RESPONSE_NO); + resp = gtk_dialog_run((GtkDialog *)dialog); + gtk_widget_destroy(dialog); + + return resp == GTK_RESPONSE_YES; +} + +static BonoboObject * +create_component (void) +{ + EvolutionShellComponentDndDestinationFolder *destination_interface; + MailOfflineHandler *offline_handler; + GdkPixbuf *icon; + int i; + + shell_component = evolution_shell_component_new (folder_types, + schema_types, + create_view, + create_folder, + remove_folder, + xfer_folder, + populate_folder_context_menu, + unpopulate_folder_context_menu, + get_dnd_selection, + request_quit, + NULL); + + g_signal_connect((shell_component), "send_receive", + G_CALLBACK (send_receive_cb), NULL); + + destination_interface = evolution_shell_component_dnd_destination_folder_new (destination_folder_handle_motion, + destination_folder_handle_drop, + shell_component); + + bonobo_object_add_interface (BONOBO_OBJECT (shell_component), + BONOBO_OBJECT (destination_interface)); + + icon = gdk_pixbuf_new_from_file (EVOLUTION_ICONSDIR "/new-message.xpm", NULL); + evolution_shell_component_add_user_creatable_item (shell_component, "message", + _("New Mail Message"), _("_Mail Message"), + _("Compose a new mail message"), + "mail", 'm', + icon); + if (icon != NULL) + g_object_unref (icon); + + icon = gdk_pixbuf_new_from_file (EVOLUTION_ICONSDIR "/post-message-16.png", NULL); + evolution_shell_component_add_user_creatable_item (shell_component, "post", + _("New Message Post"), _("_Post Message"), + _("Post a new mail message"), + "mail/public", 'p', + icon); + if (icon != NULL) + g_object_unref (icon); + + for (i=0;i<sizeof(shell_component_handlers)/sizeof(shell_component_handlers[0]);i++) { + shell_component_handlers[i].hand = g_signal_connect((shell_component), + shell_component_handlers[i].sig, + shell_component_handlers[i].func, NULL); + } + + offline_handler = mail_offline_handler_new (); + bonobo_object_add_interface (BONOBO_OBJECT (shell_component), BONOBO_OBJECT (offline_handler)); + + return BONOBO_OBJECT (shell_component); +} + +static void +notify_listener (const Bonobo_Listener listener, + GNOME_Evolution_Storage_Result corba_result) +{ + CORBA_any any; + CORBA_Environment ev; + + CORBA_exception_init (&ev); + + any._type = TC_GNOME_Evolution_Storage_Result; + any._value = &corba_result; + + Bonobo_Listener_event (listener, "result", &any, &ev); + + CORBA_exception_free (&ev); +} + +static void +notify_listener_exception(const Bonobo_Listener listener, CamelException *ex) +{ + GNOME_Evolution_Storage_Result result; + + switch(camel_exception_get_id(ex)) { + case CAMEL_EXCEPTION_SERVICE_UNAVAILABLE: + result = GNOME_Evolution_Storage_NOT_ONLINE; + break; + case CAMEL_EXCEPTION_NONE: + result = GNOME_Evolution_Storage_OK; + break; + case CAMEL_EXCEPTION_FOLDER_INVALID_PATH: + case CAMEL_EXCEPTION_SERVICE_URL_INVALID: + result = GNOME_Evolution_Storage_INVALID_URI; + break; + default: + result = GNOME_Evolution_Storage_GENERIC_ERROR; + break; + } + + notify_listener(listener, result); +} + +static void +storage_create_folder (EvolutionStorage *storage, + const Bonobo_Listener listener, + const char *path, + const char *type, + const char *description, + const char *parent_physical_uri, + gpointer user_data) +{ + CamelStore *store = user_data; + CamelFolderInfo *root, *fi; + char *name; + CamelURL *url; + CamelException ex; + + /* We could just use 'path' always here? */ + + if (!type_is_mail (type)) { + notify_listener (listener, GNOME_Evolution_Storage_UNSUPPORTED_TYPE); + return; + } + + name = strrchr (path, '/'); + if (!name) { + notify_listener (listener, GNOME_Evolution_Storage_INVALID_URI); + return; + } + name++; + + /* we can not directly create folders on a vfolder store, so fudge it */ + if (CAMEL_IS_VEE_STORE(store)) { + VfolderRule *rule; + rule = vfolder_rule_new(); + + filter_rule_set_name((FilterRule *)rule, path+1); + vfolder_gui_add_rule(rule); + } else { + camel_exception_init (&ex); + if (*parent_physical_uri) { + url = camel_url_new (parent_physical_uri, NULL); + if (!url) { + notify_listener (listener, GNOME_Evolution_Storage_INVALID_URI); + return; + } + + root = camel_store_create_folder (store, url->fragment?url->fragment:url->path + 1, name, &ex); + camel_url_free (url); + } else + root = camel_store_create_folder (store, NULL, name, &ex); + + if (camel_exception_is_set (&ex)) { + notify_listener_exception(listener, &ex); + camel_exception_clear (&ex); + return; + } + + if (camel_store_supports_subscriptions (store)) { + for (fi = root; fi; fi = fi->child) + camel_store_subscribe_folder (store, fi->full_name, NULL); + } + + camel_store_free_folder_info (store, root); + } + + notify_listener (listener, GNOME_Evolution_Storage_OK); +} + +static void +storage_remove_folder_recursive (EvolutionStorage *storage, CamelStore *store, CamelFolderInfo *root, CamelException *ex) +{ + CamelFolderInfo *fi; + + /* delete all children */ + fi = root->child; + while (fi && !camel_exception_is_set (ex)) { + storage_remove_folder_recursive (storage, store, fi, ex); + fi = fi->sibling; + } + + if (!camel_exception_is_set (ex)) { + if (camel_store_supports_subscriptions (store)) + camel_store_unsubscribe_folder (store, root->full_name, NULL); + + camel_store_delete_folder (store, root->full_name, ex); + + if (!camel_exception_is_set (ex)) + evolution_storage_removed_folder (storage, root->path); + } +} + +static void +storage_remove_folder (EvolutionStorage *storage, + const Bonobo_Listener listener, + const char *path, + const char *physical_uri, + gpointer user_data) +{ + CamelStore *store = user_data; + CamelFolderInfo *root, *fi; + CamelURL *url = NULL; + CamelException ex; + char *name; + + g_warning ("storage_remove_folder: path=\"%s\"; uri=\"%s\"", path, physical_uri); + + if (!path || !*path || !physical_uri || !strncmp (physical_uri, "vtrash:", 7) || !strncmp (physical_uri, "vjunk:", 6)) { + notify_listener (listener, GNOME_Evolution_Storage_INVALID_URI); + return; + } + + url = camel_url_new (physical_uri, NULL); + if (!url) { + notify_listener (listener, GNOME_Evolution_Storage_INVALID_URI); + return; + } + + if (url->fragment) + name = url->fragment; + else if (url->path && url->path[0]) + name = url->path+1; + else + name = ""; + + camel_exception_init (&ex); + + root = camel_store_get_folder_info (store, name, CAMEL_STORE_FOLDER_INFO_FAST | + CAMEL_STORE_FOLDER_INFO_RECURSIVE, &ex); + + if (!root || camel_exception_is_set (&ex)) { + notify_listener_exception (listener, &ex); + camel_exception_clear (&ex); + camel_url_free (url); + return; + } + + /* walk the tree until we find the particular child folder we want to delete */ + fi = root; + while (fi) { + if (!strcmp (fi->full_name, name)) + break; + fi = fi->child; + } + + camel_url_free (url); + + if (!fi) { + notify_listener (listener, GNOME_Evolution_Storage_INVALID_URI); + camel_store_free_folder_info (store, root); + return; + } + + storage_remove_folder_recursive (storage, store, fi, &ex); + camel_store_free_folder_info (store, root); + if (camel_exception_is_set (&ex)) { + notify_listener_exception (listener, &ex); + camel_exception_clear (&ex); + } else { + notify_listener (listener, GNOME_Evolution_Storage_OK); + } +} + +static void +storage_xfer_folder (EvolutionStorage *storage, + const Bonobo_Listener listener, + const char *source_path, + const char *destination_path, + gboolean remove_source, + CamelStore *store) +{ + CamelException ex; + char *src, *dst; + char sep; + + d(printf("Transfer folder on store source = '%s' dest = '%s'\n", source_path, destination_path)); + + /* FIXME: this is totally not gonna work once we have namespaces */ + + /* Remap the 'path' to the camel friendly name based on the store dir separator */ + sep = store->dir_sep; + src = g_strdup(source_path[0]=='/'?source_path+1:source_path); + dst = g_strdup(destination_path[0]=='/'?destination_path+1:destination_path); + camel_exception_init (&ex); + if (remove_source) { + d(printf("trying to rename\n")); + camel_store_rename_folder(store, src, dst, &ex); + notify_listener_exception(listener, &ex); + } else { + d(printf("No remove, can't rename\n")); + /* FIXME: Implement folder 'copy' for remote stores */ + /* This exception never goes anywhere, so it doesn't need translating or using */ + notify_listener (listener, GNOME_Evolution_Storage_UNSUPPORTED_OPERATION); + } + + g_free(src); + g_free(dst); + + camel_exception_clear (&ex); +} + +static void +storage_connected (CamelStore *store, CamelFolderInfo *info, void *listener) +{ + notify_listener (listener, (info ? GNOME_Evolution_Storage_OK : + GNOME_Evolution_Storage_GENERIC_ERROR)); +} + +static void +storage_connect (EvolutionStorage *storage, + const Bonobo_Listener listener, + const char *path, + CamelStore *store) +{ + mail_note_store (CAMEL_STORE (store), NULL, storage, CORBA_OBJECT_NIL, + storage_connected, listener); +} + +static void +add_storage (const char *name, const char *uri, CamelService *store, + GNOME_Evolution_Shell corba_shell, CamelException *ex) +{ + EvolutionStorage *storage; + EvolutionStorageResult res; + + storage = evolution_storage_new (name, FALSE); + g_signal_connect(storage, "open_folder", G_CALLBACK(storage_connect), store); + g_signal_connect(storage, "create_folder", G_CALLBACK(storage_create_folder), store); + g_signal_connect(storage, "remove_folder", G_CALLBACK(storage_remove_folder), store); + g_signal_connect(storage, "xfer_folder", G_CALLBACK(storage_xfer_folder), store); + + res = evolution_storage_register_on_shell (storage, corba_shell); + + switch (res) { + case EVOLUTION_STORAGE_OK: + evolution_storage_has_subfolders (storage, "/", _("Connecting...")); + mail_hash_storage (store, storage); + /*if (auto_connect)*/ + mail_note_store ((CamelStore *) store, NULL, storage, CORBA_OBJECT_NIL, NULL, NULL); + /* falllll */ + case EVOLUTION_STORAGE_ERROR_ALREADYREGISTERED: + case EVOLUTION_STORAGE_ERROR_EXISTS: + bonobo_object_unref (BONOBO_OBJECT (storage)); + return; + default: + camel_exception_set (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot register storage with shell")); + break; + } +} + +void +mail_add_storage (CamelStore *store, const char *name, const char *uri) +{ + EvolutionShellClient *shell_client; + GNOME_Evolution_Shell shell; + CamelException ex; + + g_return_if_fail (CAMEL_IS_STORE (store)); + + shell_client = evolution_shell_component_get_owner (shell_component); + shell = evolution_shell_client_corba_objref (shell_client); + + camel_exception_init (&ex); + + if (name == NULL) { + char *service_name; + + service_name = camel_service_get_name ((CamelService *) store, TRUE); + add_storage (service_name, uri, (CamelService *) store, shell, &ex); + g_free (service_name); + } else { + add_storage (name, uri, (CamelService *) store, shell, &ex); + } + + camel_exception_clear (&ex); +} + +void +mail_load_storage_by_uri (GNOME_Evolution_Shell shell, const char *uri, const char *name) +{ + CamelException ex; + CamelService *store; + CamelProvider *prov; + + camel_exception_init (&ex); + + /* Load the service (don't connect!). Check its provider and + * see if this belongs in the shell's folder list. If so, add + * it. + */ + + prov = camel_session_get_provider (session, uri, &ex); + if (prov == NULL) { + /* FIXME: real error dialog */ + g_warning ("couldn't get service %s: %s\n", uri, + camel_exception_get_description (&ex)); + camel_exception_clear (&ex); + return; + } + + if (!(prov->flags & CAMEL_PROVIDER_IS_STORAGE) || + (prov->flags & CAMEL_PROVIDER_IS_EXTERNAL)) + return; + + store = camel_session_get_service (session, uri, CAMEL_PROVIDER_STORE, &ex); + if (store == NULL) { + /* FIXME: real error dialog */ + g_warning ("couldn't get service %s: %s\n", uri, + camel_exception_get_description (&ex)); + camel_exception_clear (&ex); + return; + } + + if (name == NULL) { + char *service_name; + + service_name = camel_service_get_name (store, TRUE); + add_storage (service_name, uri, store, shell, &ex); + g_free (service_name); + } else + add_storage (name, uri, store, shell, &ex); + + if (camel_exception_is_set (&ex)) { + /* FIXME: real error dialog */ + g_warning ("Cannot load storage: %s", + camel_exception_get_description (&ex)); + camel_exception_clear (&ex); + } + + camel_object_unref (CAMEL_OBJECT (store)); +} + +void +mail_load_storages (GNOME_Evolution_Shell shell, EAccountList *accounts) +{ + CamelException ex; + EIterator *iter; + + camel_exception_init (&ex); + + /* Load each service (don't connect!). Check its provider and + * see if this belongs in the shell's folder list. If so, add + * it. + */ + + iter = e_list_get_iterator ((EList *) accounts); + while (e_iterator_is_valid (iter)) { + EAccountService *service; + EAccount *account; + const char *name; + + account = (EAccount *) e_iterator_get (iter); + service = account->source; + name = account->name; + + if (account->enabled && service->url != NULL) + mail_load_storage_by_uri (shell, service->url, name); + + e_iterator_next (iter); + } + + g_object_unref (iter); +} + +void +mail_hash_storage (CamelService *store, EvolutionStorage *storage) +{ + camel_object_ref (CAMEL_OBJECT (store)); + g_hash_table_insert (storages_hash, store, storage); +} + +EvolutionStorage * +mail_lookup_storage (CamelStore *store) +{ + EvolutionStorage *storage; + + /* Because the storages_hash holds a reference to each store + * used as a key in it, none of them will ever be gc'ed, meaning + * any call to camel_session_get_{service,store} with the same + * URL will always return the same object. So this works. + */ + + storage = g_hash_table_lookup (storages_hash, store); + if (storage) + bonobo_object_ref (BONOBO_OBJECT (storage)); + + return storage; +} + +static void +store_disconnect(CamelStore *store, void *event_data, void *data) +{ + camel_service_disconnect (CAMEL_SERVICE (store), TRUE, NULL); + camel_object_unref (CAMEL_OBJECT (store)); +} + +void +mail_remove_storage (CamelStore *store) +{ + EvolutionStorage *storage; + EvolutionShellClient *shell_client; + GNOME_Evolution_Shell corba_shell; + + /* Because the storages_hash holds a reference to each store + * used as a key in it, none of them will ever be gc'ed, meaning + * any call to camel_session_get_{service,store} with the same + * URL will always return the same object. So this works. + */ + + storage = g_hash_table_lookup (storages_hash, store); + if (!storage) + return; + + g_hash_table_remove (storages_hash, store); + + /* so i guess potentially we could have a race, add a store while one + being removed. ?? */ + mail_note_store_remove(store); + + shell_client = evolution_shell_component_get_owner (shell_component); + corba_shell = evolution_shell_client_corba_objref(shell_client); + + evolution_storage_deregister_on_shell (storage, corba_shell); + + mail_async_event_emit(async_event, MAIL_ASYNC_THREAD, (MailAsyncFunc)store_disconnect, store, NULL, NULL); +} + +void +mail_remove_storage_by_uri (const char *uri) +{ + CamelProvider *prov; + CamelService *store; + + prov = camel_session_get_provider (session, uri, NULL); + if (!prov) + return; + if (!(prov->flags & CAMEL_PROVIDER_IS_STORAGE) || + (prov->flags & CAMEL_PROVIDER_IS_EXTERNAL)) + return; + + store = camel_session_get_service (session, uri, CAMEL_PROVIDER_STORE, NULL); + if (store != NULL) { + mail_remove_storage (CAMEL_STORE (store)); + camel_object_unref (CAMEL_OBJECT (store)); + } +} + +int +mail_storages_count (void) +{ + return g_hash_table_size (storages_hash); +} + +void +mail_storages_foreach (GHFunc func, gpointer data) +{ + g_hash_table_foreach (storages_hash, func, data); +} + + +#define FACTORY_ID "OAFIID:GNOME_Evolution_Mail_ControlFactory" + +#define MAIL_CONFIG_IID "OAFIID:GNOME_Evolution_MailConfig" +#define WIZARD_IID "OAFIID:GNOME_Evolution_Mail_Wizard" +#define FOLDER_INFO_IID "OAFIID:GNOME_Evolution_FolderInfo" +#define COMPOSER_IID "OAFIID:GNOME_Evolution_Mail_Composer" + +static BonoboObject * +factory (BonoboGenericFactory *factory, + const char *component_id, + void *closure) +{ + if (strcmp (component_id, COMPONENT_ID) == 0) + return create_component(); + else if (strcmp(component_id, MAIL_CONFIG_IID) == 0) + return (BonoboObject *)g_object_new (evolution_mail_config_get_type (), NULL); + else if (strcmp(component_id, FOLDER_INFO_IID) == 0) + return evolution_folder_info_new(); + else if (strcmp(component_id, WIZARD_IID) == 0) + return evolution_mail_config_wizard_new(); + else if (strcmp (component_id, MAIL_ACCOUNTS_CONTROL_ID) == 0 + || strcmp (component_id, MAIL_PREFERENCES_CONTROL_ID) == 0 + || strcmp (component_id, MAIL_COMPOSER_PREFS_CONTROL_ID) == 0) + return mail_config_control_factory_cb (factory, component_id, evolution_shell_client_corba_objref (global_shell_client)); + else if (strcmp(component_id, COMPOSER_IID) == 0) + return (BonoboObject *)evolution_composer_new(em_utils_composer_send_cb, em_utils_composer_save_draft_cb); + + g_warning (FACTORY_ID ": Don't know what to do with %s", component_id); + return NULL; +} + +static Bonobo_Unknown +make_factory (PortableServer_POA poa, const char *iid, gpointer impl_ptr, CORBA_Environment *ev) +{ + static int init = 0; + + if (!init) { + /* init ? */ + mail_config_init (); + mail_msg_init (); + init = 1; + } + + return bonobo_shlib_factory_std (FACTORY_ID, poa, impl_ptr, factory, NULL, ev); +} + +static BonoboActivationPluginObject plugin_list[] = { + {FACTORY_ID, make_factory}, + { NULL } +}; +const BonoboActivationPlugin Bonobo_Plugin_info = { + plugin_list, "Evolution Mail component factory" +}; + diff --git a/mail/em-folder-view.c b/mail/em-folder-view.c index 0c52a49fe1..2b61169518 100644 --- a/mail/em-folder-view.c +++ b/mail/em-folder-view.c @@ -60,7 +60,6 @@ #include "em-format-html-display.h" #include "em-format-html-print.h" -#include "em-folder-selection.h" #include "em-folder-view.h" #include "em-message-browser.h" #include "message-list.h" @@ -294,7 +293,7 @@ em_folder_view_open_selected(EMFolderView *emfv) if (em_utils_folder_is_drafts(emfv->folder, emfv->folder_uri) || em_utils_folder_is_outbox(emfv->folder, emfv->folder_uri)) { - em_utils_edit_messages (emfv->folder, uids); + em_utils_edit_messages((GtkWidget *)emfv, emfv->folder, uids); } else { /* TODO: have an em_utils_open_messages call? */ @@ -421,7 +420,7 @@ emfv_popup_resend(GtkWidget *w, EMFolderView *emfv) return; uids = message_list_get_selected(emfv->list); - em_utils_edit_messages (emfv->folder, uids); + em_utils_edit_messages((GtkWidget *)emfv, emfv->folder, uids); } static void @@ -466,7 +465,7 @@ emfv_popup_forward(GtkWidget *w, EMFolderView *emfv) return; uids = message_list_get_selected(emfv->list); - em_utils_forward_messages (emfv->folder, uids); + em_utils_forward_messages((GtkWidget *)emfv, emfv->folder, uids); } static void @@ -524,6 +523,18 @@ emfv_popup_mark_unimportant(GtkWidget *w, EMFolderView *emfv) } static void +emfv_popup_mark_junk (GtkWidget *w, EMFolderView *emfv) +{ + mail_mark_junk (emfv->folder, emfv->list, TRUE); +} + +static void +emfv_popup_mark_nojunk (GtkWidget *w, EMFolderView *emfv) +{ + mail_mark_junk (emfv->folder, emfv->list, FALSE); +} + +static void emfv_popup_delete(GtkWidget *w, EMFolderView *emfv) { GPtrArray *uids; @@ -543,52 +554,16 @@ emfv_popup_undelete(GtkWidget *w, EMFolderView *emfv) em_folder_view_mark_selected(emfv, CAMEL_MESSAGE_DELETED, 0); } -struct _move_data { - EMFolderView *emfv; - GPtrArray *uids; - int delete; -}; - -static void -emfv_popup_move_cb(const char *uri, void *data) -{ - struct _move_data *d = data; - - if (uri) - mail_transfer_messages(d->emfv->folder, d->uids, d->delete, uri, 0, NULL, NULL); - else - em_utils_uids_free(d->uids); - - g_object_unref(d->emfv); - g_free(d); -} - static void emfv_popup_move(GtkWidget *w, EMFolderView *emfv) { - struct _move_data *d; - - d = g_malloc(sizeof(*d)); - d->emfv = emfv; - g_object_ref(emfv); - d->uids = message_list_get_selected(emfv->list); - d->delete = TRUE; - - em_select_folder ((GtkWindow *) emfv, _("Select folder"), NULL, NULL, emfv_popup_move_cb, d); + /* FIXME */ } static void emfv_popup_copy(GtkWidget *w, EMFolderView *emfv) { - struct _move_data *d; - - d = g_malloc(sizeof(*d)); - d->emfv = emfv; - g_object_ref(emfv); - d->uids = message_list_get_selected(emfv->list); - d->delete = FALSE; - - em_select_folder ((GtkWindow *) emfv, _("Select folder"), NULL, NULL, emfv_popup_move_cb, d); + /* FIXME */ } static void @@ -644,13 +619,11 @@ EMFV_POPUP_AUTO_TYPE(vfolder_type_current, emfv_popup_vfolder_subject, AUTO_SUBJ EMFV_POPUP_AUTO_TYPE(vfolder_type_current, emfv_popup_vfolder_sender, AUTO_FROM) EMFV_POPUP_AUTO_TYPE(vfolder_type_current, emfv_popup_vfolder_recipients, AUTO_TO) EMFV_POPUP_AUTO_TYPE(vfolder_type_current, emfv_popup_vfolder_mlist, AUTO_MLIST) -EMFV_POPUP_AUTO_TYPE(vfolder_type_current, emfv_popup_vfolder_thread, AUTO_THREAD) EMFV_POPUP_AUTO_TYPE(filter_type_current, emfv_popup_filter_subject, AUTO_SUBJECT) EMFV_POPUP_AUTO_TYPE(filter_type_current, emfv_popup_filter_sender, AUTO_FROM) EMFV_POPUP_AUTO_TYPE(filter_type_current, emfv_popup_filter_recipients, AUTO_TO) EMFV_POPUP_AUTO_TYPE(filter_type_current, emfv_popup_filter_mlist, AUTO_MLIST) -EMFV_POPUP_AUTO_TYPE(filter_type_current, emfv_popup_filter_thread, AUTO_THREAD) /* TODO: Move some of these to be 'standard' menu's */ @@ -676,6 +649,8 @@ static EMPopupItem emfv_popup_menu[] = { { EM_POPUP_ITEM, "30.emfv.01", N_("Mark as _Unread"), G_CALLBACK(emfv_popup_mark_unread), NULL, "mail-new.xpm", EM_POPUP_SELECT_MARK_UNREAD }, { EM_POPUP_ITEM, "30.emfv.02", N_("Mark as _Important"), G_CALLBACK(emfv_popup_mark_important), NULL, "priority-high.xpm", EM_POPUP_SELECT_MARK_IMPORTANT }, { EM_POPUP_ITEM, "30.emfv.03", N_("_Mark as Unimportant"), G_CALLBACK(emfv_popup_mark_unimportant), NULL, NULL, EM_POPUP_SELECT_MARK_UNIMPORTANT }, + { EM_POPUP_ITEM, "30.emfv.04", N_("Mark as _Junk"), G_CALLBACK(emfv_popup_mark_junk), NULL, NULL, EM_POPUP_SELECT_MARK_JUNK }, + { EM_POPUP_ITEM, "30.emfv.05", N_("Mark as _Not Junk"), G_CALLBACK(emfv_popup_mark_nojunk), NULL, NULL, EM_POPUP_SELECT_MARK_NOJUNK }, { EM_POPUP_BAR, "40.emfv" }, { EM_POPUP_ITEM, "40.emfv.00", N_("_Delete"), G_CALLBACK(emfv_popup_delete), NULL, "evolution-trash-mini.png", EM_POPUP_SELECT_DELETE }, @@ -703,17 +678,13 @@ static EMPopupItem emfv_popup_menu[] = { { EM_POPUP_ITEM, "90.filter.00/00.02", N_("VFolder on _Recipients"), G_CALLBACK(emfv_popup_vfolder_recipients), NULL, NULL, EM_POPUP_SELECT_ONE }, { EM_POPUP_ITEM, "90.filter.00/00.03", N_("VFolder on Mailing _List"), G_CALLBACK(emfv_popup_vfolder_mlist), NULL, NULL, EM_POPUP_SELECT_ONE|EM_POPUP_SELECT_MAILING_LIST }, - { EM_POPUP_ITEM, "90.filter.00/00.04", N_("VFolder on Thread"), - G_CALLBACK(emfv_popup_vfolder_thread), NULL, NULL, EM_POPUP_SELECT_ONE }, - + { EM_POPUP_BAR, "90.filter.00/10", NULL, NULL, NULL, NULL, EM_POPUP_SELECT_ONE }, { EM_POPUP_ITEM, "90.filter.00/10.00", N_("Filter on Sub_ject"), G_CALLBACK(emfv_popup_filter_subject), NULL, NULL, EM_POPUP_SELECT_ONE }, { EM_POPUP_ITEM, "90.filter.00/10.01", N_("Filter on Sen_der"), G_CALLBACK(emfv_popup_filter_sender), NULL, NULL, EM_POPUP_SELECT_ONE }, { EM_POPUP_ITEM, "90.filter.00/10.02", N_("Filter on Re_cipients"), G_CALLBACK(emfv_popup_filter_recipients), NULL, NULL, EM_POPUP_SELECT_ONE }, { EM_POPUP_ITEM, "90.filter.00/10.03", N_("Filter on _Mailing List"), G_CALLBACK(emfv_popup_filter_mlist), NULL, NULL, EM_POPUP_SELECT_ONE|EM_POPUP_SELECT_MAILING_LIST }, - { EM_POPUP_ITEM, "90.filter.00/10.04", N_("Filter on Thread"), - G_CALLBACK(emfv_popup_filter_thread), NULL, NULL, EM_POPUP_SELECT_ONE }, }; static void @@ -821,6 +792,8 @@ EMFV_MAP_CALLBACK(emfv_message_mark_read, emfv_popup_mark_read) EMFV_MAP_CALLBACK(emfv_message_mark_unread, emfv_popup_mark_unread) EMFV_MAP_CALLBACK(emfv_message_mark_important, emfv_popup_mark_important) EMFV_MAP_CALLBACK(emfv_message_mark_unimportant, emfv_popup_mark_unimportant) +EMFV_MAP_CALLBACK(emfv_message_mark_junk, emfv_popup_mark_junk) +EMFV_MAP_CALLBACK(emfv_message_mark_nojunk, emfv_popup_mark_nojunk) EMFV_MAP_CALLBACK(emfv_message_delete, emfv_popup_delete) EMFV_MAP_CALLBACK(emfv_message_undelete, emfv_popup_undelete) EMFV_MAP_CALLBACK(emfv_message_followup_flag, emfv_popup_flag_followup) @@ -936,7 +909,7 @@ emfv_message_forward_attached (BonoboUIComponent *uic, void *data, const char *p return; uids = message_list_get_selected (emfv->list); - em_utils_forward_attached (emfv->folder, uids); + em_utils_forward_attached ((GtkWidget *) emfv, emfv->folder, uids); } static void @@ -949,7 +922,7 @@ emfv_message_forward_inline (BonoboUIComponent *uic, void *data, const char *pat return; uids = message_list_get_selected (emfv->list); - em_utils_forward_inline (emfv->folder, uids); + em_utils_forward_inline ((GtkWidget *) emfv, emfv->folder, uids); } static void @@ -962,7 +935,7 @@ emfv_message_forward_quoted (BonoboUIComponent *uic, void *data, const char *pat return; uids = message_list_get_selected (emfv->list); - em_utils_forward_quoted (emfv->folder, uids); + em_utils_forward_quoted ((GtkWidget *) emfv, emfv->folder, uids); } static void @@ -976,7 +949,7 @@ emfv_message_redirect (BonoboUIComponent *uic, void *data, const char *path) if (!em_utils_check_user_can_send_mail ((GtkWidget *) emfv)) return; - em_utils_redirect_message_by_uid (emfv->folder, emfv->list->cursor_uid); + em_utils_redirect_message_by_uid ((GtkWidget *) emfv, emfv->folder, emfv->list->cursor_uid); } static void @@ -990,7 +963,7 @@ emfv_message_post_reply (BonoboUIComponent *uic, void *data, const char *path) if (!em_utils_check_user_can_send_mail ((GtkWidget *) emfv)) return; - em_utils_post_reply_to_message_by_uid (emfv->folder, emfv->list->cursor_uid); + em_utils_post_reply_to_message_by_uid ((GtkWidget *) emfv, emfv->folder, emfv->list->cursor_uid); } static void @@ -1041,10 +1014,10 @@ emfv_message_reply(EMFolderView *emfv, int mode) html_engine_save_buffer_free(state); - em_utils_reply_to_message (msg, mode); + em_utils_reply_to_message((GtkWidget *)emfv, msg, mode); camel_object_unref(msg); } else { - em_utils_reply_to_message_by_uid (emfv->folder, emfv->list->cursor_uid, mode); + em_utils_reply_to_message_by_uid ((GtkWidget *) emfv, emfv->folder, emfv->list->cursor_uid, mode); } /*g_object_unref(clip);*/ @@ -1102,7 +1075,6 @@ struct _filter_data { int type; char *uri; char *mlist; - char *references; }; static void @@ -1113,7 +1085,6 @@ filter_data_free (struct _filter_data *fdata) if (fdata->folder) camel_object_unref (fdata->folder); g_free (fdata->mlist); - g_free (fdata->references); g_free (fdata); } @@ -1164,7 +1135,6 @@ EMFV_MAP_CALLBACK(emfv_tools_filter_subject, emfv_popup_filter_subject) EMFV_MAP_CALLBACK(emfv_tools_filter_sender, emfv_popup_filter_sender) EMFV_MAP_CALLBACK(emfv_tools_filter_recipient, emfv_popup_filter_recipients) EMFV_MAP_CALLBACK(emfv_tools_filter_mlist, emfv_popup_filter_mlist) -EMFV_MAP_CALLBACK(emfv_tools_filter_thread, emfv_popup_filter_thread) static void vfolder_type_got_message (CamelFolder *folder, const char *uid, CamelMimeMessage *msg, void *user_data) @@ -1206,7 +1176,6 @@ EMFV_MAP_CALLBACK(emfv_tools_vfolder_subject, emfv_popup_vfolder_subject) EMFV_MAP_CALLBACK(emfv_tools_vfolder_sender, emfv_popup_vfolder_sender) EMFV_MAP_CALLBACK(emfv_tools_vfolder_recipient, emfv_popup_vfolder_recipients) EMFV_MAP_CALLBACK(emfv_tools_vfolder_mlist, emfv_popup_vfolder_mlist) -EMFV_MAP_CALLBACK(emfv_tools_vfolder_thread, emfv_popup_vfolder_thread) /* ********************************************************************** */ @@ -1246,6 +1215,8 @@ static BonoboUIVerb emfv_message_verbs[] = { BONOBO_UI_UNSAFE_VERB ("MessageMarkAsUnRead", emfv_message_mark_unread), BONOBO_UI_UNSAFE_VERB ("MessageMarkAsImportant", emfv_message_mark_important), BONOBO_UI_UNSAFE_VERB ("MessageMarkAsUnimportant", emfv_message_mark_unimportant), + BONOBO_UI_UNSAFE_VERB ("MessageMarkAsJunk", emfv_message_mark_junk), + BONOBO_UI_UNSAFE_VERB ("MessageMarkAsNojunk", emfv_message_mark_nojunk), BONOBO_UI_UNSAFE_VERB ("MessageFollowUpFlag", emfv_message_followup_flag), BONOBO_UI_UNSAFE_VERB ("MessageMove", emfv_message_move), BONOBO_UI_UNSAFE_VERB ("MessageOpen", emfv_message_open), @@ -1270,12 +1241,10 @@ static BonoboUIVerb emfv_message_verbs[] = { BONOBO_UI_UNSAFE_VERB ("ToolsFilterRecipient", emfv_tools_filter_recipient), BONOBO_UI_UNSAFE_VERB ("ToolsFilterSender", emfv_tools_filter_sender), BONOBO_UI_UNSAFE_VERB ("ToolsFilterSubject", emfv_tools_filter_subject), - BONOBO_UI_UNSAFE_VERB ("ToolsFilterThread", emfv_tools_filter_thread), BONOBO_UI_UNSAFE_VERB ("ToolsVFolderMailingList", emfv_tools_vfolder_mlist), BONOBO_UI_UNSAFE_VERB ("ToolsVFolderRecipient", emfv_tools_vfolder_recipient), BONOBO_UI_UNSAFE_VERB ("ToolsVFolderSender", emfv_tools_vfolder_sender), BONOBO_UI_UNSAFE_VERB ("ToolsVFolderSubject", emfv_tools_vfolder_subject), - BONOBO_UI_UNSAFE_VERB ("ToolsVFolderThread", emfv_tools_vfolder_thread), BONOBO_UI_UNSAFE_VERB ("ViewLoadImages", emfv_view_load_images), /* ViewHeaders stuff is a radio */ @@ -1348,6 +1317,8 @@ static const EMFolderViewEnable emfv_enable_map[] = { { "MessageMarkAsUnRead", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_UNREAD }, { "MessageMarkAsImportant", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_IMPORTANT }, { "MessageMarkAsUnimportant", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_UNIMPORTANT }, + { "MessageMarkAsJunk", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_JUNK }, + { "MessageMarkAsNojunk", EM_POPUP_SELECT_MANY|EM_POPUP_SELECT_MARK_NOJUNK }, { "MessageFollowUpFlag", EM_POPUP_SELECT_MANY }, { "MessageMove", EM_POPUP_SELECT_MANY }, { "MessageOpen", EM_POPUP_SELECT_MANY }, @@ -1369,13 +1340,11 @@ static const EMFolderViewEnable emfv_enable_map[] = { { "ToolsFilterMailingList", EM_POPUP_SELECT_ONE }, { "ToolsFilterRecipient", EM_POPUP_SELECT_ONE }, { "ToolsFilterSender", EM_POPUP_SELECT_ONE }, - { "ToolsFilterSubject", EM_POPUP_SELECT_ONE }, - { "ToolsFilterThread", EM_POPUP_SELECT_ONE }, + { "ToolsFilterSubject", EM_POPUP_SELECT_ONE }, { "ToolsVFolderMailingList", EM_POPUP_SELECT_ONE }, { "ToolsVFolderRecipient", EM_POPUP_SELECT_ONE }, { "ToolsVFolderSender", EM_POPUP_SELECT_ONE }, { "ToolsVFolderSubject", EM_POPUP_SELECT_ONE }, - { "ToolsVFolderThread", EM_POPUP_SELECT_ONE }, { "ViewLoadImages", EM_POPUP_SELECT_ONE }, @@ -1758,7 +1727,7 @@ static void emfv_format_link_clicked(EMFormatHTMLDisplay *efhd, const char *uri, EMFolderView *emfv) { if (!strncasecmp (uri, "mailto:", 7)) { - em_utils_compose_new_message_with_mailto (uri); + em_utils_compose_new_message_with_mailto ((GtkWidget *) efhd, uri); } else if (*uri == '#') { gtk_html_jump_to_anchor (((EMFormatHTML *) efhd)->html, uri + 1); } else if (!strncasecmp (uri, "thismessage:", 12)) { diff --git a/mail/em-junk-filter.c b/mail/em-junk-filter.c new file mode 100644 index 0000000000..04bbadc2ba --- /dev/null +++ b/mail/em-junk-filter.c @@ -0,0 +1,327 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Author: + * Radek Doulik <rodo@ximian.com> + * + * Copyright 2003 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <pthread.h> + +#include <camel/camel-data-wrapper.h> +#include <camel/camel-stream-fs.h> + +#include "em-junk-filter.h" + +#define LOCK(x) pthread_mutex_lock(&x) +#define UNLOCK(x) pthread_mutex_unlock(&x) + +static pthread_mutex_t em_junk_sa_test_lock = PTHREAD_MUTEX_INITIALIZER; + +static const char * em_junk_sa_get_name (void); +static gboolean em_junk_sa_check_junk (CamelMimeMessage *msg); +static void em_junk_sa_report_junk (CamelMimeMessage *msg); +static void em_junk_sa_report_notjunk (CamelMimeMessage *msg); +static void em_junk_sa_commit_reports (void); + +static EMJunkPlugin spam_assassin_plugin = +{ + { + em_junk_sa_get_name, + 1, + em_junk_sa_check_junk, + em_junk_sa_report_junk, + em_junk_sa_report_notjunk, + em_junk_sa_commit_reports, + }, + NULL, + NULL +}; + +static gboolean em_junk_sa_spamd_tested = FALSE; +static gboolean em_junk_sa_use_spamc = FALSE; +static gint em_junk_sa_spamd_port = -1; + +#define d(x) x + +static const char * +em_junk_sa_get_name (void) +{ + return _("Spamassassin (built-in)"); +} + +static int +pipe_to_sa (CamelMimeMessage *msg, gchar *in, int argc, gchar **argv) +{ + CamelStream *stream; + int result, status; + int in_fds[2]; + pid_t pid; + + if (argc < 1 || argv[0] == '\0') + return 0; + + if (pipe (in_fds) == -1) { + /* camel_exception_setv (fms->ex, CAMEL_EXCEPTION_SYSTEM, + _("Failed to create pipe to '%s': %s"), + argv[0]->value.string, g_strerror (errno)); */ + return -1; + } + + if (!(pid = fork ())) { + /* child process */ + int maxfd, fd; + + fd = open ("/dev/null", O_WRONLY); + + if (dup2 (in_fds[0], STDIN_FILENO) < 0 || + dup2 (fd, STDOUT_FILENO) < 0 || + dup2 (fd, STDERR_FILENO) < 0) + _exit (255); + + setsid (); + + maxfd = sysconf (_SC_OPEN_MAX); + if (maxfd > 0) { + for (fd = 0; fd < maxfd; fd++) { + if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) + close (fd); + } + } + + execvp (argv [0], argv); + + d(printf ("Could not execute %s: %s\n", argv [0], g_strerror (errno))); + _exit (255); + } else if (pid < 0) { + /* camel_exception_setv (fms->ex, CAMEL_EXCEPTION_SYSTEM, + _("Failed to create create child process '%s': %s"), + argv[0]->value.string, g_strerror (errno)); */ + return -1; + } + + /* parent process */ + close (in_fds[0]); + fcntl (in_fds[1], F_SETFL, O_NONBLOCK); + + if (msg) { + stream = camel_stream_fs_new_with_fd (in_fds[1]); + + camel_data_wrapper_write_to_stream (CAMEL_DATA_WRAPPER (msg), stream); + camel_stream_flush (stream); + camel_object_unref (CAMEL_OBJECT (stream)); + } else if (in) { + write (in_fds [1], in, strlen (in)); + close (in_fds [1]); + } + + result = waitpid (pid, &status, 0); + + if (result == -1 && errno == EINTR) { + /* child process is hanging... */ + kill (pid, SIGTERM); + sleep (1); + result = waitpid (pid, &status, WNOHANG); + if (result == 0) { + /* ...still hanging, set phasers to KILL */ + kill (pid, SIGKILL); + sleep (1); + result = waitpid (pid, &status, WNOHANG); + } + } + + if (result != -1 && WIFEXITED (status)) + return WEXITSTATUS (status); + else + return -1; +} + + +#define NPORTS 1 + +static int +em_junk_sa_test_spamd_running (gint port) +{ + static gchar *sac_args [3] = { + "/bin/sh", + "-c", + NULL + }; + int retval; + + d(fprintf (stderr, "test if spamd is running (port %d)\n", port);) + sac_args [2] = port > 0 ? g_strdup_printf ("spamc -x -p %d", port) : g_strdup_printf ("spamc -x"); + + retval = pipe_to_sa (NULL, "From test@127.0.0.1", 3, sac_args) == 0; + g_free (sac_args [2]); + + return retval; +} + +static void +em_junk_sa_test_spamd () +{ + gint i, port = 7830; + + em_junk_sa_use_spamc = FALSE; + + /* if (em_junk_sa_test_spamd_running (-1)) { + em_junk_sa_use_spamc = TRUE; + em_junk_sa_spamd_port = -1; + } else { */ + for (i = 0; i < NPORTS; i ++) { + if (em_junk_sa_test_spamd_running (port)) { + em_junk_sa_use_spamc = TRUE; + em_junk_sa_spamd_port = port; + break; + } + port ++; + } + /* } */ + + if (!em_junk_sa_use_spamc) { + static gchar *sad_args [3] = { + "/bin/sh", + "-c", + NULL + }; + gint i, port = 7830; + + d(fprintf (stderr, "looks like spamd is not running\n");) + + for (i = 0; i < NPORTS; i ++) { + d(fprintf (stderr, "trying to run spamd at port %d\n", port)); + + sad_args [2] = g_strdup_printf ("spamd --port %d --local --daemonize", port); + if (!pipe_to_sa (NULL, NULL, 3, sad_args)) { + g_free (sad_args [2]); + em_junk_sa_use_spamc = TRUE; + em_junk_sa_spamd_port = port; + d(fprintf (stderr, "success at port %d\n", port)); + break; + } + g_free (sad_args [2]); + port ++; + } + } + + d(fprintf (stderr, "use spamd %d at port %d\n", em_junk_sa_use_spamc, em_junk_sa_spamd_port);) + + em_junk_sa_spamd_tested = TRUE; +} + +static gboolean +em_junk_sa_check_junk (CamelMimeMessage *msg) +{ + static gchar *args [3] = { + "/bin/sh", + "-c", + NULL + }; + gint retval; + + d(fprintf (stderr, "em_junk_sa_check_junk\n")); + + LOCK (em_junk_sa_test_lock); + if (!em_junk_sa_spamd_tested) + em_junk_sa_test_spamd (); + UNLOCK (em_junk_sa_test_lock); + + args [2] = em_junk_sa_use_spamc + ? (em_junk_sa_spamd_port == -1 + ? g_strdup ("spamc -c") /* Exit with a non-zero exit code if the + tested message was junk */ + : g_strdup_printf ("spamc" + " -c" /* Exit with a non-zero exit code if the + tested message was junk */ + " -p %d", em_junk_sa_spamd_port)) + : g_strdup ("spamassassin" + " --exit-code" /* Exit with a non-zero exit code if the + tested message was junk */ + " --local"); /* Local tests only (no online tests) */ + + retval = pipe_to_sa (msg, NULL, 3, args); + + g_free (args [2]); + + return retval; +} + +static void +em_junk_sa_report_junk (CamelMimeMessage *msg) +{ + static gchar *args [3] = { + "/bin/sh", + "-c", + "sa-learn" + " --no-rebuild" /* do not rebuild db */ + " --spam" /* report junk */ + " --single" /* single message */ + " --local" /* local only */ + }; + + d(fprintf (stderr, "em_junk_sa_report_junk\n");) + + pipe_to_sa (msg, NULL, 3, args) > 0; +} + +static void +em_junk_sa_report_notjunk (CamelMimeMessage *msg) +{ + static gchar *args [3] = { + "/bin/sh", + "-c", + "sa-learn" + " --no-rebuild" /* do not rebuild db */ + " --ham" /* report notjunk */ + " --single" /* single message */ + " --local" /* local only */ + }; + + d(fprintf (stderr, "em_junk_sa_report_notjunk\n");) + + pipe_to_sa (msg, NULL, 3, args) > 0; +} + +static void +em_junk_sa_commit_reports (void) +{ + static gchar *args [3] = { + "/bin/sh", + "-c", + "sa-learn" + " --rebuild" /* do not rebuild db */ + " --local" /* local only */ + }; + + d(fprintf (stderr, "em_junk_sa_commit_reports\n");) + + pipe_to_sa (NULL, NULL, 3, args) > 0; +} + +const EMJunkPlugin * +em_junk_filter_get_plugin (void) +{ + return &spam_assassin_plugin; +} diff --git a/mail/em-junk-filter.h b/mail/em-junk-filter.h new file mode 100644 index 0000000000..f62ca7bfad --- /dev/null +++ b/mail/em-junk-filter.h @@ -0,0 +1,30 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Author: + * Radek Doulik <rodo@ximian.com> + * + * Copyright 2003 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifndef _EM_JUNK_FILTER_H +#define _EM_JUNK_FILTER_H + +#include "em-junk-plugin.h" + +const EMJunkPlugin * em_junk_filter_get_plugin (void); + +#endif diff --git a/mail/em-junk-plugin.c b/mail/em-junk-plugin.c new file mode 100644 index 0000000000..5ea2a40119 --- /dev/null +++ b/mail/em-junk-plugin.c @@ -0,0 +1,24 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Author: + * Radek Doulik <rodo@ximian.com> + * + * Copyright 2003 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#include "em-junk-plugin.h" + diff --git a/mail/em-junk-plugin.h b/mail/em-junk-plugin.h new file mode 100644 index 0000000000..0c7eacd165 --- /dev/null +++ b/mail/em-junk-plugin.h @@ -0,0 +1,47 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Author: + * Radek Doulik <rodo@ximian.com> + * + * Copyright 2003 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifndef _EM_JUNK_PLUGIN_H +#define _EM_JUNK_PLUGIN_H + +#include <camel/camel-junk-plugin.h> +#include <gtk/gtkwidget.h> + +#define EM_JUNK_PLUGIN(x) ((EMJunkPlugin *) x) + +typedef struct _EMJunkPlugin EMJunkPlugin; + +struct _EMJunkPlugin +{ + CamelJunkPlugin csp; + + /* when called, it should insert own GUI configuration into supplied. + container. returns data pointer which is later passed to apply, + plugin has to call (*changed_cb) (); whenever configuration + is changed to notify settings dialog about a change. + if setup_config_ui is NULL, it means there are no options */ + + gpointer (*setup_config_ui) (GtkWidget *container, void (*changed_cb) ()); + void (*apply) (gpointer data); +}; + +#endif diff --git a/mail/em-popup.c b/mail/em-popup.c index 63159848bf..e9f7932921 100644 --- a/mail/em-popup.c +++ b/mail/em-popup.c @@ -245,7 +245,8 @@ em_popup_create_menu(EMPopup *emp, guint32 hide_mask, guint32 disable_mask) if (tmp) { g_string_append_len(ppath, item->path, tmp-item->path); thismenu = g_hash_table_lookup(menu_hash, ppath->str); - g_assert(thismenu != NULL); + if (thismenu == NULL) + continue; } else { thismenu = topmenu; } @@ -353,8 +354,7 @@ em_popup_create_menu_once(EMPopup *emp, EMPopupTarget *target, guint32 hide_mask menu = em_popup_create_menu(emp, hide_mask, disable_mask); - if (target) - g_signal_connect_swapped(menu, "selection_done", G_CALLBACK(em_popup_target_free), target); + g_signal_connect_swapped(menu, "selection_done", G_CALLBACK(em_popup_target_free), target); g_signal_connect(menu, "selection_done", G_CALLBACK(emp_popup_done), emp); return menu; @@ -459,6 +459,11 @@ em_popup_target_new_select(struct _CamelFolder *folder, const char *folder_uri, mask &= ~EM_POPUP_SELECT_MARK_UNIMPORTANT; else mask &= ~EM_POPUP_SELECT_MARK_IMPORTANT; + + if (info->flags & CAMEL_MESSAGE_JUNK) + mask &= ~EM_POPUP_SELECT_MARK_NOJUNK; + else + mask &= ~EM_POPUP_SELECT_MARK_JUNK; tmp = camel_tag_get (&info->user_tags, "follow-up"); if (tmp && *tmp) { @@ -601,39 +606,34 @@ emp_part_popup_set_background(GtkWidget *w, EMPopupTarget *t) } static void -emp_part_popup_reply_sender (GtkWidget *w, EMPopupTarget *t) +emp_part_popup_reply_sender(GtkWidget *w, EMPopupTarget *t) { - CamelMimeMessage *message; - - message = (CamelMimeMessage *) camel_medium_get_content_object ((CamelMedium *) t->data.part.part); - em_utils_reply_to_message (message, REPLY_MODE_SENDER); + em_utils_reply_to_message(t->widget, + (CamelMimeMessage *)camel_medium_get_content_object((CamelMedium *)t->data.part.part), + REPLY_MODE_SENDER); } static void -emp_part_popup_reply_list (GtkWidget *w, EMPopupTarget *t) +emp_part_popup_reply_list(GtkWidget *w, EMPopupTarget *t) { - CamelMimeMessage *message; - - message = (CamelMimeMessage *) camel_medium_get_content_object ((CamelMedium *) t->data.part.part); - em_utils_reply_to_message (message, REPLY_MODE_LIST); + em_utils_reply_to_message(t->widget, + (CamelMimeMessage *)camel_medium_get_content_object((CamelMedium *)t->data.part.part), + REPLY_MODE_LIST); } static void -emp_part_popup_reply_all (GtkWidget *w, EMPopupTarget *t) +emp_part_popup_reply_all(GtkWidget *w, EMPopupTarget *t) { - CamelMimeMessage *message; - - message = (CamelMimeMessage *) camel_medium_get_content_object ((CamelMedium *) t->data.part.part); - em_utils_reply_to_message (message, REPLY_MODE_ALL); + em_utils_reply_to_message(t->widget, + (CamelMimeMessage *)camel_medium_get_content_object((CamelMedium *)t->data.part.part), + REPLY_MODE_ALL); } static void -emp_part_popup_forward (GtkWidget *w, EMPopupTarget *t) +emp_part_popup_forward(GtkWidget *w, EMPopupTarget *t) { - CamelMimeMessage *message; - - message = (CamelMimeMessage *) camel_medium_get_content_object ((CamelMedium *) t->data.part.part); - em_utils_forward_message (message); + em_utils_forward_message(t->widget, + (CamelMimeMessage *)camel_medium_get_content_object((CamelMedium *)t->data.part.part)); } static EMPopupItem emp_standard_object_popups[] = { @@ -665,9 +665,9 @@ emp_uri_popup_link_open(GtkWidget *w, EMPopupTarget *t) } static void -emp_uri_popup_address_send (GtkWidget *w, EMPopupTarget *t) +emp_uri_popup_address_send(GtkWidget *w, EMPopupTarget *t) { - em_utils_compose_new_message_with_mailto (t->data.uri); + em_utils_compose_new_message_with_mailto(t->widget, t->data.uri); } static void diff --git a/mail/em-popup.h b/mail/em-popup.h index 0ad1d0015c..7d19dea6bc 100644 --- a/mail/em-popup.h +++ b/mail/em-popup.h @@ -69,7 +69,6 @@ enum _em_popup_target_t { EM_POPUP_TARGET_SELECT, EM_POPUP_TARGET_URI, EM_POPUP_TARGET_PART, - EM_POPUP_TARGET_FOLDER, }; /* Flags that describe a TARGET_SELECT */ @@ -88,7 +87,9 @@ enum { EM_POPUP_SELECT_FLAG_COMPLETED = 1<<12, EM_POPUP_SELECT_FLAG_CLEAR = 1<<13, EM_POPUP_SELECT_ADD_SENDER = 1<<14, - EM_POPUP_SELECT_LAST = 1<<16 /* reserve 2 slots */ + EM_POPUP_SELECT_MARK_JUNK = 1<<15, + EM_POPUP_SELECT_MARK_NOJUNK = 1<<16, + EM_POPUP_SELECT_LAST = 1<<18 /* reserve 2 slots */ }; /* Flags that describe a TARGET_URI */ @@ -104,13 +105,6 @@ enum { EM_POPUP_PART_IMAGE = 1<<1, }; -/* Flags that describe TARGET_FOLDER */ -enum { - EM_POPUP_FOLDER_LOCAL = 1<<0, - EM_POPUP_FOLDER_REMOTE = 1<<1, - EM_POPUP_FOLDER_VFOLDER = 1<<2, -}; - struct _EMPopupTarget { enum _em_popup_target_t type; guint32 mask; /* depends on type, see above */ @@ -126,9 +120,6 @@ struct _EMPopupTarget { char *mime_type; struct _CamelMimePart *part; } part; - struct { - char *folder_uri; - } folder; } data; }; diff --git a/mail/mail-local.c b/mail/mail-local.c new file mode 100644 index 0000000000..12c87c2819 --- /dev/null +++ b/mail/mail-local.c @@ -0,0 +1,1571 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* mail-local.c: Local mailbox support. */ + +/* + * Authors: + * Michael Zucchi <NotZed@ximian.com> + * Peter Williams <peterw@ximian.com> + * Ettore Perazzoli <ettore@ximian.com> + * Dan Winship <danw@ximian.com> + * + * Copyright 2000 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <string.h> +#include <unistd.h> +#include <errno.h> + +#include <libxml/xmlmemory.h> +#include <glade/glade.h> + +#include "e-util/e-path.h" +#include "e-util/e-dialog-utils.h" +#include <gal/util/e-xml-utils.h> + +#include "Evolution.h" +#include "evolution-storage.h" +#include "evolution-shell-component.h" +#include "evolution-storage-listener.h" + +#include "camel/camel.h" +#include "camel/camel-vtrash-folder.h" + +#include "mail.h" +#include "mail-local.h" +#include "mail-tools.h" +#include "mail-mt.h" +#include "mail-folder-cache.h" +#include "mail-vfolder.h" +#include "mail-ops.h" + +#define d(x) + +/* sigh, required for passing around to some functions */ +static GNOME_Evolution_Storage local_corba_storage = CORBA_OBJECT_NIL; + +/* ** MailLocalStore ** (protos) ************************************************** */ + +#define MAIL_LOCAL_STORE_TYPE (mail_local_store_get_type ()) +#define MAIL_LOCAL_STORE(obj) (CAMEL_CHECK_CAST((obj), MAIL_LOCAL_STORE_TYPE, MailLocalStore)) +#define MAIL_LOCAL_STORE_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), MAIL_LOCAL_STORE_TYPE, MailLocalStoreClass)) +#define MAIL_IS_LOCAL_STORE(o) (CAMEL_CHECK_TYPE((o), MAIL_LOCAL_STORE_TYPE)) + +typedef struct { + CamelStore parent_object; + + /* stores CamelFolderInfo's of the folders we're supposed to know about, by uri */ + GHashTable *folder_infos; + GMutex *folder_info_lock; + +} MailLocalStore; + +typedef struct { + CamelStoreClass parent_class; +} MailLocalStoreClass; + +static CamelType mail_local_store_get_type (void); + +static MailLocalStore *global_local_store; + +/* ** MailLocalFolder ** (protos) ************************************************* */ + +#define MAIL_LOCAL_FOLDER_TYPE (mail_local_folder_get_type ()) +#define MAIL_LOCAL_FOLDER(obj) (CAMEL_CHECK_CAST((obj), MAIL_LOCAL_FOLDER_TYPE, MailLocalFolder)) +#define MAIL_LOCAL_FOLDER_CLASS(k) (CAMEL_CHECK_CLASS_CAST ((k), MAIL_LOCAL_FOLDER_TYPE, MailLocalFolderClass)) +#define MAIL_IS_LOCAL_FOLDER(o) (CAMEL_CHECK_TYPE((o), MAIL_LOCAL_FOLDER_TYPE)) + +#define LOCAL_STORE_LOCK(folder) (g_mutex_lock (((MailLocalStore *)folder)->folder_info_lock)) +#define LOCAL_STORE_UNLOCK(folder) (g_mutex_unlock (((MailLocalStore *)folder)->folder_info_lock)) + +struct _local_meta { + char *path; /* path of metainfo */ + + char *format; /* format of mailbox */ + char *name; /* name of actual mbox */ + int indexed; /* is body indexed? */ +}; + +typedef struct { + CamelFolder parent_object; + + CamelFolder *real_folder; + CamelStore *real_store; + + char *description; + char *real_path; + + struct _local_meta *meta; + + GMutex *real_folder_lock; /* no way to use the CamelFolder's lock, so... */ +} MailLocalFolder; + +typedef struct { + CamelFolderClass parent_class; +} MailLocalFolderClass; + +static CamelType mail_local_folder_get_type (void); + +#ifdef ENABLE_THREADS +#define LOCAL_FOLDER_LOCK(folder) (g_mutex_lock (((MailLocalFolder *)folder)->real_folder_lock)) +#define LOCAL_FOLDER_UNLOCK(folder) (g_mutex_unlock (((MailLocalFolder *)folder)->real_folder_lock)) +#else +#define LOCAL_FOLDER_LOCK(folder) +#define LOCAL_FOLDER_UNLOCK(folder) +#endif + +/* ** MailLocalFolder ************************************************************* */ + +static struct _local_meta * +load_metainfo(const char *path) +{ + xmlDocPtr doc = NULL; + xmlNodePtr node; + struct _local_meta *meta; + struct stat st; + + d(printf("Loading folder metainfo from : %s\n", path)); + + meta = g_malloc0(sizeof(*meta)); + meta->path = g_strdup(path); + + if (stat (path, &st) == -1 || !S_ISREG (st.st_mode)) + goto dodefault; + + doc = xmlParseFile(path); + if (doc == NULL) + goto dodefault; + + node = doc->children; + if (strcmp(node->name, "folderinfo")) + goto dodefault; + + node = node->children; + while (node) { + if (!strcmp(node->name, "folder")) { + char *index, *txt; + + txt = xmlGetProp(node, "type"); + meta->format = g_strdup(txt?txt:"mbox"); + xmlFree(txt); + + txt = xmlGetProp(node, "name"); + meta->name = g_strdup(txt?txt:"mbox"); + xmlFree(txt); + + index = xmlGetProp(node, "index"); + if (index) { + meta->indexed = atoi(index); + xmlFree(index); + } else + meta->indexed = TRUE; + + } + node = node->next; + } + xmlFreeDoc(doc); + return meta; + + dodefault: + meta->format = g_strdup("mbox"); /* defaults */ + meta->name = g_strdup("mbox"); + meta->indexed = TRUE; + xmlFreeDoc(doc); + return meta; +} + +static void +free_metainfo(struct _local_meta *meta) +{ + g_free(meta->path); + g_free(meta->format); + g_free(meta->name); + g_free(meta); +} + +static gboolean +save_metainfo (struct _local_meta *meta) +{ + xmlDocPtr doc; + xmlNodePtr root, node; + int ret; + + d(printf("Saving folder metainfo to : %s\n", meta->path)); + + doc = xmlNewDoc("1.0"); + root = xmlNewDocNode(doc, NULL, "folderinfo", NULL); + xmlDocSetRootElement(doc, root); + + node = xmlNewChild(root, NULL, "folder", NULL); + xmlSetProp(node, "type", meta->format); + xmlSetProp(node, "name", meta->name); + xmlSetProp(node, "index", meta->indexed?"1":"0"); + + ret = e_xml_save_file (meta->path, doc); + + xmlFreeDoc (doc); + + return ret == -1 ? FALSE : TRUE; +} + +static CamelFolderClass *mlf_parent_class = NULL; + +/* forward a bunch of functions to the real folder. This pretty + * much sucks but I haven't found a better way of doing it. + */ + +/* We need to do it without having locked our folder, otherwise + we can get sync hangs with vfolders/trash */ +static void +mlf_refresh_info(CamelFolder *folder, CamelException *ex) +{ + MailLocalFolder *mlf = MAIL_LOCAL_FOLDER(folder); + CamelFolder *f; + + LOCAL_FOLDER_LOCK(mlf); + f = mlf->real_folder; + camel_object_ref(f); + LOCAL_FOLDER_UNLOCK(mlf); + + camel_folder_refresh_info(f, ex); + camel_object_unref(f); +} + +static void +mlf_sync(CamelFolder *folder, gboolean expunge, CamelException *ex) +{ + MailLocalFolder *mlf = MAIL_LOCAL_FOLDER(folder); + CamelFolder *f; + + LOCAL_FOLDER_LOCK(mlf); + f = mlf->real_folder; + camel_object_ref(f); + LOCAL_FOLDER_UNLOCK(mlf); + + camel_folder_sync(f, expunge, ex); + camel_object_unref(f); +} + +static void +mlf_expunge(CamelFolder *folder, CamelException *ex) +{ + MailLocalFolder *mlf = MAIL_LOCAL_FOLDER(folder); + CamelFolder *f; + + LOCAL_FOLDER_LOCK(mlf); + f = mlf->real_folder; + camel_object_ref(f); + LOCAL_FOLDER_UNLOCK(mlf); + + camel_folder_expunge(f, ex); + camel_object_unref(f); +} + +static void +mlf_append_message(CamelFolder *folder, CamelMimeMessage *message, const CamelMessageInfo *info, char **appended_uid, CamelException *ex) +{ + MailLocalFolder *mlf = MAIL_LOCAL_FOLDER(folder); + CamelFolder *f; + + LOCAL_FOLDER_LOCK(mlf); + f = mlf->real_folder; + camel_object_ref(f); + LOCAL_FOLDER_UNLOCK(mlf); + + camel_folder_append_message(f, message, info, appended_uid, ex); + camel_object_unref(f); +} + +static CamelMimeMessage * +mlf_get_message(CamelFolder *folder, const char *uid, CamelException *ex) +{ + MailLocalFolder *mlf = MAIL_LOCAL_FOLDER(folder); + CamelMimeMessage *ret; + CamelFolder *f; + + LOCAL_FOLDER_LOCK(mlf); + f = mlf->real_folder; + camel_object_ref(f); + LOCAL_FOLDER_UNLOCK(mlf); + + ret = camel_folder_get_message(f, uid, ex); + camel_object_unref(f); + + return ret; +} + +static GPtrArray * +mlf_search_by_expression(CamelFolder *folder, const char *expression, CamelException *ex) +{ + MailLocalFolder *mlf = MAIL_LOCAL_FOLDER (folder); + GPtrArray *ret; + CamelFolder *f; + + LOCAL_FOLDER_LOCK(mlf); + f = mlf->real_folder; + camel_object_ref(f); + LOCAL_FOLDER_UNLOCK(mlf); + + ret = camel_folder_search_by_expression(f, expression, ex); + camel_object_unref(f); + + return ret; +} + +static GPtrArray * +mlf_search_by_uids(CamelFolder *folder, const char *expression, GPtrArray *uids, CamelException *ex) +{ + MailLocalFolder *mlf = MAIL_LOCAL_FOLDER (folder); + GPtrArray *ret; + CamelFolder *f; + + LOCAL_FOLDER_LOCK(mlf); + f = mlf->real_folder; + camel_object_ref(f); + LOCAL_FOLDER_UNLOCK(mlf); + + ret = camel_folder_search_by_uids(f, expression, uids, ex); + camel_object_unref(f); + + return ret; +} + +static void +mlf_search_free(CamelFolder *folder, GPtrArray *result) +{ + MailLocalFolder *mlf = MAIL_LOCAL_FOLDER(folder); + CamelFolder *f; + + LOCAL_FOLDER_LOCK(mlf); + f = mlf->real_folder; + camel_object_ref(f); + LOCAL_FOLDER_UNLOCK(mlf); + + camel_folder_search_free(f, result); + camel_object_unref(f); +} + +static void +mlf_set_message_flags(CamelFolder *folder, const char *uid, guint32 flags, guint32 set) +{ + MailLocalFolder *mlf = MAIL_LOCAL_FOLDER(folder); + CamelFolder *f; + + LOCAL_FOLDER_LOCK(mlf); + f = mlf->real_folder; + camel_object_ref(f); + LOCAL_FOLDER_UNLOCK(mlf); + + camel_folder_set_message_flags(mlf->real_folder, uid, flags, set); + camel_object_unref(f); +} + +static void +mlf_set_message_user_flag(CamelFolder *folder, const char *uid, const char *name, gboolean value) +{ + MailLocalFolder *mlf = MAIL_LOCAL_FOLDER(folder); + CamelFolder *f; + + LOCAL_FOLDER_LOCK(mlf); + f = mlf->real_folder; + camel_object_ref(f); + LOCAL_FOLDER_UNLOCK(mlf); + + camel_folder_set_message_user_flag(mlf->real_folder, uid, name, value); + camel_object_unref(f); +} + +static void +mlf_set_message_user_tag(CamelFolder *folder, const char *uid, const char *name, const char *value) +{ + MailLocalFolder *mlf = MAIL_LOCAL_FOLDER(folder); + CamelFolder *f; + + LOCAL_FOLDER_LOCK(mlf); + f = mlf->real_folder; + camel_object_ref(f); + LOCAL_FOLDER_UNLOCK(mlf); + + camel_folder_set_message_user_tag(mlf->real_folder, uid, name, value); + camel_object_unref(f); +} + +/* Internal store-rename call, update our strings */ +static void +mlf_rename(CamelFolder *folder, const char *new) +{ + MailLocalFolder *mlf = (MailLocalFolder *)folder; + + /* first, proxy it down */ + if (mlf->real_folder) { + char *mbox = g_strdup_printf("%s/%s", new, mlf->meta->name); + + d(printf("renaming real folder to %s\n", mbox)); + + camel_folder_rename(mlf->real_folder, mbox); + g_free(mbox); + } + + /* Then do our stuff */ + g_free(mlf->real_path); + mlf->real_path = g_strdup(new); + + g_free(mlf->meta->path); + mlf->meta->path = g_strdup_printf("%s/%s/local-metadata.xml", ((CamelService *)folder->parent_store)->url->path, new); + + /* Then pass it up */ + ((CamelFolderClass *)mlf_parent_class)->rename(folder, new); +} + +/* and, conversely, forward the real folder's signals. */ + +static void +mlf_proxy_message_changed(CamelObject *real_folder, gpointer event_data, gpointer user_data) +{ + camel_object_trigger_event(user_data, "message_changed", event_data); +} + +static void +mlf_proxy_folder_changed(CamelObject *real_folder, gpointer event_data, gpointer user_data) +{ + camel_object_trigger_event(user_data, "folder_changed", event_data); +} + +static void +mlf_unset_folder (MailLocalFolder *mlf) +{ + CamelFolder *folder = (CamelFolder *)mlf; + + g_assert(mlf->real_folder); + + camel_object_unhook_event(mlf->real_folder, + "message_changed", + mlf_proxy_message_changed, + mlf); + camel_object_unhook_event(mlf->real_folder, + "folder_changed", + mlf_proxy_folder_changed, + mlf); + + camel_object_unref(folder->summary); + folder->summary = NULL; + camel_object_unref(mlf->real_folder); + mlf->real_folder = NULL; + camel_object_unref(mlf->real_store); + mlf->real_store = NULL; + + folder->permanent_flags = 0; + folder->folder_flags = 0; +} + +static gboolean +mlf_set_folder(MailLocalFolder *mlf, guint32 flags, CamelException *ex) +{ + CamelFolder *folder = (CamelFolder *)mlf; + char *uri, *mbox; + + g_assert(mlf->real_folder == NULL); + + uri = g_strdup_printf("%s:%s", mlf->meta->format, ((CamelService *)folder->parent_store)->url->path); + + d(printf("opening real store: %s\n", uri)); + mlf->real_store = camel_session_get_store(session, uri, ex); + g_free(uri); + if (mlf->real_store == NULL) + return FALSE; + + if (mlf->meta->indexed) + flags |= CAMEL_STORE_FOLDER_BODY_INDEX; + + /* mlf->real_folder = camel_store_get_folder(mlf->real_store, mlf->meta->name, flags, ex); */ + mbox = g_strdup_printf("%s/%s", mlf->real_path, mlf->meta->name); + d(printf("Opening mbox on real path: %s\n", mbox)); + mlf->real_folder = camel_store_get_folder(mlf->real_store, mbox, flags, ex); + g_free(mbox); + if (mlf->real_folder == NULL) + return FALSE; + + if (mlf->real_folder->folder_flags & CAMEL_FOLDER_HAS_SUMMARY_CAPABILITY) { + folder->summary = mlf->real_folder->summary; + camel_object_ref(mlf->real_folder->summary); + } + + folder->permanent_flags = mlf->real_folder->permanent_flags; + folder->folder_flags = mlf->real_folder->folder_flags; + + camel_object_hook_event(mlf->real_folder, "message_changed", mlf_proxy_message_changed, mlf); + camel_object_hook_event(mlf->real_folder, "folder_changed", mlf_proxy_folder_changed, mlf); + + return TRUE; +} + +static int +mlf_getv(CamelObject *object, CamelException *ex, CamelArgGetV *args) +{ + CamelFolder *folder = (CamelFolder *)object; + MailLocalFolder *mlf = (MailLocalFolder *)object; + int i, count=args->argc; + guint32 tag; + + for (i=0;i<args->argc;i++) { + CamelArgGet *arg = &args->argv[i]; + + tag = arg->tag; + + switch (tag & CAMEL_ARG_TAG) { + /* CamelObject args */ + case CAMEL_OBJECT_ARG_DESCRIPTION: + if (mlf->description == NULL) { + int pathlen; + + /* string to describe a local folder as the location of a message */ + pathlen = strlen(evolution_dir) + strlen("local") + 1; + if (strlen(folder->full_name) > pathlen) + mlf->description = g_strdup_printf(_("Local folders/%s"), folder->full_name+pathlen); + else + mlf->description = g_strdup_printf(_("Local folders/%s"), folder->name); + } + *arg->ca_str = mlf->description; + break; + default: + count--; + continue; + } + + arg->tag = (tag & CAMEL_ARG_TYPE) | CAMEL_ARG_IGNORE; + } + + if (count) + return ((CamelObjectClass *)mlf_parent_class)->getv(object, ex, args); + + return 0; +} + +static gboolean +mlf_meta_set(CamelObject *obj, const char *name, const char *value) +{ + MailLocalFolder *mlf = MAIL_LOCAL_FOLDER(obj); + CamelFolder *f; + gboolean res; + + LOCAL_FOLDER_LOCK(mlf); + f = mlf->real_folder; + camel_object_ref(f); + LOCAL_FOLDER_UNLOCK(mlf); + + /* We must write this ourselves, since MailLocalFolder is not persistent itself */ + if ((res = camel_object_meta_set(f, name, value))) + camel_object_state_write(f); + camel_object_unref(f); + + return res; +} + +static char * +mlf_meta_get(CamelObject *obj, const char *name) +{ + MailLocalFolder *mlf = MAIL_LOCAL_FOLDER(obj); + CamelFolder *f; + char * res; + + LOCAL_FOLDER_LOCK(mlf); + f = mlf->real_folder; + camel_object_ref(f); + LOCAL_FOLDER_UNLOCK(mlf); + + res = camel_object_meta_get(f, name); + camel_object_unref(f); + + return res; +} + +static void +mlf_class_init (CamelObjectClass *camel_object_class) +{ + CamelFolderClass *camel_folder_class = CAMEL_FOLDER_CLASS (camel_object_class); + + /* override all the functions subclassed in providers/local/ */ + camel_folder_class->refresh_info = mlf_refresh_info; + camel_folder_class->sync = mlf_sync; + camel_folder_class->expunge = mlf_expunge; + camel_folder_class->append_message = mlf_append_message; + camel_folder_class->get_message = mlf_get_message; + camel_folder_class->search_free = mlf_search_free; + + camel_folder_class->search_by_expression = mlf_search_by_expression; + camel_folder_class->search_by_uids = mlf_search_by_uids; + camel_folder_class->set_message_flags = mlf_set_message_flags; + camel_folder_class->set_message_user_flag = mlf_set_message_user_flag; + camel_folder_class->set_message_user_tag = mlf_set_message_user_tag; + + camel_folder_class->rename = mlf_rename; + + camel_object_class->getv = mlf_getv; + + camel_object_class->meta_get = mlf_meta_get; + camel_object_class->meta_set = mlf_meta_set; +} + +static void +mlf_init (CamelObject *obj, CamelObjectClass *klass) +{ + MailLocalFolder *mlf = MAIL_LOCAL_FOLDER (obj); + +#ifdef ENABLE_THREADS + mlf->real_folder_lock = g_mutex_new(); +#endif +} + +static void +mlf_finalize (CamelObject *obj) +{ + MailLocalFolder *mlf = MAIL_LOCAL_FOLDER (obj); + + if (mlf->real_folder) + mlf_unset_folder(mlf); + + free_metainfo(mlf->meta); + + g_free (mlf->real_path); + +#ifdef ENABLE_THREADS + g_mutex_free (mlf->real_folder_lock); +#endif +} + +static CamelType +mail_local_folder_get_type (void) +{ + static CamelType mail_local_folder_type = CAMEL_INVALID_TYPE; + + if (mail_local_folder_type == CAMEL_INVALID_TYPE) { + mail_local_folder_type = camel_type_register(CAMEL_FOLDER_TYPE, + "MailLocalFolder", + sizeof (MailLocalFolder), + sizeof (MailLocalFolderClass), + mlf_class_init, + NULL, + mlf_init, + mlf_finalize); + mlf_parent_class = (CamelFolderClass *)CAMEL_FOLDER_TYPE; + } + + return mail_local_folder_type; +} + +static MailLocalFolder * +mail_local_folder_construct(MailLocalFolder *mlf, MailLocalStore *parent_store, const char *full_name, CamelException *ex) +{ + char *metapath, *name; + + name = g_path_get_basename (full_name); + d(printf ("constructing local folder: full = %s, name = %s\n", full_name, name)); + camel_folder_construct (CAMEL_FOLDER (mlf), CAMEL_STORE (parent_store), full_name, name); + g_free (name); + + mlf->real_path = g_strdup (((CamelFolder *) mlf)->full_name); + + metapath = g_strdup_printf ("%s/%s/local-metadata.xml", ((CamelService *) parent_store)->url->path, full_name); + mlf->meta = load_metainfo (metapath); + g_free (metapath); + + return mlf; +} + +static gboolean +mail_local_folder_reconfigure (MailLocalFolder *mlf, const char *new_format, int index_body, CamelException *ex) +{ + CamelStore *fromstore = NULL; + CamelFolder *fromfolder = NULL; + char *oldformat = NULL; + char *store_uri; + GPtrArray *uids; + int real_folder_frozen = FALSE; + int format_change, index_changed; + char *tmpname = NULL; + char *mbox = NULL; + + format_change = strcmp(mlf->meta->format, new_format) != 0; + index_changed = mlf->meta->indexed != index_body; + + if (format_change == FALSE && index_changed == FALSE) + return TRUE; + + camel_operation_start(NULL, _("Reconfiguring folder")); + + /* first things first */ + g_assert (ex); + LOCAL_FOLDER_LOCK (mlf); + + /* first, 'close' the old folder */ + if (mlf->real_folder) { + camel_folder_sync(mlf->real_folder, FALSE, ex); + if (camel_exception_is_set (ex)) + goto cleanup; + mlf_unset_folder(mlf); + } + + /* only indexed change, just re-open with new flags */ + if (!format_change) { + mlf->meta->indexed = index_body; + mlf_set_folder(mlf, CAMEL_STORE_FOLDER_CREATE, ex); + save_metainfo(mlf->meta); + goto cleanup; + } + + store_uri = g_strdup_printf("%s:%s", mlf->meta->format, ((CamelService *)((CamelFolder *)mlf)->parent_store)->url->path); + fromstore = camel_session_get_store(session, store_uri, ex); + g_free(store_uri); + if (fromstore == NULL) + goto cleanup; + + oldformat = mlf->meta->format; + mlf->meta->format = g_strdup(new_format); + + /* rename the old mbox and open it again, without indexing */ + tmpname = g_strdup_printf ("%s/%s_reconfig", mlf->real_path, mlf->meta->name); + mbox = g_strdup_printf("%s/%s", mlf->real_path, mlf->meta->name); + d(printf("renaming %s to %s, and opening it\n", mbox, tmpname)); + + camel_store_rename_folder(fromstore, mbox, tmpname, ex); + if (camel_exception_is_set(ex)) + goto cleanup; + + /* we dont need to set the create flag ... or need an index if it has one */ + fromfolder = camel_store_get_folder(fromstore, tmpname, 0, ex); + if (fromfolder == NULL || camel_exception_is_set(ex)) { + /* try and recover ... */ + camel_exception_clear(ex); + camel_store_rename_folder(fromstore, tmpname, mbox, ex); + goto cleanup; + } + + /* create a new mbox */ + d(printf("Creating the destination mbox\n")); + + if (!mlf_set_folder(mlf, CAMEL_STORE_FOLDER_CREATE, ex)) { + d(printf("cannot open destination folder\n")); + /* try and recover ... */ + camel_exception_clear(ex); + camel_store_rename_folder(fromstore, tmpname, mbox, ex); + goto cleanup; + } + + real_folder_frozen = TRUE; + camel_folder_freeze(mlf->real_folder); + + uids = camel_folder_get_uids(fromfolder); + camel_folder_transfer_messages_to(fromfolder, uids, mlf->real_folder, NULL, TRUE, ex); + camel_folder_free_uids(fromfolder, uids); + if (camel_exception_is_set(ex)) + goto cleanup; + + camel_folder_expunge(fromfolder, ex); + + d(printf("delete old mbox ...\n")); + camel_object_unref(fromfolder); + fromfolder = NULL; + camel_store_delete_folder(fromstore, tmpname, ex); + + /* switch format */ + g_free(oldformat); + oldformat = NULL; + if (save_metainfo(mlf->meta) == FALSE) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot save folder metainfo; " + "you may find you can't\n" + "open this folder anymore: %s: %s"), + mlf->meta->path, strerror(errno)); + } + + cleanup: + if (oldformat) { + g_free(mlf->meta->format); + mlf->meta->format = oldformat; + } + if (mlf->real_folder == NULL) + mlf_set_folder (mlf, CAMEL_STORE_FOLDER_CREATE, ex); + if (fromfolder) + camel_object_unref(fromfolder); + if (fromstore) + camel_object_unref(fromstore); + + g_free(tmpname); + g_free(mbox); + + LOCAL_FOLDER_UNLOCK (mlf); + + if (real_folder_frozen) + camel_folder_thaw(mlf->real_folder); + + camel_operation_end(NULL); + + return !camel_exception_is_set(ex); +} + +/* ******************************************************************************** */ + +static CamelObjectClass *local_store_parent_class = NULL; + +static CamelFolder * +mls_get_folder(CamelStore *store, const char *folder_name, guint32 flags, CamelException *ex) +{ + MailLocalStore *local_store = MAIL_LOCAL_STORE (store); + MailLocalFolder *folder; + + d(printf("--LOCAL-- get_folder: %s\n", folder_name)); + + folder = (MailLocalFolder *)camel_object_new(MAIL_LOCAL_FOLDER_TYPE); + folder = mail_local_folder_construct(folder, local_store, folder_name, ex); + if (folder == NULL) + return NULL; + + if (!mlf_set_folder(folder, flags, ex)) { + camel_object_unref(folder); + return NULL; + } + + if (flags & CAMEL_STORE_FOLDER_CREATE) { + if (save_metainfo(folder->meta) == FALSE) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot save folder metainfo to %s: %s"), + folder->meta->path, g_strerror(errno)); + camel_object_unref(folder); + return NULL; + } + } + + return (CamelFolder *)folder; +} + +static void +mls_delete_folder(CamelStore *store, const char *folder_name, CamelException *ex) +{ + CamelStore *real_store; + char *metapath, *uri, *mbox; + CamelException local_ex; + struct _local_meta *meta; + + d(printf("Deleting folder: %s %s\n", ((CamelService *)store)->url->path, folder_name)); + + camel_exception_init(&local_ex); + + /* find the real store for this folder, and proxy the call */ + metapath = g_strdup_printf("%s%s/local-metadata.xml", ((CamelService *)store)->url->path, folder_name); + meta = load_metainfo(metapath); + uri = g_strdup_printf("%s:%s", meta->format, ((CamelService *)store)->url->path); + real_store = (CamelStore *)camel_session_get_service(session, uri, CAMEL_PROVIDER_STORE, ex); + g_free(uri); + if (real_store == NULL) { + g_free(metapath); + free_metainfo(meta); + camel_object_unref(real_store); + return; + } + + mbox = g_strdup_printf("%s/%s", folder_name, meta->name); + camel_store_delete_folder(real_store, mbox, &local_ex); + g_free(mbox); + if (camel_exception_is_set(&local_ex)) { + camel_exception_xfer(ex, &local_ex); + g_free(metapath); + free_metainfo(meta); + camel_object_unref(real_store); + return; + } + + camel_object_unref((CamelObject *)real_store); + + free_metainfo(meta); + + if (unlink(metapath) == -1) { + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Cannot delete folder metadata %s: %s"), + metapath, g_strerror(errno)); + } + + g_free(metapath); +} + +static void +mls_rename_folder(CamelStore *store, const char *old_name, const char *new_name, CamelException *ex) +{ + CamelStore *real_store; + /*MailLocalStore *mls = (MailLocalStore *)store;*/ + char *uri; + /*CamelException local_ex;*/ + struct _local_meta *meta; + char *oldname, *newname; + char *oldmeta, *newmeta; + struct stat st; + + /* folder:rename() updates all our in-memory data to match */ + + /* FIXME: Need to lock the subfolder that matches this if its open + Then rename it and unlock it when done */ + + d(printf("Renaming folder from '%s' to '%s'\n", old_name, new_name)); + + oldmeta = g_strdup_printf("%s%s/local-metadata.xml", ((CamelService *)store)->url->path, old_name); + newmeta = g_strdup_printf("%s%s/local-metadata.xml", ((CamelService *)store)->url->path, new_name); + + meta = load_metainfo(oldmeta); + uri = g_strdup_printf("%s:%s", meta->format, ((CamelService *)store)->url->path); + real_store = (CamelStore *)camel_session_get_service(session, uri, CAMEL_PROVIDER_STORE, ex); + g_free(uri); + if (real_store == NULL) { + g_free(newmeta); + g_free(oldmeta); + free_metainfo(meta); + return; + } + + oldname = g_strdup_printf("%s/%s", old_name, meta->name); + newname = g_strdup_printf("%s/%s", new_name, meta->name); + + camel_store_rename_folder(real_store, oldname, newname, ex); + if (!camel_exception_is_set(ex)) { + /* If this fails? Well, doesn't really matter but 'fail' anyway */ + if (stat(oldmeta, &st) == 0 + && rename(oldmeta, newmeta) == -1) { + camel_exception_setv(ex, CAMEL_EXCEPTION_SYSTEM, + _("Could not rename folder %s to %s: %s"), + old_name, new_name, strerror(errno)); + } else { + /* So .. .the shell does a remove/add now the rename worked, so we dont + have to do this. However totally broken that idea might be */ +#if 0 + CamelFolderInfo *info; + const char *tmp; + char *olduri, *newuri; + + olduri = g_strdup_printf("%s:%s%s", ((CamelService *)store)->url->protocol, ((CamelService *)store)->url->path, old_name); + newuri = g_strdup_printf("%s:%s%s", ((CamelService *)store)->url->protocol, ((CamelService *)store)->url->path, new_name); + info = g_hash_table_lookup(mls->folder_infos, olduri); + if (info) { + CamelRenameInfo reninfo; + + g_free(info->url); + g_free(info->full_name); + g_free(info->name); + g_free(info->path); + info->url = newuri; + info->full_name = g_strdup(new_name); + info->path = g_strdup_printf("/%s", new_name); + tmp = strchr(new_name, '/'); + if (tmp == NULL) + tmp = new_name; + info->name = g_strdup(tmp); + g_hash_table_insert(mls->folder_infos, info->url, info); + + reninfo.new = info; + reninfo.old_base = (char *)old_name; + + camel_object_trigger_event(store, "folder_renamed", &reninfo); + } else { + g_free(newuri); + g_warning("Cannot find existing folder '%s' in table?\n", olduri); + } + + g_free(olduri); +#endif + } + } + + g_free(newname); + g_free(oldname); + + camel_object_unref(real_store); + + free_metainfo(meta); + + g_free(newmeta); + g_free(oldmeta); +} + +static char * +mls_get_name (CamelService *service, gboolean brief) +{ + if (brief) + return g_strdup("local"); + + return g_strdup("Local mail folders"); +} + +static void +mls_init (MailLocalStore *mls, MailLocalStoreClass *mlsclass) +{ + mls->folder_infos = g_hash_table_new(g_str_hash, g_str_equal); + mls->folder_info_lock = g_mutex_new(); +} + +static void +free_info(void *key, void *value, void *data) +{ + CamelFolderInfo *info = value; + + camel_folder_info_free (info); +} + +static void +mls_finalise(MailLocalStore *mls) +{ + g_hash_table_foreach(mls->folder_infos, (GHFunc)free_info, NULL); + g_hash_table_destroy(mls->folder_infos); + g_mutex_free(mls->folder_info_lock); +} + +static void +mls_class_init (CamelObjectClass *camel_object_class) +{ + CamelStoreClass *camel_store_class = CAMEL_STORE_CLASS(camel_object_class); + CamelServiceClass *camel_service_class = CAMEL_SERVICE_CLASS(camel_object_class); + + /* virtual method overload -- the bare minimum */ + camel_service_class->get_name = mls_get_name; + camel_store_class->get_folder = mls_get_folder; + camel_store_class->delete_folder = mls_delete_folder; + camel_store_class->rename_folder = mls_rename_folder; + + local_store_parent_class = camel_type_get_global_classfuncs (CAMEL_STORE_TYPE); +} + +static CamelType +mail_local_store_get_type (void) +{ + static CamelType mail_local_store_type = CAMEL_INVALID_TYPE; + + if (mail_local_store_type == CAMEL_INVALID_TYPE) { + mail_local_store_type = camel_type_register ( + CAMEL_STORE_TYPE, "MailLocalStore", + sizeof (MailLocalStore), + sizeof (MailLocalStoreClass), + (CamelObjectClassInitFunc) mls_class_init, + NULL, + (CamelObjectInitFunc) mls_init, + (CamelObjectFinalizeFunc) mls_finalise); + } + + return mail_local_store_type; +} + +static void mail_local_store_add_folder(MailLocalStore *mls, const char *uri, const char *path, const char *name) +{ + CamelFolderInfo *info = NULL; + CamelURL *url; + + d(printf("Shell adding folder: '%s' path = '%s'\n", uri, path)); + + url = camel_url_new(uri, NULL); + if (url == NULL) { + g_warning("Shell trying to add invalid folder url: %s", uri); + return; + } + if (url->path == NULL || url->path[0] == 0) { + g_warning("Shell trying to add invalid folder url: %s", uri); + camel_url_free(url); + return; + } + + LOCAL_STORE_LOCK(mls); + + if (g_hash_table_lookup(mls->folder_infos, uri)) { + g_warning("Shell trying to add a folder I already have!"); + } else { + info = g_malloc0(sizeof(*info)); + info->url = g_strdup(uri); + info->full_name = g_strdup(url->path+1); + info->name = g_strdup(name); + info->unread_message_count = -1; + info->path = g_strdup (path); + g_hash_table_insert(mls->folder_infos, info->url, info); + } + + LOCAL_STORE_UNLOCK(mls); + + camel_url_free(url); + + if (info) { + /* FIXME: should copy info, so we dont get a removed while we're using it? */ + camel_object_trigger_event(mls, "folder_created", info); + + /* this is just so the folder is opened at least once to setup the folder + counts etc in the display. Joy eh? The result is discarded. */ + mail_get_folder (uri, CAMEL_STORE_FOLDER_CREATE, NULL, NULL, mail_thread_queued_slow); + } +} + +struct _search_info { + const char *path; + CamelFolderInfo *info; +}; + +static void +remove_find_path(char *uri, CamelFolderInfo *info, struct _search_info *data) +{ + if (!strcmp(info->path, data->path)) + data->info = info; +} + +static void mail_local_store_remove_folder(MailLocalStore *mls, const char *path) +{ + struct _search_info data = { path, NULL }; + + d(printf("shell removing folder? '%s'\n", path)); + + /* we're keyed on uri, not path, so have to search for it manually */ + + LOCAL_STORE_LOCK(mls); + g_hash_table_foreach(mls->folder_infos, (GHFunc)remove_find_path, &data); + if (data.info) + g_hash_table_remove(mls->folder_infos, data.info->url); + LOCAL_STORE_UNLOCK(mls); + + if (data.info) { + camel_object_trigger_event(mls, "folder_deleted", data.info); + + g_free(data.info->url); + g_free(data.info->full_name); + g_free(data.info->name); + g_free(data.info); + } +} + +/* ** Local Provider ************************************************************** */ + +static CamelProvider local_provider = { + "file", "Local mail", "Local mailbox file", "mail", + CAMEL_PROVIDER_IS_STORAGE | CAMEL_PROVIDER_IS_EXTERNAL, + CAMEL_URL_NEED_PATH, + /* ... */ +}; + +/* There's only one "file:" store. */ +static guint +non_hash (gconstpointer key) +{ + return 0; +} + +static gint +non_equal (gconstpointer ap, gconstpointer bp) +{ + const CamelURL *a = ap, *b = bp; + + return strcmp(a->protocol, "file") == 0 + && strcmp(a->protocol, b->protocol) == 0; +} + +static void +mail_local_provider_init (void) +{ + /* Register with Camel to handle file: URLs */ + local_provider.object_types[CAMEL_PROVIDER_STORE] = MAIL_LOCAL_STORE_TYPE; + + local_provider.url_hash = non_hash; + local_provider.url_equal = non_equal; + camel_session_register_provider (session, &local_provider); +} + +/* ** Local Storage Listener ****************************************************** */ + +static void +local_storage_new_folder_cb_trash_or_junk (const GNOME_Evolution_Folder *folder, const gchar *path, gchar *full_name, gchar *url_base) +{ + CamelFolderInfo info; + CamelURL *url; + + url = camel_url_new(folder->physicalUri, NULL); + if (url == NULL) { + g_warning("Shell trying to add invalid folder url: %s", folder->physicalUri); + return; + } + if (url->path == NULL || url->path[0] == 0) { + g_warning("Shell trying to add invalid folder url: %s", folder->physicalUri); + camel_url_free(url); + return; + } + + memset(&info, 0, sizeof(info)); + info.full_name = full_name; + info.name = folder->displayName; + info.url = g_strdup_printf("%s:%s", url_base, folder->physicalUri); + info.unread_message_count = 0; + info.path = (char *)path; + + camel_object_trigger_event(global_local_store, "folder_created", &info); + g_free(info.url); + camel_url_free(url); +} + +static void +local_storage_new_folder_cb (EvolutionStorageListener *storage_listener, + const char *path, + const GNOME_Evolution_Folder *folder, + void *data) +{ + d(printf("Local folder new:\n")); + d(printf(" path = '%s'\n uri = '%s'\n display = '%s'\n", + path, folder->physicalUri, folder->displayName)); + + /* We dont actually add the trash/junk to our local folders list, get_trash is handled + outside our internal folder list */ + + if (strcmp(folder->type, "mail") == 0) { + mail_local_store_add_folder(global_local_store, folder->physicalUri, path, folder->displayName); + } else if (strcmp(folder->type, "vtrash") == 0) + local_storage_new_folder_cb_trash_or_junk (folder, path, CAMEL_VTRASH_NAME, "vtrash"); + else if (strcmp(folder->type, "vjunk") == 0) + local_storage_new_folder_cb_trash_or_junk (folder, path, CAMEL_VJUNK_NAME, "vjunk"); + +} + + +static void +local_storage_removed_folder_cb (EvolutionStorageListener *storage_listener, + const char *path, + void *data) +{ + d(printf("Local folder remove:\n")); + d(printf(" path = '%s'\n", path)); + + mail_local_store_remove_folder(global_local_store, path); +} + +static void +storage_listener_startup (EvolutionShellClient *shellclient) +{ + EvolutionStorageListener *local_storage_listener; + GNOME_Evolution_StorageListener corba_local_storage_listener; + GNOME_Evolution_Storage corba_storage; + CORBA_Environment ev; + + d(printf("---- CALLING STORAGE LISTENER STARTUP ---\n")); + + local_corba_storage = corba_storage = evolution_shell_client_get_local_storage (shellclient); + if (corba_storage == CORBA_OBJECT_NIL) { + g_warning ("No local storage available from shell client!"); + return; + } + + /* setup to record this store's changes */ + mail_note_store((CamelStore *)global_local_store, NULL, NULL, local_corba_storage, NULL, NULL); + + local_storage_listener = evolution_storage_listener_new (); + corba_local_storage_listener = evolution_storage_listener_corba_objref ( + local_storage_listener); + + g_signal_connect(local_storage_listener, + "new_folder", + G_CALLBACK (local_storage_new_folder_cb), + corba_storage); + g_signal_connect(local_storage_listener, + "removed_folder", + G_CALLBACK (local_storage_removed_folder_cb), + corba_storage); + + CORBA_exception_init (&ev); + GNOME_Evolution_Storage_addListener (corba_storage, + corba_local_storage_listener, &ev); + if (ev._major != CORBA_NO_EXCEPTION) { + g_warning ("Cannot add a listener to the Local Storage."); + CORBA_exception_free (&ev); + return; + } + CORBA_exception_free (&ev); +} + +/* ** The rest ******************************************************************** */ + +void +mail_local_storage_startup (EvolutionShellClient *shellclient, const char *evolution_path) +{ + mail_local_provider_init (); + + global_local_store = MAIL_LOCAL_STORE(camel_session_get_service (session, "file:/", CAMEL_PROVIDER_STORE, NULL)); + + if (!global_local_store) { + g_warning ("No local store!"); + return; + } + + storage_listener_startup (shellclient); +} + +void +mail_local_storage_shutdown (void) +{ + bonobo_object_release_unref (local_corba_storage, NULL); + local_corba_storage = CORBA_OBJECT_NIL; +} + + +/*---------------------------------------------------------------------- + * Local folder reconfiguration stuff + *----------------------------------------------------------------------*/ + +/* + open new + copy old->new + close old + rename old oldsave + rename new old + open oldsave + delete oldsave + + close old + rename oldtmp + open new + open oldtmp + copy oldtmp new + close oldtmp + close oldnew + +*/ + +/* we should have our own progress bar for this */ + +struct _reconfigure_msg { + struct _mail_msg msg; + + char *uri; + CamelFolder *folder; + + char *newtype; + unsigned int index_body:1; + GtkWidget *frame; + GtkWidget *apply; + GtkWidget *cancel; + GtkWidget *check_index_body; + GtkOptionMenu *optionlist; + + void (*done)(const char *uri, CamelFolder *folder, void*data); + void *done_data; +}; + +/* hash table of folders that the user has a reconfig-folder dialog for */ +static GHashTable *reconfigure_folder_hash = NULL; + +static char * +reconfigure_folder_describe (struct _mail_msg *mm, int done) +{ + struct _reconfigure_msg *m = (struct _reconfigure_msg *)mm; + + return g_strdup_printf (_("Changing folder \"%s\" to \"%s\" format"), + camel_folder_get_full_name (m->folder), + m->newtype); +} + +static void +reconfigure_folder_reconfigure (struct _mail_msg *mm) +{ + struct _reconfigure_msg *m = (struct _reconfigure_msg *)mm; + + d(printf("reconfiguring folder: %s to type %s\n", m->uri, m->newtype)); + + mail_local_folder_reconfigure (MAIL_LOCAL_FOLDER (m->folder), m->newtype, m->index_body, &mm->ex); +} + +static void +reconfigure_folder_reconfigured (struct _mail_msg *mm) +{ + struct _reconfigure_msg *m = (struct _reconfigure_msg *)mm; + GtkWidget *dialog; + /*char *uri;*/ + + if (camel_exception_is_set (&mm->ex)) { + dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, + GTK_BUTTONS_OK, "%s", + _("If you can no longer open this mailbox, then\n" + "you may need to repair it manually.")); + gtk_dialog_run (GTK_DIALOG (dialog)); + } + + if (m->done) + m->done(m->uri, m->folder, m->done_data); +} + +static void +reconfigure_folder_free (struct _mail_msg *mm) +{ + struct _reconfigure_msg *m = (struct _reconfigure_msg *)mm; + + /* remove this folder from our hash since we are done with it */ + g_hash_table_remove (reconfigure_folder_hash, m->folder); + if (g_hash_table_size (reconfigure_folder_hash) == 0) { + /* additional cleanup */ + g_hash_table_destroy (reconfigure_folder_hash); + reconfigure_folder_hash = NULL; + } + + if (m->folder) + camel_object_unref (m->folder); + g_free(m->uri); + g_free (m->newtype); +} + +static struct _mail_msg_op reconfigure_folder_op = { + reconfigure_folder_describe, + reconfigure_folder_reconfigure, + reconfigure_folder_reconfigured, + reconfigure_folder_free, +}; + +static void +reconfigure_response(GtkDialog *dialog, int button, struct _reconfigure_msg *m) +{ + switch(button) { + case GTK_RESPONSE_OK: { + GtkWidget *menu, *item; + + menu = gtk_option_menu_get_menu(m->optionlist); + item = gtk_menu_get_active(GTK_MENU(menu)); + m->newtype = g_strdup(g_object_get_data ((GObject *)item, "type")); + m->index_body = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(m->check_index_body)); + + gtk_widget_set_sensitive (m->frame, FALSE); + gtk_widget_set_sensitive (m->apply, FALSE); + gtk_widget_set_sensitive (m->cancel, FALSE); + + e_thread_put (mail_thread_new, (EMsg *)m); + break; } + case GTK_RESPONSE_CANCEL: + default: + if (m->done) + m->done(m->uri, NULL, m->done_data); + mail_msg_free ((struct _mail_msg *)m); + break; + } + + gtk_widget_destroy((GtkWidget *)dialog); +} + +static void +reconfigure_got_folder(char *uri, CamelFolder *folder, void *data) +{ + GladeXML *gui; + GtkDialog *gd; + struct _reconfigure_msg *m = data; + char *title; + GList *p; + GtkWidget *menu; + char *currentformat; + int index=0, history=0; + + if (folder == NULL + || !MAIL_IS_LOCAL_FOLDER (folder)) { + g_warning ("Trying to reconfigure nonexistant folder"); + /* error display ? */ + if (m->done) + m->done(uri, NULL, m->done_data); + mail_msg_free((struct _mail_msg *)m); + return; + } + + if (!reconfigure_folder_hash) + reconfigure_folder_hash = g_hash_table_new (NULL, NULL); + + if ((gd = g_hash_table_lookup (reconfigure_folder_hash, folder))) { + gdk_window_raise (GTK_WIDGET (gd)->window); + if (m->done) + m->done(uri, NULL, m->done_data); + mail_msg_free((struct _mail_msg *)m); + return; + } + + gui = glade_xml_new (EVOLUTION_GLADEDIR "/local-config.glade", "dialog_format", NULL); + gd = (GtkDialog *)glade_xml_get_widget (gui, "dialog_format"); + + title = g_strdup_printf (_("Reconfigure /%s"), + camel_folder_get_full_name (folder)); + gtk_window_set_title (GTK_WINDOW (gd), title); + g_free (title); + + m->uri = g_strdup(uri); + m->frame = glade_xml_get_widget (gui, "frame_format"); + m->apply = glade_xml_get_widget (gui, "apply_format"); + m->cancel = glade_xml_get_widget (gui, "cancel_format"); + m->optionlist = (GtkOptionMenu *)glade_xml_get_widget (gui, "option_format"); + m->check_index_body = glade_xml_get_widget (gui, "check_index_body"); + m->newtype = NULL; + m->folder = folder; + camel_object_ref(folder); + + /* dynamically create the folder type list from camel */ + /* we assume the list is static and never freed */ + currentformat = MAIL_LOCAL_FOLDER (folder)->meta->format; + p = camel_session_list_providers (session, TRUE); + menu = gtk_menu_new (); + while (p) { + CamelProvider *cp = p->data; + + /* we only want local providers */ + if (cp->flags & CAMEL_PROVIDER_IS_LOCAL) { + GtkWidget *item; + char *label; + + if (!strcmp (cp->protocol, currentformat)) + history = index; + + label = g_strdup_printf("%s (%s)", cp->protocol, _(cp->name)); + item = gtk_menu_item_new_with_label (label); + g_free (label); + g_object_set_data ((GObject *) item, "type", cp->protocol); + gtk_widget_show (item); + gtk_menu_shell_append (GTK_MENU_SHELL (menu), item); + index++; + } + p = p->next; + } + gtk_option_menu_remove_menu (GTK_OPTION_MENU (m->optionlist)); + gtk_option_menu_set_menu (GTK_OPTION_MENU(m->optionlist), menu); + gtk_option_menu_set_history(GTK_OPTION_MENU(m->optionlist), history); + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(m->check_index_body), MAIL_LOCAL_FOLDER (folder)->meta->indexed); + + gtk_label_set_text ((GtkLabel *)glade_xml_get_widget (gui, "label_format"), + MAIL_LOCAL_FOLDER (folder)->meta->format); + + g_signal_connect(gd, "response", G_CALLBACK(reconfigure_response), m); + g_object_unref(gui); + + g_hash_table_insert (reconfigure_folder_hash, (gpointer) folder, (gpointer) gd); + + gtk_widget_show((GtkWidget *)gd); +} + +void +mail_local_reconfigure_folder(const char *uri, void (*done)(const char *uri, CamelFolder *folder, void *data), void *done_data) +{ + struct _reconfigure_msg *m; + + if (strncmp(uri, "file:", 5) != 0) { + e_notice (NULL, GTK_MESSAGE_WARNING, + _("You cannot change the format of a non-local folder.")); + if (done) + done(uri, NULL, done_data); + return; + } + + m = mail_msg_new (&reconfigure_folder_op, NULL, sizeof (*m)); + m->done = done; + m->done_data = done_data; + + mail_get_folder(uri, 0, reconfigure_got_folder, m, mail_thread_new); +} diff --git a/mail/mail-ops.c b/mail/mail-ops.c index 8d3711ff4e..3474885047 100644 --- a/mail/mail-ops.c +++ b/mail/mail-ops.c @@ -38,7 +38,6 @@ #include <camel/camel-vtrash-folder.h> #include <camel/camel-vee-store.h> #include "mail.h" -#include "mail-component.h" #include "mail-tools.h" #include "mail-ops.h" #include "mail-vfolder.h" @@ -230,8 +229,7 @@ uid_cachename_hack (CamelStore *store) url->host); e_filename_make_safe (encoded_url); - filename = g_strdup_printf ("%s/mail/pop3/cache-%s", - mail_component_peek_base_directory (mail_component_peek ()), encoded_url); + filename = g_strdup_printf ("%s/mail/pop3/cache-%s", evolution_dir, encoded_url); /* lame hack, but we can't expect user's to actually migrate their cache files - brain power requirements are too @@ -240,9 +238,7 @@ uid_cachename_hack (CamelStore *store) /* This is either the first time the user has checked mail with this POP provider or else their cache file is in the old location... */ - old_location = g_strdup_printf ("%s/config/cache-%s", - mail_component_peek_base_directory (mail_component_peek ()), - encoded_url); + old_location = g_strdup_printf ("%s/config/cache-%s", evolution_dir, encoded_url); if (stat (old_location, &st) == -1) { /* old location doesn't exist either so use the new location */ g_free (old_location); @@ -1063,9 +1059,9 @@ get_folderinfo_desc (struct _mail_msg *mm, int done) } static void -add_vtrash_info (CamelStore *store, CamelFolderInfo *info) +add_vtrash_or_vjunk_info (CamelStore *store, CamelFolderInfo *info, gchar *name, gchar *full_name, gchar *url_base, gboolean unread_count) { - CamelFolderInfo *fi, *vtrash, *parent; + CamelFolderInfo *fi, *vinfo, *parent; char *uri, *path; CamelURL *url; @@ -1073,14 +1069,14 @@ add_vtrash_info (CamelStore *store, CamelFolderInfo *info) parent = NULL; for (fi = info; fi; fi = fi->sibling) { - if (!strcmp (fi->name, CAMEL_VTRASH_NAME)) + if (!strcmp (fi->name, name)) break; parent = fi; } - /* create our vTrash URL */ + /* create our vTrash/vJunk URL */ url = camel_url_new (info->url, NULL); - path = g_strdup_printf ("/%s", CAMEL_VTRASH_NAME); + path = g_strdup_printf ("/%s", name); if (url->fragment) camel_url_set_fragment (url, path); else @@ -1090,32 +1086,45 @@ add_vtrash_info (CamelStore *store, CamelFolderInfo *info) camel_url_free (url); if (fi) { - /* We're going to replace the physical Trash folder with our vTrash folder */ - vtrash = fi; - g_free (vtrash->full_name); - g_free (vtrash->name); - g_free (vtrash->url); + /* We're going to replace the physical Trash/Junk folder with our vTrash/vJunk folder */ + vinfo = fi; + g_free (vinfo->full_name); + g_free (vinfo->name); + g_free (vinfo->url); } else { - /* There wasn't a Trash folder so create a new folder entry */ - vtrash = g_new0 (CamelFolderInfo, 1); + /* There wasn't a Trash/Junk folder so create a new folder entry */ + vinfo = g_new0 (CamelFolderInfo, 1); g_assert(parent != NULL); /* link it into the right spot */ - vtrash->sibling = parent->sibling; - parent->sibling = vtrash; + vinfo->sibling = parent->sibling; + parent->sibling = vinfo; } /* Fill in the new fields */ - vtrash->full_name = g_strdup (_("Trash")); - vtrash->name = g_strdup(vtrash->full_name); - vtrash->url = g_strdup_printf ("vtrash:%s", uri); - vtrash->unread_message_count = -1; - vtrash->path = g_strdup_printf("/%s", vtrash->name); + vinfo->full_name = g_strdup (full_name); + vinfo->name = g_strdup(vinfo->full_name); + vinfo->url = g_strdup_printf ("%s:%s", url_base, uri); + if (!unread_count) + vinfo->unread_message_count = -1; + vinfo->path = g_strdup_printf("/%s", vinfo->name); g_free (uri); } static void +add_vtrash_info (CamelStore *store, CamelFolderInfo *info) +{ + add_vtrash_or_vjunk_info (store, info, CAMEL_VTRASH_NAME, _("Trash"), "vtrash", FALSE); +} + +static void +add_vjunk_info (CamelStore *store, CamelFolderInfo *info) +{ + add_vtrash_or_vjunk_info (store, info, CAMEL_VJUNK_NAME, _("Junk"), "vjunk", TRUE); +} + +static void add_unmatched_info(CamelFolderInfo *fi) { for (; fi; fi = fi->sibling) { @@ -1142,6 +1151,8 @@ get_folderinfo_get (struct _mail_msg *mm) if (m->info) { if (m->info->url && (m->store->flags & CAMEL_STORE_VTRASH)) add_vtrash_info(m->store, m->info); + if (m->info->url && (m->store->flags & CAMEL_STORE_VJUNK)) + add_vjunk_info(m->store, m->info); if (CAMEL_IS_VEE_STORE(m->store)) add_unmatched_info(m->info); } @@ -2329,3 +2340,101 @@ mail_execute_shell_command (CamelFilterDriver *driver, int argc, char **argv, vo gnome_execute_async_fds (NULL, argc, argv, TRUE); } + +/* [Un]mark junk flag */ + +struct _mark_junk_mail_msg { + struct _mail_msg msg; + + CamelFolder *folder; + MessageList *list; + gboolean junk; +}; + +static char * +mark_junk_describe (struct _mail_msg *mm, int complete) +{ + return g_strdup (_("Changing junk status")); +} + +/* filter a folder, or a subset thereof, uses source_folder/source_uids */ +/* this is shared with fetch_mail */ +static void +mark_junk_mark (struct _mail_msg *mm) +{ + struct _mark_junk_mail_msg *m = (struct _mark_junk_mail_msg *) mm; + CamelJunkPlugin *csp = NULL; + GPtrArray *uids; + gboolean commit_reports = FALSE; + int i; + + if (m->folder == NULL) + return; + + uids = message_list_get_selected (m->list); + camel_folder_freeze (m->folder); + + for (i=0; i<uids->len; i++) { + guint32 flags; + + flags = camel_folder_get_message_flags (m->folder, uids->pdata[i]); + if (((flags & CAMEL_MESSAGE_JUNK) == CAMEL_MESSAGE_JUNK) != m->junk) { + CamelMimeMessage *msg = camel_folder_get_message (m->folder, uids->pdata[i], NULL); + + if (msg) { + csp = CAMEL_SERVICE (m->folder->parent_store)->session->junk_plugin; + if (m->junk) + camel_junk_plugin_report_junk (csp, msg); + else + camel_junk_plugin_report_notjunk (csp, msg); + + commit_reports = TRUE; + camel_object_unref (msg); + } + } + camel_folder_set_message_flags(m->folder, uids->pdata[i], + CAMEL_MESSAGE_JUNK | (m->junk ? CAMEL_MESSAGE_DELETED : 0), + m->junk ? CAMEL_MESSAGE_JUNK : 0); + } + + if (commit_reports) + camel_junk_plugin_commit_reports (csp); + + message_list_free_uids(m->list, uids); + camel_folder_thaw(m->folder); +} + +static void +mark_junk_marked (struct _mail_msg *mm) +{ +} + +static void +mark_junk_free (struct _mail_msg *mm) +{ + struct _mark_junk_mail_msg *m = (struct _mark_junk_mail_msg *)mm; + + if (m->folder) + camel_object_unref (m->folder); +} + +static struct _mail_msg_op mark_junk_op = { + mark_junk_describe, + mark_junk_mark, + mark_junk_marked, + mark_junk_free, +}; + +void +mail_mark_junk (CamelFolder *folder, MessageList *list, gboolean junk) +{ + struct _mark_junk_mail_msg *m; + + m = mail_msg_new (&mark_junk_op, NULL, sizeof (*m)); + m->folder = folder; + camel_object_ref (folder); + m->list = list; + m->junk = junk; + + e_thread_put (mail_thread_new, (EMsg *) m); +} diff --git a/mail/mail-ops.h b/mail/mail-ops.h index e15d7729c2..081e1a1425 100644 --- a/mail/mail-ops.h +++ b/mail/mail-ops.h @@ -36,6 +36,8 @@ extern "C" { #include "camel/camel-mime-message.h" #include "camel/camel-operation.h" +#include "message-list.h" + #include "evolution-storage.h" /*EvolutionStorage */ #include "e-util/e-msgport.h" #include "e-util/e-account.h" @@ -164,6 +166,8 @@ int mail_store_set_offline(CamelStore *store, gboolean offline, /* filter driver execute shell command async callback */ void mail_execute_shell_command (CamelFilterDriver *driver, int argc, char **argv, void *data); +void mail_mark_junk (CamelFolder *folder, MessageList *list, gboolean junk); + #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/mail/mail-session.c b/mail/mail-session.c index 58773513c4..61adfa812c 100644 --- a/mail/mail-session.c +++ b/mail/mail-session.c @@ -40,7 +40,6 @@ #include "filter/filter-context.h" #include "filter/filter-filter.h" #include "mail.h" -#include "mail-component.h" #include "mail-config.h" #include "mail-session.h" #include "mail-tools.h" @@ -48,6 +47,7 @@ #include "mail-ops.h" #include "e-util/e-passwords.h" #include "e-util/e-msgport.h" +#include "em-junk-filter.h" #define d(x) @@ -594,7 +594,7 @@ main_get_filter_driver (CamelSession *session, const char *type, CamelException gconf = mail_config_get_gconf_client (); - user = g_strdup_printf ("%s/mail/filters.xml", mail_component_peek_base_directory (mail_component_peek ())); + user = g_strdup_printf ("%s/filters.xml", evolution_dir); system = EVOLUTION_PRIVDATADIR "/filtertypes.xml"; fc = (RuleContext *) filter_context_new (); rule_context_load (fc, system, user); @@ -626,6 +626,9 @@ main_get_filter_driver (CamelSession *session, const char *type, CamelException fsearch = g_string_new (""); faction = g_string_new (""); + + /* implicit junk check as 1st rule */ + camel_filter_driver_add_rule (driver, "Junk check", "(junk-test)", "(begin (set-system-flag \"junk\"))"); /* add the user-defined rules next */ while ((rule = rule_context_next_rule (fc, rule, type))) { @@ -634,7 +637,6 @@ main_get_filter_driver (CamelSession *session, const char *type, CamelException filter_rule_build_code (rule, fsearch); filter_filter_build_action ((FilterFilter *) rule, faction); - camel_filter_driver_add_rule (driver, rule->name, fsearch->str, faction->str); } @@ -744,17 +746,19 @@ mail_session_forget_password (const char *key) } void -mail_session_init (const char *base_directory) +mail_session_init (void) { char *camel_dir; - - if (camel_init (base_directory, TRUE) != 0) + + if (camel_init (evolution_dir, TRUE) != 0) exit (0); session = CAMEL_SESSION (camel_object_new (MAIL_SESSION_TYPE)); - camel_dir = g_strdup_printf ("%s/mail", base_directory); + camel_dir = g_strdup_printf ("%s/mail", evolution_dir); camel_session_construct (session, camel_dir); + + session->junk_plugin = CAMEL_JUNK_PLUGIN (em_junk_filter_get_plugin ()); /* The shell will tell us to go online. */ camel_session_set_online ((CamelSession *) session, FALSE); diff --git a/mail/mail-tools.c b/mail/mail-tools.c index b85a18c12f..dde9b5e9c8 100644 --- a/mail/mail-tools.c +++ b/mail/mail-tools.c @@ -46,10 +46,10 @@ #include "e-util/e-meta.h" #include "mail.h" /*session*/ -#include "mail-component.h" #include "mail-config.h" #include "mail-vfolder.h" #include "mail-tools.h" +#include "mail-local.h" #include "mail-mt.h" #include "mail-folder-cache.h" @@ -62,8 +62,7 @@ mail_tool_get_local_inbox (CamelException *ex) CamelFolder *folder; char *url; - url = g_strdup_printf("file://%s/local/Inbox", - mail_component_peek_base_directory (mail_component_peek ())); + url = g_strdup_printf("file://%s/local/Inbox", evolution_dir); folder = mail_tool_uri_to_folder (url, 0, ex); g_free (url); @@ -121,9 +120,7 @@ mail_tool_get_local_movemail_path (const unsigned char *uri) if (strchr ("/:;=|%&#!*^()\\, ", *c) || !isprint ((int) *c)) *c = '_'; - path = g_strdup_printf ("%s/local/Inbox/movemail.%s", - mail_component_peek_base_directory (mail_component_peek ()), - safe_uri); + path = g_strdup_printf ("%s/local/Inbox/movemail.%s", evolution_dir, safe_uri); g_free (safe_uri); return path; @@ -312,6 +309,8 @@ mail_tool_uri_to_folder (const char *uri, guint32 flags, CamelException *ex) /* This hack is still needed for file:/ since it's its own EvolutionStorage type */ if (!strncmp (uri, "vtrash:", 7)) offset = 7; + else if (!strncmp (uri, "vjunk:", 6)) + offset = 6; url = camel_url_new (uri + offset, ex); if (!url) { @@ -321,7 +320,7 @@ mail_tool_uri_to_folder (const char *uri, guint32 flags, CamelException *ex) store = camel_session_get_store (session, uri + offset, ex); if (store) { const char *name; - + /* if we have a fragment, then the path is actually used by the store, so the fragment is the path to the folder instead */ if (url->fragment) { @@ -333,9 +332,14 @@ mail_tool_uri_to_folder (const char *uri, guint32 flags, CamelException *ex) name = ""; } - if (offset) - folder = camel_store_get_trash (store, ex); - else + if (offset) { + if (offset == 7) + folder = camel_store_get_trash (store, ex); + else if (offset == 6) + folder = camel_store_get_junk (store, ex); + else + g_assert (FALSE); + } else folder = camel_store_get_folder (store, name, flags, ex); camel_object_unref (store); } @@ -415,7 +419,6 @@ mail_tools_folder_to_url (CamelFolder *folder) static char *meta_data_key(const char *uri, char **pathp) { - const char *base_directory = mail_component_peek_base_directory (mail_component_peek ()); CamelURL *url; GString *path; const char *key; @@ -425,12 +428,12 @@ static char *meta_data_key(const char *uri, char **pathp) if (url == NULL) { g_warning("Trying to retrieve meta-data for unparsable uri: %s", uri); - *pathp = g_build_path(base_directory, "meta/unknown", NULL); + *pathp = g_build_path(evolution_dir, "meta/unknown", NULL); return g_strdup("folder"); } - path = g_string_new(base_directory); + path = g_string_new(evolution_dir); g_string_append_printf(path, "/meta/%s/", url->protocol); if (url->host && url->host[0]) { diff --git a/mail/mail-vfolder.c b/mail/mail-vfolder.c index f47fe4c660..82635df8df 100644 --- a/mail/mail-vfolder.c +++ b/mail/mail-vfolder.c @@ -32,7 +32,6 @@ #include "evolution-storage.h" #include "evolution-shell-component.h" -#include "mail-component.h" #include "mail-vfolder.h" #include "mail-tools.h" #include "mail-autofilter.h" @@ -65,6 +64,7 @@ static GHashTable *vfolder_hash; extern EvolutionShellClient *global_shell_client; /* more globals ... */ +extern char *evolution_dir; extern CamelSession *session; static void rule_changed(FilterRule *rule, CamelFolder *folder); @@ -345,7 +345,7 @@ mail_vfolder_add_uri(CamelStore *store, const char *uri, int remove) GCompareFunc uri_cmp = CAMEL_STORE_CLASS(CAMEL_OBJECT_GET_CLASS(store))->compare_folder_name; int is_ignore; - if (CAMEL_IS_VEE_STORE(store) || !strncmp(uri, "vtrash:", 7) || context == NULL) + if (CAMEL_IS_VEE_STORE(store) || !strncmp(uri, "vtrash:", 7) || !strncmp(uri, "vjunk:", 6) || context == NULL) return; g_assert(pthread_self() == mail_gui_thread); @@ -426,7 +426,7 @@ mail_vfolder_delete_uri(CamelStore *store, const char *uri) CamelVeeFolder *vf; GString *changed; - if (context == NULL || !strncmp(uri, "vtrash:", 7)) + if (context == NULL || !strncmp(uri, "vtrash:", 7) || !strncmp(uri, "vjunk:", 6)) return; d(printf ("Deleting uri to check: %s\n", uri)); @@ -471,8 +471,7 @@ mail_vfolder_delete_uri(CamelStore *store, const char *uri) g_signal_connect_swapped (dialog, "response", G_CALLBACK (gtk_widget_destroy), dialog); gtk_widget_show (dialog); - user = g_strdup_printf ("%s/mail/vfolders.xml", - mail_component_peek_base_directory (mail_component_peek ())); + user = g_strdup_printf ("%s/vfolders.xml", evolution_dir); rule_context_save ((RuleContext *) context, user); g_free (user); } @@ -492,7 +491,7 @@ mail_vfolder_rename_uri(CamelStore *store, const char *from, const char *to) d(printf("vfolder rename uri: %s to %s\n", from, to)); - if (context == NULL || !strncmp(from, "vtrash:", 7) || !strncmp(to, "vtrash:", 7)) + if (context == NULL || !strncmp(from, "vtrash:", 7) || !strncmp(to, "vtrash:", 7) || !strncmp(from, "vjunk:", 6) || !strncmp(to, "vjunk:", 6)) return; g_assert(pthread_self() == mail_gui_thread); @@ -527,7 +526,7 @@ mail_vfolder_rename_uri(CamelStore *store, const char *from, const char *to) char *user; d(printf("Vfolders updated from renamed folder\n")); - user = g_strdup_printf("%s/mail/vfolders.xml", mail_component_peek_base_directory (mail_component_peek ())); + user = g_strdup_printf("%s/vfolders.xml", evolution_dir); rule_context_save((RuleContext *)context, user); g_free(user); } @@ -647,10 +646,7 @@ static void context_rule_removed(RuleContext *ctx, FilterRule *rule) /* TODO: remove from folder info cache? */ path = g_strdup_printf("/%s", rule->name); - - /* EPFIXME This leaks, the original code was broken too. */ - e_storage_removed_folder (mail_component_lookup_storage (mail_component_peek (), vfolder_store), path); - + evolution_storage_removed_folder(mail_lookup_storage(vfolder_store), path); g_free(path); LOCK(); @@ -700,8 +696,8 @@ store_folder_deleted(CamelObject *o, void *event_data, void *data) rule_context_remove_rule((RuleContext *)context, rule); g_object_unref(rule); g_signal_connect(context, "rule_removed", G_CALLBACK(context_rule_removed), context); - - user = g_strdup_printf("%s/mail/vfolders.xml", mail_component_peek_base_directory (mail_component_peek ())); + + user = g_strdup_printf("%s/vfolders.xml", evolution_dir); rule_context_save((RuleContext *)context, user); g_free(user); } else { @@ -742,7 +738,7 @@ store_folder_renamed(CamelObject *o, void *event_data, void *data) filter_rule_set_name(rule, info->new->full_name); g_signal_connect(rule, "changed", G_CALLBACK(rule_changed), folder); - user = g_strdup_printf("%s/mail/vfolders.xml", mail_component_peek_base_directory (mail_component_peek ())); + user = g_strdup_printf("%s/vfolders.xml", evolution_dir); rule_context_save((RuleContext *)context, user); g_free(user); @@ -754,7 +750,7 @@ store_folder_renamed(CamelObject *o, void *event_data, void *data) } void -vfolder_load_storage(void) +vfolder_load_storage(GNOME_Evolution_Shell shell) { char *user, *storeuri; FilterRule *rule; @@ -762,7 +758,7 @@ vfolder_load_storage(void) vfolder_hash = g_hash_table_new(g_str_hash, g_str_equal); /* first, create the vfolder store, and set it up */ - storeuri = g_strdup_printf("vfolder:%s/vfolder", mail_component_peek_base_directory (mail_component_peek ())); + storeuri = g_strdup_printf("vfolder:%s/vfolder", evolution_dir); vfolder_store = camel_session_get_store(session, storeuri, NULL); if (vfolder_store == NULL) { g_warning("Cannot open vfolder store - no vfolders available"); @@ -777,10 +773,10 @@ vfolder_load_storage(void) (CamelObjectEventHookFunc)store_folder_renamed, NULL); d(printf("got store '%s' = %p\n", storeuri, vfolder_store)); - mail_component_load_storage_by_uri(mail_component_peek (), storeuri, _("VFolders")); + mail_load_storage_by_uri(shell, storeuri, _("VFolders")); /* load our rules */ - user = g_strdup_printf ("%s/mail/vfolders.xml", mail_component_peek_base_directory (mail_component_peek ())); + user = g_strdup_printf ("%s/vfolders.xml", evolution_dir); context = vfolder_context_new (); if (rule_context_load ((RuleContext *)context, EVOLUTION_PRIVDATADIR "/vfoldertypes.xml", user) != 0) { @@ -810,7 +806,8 @@ vfolder_editor_response (GtkWidget *dialog, int button, void *data) { char *user; - user = g_strdup_printf ("%s/mail/vfolders.xml", mail_component_peek_base_directory (mail_component_peek ())); + user = alloca(strlen(evolution_dir)+16); + sprintf(user, "%s/vfolders.xml", evolution_dir); switch(button) { case GTK_RESPONSE_ACCEPT: @@ -823,8 +820,6 @@ vfolder_editor_response (GtkWidget *dialog, int button, void *data) vfolder_editor = NULL; gtk_widget_destroy(dialog); - - g_free (user); } void @@ -851,7 +846,7 @@ edit_rule_response(GtkWidget *w, int button, void *data) FilterRule *orig = g_object_get_data (G_OBJECT (w), "orig"); filter_rule_copy(orig, rule); - user = g_strdup_printf("%s/mail/vfolders.xml", mail_component_peek_base_directory (mail_component_peek ())); + user = g_strdup_printf("%s/vfolders.xml", evolution_dir); rule_context_save((RuleContext *)context, user); g_free(user); } @@ -882,7 +877,7 @@ vfolder_edit_rule(const char *uri) GTK_STOCK_OK, GTK_RESPONSE_OK, NULL); - gtk_container_set_border_width (GTK_CONTAINER (gd), 6); + gtk_container_set_border_width ((GtkContainer *) gd, 6); gtk_box_set_spacing ((GtkBox *) gd->vbox, 6); gtk_dialog_set_default_response(gd, GTK_RESPONSE_OK); g_object_set(gd, "allow_shrink", FALSE, "allow_grow", TRUE, NULL); @@ -930,7 +925,7 @@ new_rule_clicked(GtkWidget *w, int button, void *data) g_object_ref(rule); rule_context_add_rule((RuleContext *)context, rule); - user = g_strdup_printf("%s/mail/vfolders.xml", mail_component_peek_base_directory (mail_component_peek ())); + user = g_strdup_printf("%s/vfolders.xml", evolution_dir); rule_context_save((RuleContext *)context, user); g_free(user); } @@ -976,7 +971,7 @@ vfolder_gui_add_rule(VfolderRule *rule) GTK_RESPONSE_OK, NULL); gtk_dialog_set_default_response(gd, GTK_RESPONSE_OK); - gtk_container_set_border_width (GTK_CONTAINER (gd), 6); + gtk_container_set_border_width ((GtkContainer *) gd, 6); gtk_box_set_spacing ((GtkBox *) gd->vbox, 6); g_object_set(gd, "allow_shrink", FALSE, "allow_grow", TRUE, NULL); gtk_window_set_default_size (GTK_WINDOW (gd), 500, 500); diff --git a/mail/message-list.c b/mail/message-list.c index a16c40f265..6d0ed3146e 100644 --- a/mail/message-list.c +++ b/mail/message-list.c @@ -50,6 +50,7 @@ #include <camel/camel-file-utils.h> #include <camel/camel-folder.h> #include <camel/camel-folder-thread.h> +#include <e-util/ename/e-name-western.h> #include <e-util/e-memory.h> #include "filter/filter-label.h" @@ -188,6 +189,9 @@ static struct { { NULL, NULL } }; +/* FIXME: junk prefs */ +static gboolean junk_folder = TRUE; + #ifdef SMART_ADDRESS_COMPARE static EMailAddress * e_mail_address_new (const char *address) @@ -716,7 +720,7 @@ void message_list_paste(MessageList *ml) { gtk_selection_convert(ml->priv->invisible, GDK_SELECTION_CLIPBOARD, - gdk_atom_intern ("x-uid-list", FALSE), + gdk_atom_intern("x-evolution-message", FALSE), GDK_CURRENT_TIME); } @@ -1424,12 +1428,12 @@ ml_selection_get(GtkWidget *widget, GtkSelectionData *data, guint info, guint ti return; if (info & 2) { - /* text/plain */ + /* text_plain */ printf("setting text/plain selection for uids\n"); em_utils_selection_set_mailbox(data, ml->folder, uids); } else { - /* x-uid-list */ - printf("setting x-uid-list selection for uids\n"); + /* x-evolution-message */ + printf("setting x-evolution-message selection for uids\n"); em_utils_selection_set_uidlist(data, ml->folder_uri, uids); } } @@ -1478,7 +1482,7 @@ ml_selection_received_uidlist(MessageList *ml, GtkSelectionData *data) static void ml_selection_received(GtkWidget *widget, GtkSelectionData *data, guint time, MessageList *ml) { - if (data->target != gdk_atom_intern ("x-uid-list", FALSE)) { + if (data->target != gdk_atom_intern("x-evolution-message", FALSE)) { printf("Unknown selection received by message-list\n"); return; @@ -1488,7 +1492,7 @@ ml_selection_received(GtkWidget *widget, GtkSelectionData *data, guint time, Mes } static GtkTargetEntry ml_drag_types[] = { - { "x-uid-list", 0, 0 }, + { "x-evolution-message", 0, 0 }, { "message/rfc822", 0, 1 }, /* not included in dest types */ { "text/uri-list", 0, 2 }, @@ -1505,7 +1509,7 @@ ml_tree_drag_data_get (ETree *tree, int row, ETreePath path, int col, if (uids->len > 0) { switch (info) { - case 0 /*DND_TARGET_TYPE_X_UID_LIST */: + case 0 /*DND_TARGET_TYPE_X_EVOLUTION_MESSAGE*/: em_utils_selection_set_uidlist(data, ml->folder_uri, uids); break; case 1 /*DND_TARGET_TYPE_MESSAGE_RFC822*/: @@ -1577,7 +1581,7 @@ message_list_init (GtkObject *object) g_object_ref(p->invisible); gtk_object_sink((GtkObject *)p->invisible); - matom = gdk_atom_intern ("x-uid-list", FALSE); + matom = gdk_atom_intern("x-evolution-message", FALSE); gtk_selection_add_target(p->invisible, GDK_SELECTION_CLIPBOARD, matom, 0); gtk_selection_add_target(p->invisible, GDK_SELECTION_PRIMARY, matom, 1); gtk_selection_add_target(p->invisible, GDK_SELECTION_CLIPBOARD, GDK_SELECTION_TYPE_STRING, 2); @@ -2377,11 +2381,44 @@ build_flat_diff(MessageList *ml, CamelFolderChangeInfo *changes) static void +mail_folder_hide_by_flag (CamelFolder *folder, MessageList *ml, CamelFolderChangeInfo **changes, int flag) +{ + CamelFolderChangeInfo *newchanges, *oldchanges = *changes; + CamelMessageInfo *info; + int i; + + newchanges = camel_folder_change_info_new (); + + for (i = 0; i < oldchanges->uid_changed->len; i++) { + ETreePath node = g_hash_table_lookup (ml->uid_nodemap, oldchanges->uid_changed->pdata[i]); + + info = camel_folder_get_message_info (folder, oldchanges->uid_changed->pdata[i]); + if (node != NULL && info != NULL && (info->flags & flag) != 0) + camel_folder_change_info_remove_uid (newchanges, oldchanges->uid_changed->pdata[i]); + else if (node == NULL && info != NULL && (info->flags & flag) == 0) + camel_folder_change_info_add_uid (newchanges, oldchanges->uid_changed->pdata[i]); + else + camel_folder_change_info_change_uid (newchanges, oldchanges->uid_changed->pdata[i]); + camel_folder_free_message_info (folder, info); + } + + if (newchanges->uid_added->len > 0 || newchanges->uid_removed->len > 0) { + for (i = 0; i < oldchanges->uid_added->len; i++) + camel_folder_change_info_add_uid (newchanges, oldchanges->uid_added->pdata[i]); + for (i = 0; i < oldchanges->uid_removed->len; i++) + camel_folder_change_info_remove_uid (newchanges, oldchanges->uid_removed->pdata[i]); + camel_folder_change_info_free (oldchanges); + *changes = newchanges; + } else { + camel_folder_change_info_free (newchanges); + } +} + +static void main_folder_changed (CamelObject *o, gpointer event_data, gpointer user_data) { MessageList *ml = MESSAGE_LIST (user_data); - CamelFolderChangeInfo *changes = (CamelFolderChangeInfo *)event_data, *newchanges; - CamelMessageInfo *info; + CamelFolderChangeInfo *changes = (CamelFolderChangeInfo *)event_data; CamelFolder *folder = (CamelFolder *)o; int i; @@ -2402,35 +2439,9 @@ main_folder_changed (CamelObject *o, gpointer event_data, gpointer user_data) } /* check if the hidden state has changed, if so modify accordingly, then regenerate */ - if (ml->hidedeleted) { - newchanges = camel_folder_change_info_new (); - - for (i = 0; i < changes->uid_changed->len; i++) { - ETreePath node = g_hash_table_lookup (ml->uid_nodemap, changes->uid_changed->pdata[i]); - - info = camel_folder_get_message_info (folder, changes->uid_changed->pdata[i]); - if (node != NULL && info != NULL && (info->flags & CAMEL_MESSAGE_DELETED) != 0) { - camel_folder_change_info_remove_uid (newchanges, changes->uid_changed->pdata[i]); - } else if (node == NULL && info != NULL && (info->flags & CAMEL_MESSAGE_DELETED) == 0) { - camel_folder_change_info_add_uid (newchanges, changes->uid_changed->pdata[i]); - } else { - camel_folder_change_info_change_uid (newchanges, changes->uid_changed->pdata[i]); - } - camel_folder_free_message_info (folder, info); - } - - if (newchanges->uid_added->len > 0 || newchanges->uid_removed->len > 0) { - for (i = 0; i < changes->uid_added->len; i++) - camel_folder_change_info_add_uid (newchanges, changes->uid_added->pdata[i]); - for (i = 0; i < changes->uid_removed->len; i++) - camel_folder_change_info_remove_uid (newchanges, changes->uid_removed->pdata[i]); - camel_folder_change_info_free (changes); - changes = newchanges; - } else { - camel_folder_change_info_free (newchanges); - } - } - + if (ml->hidejunk || ml->hidedeleted) + mail_folder_hide_by_flag (folder, ml, &changes, (ml->hidejunk ? CAMEL_MESSAGE_JUNK : 0) | (ml->hidedeleted ? CAMEL_MESSAGE_DELETED : 0)); + if (changes->uid_added->len == 0 && changes->uid_removed->len == 0 && changes->uid_changed->len < 100) { for (i = 0; i < changes->uid_changed->len; i++) { ETreePath node = g_hash_table_lookup (ml->uid_nodemap, changes->uid_changed->pdata[i]); @@ -2579,6 +2590,7 @@ message_list_set_folder (MessageList *message_list, CamelFolder *folder, const c gconf = mail_config_get_gconf_client (); hide_deleted = !gconf_client_get_bool (gconf, "/apps/evolution/mail/display/show_deleted", NULL); message_list->hidedeleted = hide_deleted && !(folder->folder_flags & CAMEL_FOLDER_IS_TRASH); + message_list->hidejunk = junk_folder && !(folder->folder_flags & CAMEL_FOLDER_IS_JUNK) && !(folder->folder_flags & CAMEL_FOLDER_IS_TRASH); hide_load_state (message_list); mail_regen_list (message_list, message_list->search, NULL, NULL); @@ -3038,6 +3050,7 @@ struct _regen_list_msg { CamelFolderChangeInfo *changes; gboolean dotree; /* we are building a tree */ gboolean hidedel; /* we want to/dont want to show deleted messages */ + gboolean hidejunk; /* we want to/dont want to show junk messages */ gboolean thread_subject; CamelFolderThread *tree; @@ -3076,17 +3089,36 @@ regen_list_regen (struct _mail_msg *mm) } else if (m->hidedel) { char *expr; - if (m->search) { - expr = alloca(strlen(m->search) + 64); - sprintf(expr, "(and (match-all (not (system-flag \"deleted\")))\n %s)", m->search); - } else - expr = "(match-all (not (system-flag \"deleted\")))"; + if (m->hidejunk) { + if (m->search) { + expr = alloca(strlen(m->search) + 92); + sprintf(expr, "(and (match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\"))))\n %s)", m->search); + } else + expr = "(match-all (and (not (system-flag \"deleted\")) (not (system-flag \"junk\"))))"; + } else { + if (m->search) { + expr = alloca(strlen(m->search) + 64); + sprintf(expr, "(and (match-all (not (system-flag \"deleted\")))\n %s)", m->search); + } else + expr = "(match-all (not (system-flag \"deleted\")))"; + } searchuids = uids = camel_folder_search_by_expression (m->folder, expr, &mm->ex); } else { - if (m->search) - searchuids = uids = camel_folder_search_by_expression (m->folder, m->search, &mm->ex); - else - uids = camel_folder_get_uids (m->folder); + char *expr; + + if (m->hidejunk) { + if (m->search) { + expr = alloca(strlen(m->search) + 64); + sprintf(expr, "(and (match-all (not (system-flag \"junk\")))\n %s)", m->search); + } else + expr = "(match-all (not (system-flag \"junk\")))"; + searchuids = uids = camel_folder_search_by_expression (m->folder, expr, &mm->ex); + } else { + if (m->search) + searchuids = uids = camel_folder_search_by_expression (m->folder, m->search, &mm->ex); + else + uids = camel_folder_get_uids (m->folder); + } } if (camel_exception_is_set (&mm->ex)) @@ -3318,6 +3350,7 @@ mail_regen_list (MessageList *ml, const char *search, const char *hideexpr, Came m->changes = changes; m->dotree = ml->threaded; m->hidedel = ml->hidedeleted; + m->hidejunk = ml->hidejunk; m->thread_subject = gconf_client_get_bool (gconf, "/apps/evolution/mail/display/thread_subject", NULL); g_object_ref(ml); m->folder = ml->folder; diff --git a/mail/message-list.h b/mail/message-list.h index a5c88cf308..8052ed791b 100644 --- a/mail/message-list.h +++ b/mail/message-list.h @@ -113,6 +113,9 @@ struct _MessageList { /* do we automatically hide deleted messages? */ guint hidedeleted : 1; + + /* do we automatically hide junk messages? */ + guint hidejunk : 1; /* is the message-list object in a destroyed state? */ guint destroyed : 1; |