From: Matthew Barnes Date: Fri, 12 Nov 2010 15:19:34 +0000 (-0500) Subject: Add new ESource classes. X-Git-Tag: upstream/3.7.4~876 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=89aa9d560cab29a40d4cb4a37d63162ca491f6ec;p=platform%2Fupstream%2Fevolution-data-server.git Add new ESource classes. ESource ESourceExtension ESourceRegistry --- diff --git a/configure.ac b/configure.ac index acff506..ac1c2b4 100644 --- a/configure.ac +++ b/configure.ac @@ -1313,7 +1313,7 @@ PKG_CHECK_MODULES(SQLITE3, [sqlite3 >= sqlite_minimum_version]) dnl ****************************** dnl libedataserver flags dnl ****************************** -E_DATA_SERVER_DEPS="gio-2.0 gmodule-2.0 libxml-2.0 libsoup-2.4 gconf-2.0 $mozilla_nspr" +E_DATA_SERVER_DEPS="gio-2.0 gmodule-2.0 gnome-keyring-1 libxml-2.0 libsoup-2.4 gconf-2.0 $mozilla_nspr" EVO_SET_COMPILE_FLAGS(E_DATA_SERVER, $E_DATA_SERVER_DEPS, $MANUAL_NSPR_CFLAGS, $MANUAL_NSPR_LIBS) AC_SUBST(E_DATA_SERVER_CFLAGS) diff --git a/docs/reference/libedataserver/Makefile.am b/docs/reference/libedataserver/Makefile.am index a93fce2..7a48782 100644 --- a/docs/reference/libedataserver/Makefile.am +++ b/docs/reference/libedataserver/Makefile.am @@ -16,8 +16,10 @@ CFILE_GLOB = $(top_srcdir)/libedataserver/*.c IGNORE_HFILES = \ e-client-private.h \ + e-marshal.h \ e-gdbus-marshallers.h \ e-gdbus-templates.h \ + e-source-enumtypes.h \ libedataserver-private.h GTKDOC_CFLAGS = \ diff --git a/docs/reference/libedataserver/libedataserver-docs.sgml b/docs/reference/libedataserver/libedataserver-docs.sgml index f1ab381..daaad45 100644 --- a/docs/reference/libedataserver/libedataserver-docs.sgml +++ b/docs/reference/libedataserver/libedataserver-docs.sgml @@ -11,35 +11,41 @@ Data Sources - + + + + + Data Source Extensions + + + + + + + + + + + + + + + - + + - Generated Types - - - - - - - - - - - - - Miscellaneous Utilities @@ -50,7 +56,6 @@ - diff --git a/docs/reference/libedataserver/libedataserver-sections.txt b/docs/reference/libedataserver/libedataserver-sections.txt index f313c0d..0c4adc5 100644 --- a/docs/reference/libedataserver/libedataserver-sections.txt +++ b/docs/reference/libedataserver/libedataserver-sections.txt @@ -237,37 +237,34 @@ e_proxy_get_type ESource ESource e_source_new -e_source_new_with_absolute_uri -e_source_new_from_xml_node -e_source_new_from_standalone_xml -e_source_copy -e_source_update_from_xml_node -e_source_uid_from_xml_node -e_source_set_group -e_source_set_name -e_source_set_relative_uri -e_source_set_absolute_uri -e_source_set_color_spec -e_source_set_readonly -e_source_peek_group -e_source_peek_uid -e_source_peek_name -e_source_peek_relative_uri -e_source_peek_absolute_uri -e_source_peek_color_spec -e_source_get_readonly -e_source_get_uri -e_source_dump_to_xml_node -e_source_to_standalone_xml -e_source_get_property -e_source_set_property -e_source_foreach_property -e_source_get_duped_property -e_source_build_absolute_uri +e_source_hash e_source_equal -e_source_xmlstr_equal +e_source_changed e_source_get_uid +e_source_dup_uid +e_source_get_parent +e_source_dup_parent +e_source_set_parent +e_source_get_enabled +e_source_set_enabled +e_source_get_writable +e_source_get_removable +e_source_get_extension +e_source_has_extension +e_source_ref_dbus_object +e_source_ref_main_context e_source_get_display_name +e_source_dup_display_name +e_source_set_display_name +e_source_compare_by_display_name +e_source_to_string +e_source_parameter_to_key +e_source_remove_sync +e_source_remove +e_source_remove_finish +e_source_write_sync +e_source_write +e_source_write_finish E_SOURCE E_IS_SOURCE @@ -282,6 +279,704 @@ e_source_get_type
+e-source-address-book +ESourceAddressBook +ESourceAddressBook +E_SOURCE_EXTENSION_ADDRESS_BOOK + +E_SOURCE_ADDRESS_BOOK +E_IS_SOURCE_ADDRESS_BOOK +E_TYPE_SOURCE_ADDRESS_BOOK +E_SOURCE_ADDRESS_BOOK_CLASS +E_IS_SOURCE_ADDRESS_BOOK_CLASS +E_SOURCE_ADDRESS_BOOK_GET_CLASS +ESourceAddressBookClass + +ESourceAddressBookPrivate +e_source_address_book_get_type +
+ +
+e-source-alarms +ESourceAlarms +ESourceAlarms +E_SOURCE_EXTENSION_ALARMS +e_source_alarms_get_include_me +e_source_alarms_set_include_me +e_source_alarms_get_last_notified +e_source_alarms_dup_last_notified +e_source_alarms_set_last_notified + +E_SOURCE_ALARMS +E_IS_SOURCE_ALARMS +E_TYPE_SOURCE_ALARMS +E_SOURCE_ALARMS_CLASS +E_IS_SOURCE_ALARMS_CLASS +E_SOURCE_ALARMS_GET_CLASS +ESourceAlarmsClass + +ESourceAlarmsPrivate +e_source_alarms_get_type +
+ +
+e-source-authentication +ESourceAuthentication +ESourceAuthentication +E_SOURCE_EXTENSION_AUTHENTICATION +e_source_authentication_required +e_source_authentication_get_host +e_source_authentication_dup_host +e_source_authentication_set_host +e_source_authentication_get_method +e_source_authentication_dup_method +e_source_authentication_set_method +e_source_authentication_get_port +e_source_authentication_set_port +e_source_authentication_get_user +e_source_authentication_dup_user +e_source_authentication_set_user + +E_SOURCE_AUTHENTICATION +E_IS_SOURCE_AUTHENTICATION +E_TYPE_SOURCE_AUTHENTICATION +E_SOURCE_AUTHENTICATION_CLASS +E_IS_SOURCE_AUTHENTICATION_CLASS +E_SOURCE_AUTHENTICATION_GET_CLASS +ESourceAuthenticationClass + +ESourceAuthenticationPrivate +e_source_authentication_get_type +
+ +
+e-source-authenticator +ESourceAuthenticator +ESourceAuthenticator +ESourceAuthenticatorInterface +ESourceAuthenticationResult +e_source_authenticator_get_prompt_strings +e_source_authenticator_try_password_sync +e_source_authenticator_try_password +e_source_authenticator_try_password_finish + +E_SOURCE_AUTHENTICATOR +E_IS_SOURCE_AUTHENTICATOR +E_TYPE_SOURCE_AUTHENTICATOR +E_SOURCE_AUTHENTICATOR_GET_INTERFACE + +e_source_authenticator_get_type +
+ +
+e-source-autocomplete +ESourceAutocomplete +ESourceAutocomplete +E_SOURCE_EXTENSION_AUTOCOMPLETE +e_source_autocomplete_get_include_me +e_source_autocomplete_set_include_me + +E_SOURCE_AUTOCOMPLETE +E_IS_SOURCE_AUTOCOMPLETE +E_TYPE_SOURCE_AUTOCOMPLETE +E_SOURCE_AUTOCOMPLETE_CLASS +E_IS_SOURCE_AUTOCOMPLETE_CLASS +E_SOURCE_AUTOCOMPLETE_GET_CLASS +ESourceAutocompleteClass + +ESourceAutocompletePrivate +e_source_autocomplete_get_type +
+ +
+e-source-backend +ESourceBackend +ESourceBackend +e_source_backend_get_backend_name +e_source_backend_dup_backend_name +e_source_backend_set_backend_name + +E_SOURCE_BACKEND +E_IS_SOURCE_BACKEND +E_TYPE_SOURCE_BACKEND +E_SOURCE_BACKEND_CLASS +E_IS_SOURCE_BACKEND_CLASS +E_SOURCE_BACKEND_GET_CLASS +ESourceBackendClass + +ESourceBackendPrivate +e_source_backend_get_type +
+ +
+e-source-calendar +ESourceCalendar +ESourceCalendar +E_SOURCE_EXTENSION_CALENDAR + +E_SOURCE_CALENDAR +E_IS_SOURCE_CALENDAR +E_TYPE_SOURCE_CALENDAR +E_SOURCE_CALENDAR_CLASS +E_IS_SOURCE_CALENDAR_CLASS +E_SOURCE_CALENDAR_GET_CLASS +ESourceCalendarClass + +ESourceCalendarPrivate +e_source_calendar_get_type +
+ +
+e-source-camel +ESourceCamel +ESourceCamel +e_source_camel_register_types +e_source_camel_generate_subtype +e_source_camel_get_settings +e_source_camel_get_type_name +e_source_camel_get_extension_name +e_source_camel_configure_service + +E_SOURCE_CAMEL +E_IS_SOURCE_CAMEL +E_TYPE_SOURCE_CAMEL +E_SOURCE_CAMEL_CLASS +E_IS_SOURCE_CAMEL_CLASS +E_SOURCE_CAMEL_GET_CLASS +ESourceCamelClass + +ESourceCamelPrivate +e_source_camel_get_type +
+ +
+e-source-collection +ESourceCollection +ESourceCollection +E_SOURCE_EXTENSION_COLLECTION +e_source_collection_get_identity +e_source_collection_dup_identity +e_source_collection_set_identity +e_source_collection_get_calendar_enabled +e_source_collection_set_calendar_enabled +e_source_collection_get_contacts_enabled +e_source_collection_set_contacts_enabled +e_source_collection_get_mail_enabled +e_source_collection_set_mail_enabled + +E_SOURCE_COLLECTION +E_IS_SOURCE_COLLECTION +E_TYPE_SOURCE_COLLECTION +E_SOURCE_COLLECTION_CLASS +E_IS_SOURCE_COLLECTION_CLASS +E_SOURCE_COLLECTION_GET_CLASS +ESourceCollectionClass + +ESourceCollectionPrivate +e_source_collection_get_type +
+ +
+e-source-goa +ESourceGoa +ESourceGoa +E_SOURCE_EXTENSION_GOA +e_source_goa_get_account_id +e_source_goa_dup_account_id +e_source_goa_set_account_id + +E_SOURCE_GOA +E_IS_SOURCE_GOA +E_TYPE_SOURCE_GOA +E_SOURCE_GOA_CLASS +E_IS_SOURCE_GOA_CLASS +E_SOURCE_GOA_GET_CLASS +ESourceGoaClass + +ESourceGoaPrivate +e_source_goa_get_type +
+ +
+e-source-extension +ESourceExtension +ESourceExtension +E_SOURCE_PARAM_SETTING +e_source_extension_get_source + +E_SOURCE_EXTENSION +E_IS_SOURCE_EXTENSION +E_TYPE_SOURCE_EXTENSION +E_SOURCE_EXTENSION_CLASS +E_IS_SOURCE_EXTENSION_CLASS +E_SOURCE_EXTENSION_GET_CLASS +ESourceExtensionClass + +ESourceExtensionPrivate +e_source_extension_get_type +
+ +
+e-source-mail-account +ESourceMailAccount +ESourceMailAccount +E_SOURCE_EXTENSION_MAIL_ACCOUNT +e_source_mail_account_get_identity_uid +e_source_mail_account_dup_identity_uid +e_source_mail_account_set_identity_uid + +E_SOURCE_MAIL_ACCOUNT +E_IS_SOURCE_MAIL_ACCOUNT +E_TYPE_SOURCE_MAIL_ACCOUNT +E_SOURCE_MAIL_ACCOUNT_CLASS +E_IS_SOURCE_MAIL_ACCOUNT_CLASS +E_SOURCE_MAIL_ACCOUNT_GET_CLASS +ESourceMailAccountClass + +ESourceMailAccountPrivate +e_source_mail_account_get_type +
+ +
+e-source-mail-composition +ESourceMailComposition +ESourceMailComposition +E_SOURCE_EXTENSION_MAIL_COMPOSITION +e_source_mail_composition_get_bcc +e_source_mail_composition_dup_bcc +e_source_mail_composition_set_bcc +e_source_mail_composition_get_cc +e_source_mail_composition_dup_cc +e_source_mail_composition_set_cc +e_source_mail_composition_get_drafts_folder +e_source_mail_composition_dup_drafts_folder +e_source_mail_composition_set_drafts_folder +e_source_mail_composition_get_sign_imip +e_source_mail_composition_set_sign_imip +e_source_mail_composition_get_templates_folder +e_source_mail_composition_dup_templates_folder +e_source_mail_composition_set_templates_folder + +E_SOURCE_MAIL_COMPOSITION +E_IS_SOURCE_MAIL_COMPOSITION +E_TYPE_SOURCE_MAIL_COMPOSITION +E_SOURCE_MAIL_COMPOSITION_CLASS +E_IS_SOURCE_MAIL_COMPOSITION_CLASS +E_SOURCE_MAIL_COMPOSITION_GET_CLASS +ESourceMailCompositionClass + +ESourceMailCompositionPrivate +e_source_mail_composition_get_type +
+ +
+e-source-mail-identity +ESourceMailIdentity +ESourceMailIdentity +E_SOURCE_EXTENSION_MAIL_IDENTITY +e_source_mail_identity_get_address +e_source_mail_identity_dup_address +e_source_mail_identity_set_address +e_source_mail_identity_get_name +e_source_mail_identity_dup_name +e_source_mail_identity_set_name +e_source_mail_identity_get_organization +e_source_mail_identity_dup_organization +e_source_mail_identity_set_organization +e_source_mail_identity_get_reply_to +e_source_mail_identity_dup_reply_to +e_source_mail_identity_set_reply_to +e_source_mail_identity_get_signature_uid +e_source_mail_identity_dup_signature_uid +e_source_mail_identity_set_signature_uid + +E_SOURCE_MAIL_IDENTITY +E_IS_SOURCE_MAIL_IDENTITY +E_TYPE_SOURCE_MAIL_IDENTITY +E_SOURCE_MAIL_IDENTITY_CLASS +E_IS_SOURCE_MAIL_IDENTITY_CLASS +E_SOURCE_MAIL_IDENTITY_GET_CLASS +ESourceMailIdentityClass + +ESourceMailIdentityPrivate +e_source_mail_identity_get_type +
+ +
+e-source-mail-signature +ESourceMailSignature +ESourceMailSignature +E_SOURCE_EXTENSION_MAIL_SIGNATURE +e_source_mail_signature_get_file +e_source_mail_signature_get_mime_type +e_source_mail_signature_dup_mime_type +e_source_mail_signature_set_mime_type +e_source_mail_signature_load_sync +e_source_mail_signature_load +e_source_mail_signature_load_finish +e_source_mail_signature_replace_sync +e_source_mail_signature_replace +e_source_mail_signature_replace_finish +e_source_mail_signature_symlink_sync +e_source_mail_signature_symlink +e_source_mail_signature_symlink_finish + +E_SOURCE_MAIL_SIGNATURE +E_IS_SOURCE_MAIL_SIGNATURE +E_TYPE_SOURCE_MAIL_SIGNATURE +E_SOURCE_MAIL_SIGNATURE_CLASS +E_IS_SOURCE_MAIL_SIGNATURE_CLASS +E_SOURCE_MAIL_SIGNATURE_GET_CLASS +ESourceMailSignatureClass + +ESourceMailSignaturePrivate +e_source_mail_signature_get_type +
+ +
+e-source-mail-submission +ESourceMailSubmission +ESourceMailSubmission +E_SOURCE_EXTENSION_MAIL_SUBMISSION +e_source_mail_submission_get_sent_folder +e_source_mail_submission_dup_sent_folder +e_source_mail_submission_set_sent_folder +e_source_mail_submission_get_transport_uid +e_source_mail_submission_dup_transport_uid +e_source_mail_submission_set_transport_uid + +E_SOURCE_MAIL_SUBMISSION +E_IS_SOURCE_MAIL_SUBMISSION +E_TYPE_SOURCE_MAIL_SUBMISSION +E_SOURCE_MAIL_SUBMISSION_CLASS +E_IS_SOURCE_MAIL_SUBMISSION_CLASS +E_SOURCE_MAIL_SUBMISSION_GET_CLASS +ESourceMailSubmissionClass + +ESourceMailSubmissionPrivate +e_source_mail_submission_get_type +
+ +
+e-source-mail-transport +ESourceMailTransport +ESourceMailTransport +E_SOURCE_EXTENSION_MAIL_TRANSPORT + +E_SOURCE_MAIL_TRANSPORT +E_IS_SOURCE_MAIL_TRANSPORT +E_TYPE_SOURCE_MAIL_TRANSPORT +E_SOURCE_MAIL_TRANSPORT_CLASS +E_IS_SOURCE_MAIL_TRANSPORT_CLASS +E_SOURCE_MAIL_TRANSPORT_GET_CLASS +ESourceMailSubmissionClass + +ESourceMailTransportPrivate +e_source_mail_transport_get_type +
+ +
+e-source-mdn +ESourceMDN +ESourceMDN +E_SOURCE_EXTENSION_MDN +EMdnResponsePolicy +e_source_mdn_get_response_policy +e_source_mdn_set_response_policy + +E_SOURCE_MDN +E_IS_SOURCE_MDN +E_TYPE_SOURCE_MDN +E_SOURCE_MDN_CLASS +E_IS_SOURCE_MDN_CLASS +E_SOURCE_MDN_GET_CLASS +ESourceMDNClass + +ESourceMDNPrivate +e_source_mdn_get_type +
+ +
+e-source-memo-list +ESourceMemoList +ESourceMemoList +E_SOURCE_EXTENSION_MEMO_LIST + +E_SOURCE_MEMO_LIST +E_IS_SOURCE_MEMO_LIST +E_TYPE_SOURCE_MEMO_LIST +E_SOURCE_MEMO_LIST_CLASS +E_IS_SOURCE_MEMO_LIST_CLASS +E_SOURCE_MEMO_LIST_GET_CLASS +ESourceMemoListClass + +ESourceMemoListPrivate +e_source_memo_list_get_type +
+ +
+e-source-offline +ESourceOffline +ESourceOffline +E_SOURCE_EXTENSION_OFFLINE +e_source_offline_get_stay_synchronized +e_source_offline_set_stay_synchronized + +E_SOURCE_OFFLINE +E_IS_SOURCE_OFFLINE +E_TYPE_SOURCE_OFFLINE +E_SOURCE_OFFLINE_CLASS +E_IS_SOURCE_OFFLINE_CLASS +E_SOURCE_OFFLINE_GET_CLASS +ESourceOfflineClass + +ESourceOfflinePrivate +e_source_offline_get_type +
+ +
+e-source-openpgp +ESourceOpenPGP +ESourceOpenPGP +E_SOURCE_EXTENSION_OPENPGP +e_source_openpgp_get_always_trust +e_source_openpgp_set_always_trust +e_source_openpgp_get_encrypt_to_self +e_source_openpgp_set_encrypt_to_self +e_source_openpgp_get_key_id +e_source_openpgp_dup_key_id +e_source_openpgp_set_key_id +e_source_openpgp_get_signing_algorithm +e_source_openpgp_dup_signing_algorithm +e_source_openpgp_set_signing_algorithm +e_source_openpgp_get_sign_by_default +e_source_openpgp_set_sign_by_default + +E_SOURCE_OPENPGP +E_IS_SOURCE_OPENPGP +E_TYPE_SOURCE_OPENPGP +E_SOURCE_OPENPGP_CLASS +E_IS_SOURCE_OPENPGP_CLASS +E_SOURCE_OPENPGP_GET_CLASS +ESourceOpenPGPClass + +ESourceOpenPGPPrivate +e_source_openpgp_get_type +
+ +
+e-source-refresh +ESourceRefresh +ESourceRefresh +E_SOURCE_EXTENSION_REFRESH +e_source_refresh_get_enabled +e_source_refresh_set_enabled +e_source_refresh_get_interval_minutes +e_source_refresh_set_interval_minutes +ESourceRefreshFunc +e_source_refresh_add_timeout +e_source_refresh_force_timeout +e_source_refresh_remove_timeout +e_source_refresh_remove_timeouts_by_data + +E_SOURCE_REFRESH +E_IS_SOURCE_REFRESH +E_TYPE_SOURCE_REFRESH +E_SOURCE_REFRESH_CLASS +E_IS_SOURCE_REFRESH_CLASS +E_SOURCE_REFRESH_GET_CLASS +ESourceRefreshClass + +ESourceRefreshPrivate +e_source_refresh_get_type +
+ +
+e-source-registry +ESourceRegistry +ESourceRegistry +e_source_registry_new_sync +e_source_registry_new +e_source_registry_new_finish +e_source_registry_authenticate_sync +e_source_registry_authenticate +e_source_registry_authenticate_finish +e_source_registry_commit_source_sync +e_source_registry_commit_source +e_source_registry_commit_source_finish +e_source_registry_create_sources_sync +e_source_registry_create_sources +e_source_registry_create_sources_finish +e_source_registry_ref_source +e_source_registry_list_sources +e_source_registry_find_extension +e_source_registry_build_display_tree +e_source_registry_free_display_tree +e_source_registry_debug_dump +e_source_registry_ref_builtin_address_book +e_source_registry_ref_default_address_book +e_source_registry_set_default_address_book +e_source_registry_ref_builtin_calendar +e_source_registry_ref_default_calendar +e_source_registry_set_default_calendar +e_source_registry_ref_builtin_mail_account +e_source_registry_ref_default_mail_account +e_source_registry_set_default_mail_account +e_source_registry_ref_default_mail_identity +e_source_registry_set_default_mail_identity +e_source_registry_ref_builtin_memo_list +e_source_registry_ref_default_memo_list +e_source_registry_set_default_memo_list +e_source_registry_ref_builtin_task_list +e_source_registry_ref_default_task_list +e_source_registry_set_default_task_list +e_source_registry_ref_default_for_extension_name +e_source_registry_set_default_for_extension_name + +E_SOURCE_REGISTRY +E_IS_SOURCE_REGISTRY +E_TYPE_SOURCE_REGISTRY +E_SOURCE_REGISTRY_CLASS +E_IS_SOURCE_REGISTRY_CLASS +E_SOURCE_REGISTRY_GET_CLASS +ESourceRegistryClass + +ESourceRegistryPrivate +e_source_registry_get_type +
+ +
+e-source-security +ESourceSecurity +ESourceSecurity +E_SOURCE_EXTENSION_SECURITY +e_source_security_get_method +e_source_security_dup_method +e_source_security_set_method +e_source_security_get_secure +e_source_security_set_secure + +E_SOURCE_SECURITY +E_IS_SOURCE_SECURITY +E_TYPE_SOURCE_SECURITY +E_SOURCE_SECURITY_CLASS +E_IS_SOURCE_SECURITY_CLASS +E_SOURCE_SECURITY_GET_CLASS +ESourceSecurityClass + +ESourceSecurityPrivate +e_source_security_get_type +
+ +
+e-source-selectable +ESourceSelectable +ESourceSelectable +e_source_selectable_get_color +e_source_selectable_dup_color +e_source_selectable_set_color +e_source_selectable_get_selected +e_source_selectable_set_selected + +E_SOURCE_SELECTABLE +E_IS_SOURCE_SELECTABLE +E_TYPE_SOURCE_SELECTABLE +E_SOURCE_SELECTABLE_CLASS +E_IS_SOURCE_SELECTABLE_CLASS +E_SOURCE_SELECTABLE_GET_CLASS +ESourceSelectableClass + +ESourceSelectablePrivate +e_source_selectable_get_type +
+ +
+e-source-smime +ESourceSMIME +ESourceSMIME +E_SOURCE_EXTENSION_SMIME +e_source_smime_get_encryption_certificate +e_source_smime_dup_encryption_certificate +e_source_smime_set_encryption_certificate +e_source_smime_get_encrypt_by_default +e_source_smime_set_encrypt_by_default +e_source_smime_get_encrypt_to_self +e_source_smime_set_encrypt_to_self +e_source_smime_get_signing_algorithm +e_source_smime_dup_signing_algorithm +e_source_smime_set_signing_algorithm +e_source_smime_get_signing_certificate +e_source_smime_dup_signing_certificate +e_source_smime_set_signing_certificate +e_source_smime_get_sign_by_default +e_source_smime_set_sign_by_default + +E_SOURCE_SMIME +E_IS_SOURCE_SMIME +E_TYPE_SOURCE_SMIME +E_SOURCE_SMIME_CLASS +E_IS_SOURCE_SMIME_CLASS +E_SOURCE_SMIME_GET_CLASS +ESourceSMIMEClass + +ESourceSMIMEPrivate +e_source_smime_get_type +
+ +
+e-source-task-list +ESourceTaskList +ESourceTaskList +E_SOURCE_EXTENSION_TASK_LIST + +E_SOURCE_TASK_LIST +E_IS_SOURCE_TASK_LIST +E_TYPE_SOURCE_TASK_LIST +E_SOURCE_TASK_LIST_CLASS +E_IS_SOURCE_TASK_LIST_CLASS +E_SOURCE_TASK_LIST_GET_CLASS +ESourceTaskListClass + +ESourceTaskListPrivate +e_source_task_list_get_type +
+ +
+e-source-webdav +ESourceWebdav +ESourceWebdav +E_SOURCE_EXTENSION_WEBDAV_BACKEND +e_source_webdav_get_calendar_auto_schedule +e_source_webdav_set_calendar_auto_schedule +e_source_webdav_get_display_name +e_source_webdav_dup_display_name +e_source_webdav_set_display_name +e_source_webdav_get_email_address +e_source_webdav_dup_email_address +e_source_webdav_set_email_address +e_source_webdav_get_ignore_invalid_cert +e_source_webdav_set_ignore_invalid_cert +e_source_webdav_get_resource_path +e_source_webdav_dup_resource_path +e_source_webdav_set_resource_path +e_source_webdav_dup_soup_uri +e_source_webdav_set_soup_uri +e_source_webdav_get_avoid_ifmatch +e_source_webdav_set_avoid_ifmatch + +E_SOURCE_WEBDAV +E_IS_SOURCE_WEBDAV +E_TYPE_SOURCE_WEBDAV +E_SOURCE_WEBDAV_CLASS +E_IS_SOURCE_WEBDAV_CLASS +E_SOURCE_WEBDAV_GET_CLASS +ESourceWebdavClass + +ESourceWebdavPrivate +e_source_webdav_get_type +
+ +
e-categories e_categories_get_list e_categories_add diff --git a/docs/reference/libedataserver/libedataserver.types b/docs/reference/libedataserver/libedataserver.types index f7ced4b..586f7c9 100644 --- a/docs/reference/libedataserver/libedataserver.types +++ b/docs/reference/libedataserver/libedataserver.types @@ -4,6 +4,30 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include e_client_get_type e_iterator_get_type @@ -11,4 +35,30 @@ e_list_get_type e_list_iterator_get_type e_proxy_get_type e_source_get_type +e_source_address_book_get_type +e_source_authentication_get_type +e_source_authenticator_get_type +e_source_backend_get_type +e_source_calendar_get_type +e_source_camel_get_type +e_source_collection_get_type +e_source_extension_get_type +e_source_goa_get_type +e_source_mail_account_get_type +e_source_mail_composition_get_type +e_source_mail_identity_get_type +e_source_mail_signature_get_type +e_source_mail_submission_get_type +e_source_mail_transport_get_type +e_source_mdn_get_type +e_source_memo_list_get_type +e_source_offline_get_type +e_source_openpgp_get_type +e_source_refresh_get_type +e_source_registry_get_type +e_source_security_get_type +e_source_selectable_get_type +e_source_smime_get_type +e_source_task_list_get_type +e_source_webdav_get_type diff --git a/libedataserver/Makefile.am b/libedataserver/Makefile.am index c1eb91d..3a7b83b 100644 --- a/libedataserver/Makefile.am +++ b/libedataserver/Makefile.am @@ -1,7 +1,17 @@ +include $(top_srcdir)/glib-gen.mak +glib_enum_headers=e-source-enums.h +glib_enum_output=e-source-enumtypes +glib_enum_define=E +glib_enum_prefix=e + +ENUM_GENERATED = e-source-enumtypes.h e-source-enumtypes.c + # The marshallers MARSHAL_GENERATED = \ e-gdbus-marshallers.c \ - e-gdbus-marshallers.h + e-gdbus-marshallers.h \ + e-marshal.c \ + e-marshal.h @EVO_MARSHAL_RULE@ BUILT_SOURCES = \ @@ -19,9 +29,11 @@ libedataserver_1_2_la_CPPFLAGS = \ -DE_DATA_SERVER_LOCALEDIR=\""$(localedir)"\" \ -DE_DATA_SERVER_EXTENSIONDIR=\"$(extensiondir)\" \ -DE_DATA_SERVER_IMAGESDIR=\"$(imagesdir)\" \ + -DE_DATA_SERVER_PRIVDATADIR=\"$(privdatadir)\" \ -DE_DATA_SERVER_UI_UIDIR=\""$(uidir)"\" \ -DDEFAULT_EDS_DBUS_TIMEOUT=$(DEFAULT_EDS_DBUS_TIMEOUT) \ $(E_DATA_SERVER_CFLAGS) \ + $(GCR_BASE_CFLAGS) \ $(GIO_UNIX_CFLAGS) \ $(SOUP_CFLAGS) \ $(CODE_COVERAGE_CFLAGS) \ @@ -43,6 +55,32 @@ libedataserver_1_2_la_SOURCES = \ e-proxy.c \ e-sexp.c \ e-source.c \ + e-source-extension.c \ + e-source-address-book.c \ + e-source-alarms.c \ + e-source-authentication.c \ + e-source-authenticator.c \ + e-source-autocomplete.c \ + e-source-backend.c \ + e-source-calendar.c \ + e-source-camel.c \ + e-source-collection.c \ + e-source-goa.c \ + e-source-mail-account.c \ + e-source-mail-composition.c \ + e-source-mail-identity.c \ + e-source-mail-signature.c \ + e-source-mail-submission.c \ + e-source-mail-transport.c \ + e-source-mdn.c \ + e-source-offline.c \ + e-source-openpgp.c \ + e-source-refresh.c \ + e-source-registry.c \ + e-source-security.c \ + e-source-selectable.c \ + e-source-smime.c \ + e-source-webdav.c \ e-debug-log.c \ e-time-utils.c \ e-uid.c \ @@ -57,6 +95,7 @@ libedataserver_1_2_la_LIBADD = \ $(top_builddir)/camel/libcamel-1.2.la \ $(top_builddir)/private/libedbus-private.la \ $(E_DATA_SERVER_LIBS) \ + $(GCR_BASE_LIBS) \ $(GIO_UNIX_LIBS) \ $(ICONV_LIBS) \ $(SOCKET_LIBS) \ @@ -85,6 +124,34 @@ libedataserverinclude_HEADERS = \ e-proxy.h \ e-sexp.h \ e-source.h \ + e-source-address-book.h \ + e-source-alarms.h \ + e-source-authentication.h \ + e-source-authenticator.h \ + e-source-autocomplete.h \ + e-source-backend.h \ + e-source-calendar.h \ + e-source-camel.h \ + e-source-collection.h \ + e-source-enums.h \ + e-source-enumtypes.h \ + e-source-extension.h \ + e-source-goa.h \ + e-source-mail-account.h \ + e-source-mail-composition.h \ + e-source-mail-identity.h \ + e-source-mail-signature.h \ + e-source-mail-submission.h \ + e-source-mail-transport.h \ + e-source-mdn.h \ + e-source-offline.h \ + e-source-openpgp.h \ + e-source-refresh.h \ + e-source-registry.h \ + e-source-security.h \ + e-source-selectable.h \ + e-source-smime.h \ + e-source-webdav.h \ e-debug-log.h \ e-time-utils.h \ e-uid.h \ diff --git a/libedataserver/e-marshal.list b/libedataserver/e-marshal.list new file mode 100644 index 0000000..4abc0be --- /dev/null +++ b/libedataserver/e-marshal.list @@ -0,0 +1 @@ +NONE:OBJECT,BOXED diff --git a/libedataserver/e-source-address-book.c b/libedataserver/e-source-address-book.c new file mode 100644 index 0000000..2687f1d --- /dev/null +++ b/libedataserver/e-source-address-book.c @@ -0,0 +1,59 @@ +/* + * e-source-address-book.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-address-book + * @include: libedataserver/e-source-address-book.h + * @short_description: #ESource extension for an address book + * + * The #ESourceAddressBook extension identifies the #ESource as an + * address book. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceAddressBook *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK); + * ]| + **/ + +#include "e-source-address-book.h" + +#include + +G_DEFINE_TYPE ( + ESourceAddressBook, + e_source_address_book, + E_TYPE_SOURCE_BACKEND) + +static void +e_source_address_book_class_init (ESourceAddressBookClass *class) +{ + ESourceExtensionClass *extension_class; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_ADDRESS_BOOK; +} + +static void +e_source_address_book_init (ESourceAddressBook *extension) +{ +} diff --git a/libedataserver/e-source-address-book.h b/libedataserver/e-source-address-book.h new file mode 100644 index 0000000..bd9dc25 --- /dev/null +++ b/libedataserver/e-source-address-book.h @@ -0,0 +1,80 @@ +/* + * e-source-address-book.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_ADDRESS_BOOK_H +#define E_SOURCE_ADDRESS_BOOK_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_ADDRESS_BOOK \ + (e_source_address_book_get_type ()) +#define E_SOURCE_ADDRESS_BOOK(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_ADDRESS_BOOK, ESourceAddressBook)) +#define E_SOURCE_ADDRESS_BOOK_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_ADDRESS_BOOK, ESourceAddressBookClass)) +#define E_IS_SOURCE_ADDRESS_BOOK(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_ADDRESS_BOOK)) +#define E_IS_SOURCE_ADDRESS_BOOK_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_ADDRESS_BOOK)) +#define E_SOURCE_ADDRESS_BOOK_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_ADDRESS_BOOK, ESourceAddressBookClass)) + +/** + * E_SOURCE_EXTENSION_ADDRESS_BOOK: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceAddressBook. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_ADDRESS_BOOK "Address Book" + +G_BEGIN_DECLS + +typedef struct _ESourceAddressBook ESourceAddressBook; +typedef struct _ESourceAddressBookClass ESourceAddressBookClass; +typedef struct _ESourceAddressBookPrivate ESourceAddressBookPrivate; + +/** + * ESourceAddressBook: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceAddressBook { + ESourceBackend parent; + ESourceAddressBookPrivate *priv; +}; + +struct _ESourceAddressBookClass { + ESourceBackendClass parent_class; +}; + +GType e_source_address_book_get_type (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* E_SOURCE_ADDRESS_BOOK_H */ diff --git a/libedataserver/e-source-alarms.c b/libedataserver/e-source-alarms.c new file mode 100644 index 0000000..57bdcc4 --- /dev/null +++ b/libedataserver/e-source-alarms.c @@ -0,0 +1,312 @@ +/* + * e-source-alarms.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-alarms + * @include: libedataserver/e-source-alarms.h + * @short_description: #ESource extension for alarm state + * + * The #ESourceAlarms extension tracks alarm state for a calendar. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceAlarms *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_ALARMS); + * ]| + **/ + +#include "e-source-alarms.h" + +#define E_SOURCE_ALARMS_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_ALARMS, ESourceAlarmsPrivate)) + +struct _ESourceAlarmsPrivate { + GMutex *property_lock; + gboolean include_me; + gchar *last_notified; +}; + +enum { + PROP_0, + PROP_INCLUDE_ME, + PROP_LAST_NOTIFIED +}; + +G_DEFINE_TYPE ( + ESourceAlarms, + e_source_alarms, + E_TYPE_SOURCE_EXTENSION) + +static void +source_alarms_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_INCLUDE_ME: + e_source_alarms_set_include_me ( + E_SOURCE_ALARMS (object), + g_value_get_boolean (value)); + return; + + case PROP_LAST_NOTIFIED: + e_source_alarms_set_last_notified ( + E_SOURCE_ALARMS (object), + g_value_get_string (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_alarms_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_INCLUDE_ME: + g_value_set_boolean ( + value, + e_source_alarms_get_include_me ( + E_SOURCE_ALARMS (object))); + return; + + case PROP_LAST_NOTIFIED: + g_value_take_string ( + value, + e_source_alarms_dup_last_notified ( + E_SOURCE_ALARMS (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_alarms_finalize (GObject *object) +{ + ESourceAlarmsPrivate *priv; + + priv = E_SOURCE_ALARMS_GET_PRIVATE (object); + + g_mutex_free (priv->property_lock); + + g_free (priv->last_notified); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_alarms_parent_class)->finalize (object); +} + +static void +e_source_alarms_class_init (ESourceAlarmsClass *class) +{ + GObjectClass *object_class; + ESourceExtensionClass *extension_class; + + g_type_class_add_private (class, sizeof (ESourceAlarmsPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_alarms_set_property; + object_class->get_property = source_alarms_get_property; + object_class->finalize = source_alarms_finalize; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_ALARMS; + + g_object_class_install_property ( + object_class, + PROP_INCLUDE_ME, + g_param_spec_boolean ( + "include-me", + "IncludeMe", + "Include this source in alarm notifications", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_LAST_NOTIFIED, + g_param_spec_string ( + "last-notified", + "LastNotified", + "Last alarm notification (in ISO 8601 format)", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); +} + +static void +e_source_alarms_init (ESourceAlarms *extension) +{ + extension->priv = E_SOURCE_ALARMS_GET_PRIVATE (extension); + extension->priv->property_lock = g_mutex_new (); +} + +/** + * e_source_alarms_get_include_me: + * @extension: an #ESourceAlarms + * + * Returns whether the user should be alerted about upcoming appointments + * in the calendar described by the #ESource to which @extension belongs. + * + * Alarm daemons such as evolution-alarm-notify can use this property to + * decide which calendars to query for upcoming appointments. + * + * Returns: whether to show alarms for upcoming appointments + * + * Since: 3.6 + **/ +gboolean +e_source_alarms_get_include_me (ESourceAlarms *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_ALARMS (extension), FALSE); + + return extension->priv->include_me; +} + +/** + * e_source_alarms_set_include_me: + * @extension: an #ESourceAlarms + * @include_me: whether to show alarms for upcoming appointments + * + * Sets whether the user should be alerted about upcoming appointments in + * the calendar described by the #ESource to which @extension belongs. + * + * Alarm daemons such as evolution-alarm-notify can use this property to + * decide which calendars to query for upcoming appointments. + * + * Since: 3.6 + **/ +void +e_source_alarms_set_include_me (ESourceAlarms *extension, + gboolean include_me) +{ + g_return_if_fail (E_IS_SOURCE_ALARMS (extension)); + + extension->priv->include_me = include_me; + + g_object_notify (G_OBJECT (extension), "include-me"); +} + +/** + * e_source_alarms_get_last_notified: + * @extension: an #ESourceAlarms + * + * Returns an ISO 8601 formatted timestamp of when the user was last + * alerted about an upcoming appointment in the calendar described by + * the #ESource to which @extension belongs. If no valid timestamp + * has been set, the function will return %NULL. + * + * Returns: an ISO 8601 timestamp, or %NULL + * + * Since: 3.6 + **/ +const gchar * +e_source_alarms_get_last_notified (ESourceAlarms *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_ALARMS (extension), NULL); + + return extension->priv->last_notified; +} + +/** + * e_source_alarms_dup_last_notified: + * @extension: an #ESourceAlarms + * + * Thread-safe variation of e_source_alarms_get_last_notified(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceAlarms:last-notified + * + * Since: 3.6 + **/ +gchar * +e_source_alarms_dup_last_notified (ESourceAlarms *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_ALARMS (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_alarms_get_last_notified (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_alarms_set_last_notified: + * @extension: an #ESourceAlarms + * @last_notified: (allow-none): an ISO 8601 timestamp, or %NULL + * + * Sets an ISO 8601 formatted timestamp of when the user was last + * alerted about an upcoming appointment in the calendar described + * by the #ESource to which @extension belongs. + * + * If @last_notified is non-%NULL, the function will validate the + * timestamp before setting the #ESourceAlarms:last-notified property. + * Invalid timestamps are discarded with a runtime warning. + * + * Generally, this function should only be called by an alarm daemon + * such as evolution-alarm-notify. + * + * Since: 3.6 + **/ +void +e_source_alarms_set_last_notified (ESourceAlarms *extension, + const gchar *last_notified) +{ + g_return_if_fail (E_IS_SOURCE_ALARMS (extension)); + + if (last_notified != NULL) { + GTimeVal time_val; + + if (!g_time_val_from_iso8601 (last_notified, &time_val)) { + g_warning ("Invalid timestamp: %s", last_notified); + return; + } + } + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->last_notified); + extension->priv->last_notified = g_strdup (last_notified); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "last-notified"); +} diff --git a/libedataserver/e-source-alarms.h b/libedataserver/e-source-alarms.h new file mode 100644 index 0000000..cacc7d5 --- /dev/null +++ b/libedataserver/e-source-alarms.h @@ -0,0 +1,90 @@ +/* + * e-source-alarms.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_ALARMS_H +#define E_SOURCE_ALARMS_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_ALARMS \ + (e_source_alarms_get_type ()) +#define E_SOURCE_ALARMS(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_ALARMS, ESourceAlarms)) +#define E_SOURCE_ALARMS_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_ALARMS, ESourceAlarmsClass)) +#define E_IS_SOURCE_ALARMS(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_ALARMS)) +#define E_IS_SOURCE_ALARMS_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_ALARMS)) +#define E_SOURCE_ALARMS_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_ALARMS, ESourceAlarmsClass)) + +/** + * E_SOURCE_EXTENSION_ALARMS: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceAlarms. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_ALARMS "Alarms" + +G_BEGIN_DECLS + +typedef struct _ESourceAlarms ESourceAlarms; +typedef struct _ESourceAlarmsClass ESourceAlarmsClass; +typedef struct _ESourceAlarmsPrivate ESourceAlarmsPrivate; + +/** + * ESourceAlarms: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceAlarms { + ESourceExtension parent; + ESourceAlarmsPrivate *priv; +}; + +struct _ESourceAlarmsClass { + ESourceExtensionClass parent_class; +}; + +GType e_source_alarms_get_type (void) G_GNUC_CONST; +gboolean e_source_alarms_get_include_me (ESourceAlarms *extension); +void e_source_alarms_set_include_me (ESourceAlarms *extension, + gboolean include_me); +const gchar * e_source_alarms_get_last_notified + (ESourceAlarms *extension); +gchar * e_source_alarms_dup_last_notified + (ESourceAlarms *extension); +void e_source_alarms_set_last_notified + (ESourceAlarms *extension, + const gchar *last_notified); + +G_END_DECLS + +#endif /* E_SOURCE_ALARMS_H */ diff --git a/libedataserver/e-source-authentication.c b/libedataserver/e-source-authentication.c new file mode 100644 index 0000000..9246c2c --- /dev/null +++ b/libedataserver/e-source-authentication.c @@ -0,0 +1,537 @@ +/* + * e-source-authentication.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-authentication + * @include: libedataserver/e-source-authentication.h + * @short_description: #ESource extension for authentication settings + * + * The #ESourceAuthentication extension tracks authentication settings + * for a user account on a remote server. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceAuthentication *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION); + * ]| + **/ + +#include "e-source-authentication.h" + +#include + +#define E_SOURCE_AUTHENTICATION_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_AUTHENTICATION, ESourceAuthenticationPrivate)) + +struct _ESourceAuthenticationPrivate { + GMutex *property_lock; + gchar *host; + gchar *method; + guint16 port; + gchar *user; +}; + +enum { + PROP_0, + PROP_HOST, + PROP_METHOD, + PROP_PORT, + PROP_USER +}; + +G_DEFINE_TYPE ( + ESourceAuthentication, + e_source_authentication, + E_TYPE_SOURCE_EXTENSION) + +static void +source_authentication_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_HOST: + e_source_authentication_set_host ( + E_SOURCE_AUTHENTICATION (object), + g_value_get_string (value)); + return; + + case PROP_METHOD: + e_source_authentication_set_method ( + E_SOURCE_AUTHENTICATION (object), + g_value_get_string (value)); + return; + + case PROP_PORT: + e_source_authentication_set_port ( + E_SOURCE_AUTHENTICATION (object), + g_value_get_uint (value)); + return; + + case PROP_USER: + e_source_authentication_set_user ( + E_SOURCE_AUTHENTICATION (object), + g_value_get_string (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_authentication_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_HOST: + g_value_take_string ( + value, + e_source_authentication_dup_host ( + E_SOURCE_AUTHENTICATION (object))); + return; + + case PROP_METHOD: + g_value_take_string ( + value, + e_source_authentication_dup_method ( + E_SOURCE_AUTHENTICATION (object))); + return; + + case PROP_PORT: + g_value_set_uint ( + value, + e_source_authentication_get_port ( + E_SOURCE_AUTHENTICATION (object))); + return; + + case PROP_USER: + g_value_take_string ( + value, + e_source_authentication_dup_user ( + E_SOURCE_AUTHENTICATION (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_authentication_finalize (GObject *object) +{ + ESourceAuthenticationPrivate *priv; + + priv = E_SOURCE_AUTHENTICATION_GET_PRIVATE (object); + + g_mutex_free (priv->property_lock); + + g_free (priv->host); + g_free (priv->method); + g_free (priv->user); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_authentication_parent_class)->finalize (object); +} + +static void +e_source_authentication_class_init (ESourceAuthenticationClass *class) +{ + GObjectClass *object_class; + ESourceExtensionClass *extension_class; + + g_type_class_add_private (class, sizeof (ESourceAuthenticationPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_authentication_set_property; + object_class->get_property = source_authentication_get_property; + object_class->finalize = source_authentication_finalize; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_AUTHENTICATION; + + g_object_class_install_property ( + object_class, + PROP_HOST, + g_param_spec_string ( + "host", + "Host", + "Host name for the remote account", + "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_METHOD, + g_param_spec_string ( + "method", + "Method", + "Authentication method", + "none", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_PORT, + g_param_spec_uint ( + "port", + "Port", + "Port number for the remote account", + 0, G_MAXUINT16, 0, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_USER, + g_param_spec_string ( + "user", + "User", + "User name for the remote account", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); +} + +static void +e_source_authentication_init (ESourceAuthentication *extension) +{ + extension->priv = E_SOURCE_AUTHENTICATION_GET_PRIVATE (extension); + extension->priv->property_lock = g_mutex_new (); +} + +/** + * e_source_authentication_required: + * @extension: an #ESourceAuthentication + * + * This is a convenience function which returns whether authentication + * is required at all, regardless of the method used. This relies on + * the convention of setting #ESourceAuthentication:method to "none" + * when authentication is not required. + * + * Returns: whether authentication is required at all + * + * Since: 3.6 + **/ +gboolean +e_source_authentication_required (ESourceAuthentication *extension) +{ + const gchar *method; + + g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), FALSE); + + method = e_source_authentication_get_method (extension); + g_return_val_if_fail (method != NULL && *method != '\0', FALSE); + + return (g_strcmp0 (method, "none") != 0); +} + +/** + * e_source_authentication_get_host: + * @extension: an #ESourceAuthentication + * + * Returns the host name used to authenticate to a remote account. + * + * Returns: the host name of a remote account + * + * Since: 3.6 + **/ +const gchar * +e_source_authentication_get_host (ESourceAuthentication *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), NULL); + + return extension->priv->host; +} + +/** + * e_source_authentication_dup_host: + * @extension: an #ESourceAuthentication + * + * Thread-safe variation of e_source_authentication_get_host(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceAuthentication:host + * + * Since: 3.6 + **/ +gchar * +e_source_authentication_dup_host (ESourceAuthentication *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_authentication_get_host (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_authentication_set_host: + * @extension: an #ESourceAuthentication + * @host: (allow-none): a host name, or %NULL + * + * Sets the host name used to authenticate to a remote account. + * + * The internal copy of @host is automatically stripped of leading and + * trailing whitespace. If the resulting string is empty, %NULL is set + * instead. + * + * Since: 3.6 + **/ +void +e_source_authentication_set_host (ESourceAuthentication *extension, + const gchar *host) +{ + g_return_if_fail (E_IS_SOURCE_AUTHENTICATION (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->host); + extension->priv->host = e_util_strdup_strip (host); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "host"); +} + +/** + * e_source_authentication_get_method: + * @extension: an #ESourceAuthentication + * + * Returns the authentication method for a remote account. There are + * no pre-defined method names; backends are free to set this however + * they wish. If authentication is not required for a remote account, + * the convention is to set #ESourceAuthentication:method to "none". + * + * Returns: the authentication method for a remote account + * + * Since: 3.6 + **/ +const gchar * +e_source_authentication_get_method (ESourceAuthentication *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), NULL); + + return extension->priv->method; +} + +/** + * e_source_authentication_dup_method: + * @extension: an #ESourceAuthentication + * + * Thread-safe variation of e_source_authentication_get_method(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceAuthentication:method + * + * Since: 3.6 + **/ +gchar * +e_source_authentication_dup_method (ESourceAuthentication *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_authentication_get_method (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_authentication_set_method: + * @extension: an #ESourceAuthentication + * @method: (allow-none): authentication method, or %NULL + * + * Sets the authentication method for a remote account. There are no + * pre-defined method names; backends are free to set this however they + * wish. If authentication is not required for a remote account, the + * convention is to set the method to "none". In keeping with that + * convention, #ESourceAuthentication:method will be set to "none" if + * @method is %NULL or an empty string. + * + * Since: 3.6 + **/ +void +e_source_authentication_set_method (ESourceAuthentication *extension, + const gchar *method) +{ + g_return_if_fail (E_IS_SOURCE_AUTHENTICATION (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->method); + extension->priv->method = e_util_strdup_strip (method); + + if (extension->priv->method == NULL) + extension->priv->method = g_strdup ("none"); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "method"); +} + +/** + * e_source_authentication_get_port: + * @extension: an #ESourceAuthentication + * + * Returns the port number used to authenticate to a remote account. + * + * Returns: the port number of a remote account + * + * Since: 3.6 + **/ +guint16 +e_source_authentication_get_port (ESourceAuthentication *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), 0); + + return extension->priv->port; +} + +/** + * e_source_authentication_set_port: + * @extension: an #ESourceAuthentication + * @port: a port number + * + * Sets the port number used to authenticate to a remote account. + * + * Since: 3.6 + **/ +void +e_source_authentication_set_port (ESourceAuthentication *extension, + guint16 port) +{ + g_return_if_fail (E_SOURCE_AUTHENTICATION (extension)); + + extension->priv->port = port; + + g_object_notify (G_OBJECT (extension), "port"); +} + +/** + * e_source_authentication_get_user: + * @extension: an #ESourceAuthentication + * + * Returns the user name used to authenticate to a remote account. + * + * Returns: the user name of a remote account + * + * Since: 3.6 + **/ +const gchar * +e_source_authentication_get_user (ESourceAuthentication *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), NULL); + + return extension->priv->user; +} + +/** + * e_source_authentication_dup_user: + * @extension: an #ESourceAuthentication + * + * Thread-safe variation of e_source_authentication_get_user(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceAuthentication:user + * + * Since: 3.6 + **/ +gchar * +e_source_authentication_dup_user (ESourceAuthentication *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_authentication_get_user (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_authentication_set_user: + * @extension: an #ESourceAuthentication + * @user: (allow-none): a user name, or %NULL + * + * Sets the user name used to authenticate to a remote account. + * + * The internal copy of @user is automatically stripped of leading and + * trailing whitespace. If the resulting string is empty, %NULL is set + * instead. + * + * Since: 3.6 + **/ +void +e_source_authentication_set_user (ESourceAuthentication *extension, + const gchar *user) +{ + g_return_if_fail (E_IS_SOURCE_AUTHENTICATION (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->user); + extension->priv->user = e_util_strdup_strip (user); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "user"); +} diff --git a/libedataserver/e-source-authentication.h b/libedataserver/e-source-authentication.h new file mode 100644 index 0000000..86faaf0 --- /dev/null +++ b/libedataserver/e-source-authentication.h @@ -0,0 +1,109 @@ +/* + * e-source-authentication.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_AUTHENTICATION_H +#define E_SOURCE_AUTHENTICATION_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_AUTHENTICATION \ + (e_source_authentication_get_type ()) +#define E_SOURCE_AUTHENTICATION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_AUTHENTICATION, ESourceAuthentication)) +#define E_SOURCE_AUTHENTICATION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_AUTHENTICATION, ESourceAuthenticationClass)) +#define E_IS_SOURCE_AUTHENTICATION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_AUTHENTICATION)) +#define E_IS_SOURCE_AUTHENTICATION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_AUTHENTICATION)) +#define E_SOURCE_AUTHENTICATION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_AUTHENTICATION, ESourceAuthenticationClass)) + +/** + * E_SOURCE_EXTENSION_AUTHENTICATION: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceAuthentication. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_AUTHENTICATION "Authentication" + +G_BEGIN_DECLS + +typedef struct _ESourceAuthentication ESourceAuthentication; +typedef struct _ESourceAuthenticationClass ESourceAuthenticationClass; +typedef struct _ESourceAuthenticationPrivate ESourceAuthenticationPrivate; + +/** + * ESourceAuthentication: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceAuthentication { + ESourceExtension parent; + ESourceAuthenticationPrivate *priv; +}; + +struct _ESourceAuthenticationClass { + ESourceExtensionClass parent_class; +}; + +GType e_source_authentication_get_type + (void) G_GNUC_CONST; +gboolean e_source_authentication_required + (ESourceAuthentication *extension); +const gchar * e_source_authentication_get_host + (ESourceAuthentication *extension); +gchar * e_source_authentication_dup_host + (ESourceAuthentication *extension); +void e_source_authentication_set_host + (ESourceAuthentication *extension, + const gchar *host); +const gchar * e_source_authentication_get_method + (ESourceAuthentication *extension); +gchar * e_source_authentication_dup_method + (ESourceAuthentication *extension); +void e_source_authentication_set_method + (ESourceAuthentication *extension, + const gchar *method); +guint16 e_source_authentication_get_port + (ESourceAuthentication *extension); +void e_source_authentication_set_port + (ESourceAuthentication *extension, + guint16 port); +const gchar * e_source_authentication_get_user + (ESourceAuthentication *extension); +gchar * e_source_authentication_dup_user + (ESourceAuthentication *extension); +void e_source_authentication_set_user + (ESourceAuthentication *extension, + const gchar *user); + +G_END_DECLS + +#endif /* E_SOURCE_AUTHENTICATION_H */ diff --git a/libedataserver/e-source-authenticator.c b/libedataserver/e-source-authenticator.c new file mode 100644 index 0000000..7d99599 --- /dev/null +++ b/libedataserver/e-source-authenticator.c @@ -0,0 +1,497 @@ +/* + * e-source-authenticator.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-authenticator + * @include: libedataserver/e-source-authenticator.h + * @short_description: Interface for authentication attempts + * + * An object implementing the #ESourceAuthenticator interface gets passed + * to e_source_registry_authenticate(). The job of an #ESourceAuthenticator + * is to test whether a remote server will accept a given password, and then + * indicate the result by returning an #ESourceAuthenticationResult value. + * + * Typically only #EBackend subclasses need to implement this interface, + * as client applications are not involved in authentication. + * + * Note this interface is designed around "stateful authentication", where + * one connects to a server, provides credentials for authentication once, + * and then issues commands in an authenticated state for the remainder of + * the session. + * + * Backends requiring "stateless authentication" -- where credentials are + * included with each command -- will typically want to cache the password + * internally once it's verified as part of implementing this interface. + **/ + +#include "e-source-authenticator.h" + +#include +#include + +/* These are for building an authentication prompt. */ +#include +#include +#include +#include +#include +#include +#include + +typedef struct _AsyncContext AsyncContext; + +struct _AsyncContext { + GString *password; + ESourceAuthenticationResult result; +}; + +G_DEFINE_INTERFACE ( + ESourceAuthenticator, + e_source_authenticator, + G_TYPE_OBJECT) + +static void +async_context_free (AsyncContext *async_context) +{ + g_string_free (async_context->password, TRUE); + + g_slice_free (AsyncContext, async_context); +} + +static void +source_authenticator_get_prompt_strings (ESourceAuthenticator *auth, + ESource *source, + gchar **prompt_title, + gchar **prompt_message, + gchar **prompt_description) +{ + ESourceAuthentication *extension; + GString *description; + const gchar *message; + const gchar *extension_name; + gchar *display_name; + gchar *host_name; + gchar *user_name; + + /* Known types */ + enum { + TYPE_UNKNOWN, + TYPE_AMBIGUOUS, + TYPE_ADDRESS_BOOK, + TYPE_CALENDAR, + TYPE_MAIL_ACCOUNT, + TYPE_MAIL_TRANSPORT, + TYPE_MEMO_LIST, + TYPE_TASK_LIST + } type = TYPE_UNKNOWN; + + /* XXX This is kind of a hack but it should work for now. Build a + * suitable password prompt by checking for various extensions + * in the ESource. If no recognizable extensions are found, or + * if the result is ambiguous, just refer to the data source as + * an "account". */ + + display_name = e_source_dup_display_name (source); + + extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; + extension = e_source_get_extension (source, extension_name); + host_name = e_source_authentication_dup_host (extension); + user_name = e_source_authentication_dup_user (extension); + + extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK; + if (e_source_has_extension (source, extension_name)) { + if (type == TYPE_UNKNOWN) + type = TYPE_ADDRESS_BOOK; + else + type = TYPE_AMBIGUOUS; + } + + extension_name = E_SOURCE_EXTENSION_CALENDAR; + if (e_source_has_extension (source, extension_name)) { + if (type == TYPE_UNKNOWN) + type = TYPE_CALENDAR; + else + type = TYPE_AMBIGUOUS; + } + + extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT; + if (e_source_has_extension (source, extension_name)) { + if (type == TYPE_UNKNOWN) + type = TYPE_MAIL_ACCOUNT; + else + type = TYPE_AMBIGUOUS; + } + + extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT; + if (e_source_has_extension (source, extension_name)) { + if (type == TYPE_UNKNOWN) + type = TYPE_MAIL_TRANSPORT; + else + type = TYPE_AMBIGUOUS; + } + + extension_name = E_SOURCE_EXTENSION_MEMO_LIST; + if (e_source_has_extension (source, extension_name)) { + if (type == TYPE_UNKNOWN) + type = TYPE_MEMO_LIST; + else + type = TYPE_AMBIGUOUS; + } + + extension_name = E_SOURCE_EXTENSION_TASK_LIST; + if (e_source_has_extension (source, extension_name)) { + if (type == TYPE_UNKNOWN) + type = TYPE_TASK_LIST; + else + type = TYPE_AMBIGUOUS; + } + + switch (type) { + case TYPE_ADDRESS_BOOK: + message = _("Address book authentication request"); + break; + case TYPE_CALENDAR: + case TYPE_MEMO_LIST: + case TYPE_TASK_LIST: + message = _("Calendar authentication request"); + break; + case TYPE_MAIL_ACCOUNT: + case TYPE_MAIL_TRANSPORT: + message = _("Mail authentication request"); + break; + default: /* generic account prompt */ + message = _("Authentication request"); + break; + } + + description = g_string_sized_new (256); + + switch (type) { + case TYPE_ADDRESS_BOOK: + g_string_append_printf ( + description, + _("Please enter the password for " + "address book \"%s\"."), display_name); + break; + case TYPE_CALENDAR: + g_string_append_printf ( + description, + _("Please enter the password for " + "calendar \"%s\"."), display_name); + break; + case TYPE_MAIL_ACCOUNT: + g_string_append_printf ( + description, + _("Please enter the password for " + "mail account \"%s\"."), display_name); + break; + case TYPE_MAIL_TRANSPORT: + g_string_append_printf ( + description, + _("Please enter the password for " + "mail transport \"%s\"."), display_name); + break; + case TYPE_MEMO_LIST: + g_string_append_printf ( + description, + _("Please enter the password for " + "memo list \"%s\"."), display_name); + break; + case TYPE_TASK_LIST: + g_string_append_printf ( + description, + _("Please enter the password for " + "task list \"%s\"."), display_name); + break; + default: /* generic account prompt */ + g_string_append_printf ( + description, + _("Please enter the password for " + "account \"%s\"."), display_name); + break; + } + + if (host_name != NULL && user_name != NULL) + g_string_append_printf ( + description, "\n(user: %s, host: %s)", + user_name, host_name); + else if (host_name != NULL) + g_string_append_printf ( + description, "\n(host: %s)", host_name); + else if (user_name != NULL) + g_string_append_printf ( + description, "\n(user: %s)", user_name); + + *prompt_title = g_strdup (""); + *prompt_message = g_strdup (message); + *prompt_description = g_string_free (description, FALSE); + + g_free (display_name); + g_free (host_name); + g_free (user_name); +} + +/* Helper for source_authenticator_try_password() */ +static void +source_authenticator_try_password_thread (GSimpleAsyncResult *simple, + GObject *object, + GCancellable *cancellable) +{ + AsyncContext *async_context; + GError *error = NULL; + + async_context = g_simple_async_result_get_op_res_gpointer (simple); + + async_context->result = + e_source_authenticator_try_password_sync ( + E_SOURCE_AUTHENTICATOR (object), + async_context->password, + cancellable, &error); + + if (error != NULL) + g_simple_async_result_take_error (simple, error); +} + +static void +source_authenticator_try_password (ESourceAuthenticator *auth, + const GString *password, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + AsyncContext *async_context; + + async_context = g_slice_new0 (AsyncContext); + async_context->password = g_string_new (password->str); + + simple = g_simple_async_result_new ( + G_OBJECT (auth), callback, user_data, + source_authenticator_try_password); + + g_simple_async_result_set_check_cancellable (simple, cancellable); + + g_simple_async_result_set_op_res_gpointer ( + simple, async_context, (GDestroyNotify) async_context_free); + + g_simple_async_result_run_in_thread ( + simple, source_authenticator_try_password_thread, + G_PRIORITY_DEFAULT, cancellable); + + g_object_unref (simple); +} + +static ESourceAuthenticationResult +source_authenticator_try_password_finish (ESourceAuthenticator *auth, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + AsyncContext *async_context; + + g_return_val_if_fail ( + g_simple_async_result_is_valid ( + result, G_OBJECT (auth), + source_authenticator_try_password), + E_SOURCE_AUTHENTICATION_REJECTED); + + simple = G_SIMPLE_ASYNC_RESULT (result); + async_context = g_simple_async_result_get_op_res_gpointer (simple); + + if (g_simple_async_result_propagate_error (simple, error)) + return E_SOURCE_AUTHENTICATION_ERROR; + + return async_context->result; +} + +static void +e_source_authenticator_default_init (ESourceAuthenticatorInterface *interface) +{ + interface->get_prompt_strings = source_authenticator_get_prompt_strings; + interface->try_password = source_authenticator_try_password; + interface->try_password_finish = source_authenticator_try_password_finish; +} + +/** + * e_source_authenticator_get_prompt_strings: + * @auth: an #ESourceAuthenticator + * @source: an #ESource + * @prompt_title: (out): the title of the prompt + * @prompt_message: (out): the prompt message for the user + * @prompt_description: (out): the detailed description of the prompt + * + * Generates authentication prompt strings for @source. + * + * For registry service clients, #ESourceRegistry calls this function as + * part of e_source_registry_authenticate_sync(). In the registry service + * itself, #EAuthenticationSession calls this function during initialization. + * This function should rarely need to be called explicitly outside of those + * two cases. + * + * The #ESourceAuthenticatorInterface defines a default behavior for this + * method which should suffice in most cases. But implementors can still + * override the method if needed for special circumstances. + * + * Free each of the returned prompt strings with g_free(). + * + * Since: 3.6 + **/ +void +e_source_authenticator_get_prompt_strings (ESourceAuthenticator *auth, + ESource *source, + gchar **prompt_title, + gchar **prompt_message, + gchar **prompt_description) +{ + ESourceAuthenticatorInterface *interface; + + g_return_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth)); + g_return_if_fail (E_IS_SOURCE (source)); + g_return_if_fail (prompt_title != NULL); + g_return_if_fail (prompt_message != NULL); + g_return_if_fail (prompt_description != NULL); + + interface = E_SOURCE_AUTHENTICATOR_GET_INTERFACE (auth); + g_return_if_fail (interface->get_prompt_strings); + + interface->get_prompt_strings ( + auth, source, + prompt_title, + prompt_message, + prompt_description); +} + +/** + * e_source_authenticator_try_password_sync: + * @auth: an #ESourceAuthenticator + * @password: a user-provided password + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @error: return location for a #GError, or %NULL + * + * Attempts to authenticate using @password. + * + * The password is passed in a #GString container so its content is not + * accidentally revealed in a stack trace. + * + * If an error occurs, the function sets @error and returns + * #E_SOURCE_AUTHENTICATION_ERROR. + * + * Returns: the authentication result + * + * Since: 3.6 + **/ +ESourceAuthenticationResult +e_source_authenticator_try_password_sync (ESourceAuthenticator *auth, + const GString *password, + GCancellable *cancellable, + GError **error) +{ + ESourceAuthenticatorInterface *interface; + + g_return_val_if_fail ( + E_IS_SOURCE_AUTHENTICATOR (auth), + E_SOURCE_AUTHENTICATION_REJECTED); + g_return_val_if_fail ( + password != NULL, + E_SOURCE_AUTHENTICATION_REJECTED); + + interface = E_SOURCE_AUTHENTICATOR_GET_INTERFACE (auth); + g_return_val_if_fail ( + interface->try_password_sync != NULL, + E_SOURCE_AUTHENTICATION_REJECTED); + + return interface->try_password_sync ( + auth, password, cancellable, error); +} + +/** + * e_source_authenticator_try_password: + * @auth: an #ESourceAuthenticator + * @password: a user-provided password + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @callback: (scope async): a #GAsyncReadyCallback to call when the request + * is satisfied + * @user_data: (closure): data to pass to the callback function + * + * Asyncrhonously attempts to authenticate using @password. + * + * The password is passed in a #GString container so its content is not + * accidentally revealed in a stack trace. + * + * When the operation is finished, @callback will be called. You can then + * call e_source_authenticator_try_password_finish() to get the result of the + * operation. + * + * Since: 3.6 + **/ +void +e_source_authenticator_try_password (ESourceAuthenticator *auth, + const GString *password, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + ESourceAuthenticatorInterface *interface; + + g_return_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth)); + g_return_if_fail (password != NULL); + + interface = E_SOURCE_AUTHENTICATOR_GET_INTERFACE (auth); + g_return_if_fail (interface->try_password != NULL); + + interface->try_password ( + auth, password, cancellable, callback, user_data); +} + +/** + * e_source_authenticator_try_password_finish: + * @auth: an #ESourceAuthenticator + * @result: a #GAsyncResult + * @error: return location for a #GError, or %NULL + * + * Finishes the operation started with e_source_authenticator_try_password(). + * + * If an error occurred, the function sets @error and returns + * #E_SOURCE_AUTHENTICATION_ERROR. + * + * Returns: the authentication result + * + * Since: 3.6 + **/ +ESourceAuthenticationResult +e_source_authenticator_try_password_finish (ESourceAuthenticator *auth, + GAsyncResult *result, + GError **error) +{ + ESourceAuthenticatorInterface *interface; + + g_return_val_if_fail ( + E_IS_SOURCE_AUTHENTICATOR (auth), + E_SOURCE_AUTHENTICATION_REJECTED); + g_return_val_if_fail ( + G_IS_ASYNC_RESULT (result), + E_SOURCE_AUTHENTICATION_REJECTED); + + interface = E_SOURCE_AUTHENTICATOR_GET_INTERFACE (auth); + g_return_val_if_fail ( + interface->try_password_finish != NULL, + E_SOURCE_AUTHENTICATION_REJECTED); + + return interface->try_password_finish (auth, result, error); +} + diff --git a/libedataserver/e-source-authenticator.h b/libedataserver/e-source-authenticator.h new file mode 100644 index 0000000..c40d09b --- /dev/null +++ b/libedataserver/e-source-authenticator.h @@ -0,0 +1,103 @@ +/* + * e-source-authenticator.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_AUTHENTICATOR_H +#define E_SOURCE_AUTHENTICATOR_H + +#include +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_AUTHENTICATOR \ + (e_source_authenticator_get_type ()) +#define E_SOURCE_AUTHENTICATOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_AUTHENTICATOR, ESourceAuthenticator)) +#define E_IS_SOURCE_AUTHENTICATOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_AUTHENTICATOR)) +#define E_SOURCE_AUTHENTICATOR_GET_INTERFACE(obj) \ + (G_TYPE_INSTANCE_GET_INTERFACE \ + ((obj), E_TYPE_SOURCE_AUTHENTICATOR, ESourceAuthenticatorInterface)) + +G_BEGIN_DECLS + +/** + * ESourceAuthenticator: + * + * Since: 3.6 + **/ +typedef struct _ESourceAuthenticator ESourceAuthenticator; +typedef struct _ESourceAuthenticatorInterface ESourceAuthenticatorInterface; + +struct _ESourceAuthenticatorInterface { + GTypeInterface parent_interface; + + void (*get_prompt_strings) (ESourceAuthenticator *auth, + ESource *source, + gchar **prompt_title, + gchar **prompt_message, + gchar **prompt_description); + + /* Synchronous I/O Methods */ + ESourceAuthenticationResult + (*try_password_sync) (ESourceAuthenticator *auth, + const GString *password, + GCancellable *cancellable, + GError **error); + + /* Asynchronous I/O Methods (all have defaults) */ + void (*try_password) (ESourceAuthenticator *auth, + const GString *password, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + ESourceAuthenticationResult + (*try_password_finish) (ESourceAuthenticator *auth, + GAsyncResult *result, + GError **error); +}; + +GType e_source_authenticator_get_type (void) G_GNUC_CONST; +void e_source_authenticator_get_prompt_strings + (ESourceAuthenticator *auth, + ESource *source, + gchar **prompt_title, + gchar **prompt_message, + gchar **prompt_description); +ESourceAuthenticationResult + e_source_authenticator_try_password_sync + (ESourceAuthenticator *auth, + const GString *password, + GCancellable *cancellable, + GError **error); +void e_source_authenticator_try_password + (ESourceAuthenticator *auth, + const GString *password, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +ESourceAuthenticationResult + e_source_authenticator_try_password_finish + (ESourceAuthenticator *auth, + GAsyncResult *result, + GError **error); + +G_END_DECLS + +#endif /* E_SOURCE_AUTHENTICATOR_H */ diff --git a/libedataserver/e-source-autocomplete.c b/libedataserver/e-source-autocomplete.c new file mode 100644 index 0000000..5aa9d13 --- /dev/null +++ b/libedataserver/e-source-autocomplete.c @@ -0,0 +1,168 @@ +/* + * e-source-autocomplete.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-autocomplete + * @include: libedataserver/e-source-autocomplete.h + * @short_description: #ESource extension for autocomplete settings + * + * The #ESourceAutocomplete extension tracks contact autocompletion + * settings for an address book. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceAutocomplete *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTOCOMPLETE); + * ]| + **/ + +#include "e-source-autocomplete.h" + +#define E_SOURCE_AUTOCOMPLETE_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_AUTOCOMPLETE, ESourceAutocompletePrivate)) + +struct _ESourceAutocompletePrivate { + gboolean include_me; +}; + +enum { + PROP_0, + PROP_INCLUDE_ME +}; + +G_DEFINE_TYPE ( + ESourceAutocomplete, + e_source_autocomplete, + E_TYPE_SOURCE_EXTENSION) + +static void +source_autocomplete_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_INCLUDE_ME: + e_source_autocomplete_set_include_me ( + E_SOURCE_AUTOCOMPLETE (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_autocomplete_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_INCLUDE_ME: + g_value_set_boolean ( + value, + e_source_autocomplete_get_include_me ( + E_SOURCE_AUTOCOMPLETE (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +e_source_autocomplete_class_init (ESourceAutocompleteClass *class) +{ + GObjectClass *object_class; + ESourceExtensionClass *extension_class; + + g_type_class_add_private (class, sizeof (ESourceAutocompletePrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_autocomplete_set_property; + object_class->get_property = source_autocomplete_get_property; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_AUTOCOMPLETE; + + g_object_class_install_property ( + object_class, + PROP_INCLUDE_ME, + g_param_spec_boolean ( + "include-me", + "IncludeMe", + "Include this source when autocompleting", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); +} + +static void +e_source_autocomplete_init (ESourceAutocomplete *extension) +{ + extension->priv = E_SOURCE_AUTOCOMPLETE_GET_PRIVATE (extension); +} + +/** + * e_source_autocomplete_get_include_me: + * @extension: an #ESourceAutocomplete + * + * Returns whether the address book described by the #ESource to which + * @extension belongs should be queried when the user inputs a partial + * contact name or email address. + * + * Returns: whether to use the autocomplete feature + * + * Since: 3.6 + **/ +gboolean +e_source_autocomplete_get_include_me (ESourceAutocomplete *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_AUTOCOMPLETE (extension), FALSE); + + return extension->priv->include_me; +} + +/** + * e_source_autocomplete_set_include_me: + * @extension: an #ESourceAutocomplete + * @include_me: whether to use the autocomplete feature + * + * Sets whether the address book described by the #ESource to which + * @extension belongs should be queried when the user inputs a partial + * contact name or email address. + * + * Since: 3.6 + **/ +void +e_source_autocomplete_set_include_me (ESourceAutocomplete *extension, + gboolean include_me) +{ + g_return_if_fail (E_IS_SOURCE_AUTOCOMPLETE (extension)); + + extension->priv->include_me = include_me; + + g_object_notify (G_OBJECT (extension), "include-me"); +} diff --git a/libedataserver/e-source-autocomplete.h b/libedataserver/e-source-autocomplete.h new file mode 100644 index 0000000..db42606 --- /dev/null +++ b/libedataserver/e-source-autocomplete.h @@ -0,0 +1,86 @@ +/* + * e-source-autocomplete.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_AUTOCOMPLETE_H +#define E_SOURCE_AUTOCOMPLETE_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_AUTOCOMPLETE \ + (e_source_autocomplete_get_type ()) +#define E_SOURCE_AUTOCOMPLETE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_AUTOCOMPLETE, ESourceAutocomplete)) +#define E_SOURCE_AUTOCOMPLETE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_AUTOCOMPLETE, ESourceAutocompleteClass)) +#define E_IS_SOURCE_AUTOCOMPLETE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_AUTOCOMPLETE)) +#define E_IS_SOURCE_AUTOCOMPLETE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_AUTOCOMPLETE)) +#define E_SOURCE_AUTOCOMPLETE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_AUTOCOMPLETE, ESourceAutocompleteClass)) + +/** + * E_SOURCE_EXTENSION_AUTOCOMPLETE: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceAutocomplete. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_AUTOCOMPLETE "Autocomplete" + +G_BEGIN_DECLS + +typedef struct _ESourceAutocomplete ESourceAutocomplete; +typedef struct _ESourceAutocompleteClass ESourceAutocompleteClass; +typedef struct _ESourceAutocompletePrivate ESourceAutocompletePrivate; + +/** + * ESourceAutocomplete: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceAutocomplete { + ESourceExtension parent; + ESourceAutocompletePrivate *priv; +}; + +struct _ESourceAutocompleteClass { + ESourceExtensionClass parent_class; +}; + +GType e_source_autocomplete_get_type + (void) G_GNUC_CONST; +gboolean e_source_autocomplete_get_include_me + (ESourceAutocomplete *extension); +void e_source_autocomplete_set_include_me + (ESourceAutocomplete *extension, + gboolean include_me); + +G_END_DECLS + +#endif /* E_SOURCE_AUTOCOMPLETE_H */ diff --git a/libedataserver/e-source-backend.c b/libedataserver/e-source-backend.c new file mode 100644 index 0000000..e9be2ab --- /dev/null +++ b/libedataserver/e-source-backend.c @@ -0,0 +1,215 @@ +/* + * e-source-backend.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-backend + * @include: libedataserver/e-source-backend.h + * @short_description: Base class for backend-based data sources + * + * #ESourceBackend is an abstract base class for data sources requiring + * an associated backend to function. The extension merely records the + * name of the backend the data source should be paired with. + **/ + +#include "e-source-backend.h" + +#include + +#define E_SOURCE_BACKEND_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_BACKEND, ESourceBackendPrivate)) + +struct _ESourceBackendPrivate { + GMutex *property_lock; + gchar *backend_name; +}; + +enum { + PROP_0, + PROP_BACKEND_NAME +}; + +G_DEFINE_ABSTRACT_TYPE ( + ESourceBackend, + e_source_backend, + E_TYPE_SOURCE_EXTENSION) + +static void +source_backend_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_BACKEND_NAME: + e_source_backend_set_backend_name ( + E_SOURCE_BACKEND (object), + g_value_get_string (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_backend_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_BACKEND_NAME: + g_value_take_string ( + value, + e_source_backend_dup_backend_name ( + E_SOURCE_BACKEND (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_backend_finalize (GObject *object) +{ + ESourceBackendPrivate *priv; + + priv = E_SOURCE_BACKEND_GET_PRIVATE (object); + + g_mutex_free (priv->property_lock); + + g_free (priv->backend_name); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_backend_parent_class)->finalize (object); +} + +static void +e_source_backend_class_init (ESourceBackendClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (ESourceBackendPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_backend_set_property; + object_class->get_property = source_backend_get_property; + object_class->finalize = source_backend_finalize; + + /* We do not provide an extension name, + * which is why the class is abstract. */ + + g_object_class_install_property ( + object_class, + PROP_BACKEND_NAME, + g_param_spec_string ( + "backend-name", + "Backend Name", + "The name of the backend handling the data source", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); +} + +static void +e_source_backend_init (ESourceBackend *extension) +{ + extension->priv = E_SOURCE_BACKEND_GET_PRIVATE (extension); + extension->priv->property_lock = g_mutex_new (); +} + +/** + * e_source_backend_get_backend_name: + * @extension: an #ESourceBackend + * + * Returns the backend name for @extension. + * + * Returns: the backend name for @extension + * + * Since: 3.6 + **/ +const gchar * +e_source_backend_get_backend_name (ESourceBackend *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_BACKEND (extension), NULL); + + return extension->priv->backend_name; +} + +/** + * e_source_backend_dup_backend_name: + * @extension: an #ESourceBackend + * + * Thread-safe variation of e_source_backend_get_backend_name(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceBackend:backend-name + * + * Since: 3.6 + **/ +gchar * +e_source_backend_dup_backend_name (ESourceBackend *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_BACKEND (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_backend_get_backend_name (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_backend_set_backend_name: + * @extension: an #ESourceBackend + * @backend_name: (allow-none): a backend name, or %NULL + * + * Sets the backend name for @extension. + * + * The internal copy of @backend_name is automatically stripped of leading + * and trailing whitespace. If the resulting string is empty, %NULL is set + * instead. + * + * Since: 3.6 + **/ +void +e_source_backend_set_backend_name (ESourceBackend *extension, + const gchar *backend_name) +{ + g_return_if_fail (E_IS_SOURCE_BACKEND (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->backend_name); + extension->priv->backend_name = e_util_strdup_strip (backend_name); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "backend-name"); +} + diff --git a/libedataserver/e-source-backend.h b/libedataserver/e-source-backend.h new file mode 100644 index 0000000..5d3baee --- /dev/null +++ b/libedataserver/e-source-backend.h @@ -0,0 +1,77 @@ +/* + * e-source-backend.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_BACKEND_H +#define E_SOURCE_BACKEND_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_BACKEND \ + (e_source_backend_get_type ()) +#define E_SOURCE_BACKEND(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_BACKEND, ESourceBackend)) +#define E_SOURCE_BACKEND_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_BACKEND, ESourceBackendClass)) +#define E_IS_SOURCE_BACKEND(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_BACKEND)) +#define E_IS_SOURCE_BACKEND_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_BACKEND)) +#define E_SOURCE_BACKEND_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_BACKEND, ESourceBackendClass)) + +G_BEGIN_DECLS + +typedef struct _ESourceBackend ESourceBackend; +typedef struct _ESourceBackendClass ESourceBackendClass; +typedef struct _ESourceBackendPrivate ESourceBackendPrivate; + +/** + * ESourceBackend: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceBackend { + ESourceExtension parent; + ESourceBackendPrivate *priv; +}; + +struct _ESourceBackendClass { + ESourceExtensionClass parent_class; +}; + +GType e_source_backend_get_type (void) G_GNUC_CONST; +const gchar * e_source_backend_get_backend_name + (ESourceBackend *extension); +gchar * e_source_backend_dup_backend_name + (ESourceBackend *extension); +void e_source_backend_set_backend_name + (ESourceBackend *extension, + const gchar *backend_name); + +G_END_DECLS + +#endif /* E_SOURCE_BACKEND_H */ diff --git a/libedataserver/e-source-calendar.c b/libedataserver/e-source-calendar.c new file mode 100644 index 0000000..13d0d52 --- /dev/null +++ b/libedataserver/e-source-calendar.c @@ -0,0 +1,135 @@ +/* + * e-source-calendar.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/* FIXME: Break these into separate files after libedataserver + * moves to a single include paradigm. */ + +/** + * SECTION: e-source-calendar + * @include: libedataserver/e-source-calendar.h + * @short_description: #ESource extension for a calendar + * + * The #ESourceCalendar extension identifies the #ESource as a calendar. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceCalendar *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR); + * ]| + **/ + +/** + * SECTION: e-source-memo-list + * @include: libedataserver/e-source-calendar.h + * @short_description: #ESource extension for a memo list + * + * The #ESourceCalendar extension identifies the #ESource as a memo list. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceCalendar *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MEMO_LIST); + * ]| + **/ + +/** + * SECTION: e-source-task-list + * @include: libedataserver/e-source-calendar.h + * @short_description: #ESource extension for a task list + * + * The #ESourceCalendar extension identifies the #ESource as a task list. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceCalendar *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST); + * ]| + **/ + +#include "e-source-calendar.h" + +#include + +G_DEFINE_TYPE ( + ESourceCalendar, + e_source_calendar, + E_TYPE_SOURCE_SELECTABLE) + +G_DEFINE_TYPE ( + ESourceMemoList, + e_source_memo_list, + E_TYPE_SOURCE_SELECTABLE) + +G_DEFINE_TYPE ( + ESourceTaskList, + e_source_task_list, + E_TYPE_SOURCE_SELECTABLE) + +static void +e_source_calendar_class_init (ESourceCalendarClass *class) +{ + ESourceExtensionClass *extension_class; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_CALENDAR; +} + +static void +e_source_calendar_init (ESourceCalendar *extension) +{ +} + +static void +e_source_memo_list_class_init (ESourceMemoListClass *class) +{ + ESourceExtensionClass *extension_class; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_MEMO_LIST; +} + +static void +e_source_memo_list_init (ESourceMemoList *extension) +{ +} + +static void +e_source_task_list_class_init (ESourceTaskListClass *class) +{ + ESourceExtensionClass *extension_class; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_TASK_LIST; +} + +static void +e_source_task_list_init (ESourceTaskList *extension) +{ +} diff --git a/libedataserver/e-source-calendar.h b/libedataserver/e-source-calendar.h new file mode 100644 index 0000000..ac84bc4 --- /dev/null +++ b/libedataserver/e-source-calendar.h @@ -0,0 +1,185 @@ +/* + * e-source-calendar.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_CALENDAR_H +#define E_SOURCE_CALENDAR_H + +/* These are trivial but important ESourceSelectable subclasses. + * They identify an ESource as a calendar, memo list or task list. */ + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_CALENDAR \ + (e_source_calendar_get_type ()) +#define E_SOURCE_CALENDAR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_CALENDAR, ESourceCalendar)) +#define E_SOURCE_CALENDAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_CALENDAR, ESourceCalendarClass)) +#define E_IS_SOURCE_CALENDAR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_CALENDAR)) +#define E_IS_SOURCE_CALENDAR_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_CALENDAR)) +#define E_SOURCE_CALENDAR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_CALENDAR, ESourceCalendarClass)) + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_MEMO_LIST \ + (e_source_memo_list_get_type ()) +#define E_SOURCE_MEMO_LIST(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_MEMO_LIST, ESourceMemoList)) +#define E_SOURCE_MEMO_LIST_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_MEMO_LIST, ESourceMemoListClass)) +#define E_IS_SOURCE_MEMO_LIST(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_MEMO_LIST)) +#define E_IS_SOURCE_MEMO_LIST_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_MEMO_LIST)) +#define E_SOURCE_MEMO_LIST_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_MEMO_LIST, ESourceMemoListClass)) + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_TASK_LIST \ + (e_source_task_list_get_type ()) +#define E_SOURCE_TASK_LIST(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_TASK_LIST, ESourceTaskList)) +#define E_SOURCE_TASK_LIST_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_TASK_LIST, ESourceTaskListClass)) +#define E_IS_SOURCE_TASK_LIST(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_TASK_LIST)) +#define E_IS_SOURCE_TASK_LIST_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_TASK_LIST)) +#define E_SOURCE_TASK_LIST_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_TASK_LIST, ESourceTaskListClass)) + +/** + * E_SOURCE_EXTENSION_CALENDAR: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceCalendar. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_CALENDAR "Calendar" + +/** + * E_SOURCE_EXTENSION_MEMO_LIST: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceMemoList. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_MEMO_LIST "Memo List" + +/** + * E_SOURCE_EXTENSION_TASK_LIST: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceTaskList. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_TASK_LIST "Task List" + +G_BEGIN_DECLS + +typedef struct _ESourceCalendar ESourceCalendar; +typedef struct _ESourceCalendarClass ESourceCalendarClass; +typedef struct _ESourceCalendarPrivate ESourceCalendarPrivate; + +typedef struct _ESourceMemoList ESourceMemoList; +typedef struct _ESourceMemoListClass ESourceMemoListClass; +typedef struct _ESourceMemoListPrivate ESourceMemoListPrivate; + +typedef struct _ESourceTaskList ESourceTaskList; +typedef struct _ESourceTaskListClass ESourceTaskListClass; +typedef struct _ESourceTaskListPrivate ESourceTaskListPrivate; + +/** + * ESourceCalendar: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceCalendar { + ESourceSelectable parent; + ESourceCalendarPrivate *priv; +}; + +struct _ESourceCalendarClass { + ESourceSelectableClass parent_class; +}; + +/** + * ESourceMemoList: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceMemoList { + ESourceSelectable parent; + ESourceMemoListPrivate *priv; +}; + +struct _ESourceMemoListClass { + ESourceSelectableClass parent_class; +}; + +/** + * ESourceTaskList: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceTaskList { + ESourceSelectable parent; + ESourceTaskListPrivate *priv; +}; + +struct _ESourceTaskListClass { + ESourceSelectableClass parent_class; +}; + +GType e_source_calendar_get_type (void); +GType e_source_memo_list_get_type (void); +GType e_source_task_list_get_type (void); + +G_END_DECLS + +#endif /* E_SOURCE_CALENDAR_H */ diff --git a/libedataserver/e-source-camel.c b/libedataserver/e-source-camel.c new file mode 100644 index 0000000..df0d67b --- /dev/null +++ b/libedataserver/e-source-camel.c @@ -0,0 +1,738 @@ +/* + * e-source-camel-provider.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-camel + * @include: libedataserver/e-source-camel.h + * @short_description: #ESource extension for #CamelSettings + * + * #ESourceCamel itself is abstract. Its sole function is to + * bridge #GObject properties from the #CamelSettings framework to the + * #ESource framework. It does this by procedurally registering an + * #ESourceCamel subtype for each available #CamelService subtype, + * and then registering #GObject properties to proxy the properties in the + * corresponding #CamelSettings subtype. The #ESourceCamel owns an + * instance of the appropriate #CamelSettings subtype, and redirects all + * get/set operations on its own #GObject properties to its #CamelSettings + * instance. The #CamelSettings instance, now fully initialized from a key + * file, can then be inserted into a new #CamelService instance using + * camel_service_set_settings(). + * + * Ultimately, this is all just implementation detail for glueing two + * unrelated class hierarchies together. If you need to access provider + * specific settings, use the #CamelSettings API, not this. + **/ + +#include "e-source-camel.h" + +#include +#include + +#include +#include +#include +#include + +#define E_SOURCE_CAMEL_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_CAMEL, ESourceCamelPrivate)) + +struct _ESourceCamelPrivate { + CamelSettings *settings; + GArray *value_array; +}; + +enum { + PROP_0, + PROP_SETTINGS +}; + +typedef struct { + const gchar *extension_name; + const gchar *extension_property_name; + const gchar *settings_property_name; + GBindingTransformFunc extension_to_settings; + GBindingTransformFunc settings_to_extension; +} BindingData; + +static gboolean +transform_none_to_null (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer not_used) +{ + const gchar *v_string; + + /* XXX Camel doesn't understand ESource's convention of using + * "none" to represent no value, instead of NULL or empty + * strings. So convert "none" to NULL for Camel. */ + + v_string = g_value_get_string (source_value); + + if (g_strcmp0 (v_string, "none") == 0) + v_string = NULL; + + g_value_set_string (target_value, v_string); + + return TRUE; +} + +static BindingData bindings[] = { + + { E_SOURCE_EXTENSION_AUTHENTICATION, + "host", "host" }, + + { E_SOURCE_EXTENSION_AUTHENTICATION, + "method", "auth-mechanism", + transform_none_to_null, + NULL }, + + { E_SOURCE_EXTENSION_AUTHENTICATION, + "port", "port" }, + + { E_SOURCE_EXTENSION_AUTHENTICATION, + "user", "user" }, + + { E_SOURCE_EXTENSION_OFFLINE, + "stay-synchronized", "stay-synchronized" }, + + { E_SOURCE_EXTENSION_SECURITY, + "method", "security-method", + e_binding_transform_enum_nick_to_value, + e_binding_transform_enum_value_to_nick } +}; + +G_DEFINE_ABSTRACT_TYPE ( + ESourceCamel, + e_source_camel, + E_TYPE_SOURCE_EXTENSION) + +/* XXX A function like this belongs in GObject. I may yet propose it, + * GParamSpecClass still has some reserved slots. This fiddles with + * GParamSpec fields that are supposed to be private to GObject, but + * I have no other choice. + * + * XXX Historical note, originally I tried (ab)using override properties + * in ESourceCamel, which redirected to the equivalent CamelSettings + * property. Seemed to work at first, and I was proud of my clever + * hack, but it turns out g_object_class_list_properties() excludes + * override properties. So the ESourceCamel properties were being + * skipped in source_load_from_key_file() (e-source.c). */ +static GParamSpec * +param_spec_clone (GParamSpec *pspec) +{ + GParamSpec *clone; + GTypeQuery query; + + /* Query the instance size. */ + g_type_query (G_PARAM_SPEC_TYPE (pspec), &query); + + /* Start with a memcpy()'d buffer. */ + clone = g_slice_alloc0 (query.instance_size); + memcpy (clone, pspec, query.instance_size); + + /* This sort of mimics g_param_spec_init(). */ + +#define PARAM_FLOATING_FLAG 0x2 /* from gparam.c */ + g_datalist_set_flags (&clone->qdata, PARAM_FLOATING_FLAG); + clone->ref_count = 1; + + /* Clear the owner_type. */ + clone->owner_type = G_TYPE_INVALID; + + /* Clear the param_id. */ + clone->param_id = 0; + + /* This sort of mimics g_param_spec_internal(). */ + + /* Param name should already be canonicalized and interned. */ + + /* Always copy the nickname. */ + clone->flags &= ~G_PARAM_STATIC_NICK; + clone->_nick = g_strdup (g_param_spec_get_nick (pspec)); + + /* Always copy the blurb. */ + clone->flags &= ~G_PARAM_STATIC_BLURB; + clone->_blurb = g_strdup (g_param_spec_get_blurb (pspec)); + + /* Handle special cases. */ + + if (G_IS_PARAM_SPEC_STRING (clone)) { + GParamSpecString *clone_s; + + clone_s = (GParamSpecString *) clone; + clone_s->default_value = g_strdup (clone_s->default_value); + } + + /* Some types we don't handle but shouldn't need to. */ + g_warn_if_fail (!G_IS_PARAM_SPEC_VALUE_ARRAY (clone)); + g_warn_if_fail (!G_IS_PARAM_SPEC_OVERRIDE (clone)); + g_warn_if_fail (!G_IS_PARAM_SPEC_VARIANT (clone)); + + return clone; +} + +static gint +subclass_get_binding_index (GParamSpec *settings_property) +{ + gint ii; + + /* Return the index in the bindings list for the given + * CamelSettings property specification, or else -1. */ + + for (ii = 0; ii < G_N_ELEMENTS (bindings); ii++) { + const gchar *property_name; + + property_name = bindings[ii].settings_property_name; + if (g_strcmp0 (settings_property->name, property_name) == 0) + return ii; + } + + return -1; +} + +static void +subclass_set_property (GObject *object, + guint property_id, + const GValue *src_value, + GParamSpec *pspec) +{ + ESourceCamel *extension; + GArray *value_array; + GValue *dst_value; + + extension = E_SOURCE_CAMEL (object); + value_array = extension->priv->value_array; + + dst_value = &g_array_index (value_array, GValue, property_id - 1); + g_value_copy (src_value, dst_value); +} + +static void +subclass_get_property (GObject *object, + guint property_id, + GValue *dst_value, + GParamSpec *pspec) +{ + ESourceCamel *extension; + GArray *value_array; + GValue *src_value; + + extension = E_SOURCE_CAMEL (object); + value_array = extension->priv->value_array; + + src_value = &g_array_index (value_array, GValue, property_id - 1); + g_value_copy (src_value, dst_value); +} + +static void +subclass_class_init (gpointer g_class, + gpointer class_data) +{ + GObjectClass *object_class; + + /* e_source_camel_generate_subtype() does all the + * dynamic class initialization. We just do what static + * initialization we can here. */ + + object_class = G_OBJECT_CLASS (g_class); + object_class->set_property = subclass_set_property; + object_class->get_property = subclass_get_property; +} + +static void +subclass_instance_init (GTypeInstance *instance, + gpointer g_class) +{ + /* Nothing to do here, just makes a handy breakpoint. */ +} + +static void +source_camel_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SETTINGS: + g_value_set_object ( + value, + e_source_camel_get_settings ( + E_SOURCE_CAMEL (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_camel_dispose (GObject *object) +{ + ESourceCamelPrivate *priv; + + priv = E_SOURCE_CAMEL_GET_PRIVATE (object); + + if (priv->settings != NULL) { + g_object_unref (priv->settings); + priv->settings = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_source_camel_parent_class)->dispose (object); +} + +static void +source_camel_finalize (GObject *object) +{ + ESourceCamelPrivate *priv; + guint ii; + + priv = E_SOURCE_CAMEL_GET_PRIVATE (object); + + for (ii = 0; ii < priv->value_array->len; ii++) + g_value_unset (&g_array_index (priv->value_array, GValue, ii)); + + g_array_free (priv->value_array, TRUE); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_camel_parent_class)->finalize (object); +} + +static void +source_camel_constructed (GObject *object) +{ + ESource *source; + ESourceCamelClass *class; + ESourceCamelPrivate *priv; + GObjectClass *settings_class; + GParamSpec **properties; + guint ii, n_properties; + guint array_index = 0; + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_source_camel_parent_class)-> + constructed (object); + + class = E_SOURCE_CAMEL_GET_CLASS (object); + priv = E_SOURCE_CAMEL_GET_PRIVATE (object); + + source = e_source_extension_get_source (E_SOURCE_EXTENSION (object)); + + priv->settings = g_object_new (class->settings_type, NULL); + + /* Here we bind all the GObject properties in the newly-created + * CamelSettings instance to either our own identical properties + * or properties in another ESourceExtensions. The bindings list + * at the top of the file maps out bindings to other extensions. */ + + settings_class = G_OBJECT_GET_CLASS (priv->settings); + + properties = g_object_class_list_properties ( + settings_class, &n_properties); + + /* Allocate more elements than we need, since some CamelSettings + * properties get bound to properties of other ESourceExtensions. + * We'll trim off the extra elements later. */ + g_array_set_size (priv->value_array, n_properties); + + for (ii = 0; ii < n_properties; ii++) { + GParamSpec *pspec = properties[ii]; + GBindingTransformFunc transform_to = NULL; + GBindingTransformFunc transform_from = NULL; + ESourceExtension *extension; + const gchar *source_property; + const gchar *target_property; + gint binding_index; + + binding_index = subclass_get_binding_index (pspec); + + /* Bind the CamelSettings property to + * one in a different ESourceExtension. */ + if (binding_index >= 0) { + BindingData *binding; + + binding = &bindings[binding_index]; + + extension = e_source_get_extension ( + source, binding->extension_name); + + source_property = binding->extension_property_name; + target_property = binding->settings_property_name; + + transform_to = binding->extension_to_settings; + transform_from = binding->settings_to_extension; + + /* Bind the CamelSettings property to our own + * equivalent E_SOURCE_PARAM_SETTING property. */ + } else { + GValue *value; + + extension = E_SOURCE_EXTENSION (object); + + source_property = pspec->name; + target_property = pspec->name; + + /* Initialize the array element to + * hold the GParamSpec's value type. */ + value = &g_array_index ( + priv->value_array, GValue, array_index++); + g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec)); + + /* Set the array element to the GParamSpec's default + * value. This allows us to avoid declaring our own + * properties with a G_PARAM_CONSTRUCT flag. */ + g_param_value_set_default (pspec, value); + } + + g_object_bind_property_full ( + extension, source_property, + priv->settings, target_property, + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE, + transform_to, transform_from, + NULL, (GDestroyNotify) NULL); + } + + /* Trim off any extra array elements. */ + g_array_set_size (priv->value_array, array_index); + + g_free (properties); +} + +static void +e_source_camel_class_init (ESourceCamelClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (ESourceCamelPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->get_property = source_camel_get_property; + object_class->dispose = source_camel_dispose; + object_class->finalize = source_camel_finalize; + object_class->constructed = source_camel_constructed; + + /* CamelSettings itself has no properties. */ + class->settings_type = CAMEL_TYPE_SETTINGS; + + /* XXX This kind of stomps on CamelSettings' namespace, but it's + * unlikely a CamelSettings subclass would define a property + * named "settings". */ + g_object_class_install_property ( + object_class, + PROP_SETTINGS, + g_param_spec_object ( + "settings", + "Settings", + "The CamelSettings instance being proxied", + CAMEL_TYPE_SETTINGS, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); +} + +static void +e_source_camel_init (ESourceCamel *extension) +{ + GArray *value_array; + + /* Zero-fill array elements when they are allocated. */ + value_array = g_array_new (FALSE, TRUE, sizeof (GValue)); + + extension->priv = E_SOURCE_CAMEL_GET_PRIVATE (extension); + extension->priv->value_array = value_array; +} + +/** + * e_source_camel_register_types: + * + * Creates and registers subclasses of #ESourceCamel for each available + * #CamelProvider. This function should be called once during application + * or library initialization. + * + * Since: 3.6 + **/ +void +e_source_camel_register_types (void) +{ + GList *list, *link; + + /* This implicitly takes care of provider initialization. */ + list = camel_provider_list (TRUE); + + for (link = list; link != NULL; link = g_list_next (link)) { + CamelProvider *provider; + gint ii; + + provider = (CamelProvider *) link->data; + + /* This is the novel part: generate and register + * a new ESourceCamel subclass on-the-fly for each + * object type listed in the provider. */ + for (ii = 0; ii < CAMEL_NUM_PROVIDER_TYPES; ii++) { + CamelServiceClass *service_class = NULL; + GType service_type; + + service_type = provider->object_types[ii]; + + if (g_type_is_a (service_type, CAMEL_TYPE_SERVICE)) + service_class = g_type_class_ref (service_type); + + if (service_class != NULL) { + e_source_camel_generate_subtype ( + provider->protocol, + service_class->settings_type); + g_type_class_unref (service_class); + } + } + } + + g_list_free (list); +} + +/** + * e_source_camel_generate_subtype: + * @protocol: a #CamelProvider protocol + * @settings_type: a subtype of #CamelSettings + * + * Generates a custom #ESourceCamel subtype for @protocol. Instances of the + * new subtype will contain a #CamelSettings instance of type @settings_type. + * + * This function is called as part of e_source_camel_register_types() and + * should not be called explicitly, except by some groupware packages that + * need to share package-specific settings across their mail, calendar and + * address book components. In that case the groupware package may choose + * to subclass #CamelSettings rather than #ESourceExtension since libcamel + * is the lowest common denominator across all components. This function + * provides a way for the calendar and address book components of such a + * package to generate an #ESourceCamel subtype for its #CamelSettings + * subtype without having to load all available #CamelProvider modules. + * + * Returns: the #GType of the generated #ESourceCamel subtype + * + * Since: 3.6 + **/ +GType +e_source_camel_generate_subtype (const gchar *protocol, + GType settings_type) +{ + ESourceCamelClass *class; + GObjectClass *settings_class; + GParamSpec **properties; + guint ii, n_properties; + guint prop_id = 1; + GTypeInfo type_info; + GType parent_type; + GType type; + const gchar *type_name; + const gchar *extension_name; + + g_return_val_if_fail (protocol != NULL, G_TYPE_INVALID); + + type_name = e_source_camel_get_type_name (protocol); + extension_name = e_source_camel_get_extension_name (protocol); + + /* Check if the type name is already registered. */ + type = g_type_from_name (type_name); + if (type != G_TYPE_INVALID) + return type; + + /* The settings type must be derived from CAMEL_TYPE_SETTINGS. */ + if (!g_type_is_a (settings_type, CAMEL_TYPE_SETTINGS)) { + g_warning ( + "%s: Invalid settings type '%s' for protocol '%s'", + G_STRFUNC, g_type_name (settings_type), protocol); + return G_TYPE_INVALID; + } + + memset (&type_info, 0, sizeof (GTypeInfo)); + type_info.class_size = sizeof (ESourceCamelClass); + type_info.class_init = subclass_class_init; + type_info.instance_size = sizeof (ESourceCamel); + type_info.instance_init = subclass_instance_init; + + parent_type = E_TYPE_SOURCE_CAMEL; + type = g_type_register_static (parent_type, type_name, &type_info, 0); + + /* Since we have first access to the newly registered GType, and + * because initializing its class structure requires some of the + * arguments we were passed, we'll complete class initialization + * here rather than trying to do it all in subclass_init(). */ + + class = g_type_class_ref (type); + settings_class = g_type_class_ref (settings_type); + + /* Initialize more class members. */ + class->settings_type = G_OBJECT_CLASS_TYPE (settings_class); + class->parent_class.name = g_intern_string (extension_name); + + /* For each property in the CamelSettings class, register + * an equivalent GObject property in this class and add an + * E_SOURCE_PARAM_SETTING flag so the value gets written to + * the ESource's key file. */ + + properties = g_object_class_list_properties ( + settings_class, &n_properties); + + for (ii = 0; ii < n_properties; ii++) { + GParamSpec *pspec; + + /* Some properties in CamelSettings may be covered + * by other ESourceExtensions. Skip them here. */ + if (subclass_get_binding_index (properties[ii]) >= 0) + continue; + + pspec = param_spec_clone (properties[ii]); + pspec->flags |= E_SOURCE_PARAM_SETTING; + + /* Clear the G_PARAM_CONSTRUCT flag. We apply default + * property values to our GValue array during instance + * initialization. */ + pspec->flags &= ~G_PARAM_CONSTRUCT; + + g_object_class_install_property ( + G_OBJECT_CLASS (class), prop_id++, pspec); + } + + g_free (properties); + + g_type_class_unref (class); + g_type_class_unref (settings_class); + + return type; +} + +/** + * e_source_camel_get_settings: + * @extension: an #ESourceCamel + * + * Returns @extension's #ESourceCamel:settings instance, pre-configured + * from the #ESource to which @extension belongs. Changes to the #ESource + * will automatically propagate to the #ESourceCamel:settings instance and + * vice versa. + * + * This is essentially the glue that binds #ESource to #CamelService. + * See e_source_camel_configure_service(). + * + * Returns: a configured #CamelSettings instance + * + * Since: 3.6 + **/ +CamelSettings * +e_source_camel_get_settings (ESourceCamel *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_CAMEL (extension), NULL); + + return extension->priv->settings; +} + +/** + * e_source_camel_get_type_name: + * @protocol: a #CamelProvider protocol + * + * Returns the #GType name of the registered #ESourceCamel subtype for + * @protocol. + * + * For example, given a protocol named "imap" the function would return + * "ESourceCamelImap". + * + * Returns: the #ESourceCamel type name for @protocol + * + * Since: 3.6 + **/ +const gchar * +e_source_camel_get_type_name (const gchar *protocol) +{ + gchar *buffer; + + g_return_val_if_fail (protocol != NULL, NULL); + + buffer = g_alloca (strlen (protocol) + 16); + g_sprintf (buffer, "ESourceCamel%s", protocol); + buffer[12] = g_ascii_toupper (buffer[12]); + + return g_intern_string (buffer); +} + +/** + * e_source_camel_get_extension_name: + * @protocol: a #CamelProvider protocol + * + * Returns the extension name for the #ESourceCamel subtype for @protocol. + * The extension name can then be passed to e_source_get_extension() to + * obtain an instance of the #ESourceCamel subtype. + * + * For example, given a protocol named "imap" the function would return + * "Imap Backend". + * + * Returns: the #ESourceCamel extension name for @protocol + * + * Since: 3.6 + **/ +const gchar * +e_source_camel_get_extension_name (const gchar *protocol) +{ + gchar *buffer; + + g_return_val_if_fail (protocol != NULL, NULL); + + /* Use the term "backend" for consistency with other + * calendar and address book backend extension names. */ + buffer = g_alloca (strlen (protocol) + 16); + g_sprintf (buffer, "%s Backend", protocol); + buffer[0] = g_ascii_toupper (buffer[0]); + + return g_intern_string (buffer); +} + +/** + * e_source_camel_configure_service: + * @source: an #ESource + * @service: a #CamelService + * + * This function essentially glues together @source and @serivce so their + * configuration settings stay synchronized. The glue itself is a shared + * #CamelSettings instance. + * + * Call this function immediately after creating a new #CamelService with + * camel_session_add_service(). + * + * Since: 3.6 + **/ +void +e_source_camel_configure_service (ESource *source, + CamelService *service) +{ + ESourceCamel *extension; + CamelProvider *provider; + CamelSettings *settings; + const gchar *extension_name; + + g_return_if_fail (E_IS_SOURCE (source)); + g_return_if_fail (CAMEL_IS_SERVICE (service)); + + provider = camel_service_get_provider (service); + g_return_if_fail (provider != NULL); + + extension_name = + e_source_camel_get_extension_name (provider->protocol); + extension = e_source_get_extension (source, extension_name); + + settings = e_source_camel_get_settings (extension); + camel_service_set_settings (service, settings); +} + diff --git a/libedataserver/e-source-camel.h b/libedataserver/e-source-camel.h new file mode 100644 index 0000000..703955e --- /dev/null +++ b/libedataserver/e-source-camel.h @@ -0,0 +1,84 @@ +/* + * e-source-camel.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_CAMEL_H +#define E_SOURCE_CAMEL_H + +#include +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_CAMEL \ + (e_source_camel_get_type ()) +#define E_SOURCE_CAMEL(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_CAMEL, ESourceCamel)) +#define E_SOURCE_CAMEL_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_CAMEL, ESourceCamelClass)) +#define E_IS_SOURCE_CAMEL(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_CAMEL)) +#define E_IS_SOURCE_CAMEL_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_CAMEL)) +#define E_SOURCE_CAMEL_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_CAMEL, ESourceCamelClass)) + +G_BEGIN_DECLS + +typedef struct _ESourceCamel ESourceCamel; +typedef struct _ESourceCamelClass ESourceCamelClass; +typedef struct _ESourceCamelPrivate ESourceCamelPrivate; + +/** + * ESourceCamel: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceCamel { + ESourceExtension parent; + ESourceCamelPrivate *priv; +}; + +struct _ESourceCamelClass { + ESourceExtensionClass parent_class; + + /* Same idea as in CamelServiceClass. */ + GType settings_type; +}; + +GType e_source_camel_get_type (void) G_GNUC_CONST; +void e_source_camel_register_types (void); +GType e_source_camel_generate_subtype (const gchar *protocol, + GType settings_type); +CamelSettings * e_source_camel_get_settings (ESourceCamel *extension); +const gchar * e_source_camel_get_type_name (const gchar *protocol); +const gchar * e_source_camel_get_extension_name + (const gchar *protocol); +void e_source_camel_configure_service + (ESource *source, + CamelService *service); + +G_END_DECLS + +#endif /* E_SOURCE_CAMEL_H */ diff --git a/libedataserver/e-source-collection.c b/libedataserver/e-source-collection.c new file mode 100644 index 0000000..4a80c4a --- /dev/null +++ b/libedataserver/e-source-collection.c @@ -0,0 +1,464 @@ +/* + * e-source-collection.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-collection + * @include: libedataserver/e-source-collection.h + * @short_description: #ESource extension for grouping related resources + * + * The #ESourceCollection extension identifies the #ESource as the root + * of a data source collection. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceCollection *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION); + * ]| + **/ + +#include "e-source-collection.h" + +#include + +#define E_SOURCE_COLLECTION_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_COLLECTION, ESourceCollectionPrivate)) + +struct _ESourceCollectionPrivate { + GMutex *property_lock; + gchar *identity; + gboolean calendar_enabled; + gboolean contacts_enabled; + gboolean mail_enabled; +}; + +enum { + PROP_0, + PROP_CALENDAR_ENABLED, + PROP_CONTACTS_ENABLED, + PROP_IDENTITY, + PROP_MAIL_ENABLED +}; + +G_DEFINE_TYPE ( + ESourceCollection, + e_source_collection, + E_TYPE_SOURCE_BACKEND) + +static void +source_collection_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_CALENDAR_ENABLED: + e_source_collection_set_calendar_enabled ( + E_SOURCE_COLLECTION (object), + g_value_get_boolean (value)); + return; + + case PROP_CONTACTS_ENABLED: + e_source_collection_set_contacts_enabled ( + E_SOURCE_COLLECTION (object), + g_value_get_boolean (value)); + return; + + case PROP_IDENTITY: + e_source_collection_set_identity ( + E_SOURCE_COLLECTION (object), + g_value_get_string (value)); + return; + + case PROP_MAIL_ENABLED: + e_source_collection_set_mail_enabled ( + E_SOURCE_COLLECTION (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_collection_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_CALENDAR_ENABLED: + g_value_set_boolean ( + value, + e_source_collection_get_calendar_enabled ( + E_SOURCE_COLLECTION (object))); + return; + + case PROP_CONTACTS_ENABLED: + g_value_set_boolean ( + value, + e_source_collection_get_contacts_enabled ( + E_SOURCE_COLLECTION (object))); + return; + + case PROP_IDENTITY: + g_value_take_string ( + value, + e_source_collection_dup_identity ( + E_SOURCE_COLLECTION (object))); + return; + + case PROP_MAIL_ENABLED: + g_value_set_boolean ( + value, + e_source_collection_get_mail_enabled ( + E_SOURCE_COLLECTION (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_collection_finalize (GObject *object) +{ + ESourceCollectionPrivate *priv; + + priv = E_SOURCE_COLLECTION_GET_PRIVATE (object); + + g_mutex_free (priv->property_lock); + + g_free (priv->identity); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_collection_parent_class)->finalize (object); +} + +static void +e_source_collection_class_init (ESourceCollectionClass *class) +{ + GObjectClass *object_class; + ESourceExtensionClass *extension_class; + + g_type_class_add_private (class, sizeof (ESourceCollectionPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_collection_set_property; + object_class->get_property = source_collection_get_property; + object_class->finalize = source_collection_finalize; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_COLLECTION; + + g_object_class_install_property ( + object_class, + PROP_CALENDAR_ENABLED, + g_param_spec_boolean ( + "calendar-enabled", + "Calendar Enabled", + "Whether calendar resources are enabled", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_CONTACTS_ENABLED, + g_param_spec_boolean ( + "contacts-enabled", + "Contacts Enabled", + "Whether contact resources are enabled", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_IDENTITY, + g_param_spec_string ( + "identity", + "Identity", + "Uniquely identifies the account " + "at the service provider", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_MAIL_ENABLED, + g_param_spec_boolean ( + "mail-enabled", + "Mail Enabled", + "Whether mail resources are enabled", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); +} + +static void +e_source_collection_init (ESourceCollection *extension) +{ + extension->priv = E_SOURCE_COLLECTION_GET_PRIVATE (extension); + extension->priv->property_lock = g_mutex_new (); +} + +/** + * e_source_collection_get_identity: + * @extension: an #ESourceCollection + * + * Returns the string used to uniquely identify the user account at + * the service provider. Often this is an email address or user name. + * + * Returns: the collection identity + * + * Since: 3.6 + **/ +const gchar * +e_source_collection_get_identity (ESourceCollection *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_COLLECTION (extension), NULL); + + return extension->priv->identity; +} + +/** + * e_source_collection_dup_identity: + * @extension: an #ESourceCollection + * + * Thread-safe variation of e_source_collection_get_identity(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceCollection:identity + * + * Since: 3.6 + **/ +gchar * +e_source_collection_dup_identity (ESourceCollection *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_COLLECTION (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_collection_get_identity (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_collection_set_identity: + * @extension: an #ESourceCollection + * @identity: (allow-none): the collection identity, or %NULL + * + * Sets the string used to uniquely identify the user account at the + * service provider. Often this is an email address or user name. + * + * The internal copy of @identity is automatically stripped of leading + * and trailing whitespace. If the resulting string is empty, %NULL is + * set instead. + * + * Since: 3.6 + **/ +void +e_source_collection_set_identity (ESourceCollection *extension, + const gchar *identity) +{ + g_return_if_fail (E_IS_SOURCE_COLLECTION (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->identity); + extension->priv->identity = e_util_strdup_strip (identity); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "identity"); +} + +/** + * e_source_collection_get_calendar_enabled: + * @extension: an #ESourceCollection + * + * Returns whether calendar sources within the collection should be + * enabled. + * + * An #ECollectionBackend running within the registry D-Bus service will + * automatically synchronize any calendar sources it maintains with the + * #ESourceCollection:calendar-enabled property. + * + * Returns: whether calendar sources should be enabled + * + * Since: 3.6 + **/ +gboolean +e_source_collection_get_calendar_enabled (ESourceCollection *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_COLLECTION (extension), FALSE); + + return extension->priv->calendar_enabled; +} + +/** + * e_source_collection_set_calendar_enabled: + * @extension: an #ESourceCollection + * @calendar_enabled: whether calendar sources should be enabled + * + * Sets whether calendar sources within the collection should be enabled. + * + * An #ECollectionBackend running within the registry D-Bus service will + * automatically synchronize any calendar sources it maintains with the + * #ESourceCollection:calendar-enabled property. + * + * Calling this function from a registry service client has no effect until + * the change is submitted to the registry service through e_source_write(), + * but there should rarely be any need for clients to call this. + * + * Since: 3.6 + **/ +void +e_source_collection_set_calendar_enabled (ESourceCollection *extension, + gboolean calendar_enabled) +{ + g_return_if_fail (E_IS_SOURCE_COLLECTION (extension)); + + extension->priv->calendar_enabled = calendar_enabled; + + g_object_notify (G_OBJECT (extension), "calendar-enabled"); +} + +/** + * e_source_collection_get_contacts_enabled: + * @extension: an #ESourceCollection + * + * Returns whether address book sources within the collection should be + * enabled. + * + * An #ECollectionBackend running within the registry D-Bus service will + * automatically synchronize any address book sources it maintains with + * the #ESourceCollection:contacts-enabled property. + * + * Returns: whether address book sources should be enabled + * + * Since: 3.6 + **/ +gboolean +e_source_collection_get_contacts_enabled (ESourceCollection *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_COLLECTION (extension), FALSE); + + return extension->priv->contacts_enabled; +} + +/** + * e_source_collection_set_contacts_enabled: + * @extension: an #ESourceCollection + * @contacts_enabled: whether address book sources should be enabled + * + * Sets whether address book sources within the collection should be enabled. + * + * An #ECollectionBackend running within the registry D-Bus service will + * automatically synchronize any address book sources it maintains with + * the #ESourceCollection:contacts-enabled property. + * + * Calling this function from a registry service client has no effect until + * the change is submitted to the registry service through e_source_write(), + * but there should rarely be any need for clients to call this. + * + * Since: 3.6 + **/ +void +e_source_collection_set_contacts_enabled (ESourceCollection *extension, + gboolean contacts_enabled) +{ + g_return_if_fail (E_IS_SOURCE_COLLECTION (extension)); + + extension->priv->contacts_enabled = contacts_enabled; + + g_object_notify (G_OBJECT (extension), "contacts-enabled"); +} + +/** + * e_source_collection_get_mail_enabled: + * @extension: an #ESourceCollection + * + * Returns whether mail sources within the collection should be enabled. + * + * An #ECollectionBackend running within the registry D-Bus service will + * automatically synchronize any mail sources it maintains with the + * #ESourceCollection:mail-enabled property. + * + * Returns: whether mail sources should be enabled + * + * Since: 3.6 + **/ +gboolean +e_source_collection_get_mail_enabled (ESourceCollection *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_COLLECTION (extension), FALSE); + + return extension->priv->mail_enabled; +} + +/** + * e_source_collection_set_mail_enabled: + * @extension: an #ESourceCollection + * @mail_enabled: whether mail sources should be enabled + * + * Sets whether mail sources within the collection should be enabled. + * + * An #ECollectionBackend running within the registry D-Bus service will + * automatically synchronize any mail sources it maintains with the + * #ESourceCollection:mail-enabled property. + * + * Calling this function from a registry service client has no effect until + * the changes is submitted to the registry service through e_source_write(), + * but there should rarely be any need for clients to call this. + * + * Since: 3.6 + **/ +void +e_source_collection_set_mail_enabled (ESourceCollection *extension, + gboolean mail_enabled) +{ + g_return_if_fail (E_IS_SOURCE_COLLECTION (extension)); + + extension->priv->mail_enabled = mail_enabled; + + g_object_notify (G_OBJECT (extension), "mail-enabled"); +} + diff --git a/libedataserver/e-source-collection.h b/libedataserver/e-source-collection.h new file mode 100644 index 0000000..b7e95be --- /dev/null +++ b/libedataserver/e-source-collection.h @@ -0,0 +1,103 @@ +/* + * e-source-collection.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_COLLECTION_H +#define E_SOURCE_COLLECTION_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_COLLECTION \ + (e_source_collection_get_type ()) +#define E_SOURCE_COLLECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_COLLECTION, ESourceCollection)) +#define E_SOURCE_COLLECTION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_COLLECTION, ESourceCollectionClass)) +#define E_IS_SOURCE_COLLECTION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_COLLECTION)) +#define E_IS_SOURCE_COLLECTION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_COLLECTION)) +#define E_SOURCE_COLLECTION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_COLLECTION, ESourceCollectionClass)) + +/** + * E_SOURCE_EXTENSION_COLLECTION: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceCollection. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_COLLECTION "Collection" + +G_BEGIN_DECLS + +typedef struct _ESourceCollection ESourceCollection; +typedef struct _ESourceCollectionClass ESourceCollectionClass; +typedef struct _ESourceCollectionPrivate ESourceCollectionPrivate; + +/** + * ESourceCollection: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceCollection { + ESourceBackend parent; + ESourceCollectionPrivate *priv; +}; + +struct _ESourceCollectionClass { + ESourceBackendClass parent_class; +}; + +GType e_source_collection_get_type (void) G_GNUC_CONST; +const gchar * e_source_collection_get_identity + (ESourceCollection *extension); +gchar * e_source_collection_dup_identity + (ESourceCollection *extension); +void e_source_collection_set_identity + (ESourceCollection *extension, + const gchar *identity); +gboolean e_source_collection_get_calendar_enabled + (ESourceCollection *extension); +void e_source_collection_set_calendar_enabled + (ESourceCollection *extension, + gboolean calendar_enabled); +gboolean e_source_collection_get_contacts_enabled + (ESourceCollection *extension); +void e_source_collection_set_contacts_enabled + (ESourceCollection *extension, + gboolean contacts_enabled); +gboolean e_source_collection_get_mail_enabled + (ESourceCollection *extension); +void e_source_collection_set_mail_enabled + (ESourceCollection *extension, + gboolean mail_enabled); + +G_END_DECLS + +#endif /* E_SOURCE_COLLECTION_H */ + diff --git a/libedataserver/e-source-enums.h b/libedataserver/e-source-enums.h new file mode 100644 index 0000000..d4c7214 --- /dev/null +++ b/libedataserver/e-source-enums.h @@ -0,0 +1,62 @@ +/* + * e-source-enums.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_ENUMS_H +#define E_SOURCE_ENUMS_H + +/** + * EMdnResponsePolicy: + * @E_MDN_RESPONSE_POLICY_NEVER: + * Never respond to an MDN request. + * @E_MDN_RESPONSE_POLICY_ALWAYS: + * Always respond to an MDN request. + * @E_MDN_RESPONSE_POLICY_ASK: + * Ask the user before responding to an MDN request. + * + * Policy for responding to Message Disposition Notification requests + * (i.e. a Disposition-Notification-To header) when receiving messages. + * See RFC 2298 for more information about MDN requests. + * + * Since: 3.6 + **/ +typedef enum { + E_MDN_RESPONSE_POLICY_NEVER, + E_MDN_RESPONSE_POLICY_ALWAYS, + E_MDN_RESPONSE_POLICY_ASK +} EMdnResponsePolicy; + +/** + * ESourceAuthenticationResult: + * @E_SOURCE_AUTHENTICATION_ERROR: + * An error occurred while authenticating. + * @E_SOURCE_AUTHENTICATION_ACCEPTED: + * Server requesting authentication accepted password. + * @E_SOURCE_AUTHENTICATION_REJECTED: + * Server requesting authentication rejected password. + * + * Status codes used by the #ESourceAuthenticator interface. + * + * Since: 3.6 + **/ +typedef enum { + E_SOURCE_AUTHENTICATION_ERROR, + E_SOURCE_AUTHENTICATION_ACCEPTED, + E_SOURCE_AUTHENTICATION_REJECTED +} ESourceAuthenticationResult; + +#endif /* E_SOURCE_ENUMS_H */ diff --git a/libedataserver/e-source-extension.c b/libedataserver/e-source-extension.c new file mode 100644 index 0000000..07024cf --- /dev/null +++ b/libedataserver/e-source-extension.c @@ -0,0 +1,190 @@ +/* + * e-source-extension.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-extension + * @include: libedataserver/e-source-extension.h + * @short_description: Base class for #ESource extensions + * + * #ESourceExtension is an abstract base class for #ESource extension + * objects. An #ESourceExtension object basically just maps the keys in + * a key file group to a set of #GObject properties. The name of the key + * file group doubles as the name of the #ESourceExtension object. + * + * #ESourceExtension objects are accessed through e_source_get_extension(). + **/ + +#include "e-source-extension.h" + +#define E_SOURCE_EXTENSION_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_EXTENSION, ESourceExtensionPrivate)) + +struct _ESourceExtensionPrivate { + gpointer source; /* weak pointer */ +}; + +enum { + PROP_0, + PROP_SOURCE +}; + +G_DEFINE_ABSTRACT_TYPE ( + ESourceExtension, + e_source_extension, + G_TYPE_OBJECT) + +static void +source_extension_set_source (ESourceExtension *extension, + ESource *source) +{ + g_return_if_fail (E_IS_SOURCE (source)); + g_return_if_fail (extension->priv->source == NULL); + + extension->priv->source = source; + + g_object_add_weak_pointer ( + G_OBJECT (source), &extension->priv->source); +} + +static void +source_extension_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SOURCE: + source_extension_set_source ( + E_SOURCE_EXTENSION (object), + g_value_get_object (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_extension_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SOURCE: + g_value_set_object ( + value, e_source_extension_get_source ( + E_SOURCE_EXTENSION (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_extension_dispose (GObject *object) +{ + ESourceExtensionPrivate *priv; + + priv = E_SOURCE_EXTENSION_GET_PRIVATE (object); + + if (priv->source != NULL) { + g_object_remove_weak_pointer ( + G_OBJECT (priv->source), &priv->source); + priv->source = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_source_extension_parent_class)->dispose (object); +} + +static void +source_extension_notify (GObject *object, + GParamSpec *pspec) +{ + ESource *source; + ESourceExtension *extension; + + extension = E_SOURCE_EXTENSION (object); + source = e_source_extension_get_source (extension); + g_return_if_fail (source != NULL); + + if ((pspec->flags & E_SOURCE_PARAM_SETTING) != 0) + e_source_changed (source); +} + +static void +source_extension_constructed (GObject *object) +{ + /* This allows subclasses to chain up safely since GObject + * does not implement this method, and we might want to do + * something here in the future. */ +} + +static void +e_source_extension_class_init (ESourceExtensionClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (ESourceExtensionPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_extension_set_property; + object_class->get_property = source_extension_get_property; + object_class->dispose = source_extension_dispose; + object_class->notify = source_extension_notify; + object_class->constructed = source_extension_constructed; + + g_object_class_install_property ( + object_class, + PROP_SOURCE, + g_param_spec_object ( + "source", + "Source", + "The ESource being extended", + E_TYPE_SOURCE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} + +static void +e_source_extension_init (ESourceExtension *extension) +{ + extension->priv = E_SOURCE_EXTENSION_GET_PRIVATE (extension); +} + +/** + * e_source_extension_get_source: + * @extension: an #ESourceExtension + * + * Returns the #ESource instance to which @extension belongs. + * + * Returns: the #ESource instance + **/ +ESource * +e_source_extension_get_source (ESourceExtension *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_EXTENSION (extension), NULL); + + /* If the ESource was finalized and our weak pointer set this + * to NULL, then the type cast macro will fail and we'll get a + * runtime warning about it, which is what we want. */ + return E_SOURCE (extension->priv->source); +} + diff --git a/libedataserver/e-source-extension.h b/libedataserver/e-source-extension.h new file mode 100644 index 0000000..4095696 --- /dev/null +++ b/libedataserver/e-source-extension.h @@ -0,0 +1,73 @@ +/* + * e-source-extension.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_EXTENSION_H +#define E_SOURCE_EXTENSION_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_EXTENSION \ + (e_source_extension_get_type ()) +#define E_SOURCE_EXTENSION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_EXTENSION, ESourceExtension)) +#define E_SOURCE_EXTENSION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_EXTENSION, ESourceExtensionClass)) +#define E_IS_SOURCE_EXTENSION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_EXTENSION)) +#define E_IS_SOURCE_EXTENSION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_EXTENSION)) +#define E_SOURCE_EXTENSION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_EXTENSION, ESourceExtensionClass)) + +G_BEGIN_DECLS + +typedef struct _ESourceExtension ESourceExtension; +typedef struct _ESourceExtensionClass ESourceExtensionClass; +typedef struct _ESourceExtensionPrivate ESourceExtensionPrivate; + +/** + * ESourceExtension: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceExtension { + GObject parent; + ESourceExtensionPrivate *priv; +}; + +struct _ESourceExtensionClass { + GObjectClass parent_class; + + const gchar *name; +}; + +GType e_source_extension_get_type (void) G_GNUC_CONST; +ESource * e_source_extension_get_source (ESourceExtension *extension); + +G_END_DECLS + +#endif /* E_SOURCE_EXTENSION_H */ diff --git a/libedataserver/e-source-goa.c b/libedataserver/e-source-goa.c new file mode 100644 index 0000000..3fd17b9 --- /dev/null +++ b/libedataserver/e-source-goa.c @@ -0,0 +1,228 @@ +/* + * e-source-goa.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-goa + * @include: libedataserver/e-source-goa.h + * @short_description: #ESource extension for GNOME Online Accounts + * + * The #ESourceGoa extension associates an #ESource with a #GoaAccount. + * This extension is usually found in a top-level #ESource, with various + * mail, calendar and address book data sources as children. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceGoa *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_GOA); + * ]| + **/ + +#include "e-source-goa.h" + +#include + +#define E_SOURCE_GOA_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_GOA, ESourceGoaPrivate)) + +struct _ESourceGoaPrivate { + GMutex *property_lock; + gchar *account_id; +}; + +enum { + PROP_0, + PROP_ACCOUNT_ID +}; + +G_DEFINE_TYPE ( + ESourceGoa, + e_source_goa, + E_TYPE_SOURCE_EXTENSION) + +static void +source_goa_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ACCOUNT_ID: + e_source_goa_set_account_id ( + E_SOURCE_GOA (object), + g_value_get_string (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_goa_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ACCOUNT_ID: + g_value_take_string ( + value, + e_source_goa_dup_account_id ( + E_SOURCE_GOA (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_goa_finalize (GObject *object) +{ + ESourceGoaPrivate *priv; + + priv = E_SOURCE_GOA_GET_PRIVATE (object); + + g_mutex_free (priv->property_lock); + + g_free (priv->account_id); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_goa_parent_class)->finalize (object); +} + +static void +e_source_goa_class_init (ESourceGoaClass *class) +{ + GObjectClass *object_class; + ESourceExtensionClass *extension_class; + + g_type_class_add_private (class, sizeof (ESourceGoaPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_goa_set_property; + object_class->get_property = source_goa_get_property; + object_class->finalize = source_goa_finalize; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_GOA; + + g_object_class_install_property ( + object_class, + PROP_ACCOUNT_ID, + g_param_spec_string ( + "account-id", + "Account ID", + "GNOME Online Account ID", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); +} + +static void +e_source_goa_init (ESourceGoa *extension) +{ + extension->priv = E_SOURCE_GOA_GET_PRIVATE (extension); + extension->priv->property_lock = g_mutex_new (); +} + +/** + * e_source_goa_get_account_id: + * @extension: an #ESourceGoa + * + * Returns the identifier string of the GNOME Online Account associated + * with the #ESource to which @extension belongs. + * + * Returns: the associated GNOME Online Account ID + * + * Since: 3.6 + **/ +const gchar * +e_source_goa_get_account_id (ESourceGoa *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_GOA (extension), NULL); + + return extension->priv->account_id; +} + +/** + * e_source_goa_dup_account_id: + * @extension: an #ESourceGoa + * + * Thread-safe variation of e_source_goa_get_account_id(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceGoa:account-id + * + * Since: 3.6 + **/ +gchar * +e_source_goa_dup_account_id (ESourceGoa *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_GOA (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_goa_get_account_id (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_goa_set_account_id: + * @extension: an #ESourceGoa + * @account_id: (allow-none): the associated GNOME Online Account ID, or %NULL + * + * Sets the identifier string of the GNOME Online Account associated + * with the #ESource to which @extension belongs. + * + * The internal copy of @account_id is automatically stripped of leading + * and trailing whitespace. If the resulting string is empty, %NULL is set + * instead. + * + * Since: 3.6 + **/ +void +e_source_goa_set_account_id (ESourceGoa *extension, + const gchar *account_id) +{ + g_return_if_fail (E_IS_SOURCE_GOA (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->account_id); + extension->priv->account_id = e_util_strdup_strip (account_id); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "account-id"); +} + diff --git a/libedataserver/e-source-goa.h b/libedataserver/e-source-goa.h new file mode 100644 index 0000000..ed68d9f --- /dev/null +++ b/libedataserver/e-source-goa.h @@ -0,0 +1,85 @@ +/* + * e-source-goa.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_GOA_H +#define E_SOURCE_GOA_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_GOA \ + (e_source_goa_get_type ()) +#define E_SOURCE_GOA(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_GOA, ESourceGoa)) +#define E_SOURCE_GOA_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_GOA, ESourceGoaClass)) +#define E_IS_SOURCE_GOA(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_GOA)) +#define E_IS_SOURCE_GOA_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_GOA)) +#define E_SOURCE_GOA_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_GOA, ESourceGoaClass)) + +/** + * E_SOURCE_EXTENSION_GOA: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceGoa. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_GOA "GNOME Online Accounts" + +G_BEGIN_DECLS + +typedef struct _ESourceGoa ESourceGoa; +typedef struct _ESourceGoaClass ESourceGoaClass; +typedef struct _ESourceGoaPrivate ESourceGoaPrivate; + +/** + * ESourceGoa: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceGoa { + ESourceExtension parent; + ESourceGoaPrivate *priv; +}; + +struct _ESourceGoaClass { + ESourceExtensionClass parent_class; +}; + +GType e_source_goa_get_type (void) G_GNUC_CONST; +const gchar * e_source_goa_get_account_id (ESourceGoa *extension); +gchar * e_source_goa_dup_account_id (ESourceGoa *extension); +void e_source_goa_set_account_id (ESourceGoa *extension, + const gchar *account_id); + +G_END_DECLS + +#endif /* E_SOURCE_GOA_H */ + diff --git a/libedataserver/e-source-mail-account.c b/libedataserver/e-source-mail-account.c new file mode 100644 index 0000000..65c2698 --- /dev/null +++ b/libedataserver/e-source-mail-account.c @@ -0,0 +1,225 @@ +/* + * e-source-mail-account.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-mail-account + * @include: libedataserver/e-source-mail-account.h + * @short_description: #ESource extension for an email account + * + * The #ESourceMailAccount extension identifies the #ESource as a + * mail account and also links to a default "mail identity" to use. + * See #ESourceMailIdentity for more information about identities. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceMailAccount *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT); + * ]| + **/ + +#include "e-source-mail-account.h" + +#include +#include + +#define E_SOURCE_MAIL_ACCOUNT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_MAIL_ACCOUNT, ESourceMailAccountPrivate)) + +struct _ESourceMailAccountPrivate { + GMutex *property_lock; + gchar *identity_uid; +}; + +enum { + PROP_0, + PROP_IDENTITY_UID +}; + +G_DEFINE_TYPE ( + ESourceMailAccount, + e_source_mail_account, + E_TYPE_SOURCE_BACKEND) + +static void +source_mail_account_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_IDENTITY_UID: + e_source_mail_account_set_identity_uid ( + E_SOURCE_MAIL_ACCOUNT (object), + g_value_get_string (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_mail_account_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_IDENTITY_UID: + g_value_take_string ( + value, + e_source_mail_account_dup_identity_uid ( + E_SOURCE_MAIL_ACCOUNT (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_mail_account_finalize (GObject *object) +{ + ESourceMailAccountPrivate *priv; + + priv = E_SOURCE_MAIL_ACCOUNT_GET_PRIVATE (object); + + g_mutex_free (priv->property_lock); + + g_free (priv->identity_uid); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_mail_account_parent_class)->finalize (object); +} + +static void +e_source_mail_account_class_init (ESourceMailAccountClass *class) +{ + GObjectClass *object_class; + ESourceExtensionClass *extension_class; + + g_type_class_add_private (class, sizeof (ESourceMailAccountPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_mail_account_set_property; + object_class->get_property = source_mail_account_get_property; + object_class->finalize = source_mail_account_finalize; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_MAIL_ACCOUNT; + + g_object_class_install_property ( + object_class, + PROP_IDENTITY_UID, + g_param_spec_string ( + "identity-uid", + "Identity UID", + "ESource UID of a Mail Identity", + "self", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); +} + +static void +e_source_mail_account_init (ESourceMailAccount *extension) +{ + extension->priv = E_SOURCE_MAIL_ACCOUNT_GET_PRIVATE (extension); + extension->priv->property_lock = g_mutex_new (); +} + +/** + * e_source_mail_account_get_identity_uid: + * @extension: an #ESourceMailAccount + * + * Returns the #ESource:uid of the #ESource that describes the mail + * identity to be used for this account. + * + * Returns: the mail identity #ESource:uid + * + * Since: 3.6 + **/ +const gchar * +e_source_mail_account_get_identity_uid (ESourceMailAccount *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_MAIL_ACCOUNT (extension), NULL); + + return extension->priv->identity_uid; +} + +/** + * e_source_mail_account_dup_identity_uid: + * @extension: an #ESourceMailAccount + * + * Thread-safe variation of e_source_mail_account_get_identity_uid(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceMailAccount:identity-uid + * + * Since: 3.6 + **/ +gchar * +e_source_mail_account_dup_identity_uid (ESourceMailAccount *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_MAIL_ACCOUNT (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_mail_account_get_identity_uid (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_mail_account_set_identity_uid: + * @extension: an #ESourceMailAccount + * @identity_uid: (allow-none): the mail identity #ESource:uid, or %NULL + * + * Sets the #ESource:uid of the #ESource that describes the mail + * identity to be used for this account. + * + * Since: 3.6 + **/ +void +e_source_mail_account_set_identity_uid (ESourceMailAccount *extension, + const gchar *identity_uid) +{ + g_return_if_fail (E_IS_SOURCE_MAIL_ACCOUNT (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->identity_uid); + extension->priv->identity_uid = g_strdup (identity_uid); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "identity-uid"); +} + diff --git a/libedataserver/e-source-mail-account.h b/libedataserver/e-source-mail-account.h new file mode 100644 index 0000000..c956ae3 --- /dev/null +++ b/libedataserver/e-source-mail-account.h @@ -0,0 +1,88 @@ +/* + * e-source-mail-account.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_MAIL_ACCOUNT_H +#define E_SOURCE_MAIL_ACCOUNT_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_MAIL_ACCOUNT \ + (e_source_mail_account_get_type ()) +#define E_SOURCE_MAIL_ACCOUNT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_MAIL_ACCOUNT, ESourceMailAccount)) +#define E_SOURCE_MAIL_ACCOUNT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_MAIL_ACCOUNT, ESourceMailAccountClass)) +#define E_IS_SOURCE_MAIL_ACCOUNT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_MAIL_ACCOUNT)) +#define E_IS_SOURCE_MAIL_ACCOUNT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_MAIL_ACCOUNT)) +#define E_SOURCE_MAIL_ACCOUNT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_MAIL_ACCOUNT, ESourceMailAccountClass)) + +/** + * E_SOURCE_EXTENSION_MAIL_ACCOUNT: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceMailAccount. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_MAIL_ACCOUNT "Mail Account" + +G_BEGIN_DECLS + +typedef struct _ESourceMailAccount ESourceMailAccount; +typedef struct _ESourceMailAccountClass ESourceMailAccountClass; +typedef struct _ESourceMailAccountPrivate ESourceMailAccountPrivate; + +/** + * ESourceMailAccount: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceMailAccount { + ESourceBackend parent; + ESourceMailAccountPrivate *priv; +}; + +struct _ESourceMailAccountClass { + ESourceBackendClass parent_class; +}; + +GType e_source_mail_account_get_type + (void) G_GNUC_CONST; +const gchar * e_source_mail_account_get_identity_uid + (ESourceMailAccount *extension); +gchar * e_source_mail_account_dup_identity_uid + (ESourceMailAccount *extension); +void e_source_mail_account_set_identity_uid + (ESourceMailAccount *extension, + const gchar *identity_uid); + +G_END_DECLS + +#endif /* E_SOURCE_MAIL_ACCOUNT_H */ diff --git a/libedataserver/e-source-mail-composition.c b/libedataserver/e-source-mail-composition.c new file mode 100644 index 0000000..db42b88 --- /dev/null +++ b/libedataserver/e-source-mail-composition.c @@ -0,0 +1,630 @@ +/* + * e-source-mail-composition.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-mail-composition + * @include: libedataserver/e-source-mail-composition.h + * @short_description: #ESource extension for mail composition settings + * + * The #ESourceMailComposition extension tracks settings to be applied + * when composing a new mail message. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceMailComposition *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_COMPOSITION); + * ]| + **/ + +#include "e-source-mail-composition.h" + +#include + +#define E_SOURCE_MAIL_COMPOSITION_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_MAIL_COMPOSITION, ESourceMailCompositionPrivate)) + +struct _ESourceMailCompositionPrivate { + GMutex *property_lock; + gchar **bcc; + gchar **cc; + gchar *drafts_folder; + gchar *templates_folder; + gboolean sign_imip; +}; + +enum { + PROP_0, + PROP_BCC, + PROP_CC, + PROP_DRAFTS_FOLDER, + PROP_SIGN_IMIP, + PROP_TEMPLATES_FOLDER +}; + +G_DEFINE_TYPE ( + ESourceMailComposition, + e_source_mail_composition, + E_TYPE_SOURCE_EXTENSION) + +static void +source_mail_composition_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_BCC: + e_source_mail_composition_set_bcc ( + E_SOURCE_MAIL_COMPOSITION (object), + g_value_get_boxed (value)); + return; + + case PROP_CC: + e_source_mail_composition_set_cc ( + E_SOURCE_MAIL_COMPOSITION (object), + g_value_get_boxed (value)); + return; + + case PROP_DRAFTS_FOLDER: + e_source_mail_composition_set_drafts_folder ( + E_SOURCE_MAIL_COMPOSITION (object), + g_value_get_string (value)); + return; + + case PROP_SIGN_IMIP: + e_source_mail_composition_set_sign_imip ( + E_SOURCE_MAIL_COMPOSITION (object), + g_value_get_boolean (value)); + return; + + case PROP_TEMPLATES_FOLDER: + e_source_mail_composition_set_templates_folder ( + E_SOURCE_MAIL_COMPOSITION (object), + g_value_get_string (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_mail_composition_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_BCC: + g_value_take_boxed ( + value, + e_source_mail_composition_dup_bcc ( + E_SOURCE_MAIL_COMPOSITION (object))); + return; + + case PROP_CC: + g_value_take_boxed ( + value, + e_source_mail_composition_dup_cc ( + E_SOURCE_MAIL_COMPOSITION (object))); + return; + + case PROP_DRAFTS_FOLDER: + g_value_take_string ( + value, + e_source_mail_composition_dup_drafts_folder ( + E_SOURCE_MAIL_COMPOSITION (object))); + return; + + case PROP_SIGN_IMIP: + g_value_set_boolean ( + value, + e_source_mail_composition_get_sign_imip ( + E_SOURCE_MAIL_COMPOSITION (object))); + return; + + case PROP_TEMPLATES_FOLDER: + g_value_take_string ( + value, + e_source_mail_composition_dup_templates_folder ( + E_SOURCE_MAIL_COMPOSITION (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_mail_composition_finalize (GObject *object) +{ + ESourceMailCompositionPrivate *priv; + + priv = E_SOURCE_MAIL_COMPOSITION_GET_PRIVATE (object); + + g_mutex_free (priv->property_lock); + + g_strfreev (priv->bcc); + g_strfreev (priv->cc); + g_free (priv->drafts_folder); + g_free (priv->templates_folder); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_mail_composition_parent_class)-> + finalize (object); +} + +static void +e_source_mail_composition_class_init (ESourceMailCompositionClass *class) +{ + GObjectClass *object_class; + ESourceExtensionClass *extension_class; + + g_type_class_add_private ( + class, sizeof (ESourceMailCompositionPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_mail_composition_set_property; + object_class->get_property = source_mail_composition_get_property; + object_class->finalize = source_mail_composition_finalize; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_MAIL_COMPOSITION; + + g_object_class_install_property ( + object_class, + PROP_BCC, + g_param_spec_boxed ( + "bcc", + "Bcc", + "Recipients to blind carbon-copy", + G_TYPE_STRV, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_CC, + g_param_spec_boxed ( + "cc", + "Cc", + "Recipients to carbon-copy", + G_TYPE_STRV, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_DRAFTS_FOLDER, + g_param_spec_string ( + "drafts-folder", + "Drafts Folder", + "Preferred folder for draft messages", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_SIGN_IMIP, + g_param_spec_boolean ( + "sign-imip", + "Sign iMIP", + "Include iMIP messages when signing", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_TEMPLATES_FOLDER, + g_param_spec_string ( + "templates-folder", + "Templates Folder", + "Preferred folder for message templates", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); +} + +static void +e_source_mail_composition_init (ESourceMailComposition *extension) +{ + extension->priv = E_SOURCE_MAIL_COMPOSITION_GET_PRIVATE (extension); + extension->priv->property_lock = g_mutex_new (); +} + +/** + * e_source_mail_composition_get_bcc: + * @extension: an #ESourceMailComposition + * + * Returns a %NULL-terminated string array of recipients which should + * automatically be added to the blind carbon-copy (Bcc) list when + * composing a new mail message. The recipient strings should be of + * the form "Full Name ". The returned array is owned + * by @extension and should not be modified or freed. + * + * Returns: a %NULL-terminated string array of Bcc recipients + * + * Since: 3.6 + **/ +const gchar * const * +e_source_mail_composition_get_bcc (ESourceMailComposition *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), NULL); + + return (const gchar * const *) extension->priv->bcc; +} + +/** + * e_source_mail_composition_dup_bcc: + * @extension: an #ESourceMailComposition + * + * Thread-safe variation of e_source_mail_composition_get_bcc(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string array should be freed with g_strfreev() when no + * longer needed. + * + * Returns: a newly-allocated copy of #ESourceMailComposition:bcc + * + * Since: 3.6 + **/ +gchar ** +e_source_mail_composition_dup_bcc (ESourceMailComposition *extension) +{ + const gchar * const *protected; + gchar **duplicate; + + g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_mail_composition_get_bcc (extension); + duplicate = g_strdupv ((gchar **) protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_mail_composition_set_bcc: + * @extension: an #ESource + * @bcc: (allow-none): a %NULL-terminated string array of Bcc recipients + * + * Sets the recipients which should automatically be added to the blind + * carbon-copy (Bcc) list when composing a new mail message. The recipient + * strings should be of the form "Full Name ". + * + * Since: 3.6 + **/ +void +e_source_mail_composition_set_bcc (ESourceMailComposition *extension, + const gchar * const *bcc) +{ + g_return_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_strfreev (extension->priv->bcc); + extension->priv->bcc = g_strdupv ((gchar **) bcc); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "bcc"); +} + +/** + * e_source_mail_composition_get_cc: + * @extension: an #ESourceMailComposition + * + * Returns a %NULL-terminated string array of recipients which should + * automatically be added to the carbon-copy (Cc) list when composing a + * new mail message. The recipient strings should be of the form "Full + * Name ". The returned array is owned by @extension and + * should not be modified or freed. + * + * Returns: a %NULL-terminated string array of Cc recipients + * + * Since: 3.6 + **/ +const gchar * const * +e_source_mail_composition_get_cc (ESourceMailComposition *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), NULL); + + return (const gchar * const *) extension->priv->cc; +} + +/** + * e_source_mail_composition_dup_cc: + * @extension: an #ESourceMailComposition + * + * Thread-safe variation of e_source_mail_composition_get_cc(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string array should be freed with g_strfreev() when no + * longer needed. + * + * Returns: a newly-allocated copy of #ESourceMailComposition:cc + * + * Since: 3.6 + **/ +gchar ** +e_source_mail_composition_dup_cc (ESourceMailComposition *extension) +{ + const gchar * const *protected; + gchar **duplicate; + + g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_mail_composition_get_cc (extension); + duplicate = g_strdupv ((gchar **) protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_mail_composition_set_cc: + * @extension: an #ESourceMailComposition + * @cc: (allow-none): a %NULL-terminated string array of Cc recipients + * + * Sets the recipients which should automatically be added to the carbon + * copy (Cc) list when composing a new mail message. The recipient strings + * should be of the form "Full Name ". + * + * Since: 3.6 + **/ +void +e_source_mail_composition_set_cc (ESourceMailComposition *extension, + const gchar * const *cc) +{ + g_return_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_strfreev (extension->priv->cc); + extension->priv->cc = g_strdupv ((gchar **) cc); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "cc"); +} + +/** + * e_source_mail_composition_get_drafts_folder: + * @extension: an #ESourceMailComposition + * + * Returns a string identifying the preferred folder for draft messages. + * The format of the identifier string is defined by the client application. + * + * Returns: an identifier for the preferred drafts folder + * + * Since: 3.6 + **/ +const gchar * +e_source_mail_composition_get_drafts_folder (ESourceMailComposition *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), NULL); + + return extension->priv->drafts_folder; +} + +/** + * e_source_mail_composition_dup_drafts_folder: + * @extension: an #ESourceMailComposition + * + * Thread-safe variation of e_source_mail_composition_get_drafts_folder(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceMailComposition:drafts-folder + * + * Since: 3.6 + **/ +gchar * +e_source_mail_composition_dup_drafts_folder (ESourceMailComposition *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_mail_composition_get_drafts_folder (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_mail_composition_set_drafts_folder: + * @extension: an #ESourceMailComposition + * @drafts_folder: (allow-none): an identifier for the preferred drafts + * folder, or %NULL + * + * Sets the preferred folder for draft messages by an identifier string. + * The format of the identifier string is defined by the client application. + * + * The internal copy of @drafts_folder is automatically stripped of + * leading and trailing whitespace. If the resulting string is empty, + * %NULL is set instead. + * + * Since: 3.6 + **/ +void +e_source_mail_composition_set_drafts_folder (ESourceMailComposition *extension, + const gchar *drafts_folder) +{ + g_return_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->drafts_folder); + extension->priv->drafts_folder = e_util_strdup_strip (drafts_folder); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "drafts-folder"); +} + +/** + * e_source_mail_composition_get_sign_imip: + * @extension: an #ESourceMailComposition + * + * Returns whether outgoing iMIP messages such as meeting requests should + * also be signed. This is primarily intended as a workaround for certain + * versions of Microsoft Outlook which can't handle signed iMIP messages. + * + * Returns: whether outgoing iMIP messages should be signed + * + * Since: 3.6 + **/ +gboolean +e_source_mail_composition_get_sign_imip (ESourceMailComposition *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), FALSE); + + return extension->priv->sign_imip; +} + +/** + * e_source_mail_composition_set_sign_imip: + * @extension: an #ESourceMailComposition + * @sign_imip: whether outgoing iMIP messages should be signed + * + * Sets whether outgoing iMIP messages such as meeting requests should + * also be signed. This is primarily intended as a workaround for certain + * versions of Microsoft Outlook which can't handle signed iMIP messages. + * + * Since: 3.6 + **/ +void +e_source_mail_composition_set_sign_imip (ESourceMailComposition *extension, + gboolean sign_imip) +{ + g_return_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension)); + + extension->priv->sign_imip = sign_imip; + + g_object_notify (G_OBJECT (extension), "sign-imip"); +} + +/** + * e_source_mail_composition_get_templates_folder: + * @extension: an #ESourceMailComposition + * + * Returns a string identifying the preferred folder for message templates. + * The format of the identifier string is defined by the client application. + * + * Returns: an identifier for the preferred templates folder + * + * Since: 3.6 + **/ +const gchar * +e_source_mail_composition_get_templates_folder (ESourceMailComposition *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), NULL); + + return extension->priv->templates_folder; +} + +/** + * e_source_mail_composition_dup_templates_folder: + * @extension: an #ESourceMailComposition + * + * Thread-safe variation of e_source_mail_composition_get_templates_folder(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceMailComposition:templates-folder + * + * Since: 3.6 + **/ +gchar * +e_source_mail_composition_dup_templates_folder (ESourceMailComposition *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_mail_composition_get_templates_folder (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_mail_composition_set_templates_folder: + * @extension: an #ESourceMailComposition + * @templates_folder: (allow-none): an identifier for the preferred templates + * folder, or %NULL + * + * Sets the preferred folder for message templates by an identifier string. + * The format of the identifier string is defined by the client application. + * + * The internal copy of @templates_folder is automatically stripped of + * leading and trailing whitespace. If the resulting string is empty, + * %NULL is set instead. + * + * Since: 3.6 + **/ +void +e_source_mail_composition_set_templates_folder (ESourceMailComposition *extension, + const gchar *templates_folder) +{ + g_return_if_fail (E_IS_SOURCE_MAIL_COMPOSITION (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->templates_folder); + extension->priv->templates_folder = e_util_strdup_strip (templates_folder); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "templates-folder"); +} + diff --git a/libedataserver/e-source-mail-composition.h b/libedataserver/e-source-mail-composition.h new file mode 100644 index 0000000..29c8ffd --- /dev/null +++ b/libedataserver/e-source-mail-composition.h @@ -0,0 +1,116 @@ +/* + * e-source-mail-composition.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_MAIL_COMPOSITION_H +#define E_SOURCE_MAIL_COMPOSITION_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_MAIL_COMPOSITION \ + (e_source_mail_composition_get_type ()) +#define E_SOURCE_MAIL_COMPOSITION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_MAIL_COMPOSITION, ESourceMailComposition)) +#define E_SOURCE_MAIL_COMPOSITION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_MAIL_COMPOSITION, ESourceMailCompositionClass)) +#define E_IS_SOURCE_MAIL_COMPOSITION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_MAIL_COMPOSITION)) +#define E_IS_SOURCE_MAIL_COMPOSITION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_MAIL_COMPOSITION)) +#define E_SOURCE_MAIL_COMPOSITION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_MAIL_COMPOSITION, ESourceMailCompositionClass)) + +/** + * E_SOURCE_EXTENSION_MAIL_COMPOSITION: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceMailComposition. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_MAIL_COMPOSITION "Mail Composition" + +G_BEGIN_DECLS + +typedef struct _ESourceMailComposition ESourceMailComposition; +typedef struct _ESourceMailCompositionClass ESourceMailCompositionClass; +typedef struct _ESourceMailCompositionPrivate ESourceMailCompositionPrivate; + +/** + * ESourceMailComposition: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceMailComposition { + ESourceExtension parent; + ESourceMailCompositionPrivate *priv; +}; + +struct _ESourceMailCompositionClass { + ESourceExtensionClass parent_class; +}; + +GType e_source_mail_composition_get_type + (void) G_GNUC_CONST; +const gchar * const * + e_source_mail_composition_get_bcc + (ESourceMailComposition *extension); +gchar ** e_source_mail_composition_dup_bcc + (ESourceMailComposition *extension); +void e_source_mail_composition_set_bcc + (ESourceMailComposition *extension, + const gchar * const *bcc); +const gchar * const * + e_source_mail_composition_get_cc + (ESourceMailComposition *extension); +gchar ** e_source_mail_composition_dup_cc + (ESourceMailComposition *extension); +void e_source_mail_composition_set_cc + (ESourceMailComposition *extension, + const gchar * const *cc); +const gchar * e_source_mail_composition_get_drafts_folder + (ESourceMailComposition *extension); +gchar * e_source_mail_composition_dup_drafts_folder + (ESourceMailComposition *extension); +void e_source_mail_composition_set_drafts_folder + (ESourceMailComposition *extension, + const gchar *drafts_folder); +gboolean e_source_mail_composition_get_sign_imip + (ESourceMailComposition *extension); +void e_source_mail_composition_set_sign_imip + (ESourceMailComposition *extension, + gboolean sign_imip); +const gchar * e_source_mail_composition_get_templates_folder + (ESourceMailComposition *extension); +gchar * e_source_mail_composition_dup_templates_folder + (ESourceMailComposition *extension); +void e_source_mail_composition_set_templates_folder + (ESourceMailComposition *extension, + const gchar *templates_folder); + +G_END_DECLS + +#endif /* E_SOURCE_MAIL_COMPOSITION_H */ diff --git a/libedataserver/e-source-mail-identity.c b/libedataserver/e-source-mail-identity.c new file mode 100644 index 0000000..2f1797b --- /dev/null +++ b/libedataserver/e-source-mail-identity.c @@ -0,0 +1,669 @@ +/* + * e-source-mail-identity.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-mail-identity + * @include: libedataserver/e-source-mail-identity.h + * @short_description: #ESource extension for an email identity + * + * The #ESourceMailIdentity extension describes an "identity" for a mail + * account, which is the information that other people see when they read + * your messages. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceMailIdentity *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_IDENTITY); + * ]| + **/ + +#include "e-source-mail-identity.h" + +#include + +#define E_SOURCE_MAIL_IDENTITY_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_MAIL_IDENTITY, ESourceMailIdentityPrivate)) + +struct _ESourceMailIdentityPrivate { + GMutex *property_lock; + gchar *address; + gchar *name; + gchar *organization; + gchar *reply_to; + gchar *signature_uid; +}; + +enum { + PROP_0, + PROP_ADDRESS, + PROP_NAME, + PROP_ORGANIZATION, + PROP_REPLY_TO, + PROP_SIGNATURE_UID +}; + +G_DEFINE_TYPE ( + ESourceMailIdentity, + e_source_mail_identity, + E_TYPE_SOURCE_EXTENSION) + +static void +source_mail_identity_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ADDRESS: + e_source_mail_identity_set_address ( + E_SOURCE_MAIL_IDENTITY (object), + g_value_get_string (value)); + return; + + case PROP_NAME: + e_source_mail_identity_set_name ( + E_SOURCE_MAIL_IDENTITY (object), + g_value_get_string (value)); + return; + + case PROP_ORGANIZATION: + e_source_mail_identity_set_organization ( + E_SOURCE_MAIL_IDENTITY (object), + g_value_get_string (value)); + return; + + case PROP_REPLY_TO: + e_source_mail_identity_set_reply_to ( + E_SOURCE_MAIL_IDENTITY (object), + g_value_get_string (value)); + return; + + case PROP_SIGNATURE_UID: + e_source_mail_identity_set_signature_uid ( + E_SOURCE_MAIL_IDENTITY (object), + g_value_get_string (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_mail_identity_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ADDRESS: + g_value_take_string ( + value, + e_source_mail_identity_dup_address ( + E_SOURCE_MAIL_IDENTITY (object))); + return; + + case PROP_NAME: + g_value_take_string ( + value, + e_source_mail_identity_dup_name ( + E_SOURCE_MAIL_IDENTITY (object))); + return; + + case PROP_ORGANIZATION: + g_value_take_string ( + value, + e_source_mail_identity_dup_organization ( + E_SOURCE_MAIL_IDENTITY (object))); + return; + + case PROP_REPLY_TO: + g_value_take_string ( + value, + e_source_mail_identity_dup_reply_to ( + E_SOURCE_MAIL_IDENTITY (object))); + return; + + case PROP_SIGNATURE_UID: + g_value_take_string ( + value, + e_source_mail_identity_dup_signature_uid ( + E_SOURCE_MAIL_IDENTITY (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_mail_identity_finalize (GObject *object) +{ + ESourceMailIdentityPrivate *priv; + + priv = E_SOURCE_MAIL_IDENTITY_GET_PRIVATE (object); + + g_mutex_free (priv->property_lock); + + g_free (priv->address); + g_free (priv->name); + g_free (priv->organization); + g_free (priv->reply_to); + g_free (priv->signature_uid); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_mail_identity_parent_class)->finalize (object); +} + +static void +e_source_mail_identity_class_init (ESourceMailIdentityClass *class) +{ + GObjectClass *object_class; + ESourceExtensionClass *extension_class; + + g_type_class_add_private (class, sizeof (ESourceMailIdentityPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_mail_identity_set_property; + object_class->get_property = source_mail_identity_get_property; + object_class->finalize = source_mail_identity_finalize; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_MAIL_IDENTITY; + + g_object_class_install_property ( + object_class, + PROP_ADDRESS, + g_param_spec_string ( + "address", + "Address", + "Sender's email address", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_NAME, + g_param_spec_string ( + "name", + "Name", + "Sender's name", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_ORGANIZATION, + g_param_spec_string ( + "organization", + "Organization", + "Sender's organization", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_REPLY_TO, + g_param_spec_string ( + "reply-to", + "Reply-To", + "Sender's reply-to address", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_SIGNATURE_UID, + g_param_spec_string ( + "signature-uid", + "Signature UID", + "ESource UID of the sender's signature", + "none", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); +} + +static void +e_source_mail_identity_init (ESourceMailIdentity *extension) +{ + extension->priv = E_SOURCE_MAIL_IDENTITY_GET_PRIVATE (extension); + extension->priv->property_lock = g_mutex_new (); +} + +/** + * e_source_mail_identity_get_address: + * @extension: an #ESourceMailIdentity + * + * Returns the email address for this identity from which to send messages. + * This may be an empty string but will never be %NULL. + * + * Returns: the sender's email address + * + * Since: 3.6 + **/ +const gchar * +e_source_mail_identity_get_address (ESourceMailIdentity *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL); + + return extension->priv->address; +} + +/** + * e_source_mail_identity_dup_address: + * @extension: an #ESourceMailIdentity + * + * Thread-safe variation of e_source_mail_identity_get_address(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceMailIdentity:address + * + * Since: 3.6 + **/ +gchar * +e_source_mail_identity_dup_address (ESourceMailIdentity *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_mail_identity_get_address (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_mail_identity_set_address: + * @extension: an #ESourceMailIdentity + * @address: (allow-none): the sender's email address, or %NULL + * + * Sets the email address for this identity from which to send messages. + * + * The internal copy of @address is automatically stripped of leading and + * trailing whitespace. If the resulting string is empty, %NULL is set + * instead. + * + * Since: 3.6 + **/ +void +e_source_mail_identity_set_address (ESourceMailIdentity *extension, + const gchar *address) +{ + g_return_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->address); + extension->priv->address = e_util_strdup_strip (address); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "address"); +} + +/** + * e_source_mail_identity_get_name: + * @extension: an #ESourceMailIdentity + * + * Returns the sender's name for this identity. + * + * Returns: the sender's name + * + * Since: 3.6 + **/ +const gchar * +e_source_mail_identity_get_name (ESourceMailIdentity *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL); + + return extension->priv->name; +} + +/** + * e_source_mail_identity_dup_name: + * @extension: an #ESourceMailIdentity + * + * Thread-safe variation of e_source_mail_identity_get_name(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceMailIdentity:name + * + * Since: 3.6 + **/ +gchar * +e_source_mail_identity_dup_name (ESourceMailIdentity *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_mail_identity_get_name (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_mail_identity_set_name: + * @extension: an #ESourceMailIdentity + * @name: (allow-none): the sender's name, or %NULL + * + * Sets the sender's name for this identity. + * + * The internal copy of @name is automatically stripped of leading and + * trailing whitespace. If @name is %NULL or the resulting string is + * empty, the result of g_get_real_name() is set instead. + * + * Since: 3.6 + **/ +void +e_source_mail_identity_set_name (ESourceMailIdentity *extension, + const gchar *name) +{ + g_return_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->name); + extension->priv->name = e_util_strdup_strip (name); + + if (extension->priv->name == NULL) + extension->priv->name = g_strdup (g_get_real_name ()); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "name"); +} + +/** + * e_source_mail_identity_get_organization: + * @extension: an #ESourceMailIdentity + * + * Returns the sender's organization for this identity. + * + * Returns: the sender's organization + * + * Since: 3.6 + **/ +const gchar * +e_source_mail_identity_get_organization (ESourceMailIdentity *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL); + + return extension->priv->organization; +} + +/** + * e_source_mail_identity_dup_organization: + * @extension: an #ESourceMailIdentity + * + * Thread-safe variation of e_source_mail_identity_dup_organization(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceMailIdentity:organization + * + * Since: 3.6 + **/ +gchar * +e_source_mail_identity_dup_organization (ESourceMailIdentity *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_mail_identity_get_organization (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_mail_identity_set_organization: + * @extension: an #ESourceMailIdentity + * @organization: (allow-none): the sender's organization, or %NULL + * + * Sets the sender's organization for this identity. + * + * The internal copy of @organization is automatically stripped of leading + * and trailing whitespace. If the resulting string is empty, %NULL is set + * instead. + * + * Since: 3.6 + **/ +void +e_source_mail_identity_set_organization (ESourceMailIdentity *extension, + const gchar *organization) +{ + g_return_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->organization); + extension->priv->organization = e_util_strdup_strip (organization); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "organization"); +} + +/** + * e_source_mail_identity_get_reply_to: + * @extension: an #ESourceMailIdentity + * + * Returns the email address for this identity to which recipients should + * send replies. + * + * Returns: the sender's reply-to address + * + * Since: 3.6 + **/ +const gchar * +e_source_mail_identity_get_reply_to (ESourceMailIdentity *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL); + + return extension->priv->reply_to; +} + +/** + * e_source_mail_identity_dup_reply_to: + * @extension: an #ESourceMailIdentity + * + * Thread-safe variation of e_source_mail_identity_get_reply_to(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceMailIdentity:reply-to + * + * Since: 3.6 + **/ +gchar * +e_source_mail_identity_dup_reply_to (ESourceMailIdentity *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_mail_identity_get_reply_to (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_mail_identity_set_reply_to: + * @extension: an #ESourceMailIdentity + * @reply_to: (allow-none): the sender's reply-to address, or %NULL + * + * Sets the email address for this identity to which recipients should + * send replies. + * + * The internal copy of @reply_to is automatically stripped of leading + * and trailing whitespace. If the resulting string is empty, %NULL is + * set instead. + * + * Since: 3.6 + **/ +void +e_source_mail_identity_set_reply_to (ESourceMailIdentity *extension, + const gchar *reply_to) +{ + g_return_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->reply_to); + extension->priv->reply_to = e_util_strdup_strip (reply_to); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "reply-to"); +} + +/** + * e_source_mail_identity_get_signature_uid: + * @extension: an #ESourceMailIdentity + * + * Returns the #ESource:uid of an #ESource describing a mail signature. + * + * If the user does not want to use a signature for this identity, the + * convention is to set the #ESourceMailIdentity:signature-uid property + * to "none". + * + * Returns: the sender's signature ID, or "none" + * + * Since: 3.6 + **/ +const gchar * +e_source_mail_identity_get_signature_uid (ESourceMailIdentity *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL); + + return extension->priv->signature_uid; +} + +/** + * e_source_mail_identity_dup_signature_uid: + * @extension: an #ESourceMailIdentity + * + * Thread-safe variation of e_source_mail_identity_get_signature_uid(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceMailIdentity:signature-uid + * + * Since: 3.6 + **/ +gchar * +e_source_mail_identity_dup_signature_uid (ESourceMailIdentity *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_mail_identity_get_signature_uid (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_mail_identity_set_signature_uid: + * @extension: an #ESourceMailIdentity + * @signature_uid: (allow-none): the sender's signature ID, or %NULL + * + * Sets the #ESource:uid of an #ESource describing a mail signature. + * + * If the user does not want to use a signature for this identity, the + * convention is to set the #ESourceMailIdentity:signature-uid property + * to "none". In keeping with that convention, the property will be set + * to "none" if @signature is %NULL or an empty string. + * + * Since: 3.6 + **/ +void +e_source_mail_identity_set_signature_uid (ESourceMailIdentity *extension, + const gchar *signature_uid) +{ + g_return_if_fail (E_IS_SOURCE_MAIL_IDENTITY (extension)); + + /* Convert empty strings to "none". */ + if (signature_uid == NULL || *signature_uid == '\0') + signature_uid = "none"; + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->signature_uid); + extension->priv->signature_uid = g_strdup (signature_uid); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "signature-uid"); +} + diff --git a/libedataserver/e-source-mail-identity.h b/libedataserver/e-source-mail-identity.h new file mode 100644 index 0000000..276fc84 --- /dev/null +++ b/libedataserver/e-source-mail-identity.h @@ -0,0 +1,116 @@ +/* + * e-source-mail-identity.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_MAIL_IDENTITY_H +#define E_SOURCE_MAIL_IDENTITY_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_MAIL_IDENTITY \ + (e_source_mail_identity_get_type ()) +#define E_SOURCE_MAIL_IDENTITY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_MAIL_IDENTITY, ESourceMailIdentity)) +#define E_SOURCE_MAIL_IDENTITY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_MAIL_IDENTITY, ESourceMailIdentityClass)) +#define E_IS_SOURCE_MAIL_IDENTITY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_MAIL_IDENTITY)) +#define E_IS_SOURCE_MAIL_IDENTITY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_MAIL_IDENTITY)) +#define E_SOURCE_MAIL_IDENTITY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_MAIL_IDENTITY, ESourceMailIdentityClass)) + +/** + * E_SOURCE_EXTENSION_MAIL_IDENTITY: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceMailIdentity. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_MAIL_IDENTITY "Mail Identity" + +G_BEGIN_DECLS + +typedef struct _ESourceMailIdentity ESourceMailIdentity; +typedef struct _ESourceMailIdentityClass ESourceMailIdentityClass; +typedef struct _ESourceMailIdentityPrivate ESourceMailIdentityPrivate; + +/** + * ESourceMailIdentity: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceMailIdentity { + ESourceExtension parent; + ESourceMailIdentityPrivate *priv; +}; + +struct _ESourceMailIdentityClass { + ESourceExtensionClass parent_class; +}; + +GType e_source_mail_identity_get_type + (void) G_GNUC_CONST; +const gchar * e_source_mail_identity_get_address + (ESourceMailIdentity *extension); +gchar * e_source_mail_identity_dup_address + (ESourceMailIdentity *extension); +void e_source_mail_identity_set_address + (ESourceMailIdentity *extension, + const gchar *address); +const gchar * e_source_mail_identity_get_name + (ESourceMailIdentity *extension); +gchar * e_source_mail_identity_dup_name + (ESourceMailIdentity *extension); +void e_source_mail_identity_set_name + (ESourceMailIdentity *extension, + const gchar *name); +const gchar * e_source_mail_identity_get_organization + (ESourceMailIdentity *extension); +gchar * e_source_mail_identity_dup_organization + (ESourceMailIdentity *extension); +void e_source_mail_identity_set_organization + (ESourceMailIdentity *extension, + const gchar *organization); +const gchar * e_source_mail_identity_get_reply_to + (ESourceMailIdentity *extension); +gchar * e_source_mail_identity_dup_reply_to + (ESourceMailIdentity *extension); +void e_source_mail_identity_set_reply_to + (ESourceMailIdentity *extension, + const gchar *reply_to); +const gchar * e_source_mail_identity_get_signature_uid + (ESourceMailIdentity *extension); +gchar * e_source_mail_identity_dup_signature_uid + (ESourceMailIdentity *extension); +void e_source_mail_identity_set_signature_uid + (ESourceMailIdentity *extension, + const gchar *signature_uid); + +G_END_DECLS + +#endif /* E_SOURCE_MAIL_IDENTITY_H */ diff --git a/libedataserver/e-source-mail-signature.c b/libedataserver/e-source-mail-signature.c new file mode 100644 index 0000000..fc198d7 --- /dev/null +++ b/libedataserver/e-source-mail-signature.c @@ -0,0 +1,961 @@ +/* + * e-source-mail-signature.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-mail-signature + * @include: libedataserver/e-source-mail-signature.h + * @short_description: #ESource extension for email signatures + * + * The #ESourceMailSignature extension refers to a personalized email + * signature. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceMailSignature *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_SIGNATURE); + * ]| + **/ + +#include "e-source-mail-signature.h" + +#include +#include +#include + +#include + +#define E_SOURCE_MAIL_SIGNATURE_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_MAIL_SIGNATURE, ESourceMailSignaturePrivate)) + +typedef struct _AsyncContext AsyncContext; + +struct _ESourceMailSignaturePrivate { + GMutex *property_lock; + GFile *file; + gchar *mime_type; +}; + +struct _AsyncContext { + gchar *contents; + gchar *symlink_target; + gsize length; +}; + +enum { + PROP_0, + PROP_FILE, + PROP_MIME_TYPE +}; + +G_DEFINE_TYPE ( + ESourceMailSignature, + e_source_mail_signature, + E_TYPE_SOURCE_EXTENSION) + +static void +async_context_free (AsyncContext *async_context) +{ + g_free (async_context->contents); + g_free (async_context->symlink_target); + + g_slice_free (AsyncContext, async_context); +} + +static void +source_mail_signature_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_MIME_TYPE: + e_source_mail_signature_set_mime_type ( + E_SOURCE_MAIL_SIGNATURE (object), + g_value_get_string (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_mail_signature_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_FILE: + g_value_set_object ( + value, + e_source_mail_signature_get_file ( + E_SOURCE_MAIL_SIGNATURE (object))); + return; + + case PROP_MIME_TYPE: + g_value_take_string ( + value, + e_source_mail_signature_dup_mime_type ( + E_SOURCE_MAIL_SIGNATURE (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_mail_signature_dispose (GObject *object) +{ + ESourceMailSignaturePrivate *priv; + + priv = E_SOURCE_MAIL_SIGNATURE_GET_PRIVATE (object); + + if (priv->file != NULL) { + g_object_unref (priv->file); + priv->file = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_source_mail_signature_parent_class)-> + dispose (object); +} + +static void +source_mail_signature_finalize (GObject *object) +{ + ESourceMailSignaturePrivate *priv; + + priv = E_SOURCE_MAIL_SIGNATURE_GET_PRIVATE (object); + + g_mutex_free (priv->property_lock); + + g_free (priv->mime_type); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_mail_signature_parent_class)-> + finalize (object); +} + +static void +source_mail_signature_constructed (GObject *object) +{ + ESourceMailSignaturePrivate *priv; + ESourceExtension *extension; + ESource *source; + const gchar *config_dir; + const gchar *uid; + gchar *base_dir; + gchar *path; + + priv = E_SOURCE_MAIL_SIGNATURE_GET_PRIVATE (object); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_source_mail_signature_parent_class)-> + constructed (object); + + extension = E_SOURCE_EXTENSION (object); + source = e_source_extension_get_source (extension); + uid = e_source_get_uid (source); + + config_dir = e_get_user_config_dir (); + base_dir = g_build_filename (config_dir, "signatures", NULL); + path = g_build_filename (base_dir, uid, NULL); + priv->file = g_file_new_for_path (path); + g_mkdir_with_parents (base_dir, 0700); + g_free (base_dir); + g_free (path); +} + +static void +e_source_mail_signature_class_init (ESourceMailSignatureClass *class) +{ + GObjectClass *object_class; + ESourceExtensionClass *extension_class; + + g_type_class_add_private ( + class, sizeof (ESourceMailSignaturePrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_mail_signature_set_property; + object_class->get_property = source_mail_signature_get_property; + object_class->dispose = source_mail_signature_dispose; + object_class->finalize = source_mail_signature_finalize; + object_class->constructed = source_mail_signature_constructed; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_MAIL_SIGNATURE; + + g_object_class_install_property ( + object_class, + PROP_FILE, + g_param_spec_object ( + "file", + "File", + "File containing signature content", + G_TYPE_FILE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_MIME_TYPE, + g_param_spec_string ( + "mime-type", + "MIME Type", + "MIME type of the signature content", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); +} + +static void +e_source_mail_signature_init (ESourceMailSignature *extension) +{ + extension->priv = E_SOURCE_MAIL_SIGNATURE_GET_PRIVATE (extension); + extension->priv->property_lock = g_mutex_new (); +} + +/** + * e_source_mail_signature_get_file: + * @extension: an #ESourceMailSignature + * + * Returns a #GFile instance pointing to the signature file for @extension. + * The signature file may be a regular file containing the static signature + * content, or it may be a symbolic link to an executable file that produces + * the signature content. + * + * e_source_mail_signature_load() uses this to load the signature content. + * + * Returns: a #GFile + * + * Since: 3.6 + **/ +GFile * +e_source_mail_signature_get_file (ESourceMailSignature *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_MAIL_SIGNATURE (extension), NULL); + + return extension->priv->file; +} + +/** + * e_source_mail_signature_get_mime_type: + * @extension: an #ESourceMailSignature + * + * Returns the MIME type of the signature content for @extension, or %NULL + * if it has not yet been determined. + * + * e_source_mail_signature_load() sets this automatically if the MIME type + * has not yet been determined. + * + * Returns: the MIME type of the signature content, or %NULL + * + * Since: 3.6 + **/ +const gchar * +e_source_mail_signature_get_mime_type (ESourceMailSignature *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_MAIL_SIGNATURE (extension), NULL); + + return extension->priv->mime_type; +} + +/** + * e_source_mail_signature_dup_mime_type: + * @extension: an #ESourceMailSignature + * + * Thread-safe variation of e_source_mail_signature_get_mime_type(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceMailSignature:mime-type + * + * Since: 3.6 + **/ +gchar * +e_source_mail_signature_dup_mime_type (ESourceMailSignature *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_MAIL_SIGNATURE (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_mail_signature_get_mime_type (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_mail_signature_set_mime_type: + * @extension: an #ESourceMailSignature + * @mime_type: (allow-none): a MIME type, or %NULL + * + * Sets the MIME type of the signature content for @extension. + * + * e_source_mail_signature_load() sets this automatically if the MIME type + * has not yet been determined. + * + * The internal copy of @mime_type is automatically stripped of leading + * and trailing whitespace. If the resulting string is empty, %NULL is + * set instead. + * + * Since: 3.6 + **/ +void +e_source_mail_signature_set_mime_type (ESourceMailSignature *extension, + const gchar *mime_type) +{ + g_return_if_fail (E_IS_SOURCE_MAIL_SIGNATURE (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->mime_type); + extension->priv->mime_type = e_util_strdup_strip (mime_type); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "mime-type"); +} + +/********************** e_source_mail_signature_load() ***********************/ + +/* Helper for e_source_mail_signature_load() */ +static void +source_mail_signature_load_thread (GSimpleAsyncResult *simple, + GObject *object, + GCancellable *cancellable) +{ + AsyncContext *async_context; + GError *error = NULL; + + async_context = g_simple_async_result_get_op_res_gpointer (simple); + + e_source_mail_signature_load_sync ( + E_SOURCE (object), + &async_context->contents, + &async_context->length, + cancellable, &error); + + if (error != NULL) + g_simple_async_result_take_error (simple, error); +} + +/** + * e_source_mail_signature_load_sync: + * @source: an #ESource + * @contents: return location for the signature content + * @length: (allow-none): return location for the length of the signature + * content, or %NULL if the length is not needed + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @error: return location for a #GError, or %NULL + * + * Loads a signature from the signature file for @source, which is + * given by e_source_mail_signature_get_file(). The signature contents + * are placed in @contents, and @length is set to the size of the @contents + * string. The @contents string should be freed with g_free() when no + * longer needed. + * + * If the signature file is executable, it will be executed and its output + * captured as the email signature content. If the signature file is not + * executable, the email signature content is read directly from the file. + * + * Returns; %TRUE on success, %FALSE on failure + * + * Since: 3.6 + **/ +gboolean +e_source_mail_signature_load_sync (ESource *source, + gchar **contents, + gsize *length, + GCancellable *cancellable, + GError **error) +{ + ESourceMailSignature *extension; + GFileInfo *file_info; + GFile *file; + const gchar *content_type; + const gchar *extension_name; + gchar *local_contents = NULL; + gboolean can_execute; + gboolean success; + gchar *guessed_content_type; + gchar *command_line; + gchar *mime_type; + gchar *path; + + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + g_return_val_if_fail (contents != NULL, FALSE); + + extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE; + extension = e_source_get_extension (source, extension_name); + file = e_source_mail_signature_get_file (extension); + + file_info = g_file_query_info ( + file, + G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE "," + G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + G_FILE_QUERY_INFO_NONE, + cancellable, error); + + if (file_info == NULL) + return FALSE; + + can_execute = g_file_info_get_attribute_boolean ( + file_info, G_FILE_ATTRIBUTE_ACCESS_CAN_EXECUTE); + + content_type = g_file_info_get_content_type (file_info); + mime_type = g_content_type_get_mime_type (content_type); + + if (can_execute) + goto execute; + + /*** Load signature file contents ***/ + + success = g_file_load_contents ( + file, cancellable, &local_contents, NULL, NULL, error); + + if (!success) + goto exit; + + g_return_val_if_fail (local_contents != NULL, FALSE); + + /* Signatures are saved as UTF-8, but we still need to check that + * the signature is valid UTF-8 because the user may be opening a + * signature file this is in his/her locale character set. If it + * is not UTF-8 then try converting from the current locale. */ + if (!g_utf8_validate (local_contents, -1, NULL)) { + gchar *utf8; + + utf8 = g_locale_to_utf8 ( + local_contents, -1, NULL, NULL, error); + + if (utf8 == NULL) { + success = FALSE; + goto exit; + } + + g_free (local_contents); + local_contents = utf8; + } + + goto exit; + +execute: + + /*** Execute signature file and capture output ***/ + + path = g_file_get_path (file); + + if (path == NULL) { + g_set_error ( + error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("Signature script must be a local file")); + success = FALSE; + goto exit; + } + + /* Enclose the path in single-quotes for compatibility on Windows. + * (See g_spawn_command_line_sync() documentation for rationale.) */ + command_line = g_strdup_printf ("'%s'", path); + + success = g_spawn_command_line_sync ( + command_line, &local_contents, NULL, NULL, error); + + g_free (command_line); + g_free (path); + + /* Check if we failed to spawn the script. */ + if (!success) + goto exit; + + /* Check if we were cancelled while the script was running. */ + if (g_cancellable_set_error_if_cancelled (cancellable, error)) { + success = FALSE; + goto exit; + } + + g_return_val_if_fail (local_contents != NULL, FALSE); + + /* Signature scripts are supposed to generate UTF-8 content, but + * because users are known to never read the manual, we try to do + * our best if the content isn't valid UTF-8 by assuming that the + * content is in the user's locale character set. */ + if (!g_utf8_validate (local_contents, -1, NULL)) { + gchar *utf8; + + utf8 = g_locale_to_utf8 ( + local_contents, -1, NULL, NULL, error); + + if (utf8 == NULL) { + success = FALSE; + goto exit; + } + + g_free (local_contents); + local_contents = utf8; + } + + g_free (mime_type); + + /* Try and guess the content type of the script output + * so it can be applied correctly to the mail message. */ + guessed_content_type = g_content_type_guess ( + NULL, (guchar *) local_contents, + strlen (local_contents), NULL); + mime_type = g_content_type_get_mime_type (guessed_content_type); + g_free (guessed_content_type); + +exit: + if (success) { + const gchar *ext_mime_type; + + if (length != NULL) + *length = strlen (local_contents); + + *contents = local_contents; + local_contents = NULL; + + ext_mime_type = + e_source_mail_signature_get_mime_type (extension); + + /* Don't override the MIME type if it's already set. */ + if (ext_mime_type == NULL || *ext_mime_type == '\0') + e_source_mail_signature_set_mime_type ( + extension, mime_type); + } + + g_object_unref (file_info); + g_free (local_contents); + g_free (mime_type); + + return success; +} + +/** + * e_source_mail_signature_load: + * @source: an #ESource + * @io_priority: the I/O priority of the request + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @callback: (scope async): a #GAsyncReadyCallback to call when the request + * is satisfied + * @user_data: (closure): data to pass to the callback function + * + * Asynchronously loads a signature from the signature file for @source, + * which is given by e_source_mail_signature_get_file(). + * + * If the signature file is executable, it will be executed and its output + * captured as the email signature content. If the signature file is not + * executable, the email signature content is read directly from the file. + * + * When the operation is finished, @callback will be called. You can + * then call e_source_mail_signature_load_finish() to get the result of + * the operation. + * + * Since: 3.6 + **/ +void +e_source_mail_signature_load (ESource *source, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + AsyncContext *async_context; + + g_return_if_fail (E_IS_SOURCE (source)); + + async_context = g_slice_new0 (AsyncContext); + + simple = g_simple_async_result_new ( + G_OBJECT (source), callback, user_data, + e_source_mail_signature_load); + + g_simple_async_result_set_check_cancellable (simple, cancellable); + + g_simple_async_result_set_op_res_gpointer ( + simple, async_context, (GDestroyNotify) async_context_free); + + g_simple_async_result_run_in_thread ( + simple, source_mail_signature_load_thread, + io_priority, cancellable); + + g_object_unref (simple); +} + +/** + * e_source_mail_signature_load_finish: + * @source: an #ESource + * @result: a #GAsyncResult + * @contents: return location for the signature content + * @length: (allow-none): return location for the length of the signature + * content, or %NULL if the length is not needed + * @error: return location for a #GError, or %NULL + * + * Finishes an operation started with e_source_mail_signature_load(). The + * signature file contents are placed in @contents, and @length is set to + * the size of the @contents string. The @contents string should be freed + * with g_free() when no longer needed. + * + * Returns: %TRUE on success, %FALSE on failure + * + * Since: 3.6 + **/ +gboolean +e_source_mail_signature_load_finish (ESource *source, + GAsyncResult *result, + gchar **contents, + gsize *length, + GError **error) +{ + GSimpleAsyncResult *simple; + AsyncContext *async_context; + + g_return_val_if_fail ( + g_simple_async_result_is_valid ( + result, G_OBJECT (source), + e_source_mail_signature_load), FALSE); + + g_return_val_if_fail (contents != NULL, FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + async_context = g_simple_async_result_get_op_res_gpointer (simple); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + + g_return_val_if_fail (async_context->contents != NULL, FALSE); + + *contents = async_context->contents; + async_context->contents = NULL; + + if (length != NULL) + *length = async_context->length; + + return TRUE; +} + +/********************* e_source_mail_signature_replace() *********************/ + +/* Helper for e_source_mail_signature_replace() */ +static void +source_mail_signature_replace_thread (GSimpleAsyncResult *simple, + GObject *object, + GCancellable *cancellable) +{ + AsyncContext *async_context; + GError *error = NULL; + + async_context = g_simple_async_result_get_op_res_gpointer (simple); + + e_source_mail_signature_replace_sync ( + E_SOURCE (object), async_context->contents, + async_context->length, cancellable, &error); + + if (error != NULL) + g_simple_async_result_take_error (simple, error); +} + +/** + * e_source_mail_signature_replace_sync: + * @source: an #ESource + * @contents: the signature contents + * @length: the length of @contents in bytes + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @error: return location for a #GError, or %NULL + * + * Replaces the signature file for @source with the given @contents + * of @length bytes. The signature file for @source is given by + * e_source_mail_signature_get_file(). + * + * Returns: %TRUE on success, %FALSE on failure + * + * Since: 3.6 + **/ +gboolean +e_source_mail_signature_replace_sync (ESource *source, + const gchar *contents, + gsize length, + GCancellable *cancellable, + GError **error) +{ + ESourceMailSignature *extension; + const gchar *extension_name; + GFile *file; + + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + g_return_val_if_fail (contents != NULL, FALSE); + + extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE; + extension = e_source_get_extension (source, extension_name); + file = e_source_mail_signature_get_file (extension); + + return g_file_replace_contents ( + file, contents, length, NULL, FALSE, + G_FILE_CREATE_REPLACE_DESTINATION, + NULL, cancellable, error); +} + +/** + * e_source_mail_signature_replace: + * @source: an #ESource + * @contents: the signature contents + * @length: the length of @contents in bytes + * @io_priority: the I/O priority of the request + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @callback: (scope async): a #GAsyncReadyCallback to call when the request + * is satisfied + * @user_data: (closure): data to pass to the callback function + * + * Asynchrously replaces the signature file for @source with the given + * @contents of @length bytes. The signature file for @source is given + * by e_source_mail_signature_get_file(). + * + * When the operation is finished, @callback will be called. You can + * then call e_source_mail_signature_replace_finish() to get the result + * of the operation. + * + * Since: 3.6 + **/ +void +e_source_mail_signature_replace (ESource *source, + const gchar *contents, + gsize length, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + AsyncContext *async_context; + + g_return_if_fail (E_IS_SOURCE (source)); + g_return_if_fail (contents != NULL); + + async_context = g_slice_new0 (AsyncContext); + async_context->contents = g_strdup (contents); + async_context->length = length; + + simple = g_simple_async_result_new ( + G_OBJECT (source), callback, user_data, + e_source_mail_signature_replace); + + g_simple_async_result_set_check_cancellable (simple, cancellable); + + g_simple_async_result_set_op_res_gpointer ( + simple, async_context, (GDestroyNotify) async_context_free); + + g_simple_async_result_run_in_thread ( + simple, source_mail_signature_replace_thread, + io_priority, cancellable); + + g_object_unref (simple); +} + +/** + * e_source_mail_signature_replace_finish: + * @source: an #ESource + * @result: a #GAsyncResult + * @error: return location for a #GError, or %NULL + * + * Finishes an operation started with e_source_mail_signature_replace(). + * + * Returns: %TRUE on success, %FALSE on failure + * + * Since: 3.6 + **/ +gboolean +e_source_mail_signature_replace_finish (ESource *source, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail ( + g_simple_async_result_is_valid ( + result, G_OBJECT (source), + e_source_mail_signature_replace), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + /* Assume success unless a GError is set. */ + return !g_simple_async_result_propagate_error (simple, error); +} + +/********************* e_source_mail_signature_symlink() *********************/ + +/* Helper for e_source_mail_signature_symlink() */ +static void +source_mail_signature_symlink_thread (GSimpleAsyncResult *simple, + GObject *object, + GCancellable *cancellable) +{ + AsyncContext *async_context; + GError *error = NULL; + + async_context = g_simple_async_result_get_op_res_gpointer (simple); + + e_source_mail_signature_symlink_sync ( + E_SOURCE (object), + async_context->symlink_target, + cancellable, &error); + + if (error != NULL) + g_simple_async_result_take_error (simple, error); +} + +/** + * e_source_mail_signature_symlink_sync: + * @source: an #ESource + * @symlink_target: executable filename to link to + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @error: return location for a #GError, or %NULL + * + * Replaces the signature file for @source with a symbolic link to + * @symlink_target, which should be an executable file that prints + * a mail signature to standard output. The signature file for + * @source is given by e_source_mail_signature_get_file(). + * + * Returns: %TRUE on success, %FALSE on failure + * + * Since: 3.6 + **/ +gboolean +e_source_mail_signature_symlink_sync (ESource *source, + const gchar *symlink_target, + GCancellable *cancellable, + GError **error) +{ + ESourceMailSignature *extension; + const gchar *extension_name; + GFile *file; + + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + g_return_val_if_fail (symlink_target != NULL, FALSE); + + extension_name = E_SOURCE_EXTENSION_MAIL_SIGNATURE; + extension = e_source_get_extension (source, extension_name); + file = e_source_mail_signature_get_file (extension); + + /* The file may not exist, so we don't care if this fails. + * If it fails for a different reason than G_IO_ERROR_NOT_FOUND + * then the next step will probably also fail and we'll capture + * THAT error. */ + g_file_delete (file, cancellable, NULL); + + return g_file_make_symbolic_link ( + file, symlink_target, cancellable, error); +} + +/** + * e_source_mail_signature_symlink: + * @source: an #ESource + * @symlink_target: executable filename to link to + * @io_priority: the I/O priority of the request + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @callback: (scope async): a #GAsyncReadyCallback to call when the request + * is satisfied + * @user_data: (closure): data to pass to the callback function + * + * Asynchronously replaces the signature file for @source with a symbolic + * link to @symlink_target, which should be an executable file that prints + * a mail signature to standard output. The signature file for @source + * is given by e_source_mail_signature_get_file(). + * + * When the operation is finished, @callback will be called. You can + * then call e_source_mail_signature_symlink_finish() to get the result + * of the operation. + * + * Since: 3.6 + **/ +void +e_source_mail_signature_symlink (ESource *source, + const gchar *symlink_target, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + AsyncContext *async_context; + + g_return_if_fail (E_IS_SOURCE (source)); + g_return_if_fail (symlink_target != NULL); + + async_context = g_slice_new0 (AsyncContext); + async_context->symlink_target = g_strdup (symlink_target); + + simple = g_simple_async_result_new ( + G_OBJECT (source), callback, user_data, + e_source_mail_signature_symlink); + + g_simple_async_result_set_check_cancellable (simple, cancellable); + + g_simple_async_result_set_op_res_gpointer ( + simple, async_context, (GDestroyNotify) async_context_free); + + g_simple_async_result_run_in_thread ( + simple, source_mail_signature_symlink_thread, + io_priority, cancellable); + + g_object_unref (simple); +} + +/** + * e_source_mail_signature_symlink_finish: + * @source: an #ESource + * @result: a #GAsyncResult + * @error: return location for a #GError, or %NULL + * + * Finishes an operation started with e_source_mail_signature_symlink(). + * + * Returns: %TRUE on success, %FALSE on failure + * + * Since: 3.6 + **/ +gboolean +e_source_mail_signature_symlink_finish (ESource *source, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail ( + g_simple_async_result_is_valid ( + result, G_OBJECT (source), + e_source_mail_signature_symlink), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + /* Assume success unless a GError is set. */ + return !g_simple_async_result_propagate_error (simple, error); +} + diff --git a/libedataserver/e-source-mail-signature.h b/libedataserver/e-source-mail-signature.h new file mode 100644 index 0000000..3922f8f --- /dev/null +++ b/libedataserver/e-source-mail-signature.h @@ -0,0 +1,143 @@ +/* + * e-source-mail-signature.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_MAIL_SIGNATURE_H +#define E_SOURCE_MAIL_SIGNATURE_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_MAIL_SIGNATURE \ + (e_source_mail_signature_get_type ()) +#define E_SOURCE_MAIL_SIGNATURE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_MAIL_SIGNATURE, ESourceMailSignature)) +#define E_SOURCE_MAIL_SIGNATURE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_MAIL_SIGNATURE, ESourceMailSignatureClass)) +#define E_IS_SOURCE_MAIL_SIGNATURE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_MAIL_SIGNATURE)) +#define E_IS_SOURCE_MAIL_SIGNATURE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_MAIL_SIGNATURE)) +#define E_SOURCE_MAIL_SIGNATURE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_MAIL_SIGNATURE, ESourceMailSignatureClass)) + +/** + * E_SOURCE_EXTENSION_MAIL_SIGNATURE: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceMailSignature. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_MAIL_SIGNATURE "Mail Signature" + +G_BEGIN_DECLS + +typedef struct _ESourceMailSignature ESourceMailSignature; +typedef struct _ESourceMailSignatureClass ESourceMailSignatureClass; +typedef struct _ESourceMailSignaturePrivate ESourceMailSignaturePrivate; + +/** + * ESourceMailSignature: + * + * Contains only private data that should be read and manipulated using the + * function below. + * + * Since: 3.6 + **/ +struct _ESourceMailSignature { + ESourceExtension parent; + ESourceMailSignaturePrivate *priv; +}; + +struct _ESourceMailSignatureClass { + ESourceExtensionClass parent_class; +}; + +GType e_source_mail_signature_get_type + (void) G_GNUC_CONST; +GFile * e_source_mail_signature_get_file + (ESourceMailSignature *extension); +const gchar * e_source_mail_signature_get_mime_type + (ESourceMailSignature *extension); +gchar * e_source_mail_signature_dup_mime_type + (ESourceMailSignature *extension); +void e_source_mail_signature_set_mime_type + (ESourceMailSignature *extension, + const gchar *mime_type); + +gboolean e_source_mail_signature_load_sync + (ESource *source, + gchar **contents, + gsize *length, + GCancellable *cancellable, + GError **error); +void e_source_mail_signature_load + (ESource *source, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean e_source_mail_signature_load_finish + (ESource *source, + GAsyncResult *result, + gchar **contents, + gsize *length, + GError **error); +gboolean e_source_mail_signature_replace_sync + (ESource *source, + const gchar *contents, + gsize length, + GCancellable *cancellable, + GError **error); +void e_source_mail_signature_replace + (ESource *source, + const gchar *contents, + gsize length, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean e_source_mail_signature_replace_finish + (ESource *source, + GAsyncResult *result, + GError **error); +gboolean e_source_mail_signature_symlink_sync + (ESource *source, + const gchar *symlink_target, + GCancellable *cancellable, + GError **error); +void e_source_mail_signature_symlink + (ESource *source, + const gchar *symlink_target, + gint io_priority, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean e_source_mail_signature_symlink_finish + (ESource *source, + GAsyncResult *result, + GError **error); + +G_END_DECLS + +#endif /* E_SOURCE_MAIL_SIGNATURE_H */ diff --git a/libedataserver/e-source-mail-submission.c b/libedataserver/e-source-mail-submission.c new file mode 100644 index 0000000..32d84c7 --- /dev/null +++ b/libedataserver/e-source-mail-submission.c @@ -0,0 +1,334 @@ +/* + * e-source-mail-submission.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-mail-submission + * @include: libedataserver/e-source-mail-submission.h + * @short_description: #ESource extension for submitting emails + * + * The #ESourceMailSubmission extension tracks settings to be applied + * when submitting a mail message for delivery. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceMailSubmission *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_SUBMISSION); + * ]| + **/ + +#include "e-source-mail-submission.h" + +#include + +#define E_SOURCE_MAIL_SUBMISSION_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_MAIL_SUBMISSION, ESourceMailSubmissionPrivate)) + +struct _ESourceMailSubmissionPrivate { + GMutex *property_lock; + gchar *sent_folder; + gchar *transport_uid; +}; + +enum { + PROP_0, + PROP_SENT_FOLDER, + PROP_TRANSPORT_UID +}; + +G_DEFINE_TYPE ( + ESourceMailSubmission, + e_source_mail_submission, + E_TYPE_SOURCE_EXTENSION) + +static void +source_mail_submission_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SENT_FOLDER: + e_source_mail_submission_set_sent_folder ( + E_SOURCE_MAIL_SUBMISSION (object), + g_value_get_string (value)); + return; + + case PROP_TRANSPORT_UID: + e_source_mail_submission_set_transport_uid ( + E_SOURCE_MAIL_SUBMISSION (object), + g_value_get_string (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_mail_submission_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_SENT_FOLDER: + g_value_take_string ( + value, + e_source_mail_submission_dup_sent_folder ( + E_SOURCE_MAIL_SUBMISSION (object))); + return; + + case PROP_TRANSPORT_UID: + g_value_take_string ( + value, + e_source_mail_submission_dup_transport_uid ( + E_SOURCE_MAIL_SUBMISSION (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_mail_submission_finalize (GObject *object) +{ + ESourceMailSubmissionPrivate *priv; + + priv = E_SOURCE_MAIL_SUBMISSION_GET_PRIVATE (object); + + g_mutex_free (priv->property_lock); + + g_free (priv->sent_folder); + g_free (priv->transport_uid); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_mail_submission_parent_class)-> + finalize (object); +} + +static void +e_source_mail_submission_class_init (ESourceMailSubmissionClass *class) +{ + GObjectClass *object_class; + ESourceExtensionClass *extension_class; + + g_type_class_add_private ( + class, sizeof (ESourceMailSubmissionPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_mail_submission_set_property; + object_class->get_property = source_mail_submission_get_property; + object_class->finalize = source_mail_submission_finalize; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_MAIL_SUBMISSION; + + g_object_class_install_property ( + object_class, + PROP_SENT_FOLDER, + g_param_spec_string ( + "sent-folder", + "Sent Folder", + "Preferred folder for sent messages", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_TRANSPORT_UID, + g_param_spec_string ( + "transport-uid", + "Transport UID", + "ESource UID of a Mail Transport", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); +} + +static void +e_source_mail_submission_init (ESourceMailSubmission *extension) +{ + extension->priv = E_SOURCE_MAIL_SUBMISSION_GET_PRIVATE (extension); + extension->priv->property_lock = g_mutex_new (); +} + +/** + * e_source_mail_submission_get_sent_folder: + * @extension: an #ESourceMailSubmission + * + * Returns a string identifying the preferred folder for sent messages. + * The format of the identifier string is defined by the client application. + * + * Returns: an identifier for the preferred sent folder + * + * Since: 3.6 + **/ +const gchar * +e_source_mail_submission_get_sent_folder (ESourceMailSubmission *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_MAIL_SUBMISSION (extension), NULL); + + return extension->priv->sent_folder; +} + +/** + * e_source_mail_submission_dup_sent_folder: + * @extension: an #ESourceMailSubmission + * + * Thread-safe variation of e_source_mail_submission_get_sent_folder(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceMailSubmission:sent-folder + * + * Since: 3.6 + **/ +gchar * +e_source_mail_submission_dup_sent_folder (ESourceMailSubmission *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_MAIL_SUBMISSION (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_mail_submission_get_sent_folder (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_mail_submission_set_sent_folder: + * @extension: an #ESourceMailSubmission + * @sent_folder: (allow-none): an identifier for the preferred sent folder, + * or %NULL + * + * Sets the preferred folder for sent messages by an identifier string. + * The format of the identifier string is defined by the client application. + * + * The internal copy of @sent_folder is automatically stripped of leading + * and trailing whitespace. If the resulting string is empty, %NULL is set + * instead. + * + * Since: 3.6 + **/ +void +e_source_mail_submission_set_sent_folder (ESourceMailSubmission *extension, + const gchar *sent_folder) +{ + g_return_if_fail (E_IS_SOURCE_MAIL_SUBMISSION (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->sent_folder); + extension->priv->sent_folder = e_util_strdup_strip (sent_folder); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "sent-folder"); +} + +/** + * e_source_mail_submission_get_transport_uid: + * @extension: an #ESourceMailSubmission + * + * Returns the #ESource:uid of the #ESource that describes the mail + * transport to be used for outgoing messages. + * + * Returns: the mail transport #ESource:uid + * + * Since: 3.6 + **/ +const gchar * +e_source_mail_submission_get_transport_uid (ESourceMailSubmission *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_MAIL_SUBMISSION (extension), NULL); + + return extension->priv->transport_uid; +} + +/** + * e_source_mail_submission_dup_transport_uid: + * @extension: an #ESourceMailSubmission + * + * Thread-safe variation of e_source_mail_submission_get_transport_uid(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceMailSubmission:transport-uid + * + * Since: 3.6 + **/ +gchar * +e_source_mail_submission_dup_transport_uid (ESourceMailSubmission *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_MAIL_SUBMISSION (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_mail_submission_get_transport_uid (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_mail_submission_set_transport_uid: + * @extension: an #ESourceMailSubmission + * @transport_uid: (allow-none): the mail transport #ESource:uid, or %NULL + * + * Sets the #ESource:uid of the #ESource that describes the mail + * transport to be used for outgoing messages. + * + * Since: 3.6 + **/ +void +e_source_mail_submission_set_transport_uid (ESourceMailSubmission *extension, + const gchar *transport_uid) +{ + g_return_if_fail (E_IS_SOURCE_MAIL_SUBMISSION (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->transport_uid); + extension->priv->transport_uid = g_strdup (transport_uid); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "transport-uid"); +} diff --git a/libedataserver/e-source-mail-submission.h b/libedataserver/e-source-mail-submission.h new file mode 100644 index 0000000..0833609 --- /dev/null +++ b/libedataserver/e-source-mail-submission.h @@ -0,0 +1,95 @@ +/* + * e-source-mail-submission.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_MAIL_SUBMISSION_H +#define E_SOURCE_MAIL_SUBMISSION_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_MAIL_SUBMISSION \ + (e_source_mail_submission_get_type ()) +#define E_SOURCE_MAIL_SUBMISSION(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_MAIL_SUBMISSION, ESourceMailSubmission)) +#define E_SOURCE_MAIL_SUBMISSION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_MAIL_SUBMISSION, ESourceMailSubmissionClass)) +#define E_IS_SOURCE_MAIL_SUBMISSION(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_MAIL_SUBMISSION)) +#define E_IS_SOURCE_MAIL_SUBMISSION_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_MAIL_SUBMISSION)) +#define E_SOURCE_MAIL_SUBMISSION_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_MAIL_SUBMISSION, ESourceMailSubmissionClass)) + +/** + * E_SOURCE_EXTENSION_MAIL_SUBMISSION: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceMailSubmission. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_MAIL_SUBMISSION "Mail Submission" + +G_BEGIN_DECLS + +typedef struct _ESourceMailSubmission ESourceMailSubmission; +typedef struct _ESourceMailSubmissionClass ESourceMailSubmissionClass; +typedef struct _ESourceMailSubmissionPrivate ESourceMailSubmissionPrivate; + +/** + * ESourceMailSubmission: + * + * Contains only private data that should be read and manipulated using the + * function below. + * + * Since: 3.6 + **/ +struct _ESourceMailSubmission { + ESourceExtension parent; + ESourceMailSubmissionPrivate *priv; +}; + +struct _ESourceMailSubmissionClass { + ESourceExtensionClass parent_class; +}; + +GType e_source_mail_submission_get_type + (void) G_GNUC_CONST; +const gchar * e_source_mail_submission_get_sent_folder + (ESourceMailSubmission *extension); +gchar * e_source_mail_submission_dup_sent_folder + (ESourceMailSubmission *extension); +void e_source_mail_submission_set_sent_folder + (ESourceMailSubmission *extension, + const gchar *sent_folder); +const gchar * e_source_mail_submission_get_transport_uid + (ESourceMailSubmission *extension); +gchar * e_source_mail_submission_dup_transport_uid + (ESourceMailSubmission *extension); +void e_source_mail_submission_set_transport_uid + (ESourceMailSubmission *extension, + const gchar *transport_uid); + +G_END_DECLS + +#endif /* E_SOURCE_MAIL_SUBMISSION_H */ diff --git a/libedataserver/e-source-mail-transport.c b/libedataserver/e-source-mail-transport.c new file mode 100644 index 0000000..d54f993 --- /dev/null +++ b/libedataserver/e-source-mail-transport.c @@ -0,0 +1,62 @@ +/* + * e-source-mail-transport.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-mail-transport + * @include: libedataserver/e-source-mail-transport.h + * @short_description: #ESource extension for an email transport + * + * The #ESourceMailTransport extension identifies the #ESource as a + * mail transport which describes where to send outgoing messages. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceMailTransport *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_TRANSPORT); + * ]| + **/ + +#include "e-source-mail-transport.h" + +#define E_SOURCE_MAIL_TRANSPORT_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_MAIL_TRANSPORT, ESourceMailTransportPrivate)) + +G_DEFINE_TYPE ( + ESourceMailTransport, + e_source_mail_transport, + E_TYPE_SOURCE_BACKEND) + +static void +e_source_mail_transport_class_init (ESourceMailTransportClass *class) +{ + ESourceExtensionClass *extension_class; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_MAIL_TRANSPORT; +} + +static void +e_source_mail_transport_init (ESourceMailTransport *extension) +{ +} + diff --git a/libedataserver/e-source-mail-transport.h b/libedataserver/e-source-mail-transport.h new file mode 100644 index 0000000..de55111 --- /dev/null +++ b/libedataserver/e-source-mail-transport.h @@ -0,0 +1,81 @@ +/* + * e-source-mail-transport.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_MAIL_TRANSPORT_H +#define E_SOURCE_MAIL_TRANSPORT_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_MAIL_TRANSPORT \ + (e_source_mail_transport_get_type ()) +#define E_SOURCE_MAIL_TRANSPORT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_MAIL_TRANSPORT, ESourceMailTransport)) +#define E_SOURCE_MAIL_TRANSPORT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_MAIL_TRANSPORT, ESourceMailTransportClass)) +#define E_IS_SOURCE_MAIL_TRANSPORT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_MAIL_TRANSPORT)) +#define E_IS_SOURCE_MAIL_TRANSPORT_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_MAIL_TRANSPORT)) +#define E_SOURCE_MAIL_TRANSPORT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_MAIL_TRANSPORT, ESourceMailTransportClass)) + +/** + * E_SOURCE_EXTENSION_MAIL_TRANSPORT: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceMailTransport. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_MAIL_TRANSPORT "Mail Transport" + +G_BEGIN_DECLS + +typedef struct _ESourceMailTransport ESourceMailTransport; +typedef struct _ESourceMailTransportClass ESourceMailTransportClass; +typedef struct _ESourceMailTransportPrivate ESourceMailTransportPrivate; + +/** + * ESourceMailTransport: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceMailTransport { + ESourceBackend parent; + ESourceMailTransportPrivate *priv; +}; + +struct _ESourceMailTransportClass { + ESourceBackendClass parent_class; +}; + +GType e_source_mail_transport_get_type + (void) G_GNUC_CONST; + +G_END_DECLS + +#endif /* E_SOURCE_MAIL_TRANSPORT_H */ diff --git a/libedataserver/e-source-mdn.c b/libedataserver/e-source-mdn.c new file mode 100644 index 0000000..e63d645 --- /dev/null +++ b/libedataserver/e-source-mdn.c @@ -0,0 +1,172 @@ +/* + * e-source-mdn.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-mdn + * @include: libedataserver/e-source-mdn.h + * @short_description: #ESource extension for MDN settings + * + * The #ESourceMDN extension tracks Message Disposition Notification + * settings for a mail account. See RFC 2298 for more information about + * Message Disposition Notification. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceMDN *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MDN); + * ]| + **/ + +#include "e-source-mdn.h" + +#include + +#define E_SOURCE_MDN_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_MDN, ESourceMDNPrivate)) + +struct _ESourceMDNPrivate { + EMdnResponsePolicy response_policy; +}; + +enum { + PROP_0, + PROP_RESPONSE_POLICY +}; + +G_DEFINE_TYPE ( + ESourceMDN, + e_source_mdn, + E_TYPE_SOURCE_EXTENSION) + +static void +source_mdn_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_RESPONSE_POLICY: + e_source_mdn_set_response_policy ( + E_SOURCE_MDN (object), + g_value_get_enum (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_mdn_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_RESPONSE_POLICY: + g_value_set_enum ( + value, + e_source_mdn_get_response_policy ( + E_SOURCE_MDN (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +e_source_mdn_class_init (ESourceMDNClass *class) +{ + GObjectClass *object_class; + ESourceExtensionClass *extension_class; + + g_type_class_add_private (class, sizeof (ESourceMDNPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_mdn_set_property; + object_class->get_property = source_mdn_get_property; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_MDN; + + g_object_class_install_property ( + object_class, + PROP_RESPONSE_POLICY, + g_param_spec_enum ( + "response-policy", + "Response Policy", + "Policy for responding to MDN requests", + E_TYPE_MDN_RESPONSE_POLICY, + E_MDN_RESPONSE_POLICY_ASK, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); +} + +static void +e_source_mdn_init (ESourceMDN *extension) +{ + extension->priv = E_SOURCE_MDN_GET_PRIVATE (extension); +} + +/** + * e_source_mdn_get_response_policy: + * @extension: an #ESourceMDN + * + * Returns the policy for this mail account on responding to Message + * Disposition Notification requests when receiving mail messages. + * + * Returns: the #EMdnResponsePolicy for this account + * + * Since: 3.6 + **/ +EMdnResponsePolicy +e_source_mdn_get_response_policy (ESourceMDN *extension) +{ + g_return_val_if_fail ( + E_IS_SOURCE_MDN (extension), + E_MDN_RESPONSE_POLICY_NEVER); + + return extension->priv->response_policy; +} + +/** + * e_source_mdn_set_response_policy: + * @extension: an #ESourceMDN + * @response_policy: the #EMdnResponsePolicy + * + * Sets the policy for this mail account on responding to Message + * Disposition Notification requests when receiving mail messages. + * + * Since: 3.6 + **/ +void +e_source_mdn_set_response_policy (ESourceMDN *extension, + EMdnResponsePolicy response_policy) +{ + g_return_if_fail (E_IS_SOURCE_MDN (extension)); + + extension->priv->response_policy = response_policy; + + g_object_notify (G_OBJECT (extension), "response-policy"); +} diff --git a/libedataserver/e-source-mdn.h b/libedataserver/e-source-mdn.h new file mode 100644 index 0000000..016ef7d --- /dev/null +++ b/libedataserver/e-source-mdn.h @@ -0,0 +1,88 @@ +/* + * e-source-mdn.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_MDN_H +#define E_SOURCE_MDN_H + +#include +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_MDN \ + (e_source_mdn_get_type ()) +#define E_SOURCE_MDN(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_MDN, ESourceMDN)) +#define E_SOURCE_MDN_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_MDN, ESourceMDNClass)) +#define E_IS_SOURCE_MDN(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_MDN)) +#define E_IS_SOURCE_MDN_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_MDN)) +#define E_SOURCE_MDN_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_MDN, ESourceMDNClass)) + +/** + * E_SOURCE_EXTENSION_MDN: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceMDN. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_MDN "Message Disposition Notifications" + +G_BEGIN_DECLS + +typedef struct _ESourceMDN ESourceMDN; +typedef struct _ESourceMDNClass ESourceMDNClass; +typedef struct _ESourceMDNPrivate ESourceMDNPrivate; + +/** + * ESourceMDN: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceMDN { + ESourceExtension parent; + ESourceMDNPrivate *priv; +}; + +struct _ESourceMDNClass { + ESourceExtensionClass parent_class; +}; + +GType e_source_mdn_get_type + (void) G_GNUC_CONST; +EMdnResponsePolicy + e_source_mdn_get_response_policy + (ESourceMDN *extension); +void e_source_mdn_set_response_policy + (ESourceMDN *extension, + EMdnResponsePolicy response_policy); + +G_END_DECLS + +#endif /* E_SOURCE_MDN_H */ diff --git a/libedataserver/e-source-offline.c b/libedataserver/e-source-offline.c new file mode 100644 index 0000000..b5f60f4 --- /dev/null +++ b/libedataserver/e-source-offline.c @@ -0,0 +1,168 @@ +/* + * e-source-offline.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-offline + * @include: libedataserver/e-source-offline.h + * @short_description: #ESource extension for offline settings + * + * The #ESourceOffline extension tracks whether data from a remote + * server should be cached locally for viewing while offline. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceOffline *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_OFFLINE); + * ]| + **/ + +#include "e-source-offline.h" + +#define E_SOURCE_OFFLINE_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_OFFLINE, ESourceOfflinePrivate)) + +struct _ESourceOfflinePrivate { + gboolean stay_synchronized; +}; + +enum { + PROP_0, + PROP_STAY_SYNCHRONIZED +}; + +G_DEFINE_TYPE ( + ESourceOffline, + e_source_offline, + E_TYPE_SOURCE_EXTENSION) + +static void +source_offline_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_STAY_SYNCHRONIZED: + e_source_offline_set_stay_synchronized ( + E_SOURCE_OFFLINE (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_offline_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_STAY_SYNCHRONIZED: + g_value_set_boolean ( + value, + e_source_offline_get_stay_synchronized ( + E_SOURCE_OFFLINE (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +e_source_offline_class_init (ESourceOfflineClass *class) +{ + GObjectClass *object_class; + ESourceExtensionClass *extension_class; + + g_type_class_add_private (class, sizeof (ESourceOfflinePrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_offline_set_property; + object_class->get_property = source_offline_get_property; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_OFFLINE; + + g_object_class_install_property ( + object_class, + PROP_STAY_SYNCHRONIZED, + g_param_spec_boolean ( + "stay-synchronized", + "StaySynchronized", + "Keep remote content synchronized locally", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); +} + +static void +e_source_offline_init (ESourceOffline *extension) +{ + extension->priv = E_SOURCE_OFFLINE_GET_PRIVATE (extension); +} + +/** + * e_source_offline_get_stay_synchronized: + * @extension: an #ESourceOffline + * + * Returns whether data from a remote server should be cached locally + * for viewing while offline. Backends are responsible for implementing + * such caching. + * + * Returns: whether data should be cached for offline + * + * Since: 3.6 + **/ +gboolean +e_source_offline_get_stay_synchronized (ESourceOffline *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_OFFLINE (extension), FALSE); + + return extension->priv->stay_synchronized; +} + +/** + * e_source_offline_set_stay_synchronized: + * @extension: an #ESourceOffline + * @stay_synchronized: whether data should be cached for offline + * + * Sets whether data from a remote server should be cached locally for + * viewing while offline. Backends are responsible for implementing + * such caching. + * + * Since: 3.6 + **/ +void +e_source_offline_set_stay_synchronized (ESourceOffline *extension, + gboolean stay_synchronized) +{ + g_return_if_fail (E_IS_SOURCE_OFFLINE (extension)); + + extension->priv->stay_synchronized = stay_synchronized; + + g_object_notify (G_OBJECT (extension), "stay-synchronized"); +} diff --git a/libedataserver/e-source-offline.h b/libedataserver/e-source-offline.h new file mode 100644 index 0000000..00053db --- /dev/null +++ b/libedataserver/e-source-offline.h @@ -0,0 +1,85 @@ +/* + * e-source-offline.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_OFFLINE_H +#define E_SOURCE_OFFLINE_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_OFFLINE \ + (e_source_offline_get_type ()) +#define E_SOURCE_OFFLINE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_OFFLINE, ESourceOffline)) +#define E_SOURCE_OFFLINE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_OFFLINE, ESourceOfflineClass)) +#define E_IS_SOURCE_OFFLINE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_OFFLINE)) +#define E_IS_SOURCE_OFFLINE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_OFFLINE)) +#define E_SOURCE_OFFLINE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_OFFLINE, ESourceOfflineClass)) + +/** + * E_SOURCE_EXTENSION_OFFLINE: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceOffline. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_OFFLINE "Offline" + +G_BEGIN_DECLS + +typedef struct _ESourceOffline ESourceOffline; +typedef struct _ESourceOfflineClass ESourceOfflineClass; +typedef struct _ESourceOfflinePrivate ESourceOfflinePrivate; + +/** + * ESourceOffline: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceOffline { + ESourceExtension parent; + ESourceOfflinePrivate *priv; +}; + +struct _ESourceOfflineClass { + ESourceExtensionClass parent_class; +}; + +GType e_source_offline_get_type (void) G_GNUC_CONST; +gboolean e_source_offline_get_stay_synchronized + (ESourceOffline *extension); +void e_source_offline_set_stay_synchronized + (ESourceOffline *extension, + gboolean stay_synchronized); + +G_END_DECLS + +#endif /* E_SOURCE_OFFLINE_H */ diff --git a/libedataserver/e-source-openpgp.c b/libedataserver/e-source-openpgp.c new file mode 100644 index 0000000..18aa3ab --- /dev/null +++ b/libedataserver/e-source-openpgp.c @@ -0,0 +1,538 @@ +/* + * e-source-openpgp.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-openpgp + * @include: libedataserver/e-source-openpgp.h + * @short_description: #ESource extension for OpenPGP settings + * + * The #ESourceOpenPGP extension tracks OpenPGP (RFC 4880) settings to be + * applied to outgoing mail messages. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceOpenPGP *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_OPENPGP); + * ]| + **/ + +#include "e-source-openpgp.h" + +#include + +#define E_SOURCE_OPENPGP_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_OPENPGP, ESourceOpenPGPPrivate)) + +struct _ESourceOpenPGPPrivate { + GMutex *property_lock; + gchar *key_id; + gchar *signing_algorithm; + + gboolean always_trust; + gboolean encrypt_to_self; + gboolean sign_by_default; +}; + +enum { + PROP_0, + PROP_ALWAYS_TRUST, + PROP_ENCRYPT_TO_SELF, + PROP_KEY_ID, + PROP_SIGNING_ALGORITHM, + PROP_SIGN_BY_DEFAULT +}; + +G_DEFINE_TYPE ( + ESourceOpenPGP, + e_source_openpgp, + E_TYPE_SOURCE_EXTENSION) + +static void +source_openpgp_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ALWAYS_TRUST: + e_source_openpgp_set_always_trust ( + E_SOURCE_OPENPGP (object), + g_value_get_boolean (value)); + return; + + case PROP_ENCRYPT_TO_SELF: + e_source_openpgp_set_encrypt_to_self ( + E_SOURCE_OPENPGP (object), + g_value_get_boolean (value)); + return; + + case PROP_KEY_ID: + e_source_openpgp_set_key_id ( + E_SOURCE_OPENPGP (object), + g_value_get_string (value)); + return; + + case PROP_SIGNING_ALGORITHM: + e_source_openpgp_set_signing_algorithm ( + E_SOURCE_OPENPGP (object), + g_value_get_string (value)); + return; + + case PROP_SIGN_BY_DEFAULT: + e_source_openpgp_set_sign_by_default ( + E_SOURCE_OPENPGP (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_openpgp_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ALWAYS_TRUST: + g_value_set_boolean ( + value, + e_source_openpgp_get_always_trust ( + E_SOURCE_OPENPGP (object))); + return; + + case PROP_ENCRYPT_TO_SELF: + g_value_set_boolean ( + value, + e_source_openpgp_get_encrypt_to_self ( + E_SOURCE_OPENPGP (object))); + return; + + case PROP_KEY_ID: + g_value_take_string ( + value, + e_source_openpgp_dup_key_id ( + E_SOURCE_OPENPGP (object))); + return; + + case PROP_SIGNING_ALGORITHM: + g_value_take_string ( + value, + e_source_openpgp_dup_signing_algorithm ( + E_SOURCE_OPENPGP (object))); + return; + + case PROP_SIGN_BY_DEFAULT: + g_value_set_boolean ( + value, + e_source_openpgp_get_sign_by_default ( + E_SOURCE_OPENPGP (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_openpgp_finalize (GObject *object) +{ + ESourceOpenPGPPrivate *priv; + + priv = E_SOURCE_OPENPGP_GET_PRIVATE (object); + + g_mutex_free (priv->property_lock); + + g_free (priv->key_id); + g_free (priv->signing_algorithm); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_openpgp_parent_class)->finalize (object); +} + +static void +e_source_openpgp_class_init (ESourceOpenPGPClass *class) +{ + GObjectClass *object_class; + ESourceExtensionClass *extension_class; + + g_type_class_add_private (class, sizeof (ESourceOpenPGPPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_openpgp_set_property; + object_class->get_property = source_openpgp_get_property; + object_class->finalize = source_openpgp_finalize; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_OPENPGP; + + g_object_class_install_property ( + object_class, + PROP_ALWAYS_TRUST, + g_param_spec_boolean ( + "always-trust", + "Always Trust", + "Always trust keys in my keyring", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_ENCRYPT_TO_SELF, + g_param_spec_boolean ( + "encrypt-to-self", + "Encrypt To Self", + "Always encrypt to myself", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_KEY_ID, + g_param_spec_string ( + "key-id", + "Key ID", + "PGP/GPG Key ID", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_SIGNING_ALGORITHM, + g_param_spec_string ( + "signing-algorithm", + "Signing Algorithm", + "Hash algorithm used to sign messages", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_SIGN_BY_DEFAULT, + g_param_spec_boolean ( + "sign-by-default", + "Sign By Default", + "Sign outgoing messages by default", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); +} + +static void +e_source_openpgp_init (ESourceOpenPGP *extension) +{ + extension->priv = E_SOURCE_OPENPGP_GET_PRIVATE (extension); + extension->priv->property_lock = g_mutex_new (); +} + +/** + * e_source_openpgp_get_always_trust: + * @extension: an #ESourceOpenPGP + * + * Returns whether to skip key validation and assume that used keys are + * always fully trusted. + * + * Returns: whether used keys are always fully trusted + * + * Since: 3.6 + **/ +gboolean +e_source_openpgp_get_always_trust (ESourceOpenPGP *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_OPENPGP (extension), FALSE); + + return extension->priv->always_trust; +} + +/** + * e_source_openpgp_set_always_trust: + * @extension: an #ESourceOpenPGP + * @always_trust: whether used keys are always fully trusted + * + * Sets whether to skip key validation and assume that used keys are + * always fully trusted. + * + * Since: 3.6 + **/ +void +e_source_openpgp_set_always_trust (ESourceOpenPGP *extension, + gboolean always_trust) +{ + g_return_if_fail (E_IS_SOURCE_OPENPGP (extension)); + + extension->priv->always_trust = always_trust; + + g_object_notify (G_OBJECT (extension), "always-trust"); +} + +/** + * e_source_openpgp_get_encrypt_to_self: + * @extension: an #ESourceOpenPGP + * + * Returns whether to "encrypt-to-self" when sending encrypted messages. + * + * Returns: whether to "encrypt-to-self" + * + * Since: 3.6 + **/ +gboolean +e_source_openpgp_get_encrypt_to_self (ESourceOpenPGP *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_OPENPGP (extension), FALSE); + + return extension->priv->encrypt_to_self; +} + +/** + * e_source_openpgp_set_encrypt_to_self: + * @extension: an #ESourceOpenPGP + * @encrypt_to_self: whether to "encrypt-to-self" + * + * Sets whether to "encrypt-to-self" when sending encrypted messages. + * + * Since: 3.6 + **/ +void +e_source_openpgp_set_encrypt_to_self (ESourceOpenPGP *extension, + gboolean encrypt_to_self) +{ + g_return_if_fail (E_IS_SOURCE_OPENPGP (extension)); + + extension->priv->encrypt_to_self = encrypt_to_self; + + g_object_notify (G_OBJECT (extension), "encrypt-to-self"); +} + +/** + * e_source_openpgp_get_key_id: + * @extension: an #ESourceOpenPGP + * + * Returns the OpenPGP key ID used to sign and encrypt messages. + * + * Returns: the key ID used to sign and encrypt messages + * + * Since: 3.6 + **/ +const gchar * +e_source_openpgp_get_key_id (ESourceOpenPGP *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_OPENPGP (extension), NULL); + + return extension->priv->key_id; +} + +/** + * e_source_openpgp_dup_key_id: + * @extension: an #ESourceOpenPGP + * + * Thread-safe variation of e_source_openpgp_get_key_id(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceOpenPGP:key-id + * + * Since: 3.6 + **/ +gchar * +e_source_openpgp_dup_key_id (ESourceOpenPGP *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_OPENPGP (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_openpgp_get_key_id (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_openpgp_set_key_id: + * @extension: an #ESourceOpenPGP + * @key_id: the key ID used to sign and encrypt messages + * + * Sets the OpenPGP key ID used to sign and encrypt messages. + * + * The internal copy of @key_id is automatically stripped of leading and + * trailing whitespace. If the resulting string is empty, %NULL is set + * instead. + * + * Since: 3.6 + **/ +void +e_source_openpgp_set_key_id (ESourceOpenPGP *extension, + const gchar *key_id) +{ + g_return_if_fail (E_IS_SOURCE_OPENPGP (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->key_id); + extension->priv->key_id = e_util_strdup_strip (key_id); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "key-id"); +} + +/** + * e_source_openpgp_get_signing_algorithm: + * @extension: an #ESourceOpenPGP + * + * Returns the name of the hash algorithm used to digitally sign outgoing + * messages. + * + * Returns: the signing algorithm for outgoing messages + * + * Since: 3.6 + **/ +const gchar * +e_source_openpgp_get_signing_algorithm (ESourceOpenPGP *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_OPENPGP (extension), NULL); + + return extension->priv->signing_algorithm; +} + +/** + * e_source_openpgp_dup_signing_algorithm: + * @extension: an #ESourceOpenPGP + * + * Thread-safe variation of e_source_openpgp_get_signing_algorithm(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceOpenPGP:signing-algorithm + * + * Since: 3.6 + **/ +gchar * +e_source_openpgp_dup_signing_algorithm (ESourceOpenPGP *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_OPENPGP (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_openpgp_get_signing_algorithm (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_openpgp_set_signing_algorithm: + * @extension: an #ESourceOpenPGP + * @signing_algorithm: the signing algorithm for outgoing messages + * + * Sets the name of the hash algorithm used to digitally sign outgoing + * messages. + * + * The internal copy of @signing_algorithm is automatically stripped of + * leading and trailing whitespace. If the resulting string is empty, + * %NULL is set instead. + * + * Since: 3.6 + **/ +void +e_source_openpgp_set_signing_algorithm (ESourceOpenPGP *extension, + const gchar *signing_algorithm) +{ + g_return_if_fail (E_IS_SOURCE_OPENPGP (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->signing_algorithm); + extension->priv->signing_algorithm = + e_util_strdup_strip (signing_algorithm); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "signing-algorithm"); +} + +/** + * e_source_openpgp_get_sign_by_default: + * @extension: an #ESourceOpenPGP + * + * Returns whether to digitally sign outgoing messages by default using + * OpenPGP-compliant software such as GNU Privacy Guard (GnuPG). + * + * Returns: whether to sign outgoing messages by default + * + * Since: 3.6 + **/ +gboolean +e_source_openpgp_get_sign_by_default (ESourceOpenPGP *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_OPENPGP (extension), FALSE); + + return extension->priv->sign_by_default; +} + +/** + * e_source_openpgp_set_sign_by_default: + * @extension: an #ESourceOpenPGP + * @sign_by_default: whether to sign outgoing messages by default + * + * Sets whether to digitally sign outgoing messages by default using + * OpenPGP-compliant software such as GNU Privacy Guard (GnuPG). + * + * Since: 3.6 + **/ +void +e_source_openpgp_set_sign_by_default (ESourceOpenPGP *extension, + gboolean sign_by_default) +{ + g_return_if_fail (E_IS_SOURCE_OPENPGP (extension)); + + extension->priv->sign_by_default = sign_by_default; + + g_object_notify (G_OBJECT (extension), "sign-by-default"); +} + diff --git a/libedataserver/e-source-openpgp.h b/libedataserver/e-source-openpgp.h new file mode 100644 index 0000000..840b929 --- /dev/null +++ b/libedataserver/e-source-openpgp.h @@ -0,0 +1,107 @@ +/* + * e-source-openpgp.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_OPENPGP_H +#define E_SOURCE_OPENPGP_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_OPENPGP \ + (e_source_openpgp_get_type ()) +#define E_SOURCE_OPENPGP(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_OPENPGP, ESourceOpenPGP)) +#define E_SOURCE_OPENPGP_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_OPENPGP, ESourceOpenPGPClass)) +#define E_IS_SOURCE_OPENPGP(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_OPENPGP)) +#define E_IS_SOURCE_OPENPGP_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_OPENPGP)) +#define E_SOURCE_OPENPGP_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_OPENPGP, ESourceOpenPGPClass)) + +/** + * E_SOURCE_EXTENSION_OPENPGP: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceOpenPGP. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_OPENPGP "Pretty Good Privacy (OpenPGP)" + +G_BEGIN_DECLS + +typedef struct _ESourceOpenPGP ESourceOpenPGP; +typedef struct _ESourceOpenPGPClass ESourceOpenPGPClass; +typedef struct _ESourceOpenPGPPrivate ESourceOpenPGPPrivate; + +/** + * ESourceOpenPGP: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceOpenPGP { + ESourceExtension parent; + ESourceOpenPGPPrivate *priv; +}; + +struct _ESourceOpenPGPClass { + ESourceExtensionClass parent_class; +}; + +GType e_source_openpgp_get_type (void) G_GNUC_CONST; +gboolean e_source_openpgp_get_always_trust + (ESourceOpenPGP *extension); +void e_source_openpgp_set_always_trust + (ESourceOpenPGP *extension, + gboolean always_trust); +gboolean e_source_openpgp_get_encrypt_to_self + (ESourceOpenPGP *extension); +void e_source_openpgp_set_encrypt_to_self + (ESourceOpenPGP *extension, + gboolean encrypt_to_self); +const gchar * e_source_openpgp_get_key_id (ESourceOpenPGP *extension); +gchar * e_source_openpgp_dup_key_id (ESourceOpenPGP *extension); +void e_source_openpgp_set_key_id (ESourceOpenPGP *extension, + const gchar *key_id); +const gchar * e_source_openpgp_get_signing_algorithm + (ESourceOpenPGP *extension); +gchar * e_source_openpgp_dup_signing_algorithm + (ESourceOpenPGP *extension); +void e_source_openpgp_set_signing_algorithm + (ESourceOpenPGP *extension, + const gchar *signing_algorithm); +gboolean e_source_openpgp_get_sign_by_default + (ESourceOpenPGP *extension); +void e_source_openpgp_set_sign_by_default + (ESourceOpenPGP *extension, + gboolean sign_by_default); + +G_END_DECLS + +#endif /* E_SOURCE_OPENPGP_H */ + diff --git a/libedataserver/e-source-refresh.c b/libedataserver/e-source-refresh.c new file mode 100644 index 0000000..7c5fa57 --- /dev/null +++ b/libedataserver/e-source-refresh.c @@ -0,0 +1,631 @@ +/* + * e-source-refresh.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-refresh + * @include: libedataserver/e-source-refresh.h + * @short_description: #ESource extension for refresh settings + * + * The #ESourceRefresh extension tracks the interval for fetching + * updates from a remote server. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceRefresh *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_REFRESH); + * ]| + **/ + +#include "e-source-refresh.h" + +#define E_SOURCE_REFRESH_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_REFRESH, ESourceRefreshPrivate)) + +typedef struct _TimeoutNode TimeoutNode; + +struct _ESourceRefreshPrivate { + gboolean enabled; + guint interval_minutes; + + GMutex *timeout_lock; + GHashTable *timeout_table; + guint next_timeout_id; +}; + +struct _TimeoutNode { + GSource *source; + GMainContext *context; + ESourceRefresh *extension; /* not referenced */ + + ESourceRefreshFunc callback; + gpointer user_data; + GDestroyNotify notify; +}; + +enum { + PROP_0, + PROP_ENABLED, + PROP_INTERVAL_MINUTES +}; + +G_DEFINE_TYPE ( + ESourceRefresh, + e_source_refresh, + E_TYPE_SOURCE_EXTENSION) + +static TimeoutNode * +timeout_node_new (ESourceRefresh *extension, + GMainContext *context, + ESourceRefreshFunc callback, + gpointer user_data, + GDestroyNotify notify) +{ + TimeoutNode *node; + + if (context != NULL) + g_main_context_ref (context); + + node = g_slice_new0 (TimeoutNode); + node->context = context; + node->callback = callback; + node->user_data = user_data; + node->notify = notify; + + /* Do not reference. The timeout node will + * not outlive the ESourceRefresh extension. */ + node->extension = extension; + + return node; +} + +static gboolean +timeout_node_invoke (gpointer data) +{ + TimeoutNode *node = data; + ESourceExtension *extension; + ESource *source; + + extension = E_SOURCE_EXTENSION (node->extension); + source = e_source_extension_get_source (extension); + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + + /* We allow timeouts to be scheduled for disabled data sources + * but we don't invoke the callback. Keeps the logic simple. */ + if (e_source_get_enabled (source)) + node->callback (source, node->user_data); + + return TRUE; +} + +static void +timeout_node_attach (TimeoutNode *node) +{ + guint interval_minutes; + + if (node->source != NULL) + return; + + interval_minutes = + e_source_refresh_get_interval_minutes (node->extension); + node->source = g_timeout_source_new_seconds (interval_minutes * 60); + + g_source_set_callback ( + node->source, + timeout_node_invoke, + node, + (GDestroyNotify) NULL); + + g_source_attach (node->source, node->context); +} + +static void +timeout_node_detach (TimeoutNode *node) +{ + if (node->source == NULL) + return; + + g_source_destroy (node->source); + g_source_unref (node->source); + node->source = NULL; +} + +static void +timeout_node_free (TimeoutNode *node) +{ + if (node->source != NULL) + timeout_node_detach (node); + + if (node->context != NULL) + g_main_context_unref (node->context); + + if (node->notify != NULL) + node->notify (node->user_data); + + g_slice_free (TimeoutNode, node); +} + +static void +source_refresh_update_timeouts (ESourceRefresh *extension, + gboolean invoke_callbacks) +{ + GList *list, *link; + + g_mutex_lock (extension->priv->timeout_lock); + + list = g_hash_table_get_values (extension->priv->timeout_table); + + for (link = list; link != NULL; link = g_list_next (link)) { + TimeoutNode *node = link->data; + + timeout_node_detach (node); + + if (invoke_callbacks) + timeout_node_invoke (node); + + if (e_source_refresh_get_enabled (extension)) + timeout_node_attach (node); + } + + g_list_free (list); + + g_mutex_unlock (extension->priv->timeout_lock); +} + +static void +source_refresh_notify_enabled_cb (ESource *source, + GParamSpec *pspec, + ESourceRefresh *extension) +{ + if (e_source_get_enabled (source)) + e_source_refresh_force_timeout (source); +} + +static void +source_refresh_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ENABLED: + e_source_refresh_set_enabled ( + E_SOURCE_REFRESH (object), + g_value_get_boolean (value)); + return; + + case PROP_INTERVAL_MINUTES: + e_source_refresh_set_interval_minutes ( + E_SOURCE_REFRESH (object), + g_value_get_uint (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_refresh_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ENABLED: + g_value_set_boolean ( + value, + e_source_refresh_get_enabled ( + E_SOURCE_REFRESH (object))); + return; + + case PROP_INTERVAL_MINUTES: + g_value_set_uint ( + value, + e_source_refresh_get_interval_minutes ( + E_SOURCE_REFRESH (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_refresh_dispose (GObject *object) +{ + ESourceRefreshPrivate *priv; + + priv = E_SOURCE_REFRESH_GET_PRIVATE (object); + + g_hash_table_remove_all (priv->timeout_table); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_source_refresh_parent_class)->dispose (object); +} + +static void +source_refresh_finalize (GObject *object) +{ + ESourceRefreshPrivate *priv; + + priv = E_SOURCE_REFRESH_GET_PRIVATE (object); + + g_mutex_free (priv->timeout_lock); + g_hash_table_destroy (priv->timeout_table); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_refresh_parent_class)->finalize (object); +} + +static void +source_refresh_constructed (GObject *object) +{ + ESourceExtension *extension; + ESource *source; + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_source_refresh_parent_class)->constructed (object); + + extension = E_SOURCE_EXTENSION (object); + source = e_source_extension_get_source (extension); + + /* There should be no lifecycle issues here + * since we get finalized with our ESource. */ + g_signal_connect ( + source, "notify::enabled", + G_CALLBACK (source_refresh_notify_enabled_cb), + extension); +} + +static void +e_source_refresh_class_init (ESourceRefreshClass *class) +{ + GObjectClass *object_class; + ESourceExtensionClass *extension_class; + + g_type_class_add_private (class, sizeof (ESourceRefreshPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_refresh_set_property; + object_class->get_property = source_refresh_get_property; + object_class->dispose = source_refresh_dispose; + object_class->finalize = source_refresh_finalize; + object_class->constructed = source_refresh_constructed; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_REFRESH; + + g_object_class_install_property ( + object_class, + PROP_ENABLED, + g_param_spec_boolean ( + "enabled", + "Enabled", + "Whether to periodically refresh", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_INTERVAL_MINUTES, + g_param_spec_uint ( + "interval-minutes", + "Interval in Minutes", + "Refresh interval in minutes", + 0, G_MAXUINT, 60, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); +} + +static void +e_source_refresh_init (ESourceRefresh *extension) +{ + GHashTable *timeout_table; + + timeout_table = g_hash_table_new_full ( + (GHashFunc) g_direct_hash, + (GEqualFunc) g_direct_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) timeout_node_free); + + extension->priv = E_SOURCE_REFRESH_GET_PRIVATE (extension); + extension->priv->timeout_lock = g_mutex_new (); + extension->priv->timeout_table = timeout_table; + extension->priv->next_timeout_id = 1; +} + +/** + * e_source_refresh_get_enabled: + * @extension: an #ESourceRefresh + * + * Returns whether to periodically fetch updates from a remote server. + * + * The refresh interval is determined by the #ESourceRefresh:interval-minutes + * property. + * + * Returns: whether periodic refresh is enabled + * + * Since: 3.6 + **/ +gboolean +e_source_refresh_get_enabled (ESourceRefresh *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_REFRESH (extension), FALSE); + + return extension->priv->enabled; +} + +/** + * e_source_refresh_set_enabled: + * @extension: an #ESourceRefresh + * @enabled: whether to enable periodic refresh + * + * Sets whether to periodically fetch updates from a remote server. + * + * The refresh interval is determined by the #ESourceRefresh:interval-minutes + * property. + * + * Since: 3.6 + **/ +void +e_source_refresh_set_enabled (ESourceRefresh *extension, + gboolean enabled) +{ + g_return_if_fail (E_IS_SOURCE_REFRESH (extension)); + + if (enabled == extension->priv->enabled) + return; + + extension->priv->enabled = enabled; + + g_object_notify (G_OBJECT (extension), "enabled"); + + source_refresh_update_timeouts (extension, FALSE); +} + +/** + * e_source_refresh_get_interval_minutes: + * @extension: an #ESourceRefresh + * + * Returns the interval for fetching updates from a remote server. + * + * Note this value is only effective when the #ESourceRefresh:enabled + * property is %TRUE. + * + * Returns: the interval in minutes + * + * Since: 3.6 + **/ +guint +e_source_refresh_get_interval_minutes (ESourceRefresh *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_REFRESH (extension), FALSE); + + return extension->priv->interval_minutes; +} + +/** + * e_source_refresh_set_interval_minutes: + * @extension: an #ESourceRefresh + * @interval_minutes: the interval in minutes + * + * Sets the interval for fetching updates from a remote server. + * + * Note this value is only effective when the #ESourceRefresh:enabled + * property is %TRUE. + * + * Since: 3.6 + **/ +void +e_source_refresh_set_interval_minutes (ESourceRefresh *extension, + guint interval_minutes) +{ + g_return_if_fail (E_IS_SOURCE_REFRESH (extension)); + + if (interval_minutes == extension->priv->interval_minutes) + return; + + extension->priv->interval_minutes = interval_minutes; + + g_object_notify (G_OBJECT (extension), "interval-minutes"); + + source_refresh_update_timeouts (extension, FALSE); +} + +/** + * e_source_refresh_add_timeout: + * @source: an #ESource + * @context: (allow-none): a #GMainContext, or %NULL (if %NULL, the default + * context will be used) + * @callback: function to call on each timeout + * @user_data: data to pass to @callback + * @notify: (allow-none): function to call when the timeout is removed, + * or %NULL + * + * This is a simple way to schedule a periodic data source refresh. + * + * Adds a timeout #GSource to @context and handles all the bookkeeping + * if @source's refresh #ESourceRefresh:enabled state or its refresh + * #ESourceRefresh:interval-minutes value changes. The @callback is + * expected to dispatch an asynchronous job to connect to and fetch + * updates from a remote server. + * + * The returned ID can be passed to e_source_refresh_remove_timeout() to + * remove the timeout from @context. Note the ID is a private handle and + * cannot be passed to g_source_remove(). + * + * Returns: a refresh timeout ID + * + * Since: 3.6 + **/ +guint +e_source_refresh_add_timeout (ESource *source, + GMainContext *context, + ESourceRefreshFunc callback, + gpointer user_data, + GDestroyNotify notify) +{ + ESourceRefresh *extension; + const gchar *extension_name; + TimeoutNode *node; + guint timeout_id; + gpointer key; + + g_return_val_if_fail (E_IS_SOURCE (source), 0); + g_return_val_if_fail (callback != NULL, 0); + + extension_name = E_SOURCE_EXTENSION_REFRESH; + extension = e_source_get_extension (source, extension_name); + + g_mutex_lock (extension->priv->timeout_lock); + + timeout_id = extension->priv->next_timeout_id++; + + key = GUINT_TO_POINTER (timeout_id); + node = timeout_node_new ( + extension, context, callback, user_data, notify); + g_hash_table_insert (extension->priv->timeout_table, key, node); + + if (e_source_refresh_get_enabled (extension)) + timeout_node_attach (node); + + g_mutex_unlock (extension->priv->timeout_lock); + + return timeout_id; +} + +/** + * e_source_refresh_force_timeout: + * @source: an #ESource + * + * For all timeouts added with e_source_refresh_add_timeout(), invokes + * the #ESourceRefreshFunc callback immediately and then, if the refresh + * #ESourceRefresh:enabled state is TRUE, reschedules the timeout. + * + * This function is called automatically when the #ESource switches from + * disabled to enabled, but can also be useful when a network connection + * becomes available or when waking up from hibernation or suspend. + * + * Since: 3.6 + **/ +void +e_source_refresh_force_timeout (ESource *source) +{ + ESourceRefresh *extension; + const gchar *extension_name; + + g_return_if_fail (E_IS_SOURCE (source)); + + extension_name = E_SOURCE_EXTENSION_REFRESH; + extension = e_source_get_extension (source, extension_name); + + source_refresh_update_timeouts (extension, TRUE); +} + +/** + * e_source_refresh_remove_timeout: + * @source: an #ESource + * @refresh_timeout_id: a refresh timeout ID + * + * Removes a timeout #GSource added by e_source_refresh_add_timeout(). + * + * Returns: %TRUE if the timeout was found and removed + * + * Since: 3.6 + **/ +gboolean +e_source_refresh_remove_timeout (ESource *source, + guint refresh_timeout_id) +{ + ESourceRefresh *extension; + const gchar *extension_name; + gboolean removed; + gpointer key; + + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + g_return_val_if_fail (refresh_timeout_id > 0, FALSE); + + extension_name = E_SOURCE_EXTENSION_REFRESH; + extension = e_source_get_extension (source, extension_name); + + g_mutex_lock (extension->priv->timeout_lock); + + key = GUINT_TO_POINTER (refresh_timeout_id); + removed = g_hash_table_remove (extension->priv->timeout_table, key); + + g_mutex_unlock (extension->priv->timeout_lock); + + return removed; +} + +/** + * e_source_refresh_remove_timeouts_by_data: + * @source: an #ESource + * @user_data: user data to match against timeout callbacks + * + * Removes all timeout #GSource's added by e_source_refresh_add_timeout() + * whose callback data pointer matches @user_data. + * + * Returns: the number of timeouts found and removed + * + * Since: 3.6 + **/ +guint +e_source_refresh_remove_timeouts_by_data (ESource *source, + gpointer user_data) +{ + ESourceRefresh *extension; + const gchar *extension_name; + GQueue trash = G_QUEUE_INIT; + GHashTableIter iter; + gpointer key, value; + guint n_removed = 0; + + g_return_val_if_fail (E_IS_SOURCE (source), 0); + + extension_name = E_SOURCE_EXTENSION_REFRESH; + extension = e_source_get_extension (source, extension_name); + + g_mutex_lock (extension->priv->timeout_lock); + + g_hash_table_iter_init (&iter, extension->priv->timeout_table); + + while (g_hash_table_iter_next (&iter, &key, &value)) { + TimeoutNode *node = value; + + if (node->user_data == user_data) + g_queue_push_tail (&trash, key); + } + + while ((key = g_queue_pop_head (&trash)) != NULL) + if (g_hash_table_remove (extension->priv->timeout_table, key)) + n_removed++; + + g_mutex_unlock (extension->priv->timeout_lock); + + return n_removed; +} + diff --git a/libedataserver/e-source-refresh.h b/libedataserver/e-source-refresh.h new file mode 100644 index 0000000..9b05ba9 --- /dev/null +++ b/libedataserver/e-source-refresh.h @@ -0,0 +1,103 @@ +/* + * e-source-refresh.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_REFRESH_H +#define E_SOURCE_REFRESH_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_REFRESH \ + (e_source_refresh_get_type ()) +#define E_SOURCE_REFRESH(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_REFRESH, ESourceRefresh)) +#define E_SOURCE_REFRESH_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_REFRESH, ESourceRefreshClass)) +#define E_IS_SOURCE_REFRESH(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_REFRESH)) +#define E_IS_SOURCE_REFRESH_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_REFRESH)) +#define E_SOURCE_REFRESH_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_REFRESH, ESourceRefreshClass)) + +/** + * E_SOURCE_EXTENSION_REFRESH: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceRefresh. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_REFRESH "Refresh" + +G_BEGIN_DECLS + +typedef struct _ESourceRefresh ESourceRefresh; +typedef struct _ESourceRefreshClass ESourceRefreshClass; +typedef struct _ESourceRefreshPrivate ESourceRefreshPrivate; + +/** + * ESourceRefresh: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceRefresh { + ESourceExtension parent; + ESourceRefreshPrivate *priv; +}; + +struct _ESourceRefreshClass { + ESourceExtensionClass parent_class; +}; + +typedef void (*ESourceRefreshFunc) (ESource *source, + gpointer user_data); + +GType e_source_refresh_get_type (void) G_GNUC_CONST; +gboolean e_source_refresh_get_enabled (ESourceRefresh *extension); +void e_source_refresh_set_enabled (ESourceRefresh *extension, + gboolean enabled); +guint e_source_refresh_get_interval_minutes + (ESourceRefresh *extension); +void e_source_refresh_set_interval_minutes + (ESourceRefresh *extension, + guint interval_minutes); + +guint e_source_refresh_add_timeout (ESource *source, + GMainContext *context, + ESourceRefreshFunc callback, + gpointer user_data, + GDestroyNotify notify); +void e_source_refresh_force_timeout (ESource *source); +gboolean e_source_refresh_remove_timeout (ESource *source, + guint refresh_timeout_id); +guint e_source_refresh_remove_timeouts_by_data + (ESource *source, + gpointer user_data); + +G_END_DECLS + +#endif /* E_SOURCE_REFRESH_H */ diff --git a/libedataserver/e-source-registry.c b/libedataserver/e-source-registry.c new file mode 100644 index 0000000..6d4f5a0 --- /dev/null +++ b/libedataserver/e-source-registry.c @@ -0,0 +1,3225 @@ +/* + * e-source-registry.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-registry + * @include: libedataserver/e-source-registry.h + * @short_description: A central repository for data sources + * + * The #ESourceRegistry is a global singleton store for all #ESource + * instances. It uses file monitors to react to key file creation and + * deletion events, either constructing an #ESource instance from the + * newly created key file, or removing from the logical #ESource + * hierarchy the instance corresponding to the deleted key file. + * + * The #ESourceRegistry can be queried for individual #ESource instances + * by their unique identifier string or key file path, for collections of + * #ESource instances having a particular extension, or for all available + * #ESource instances. + * + * The #ESourceRegistry API also provides a front-end for the + * "org.gnome.Evolution.DefaultSources" #GSettings schema which tracks + * which #ESource instances are designated to be the user's default address + * book, calendar, memo list and task list for desktop integration. + **/ + +#include "e-source-registry.h" + +#include +#include +#include + +/* XXX Yeah, yeah... */ +#define GCR_API_SUBJECT_TO_CHANGE + +#include + +/* Private D-Bus classes. */ +#include +#include + +#include +#include +#include + +/* Needed for the defaults API. */ +#include +#include +#include +#include + +#include "e-dbus-authenticator.h" + +#define E_SOURCE_REGISTRY_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_REGISTRY, ESourceRegistryPrivate)) + +#define DBUS_OBJECT_PATH "/org/gnome/evolution/dataserver/SourceManager" +#define GSETTINGS_SCHEMA "org.gnome.Evolution.DefaultSources" + +/* Built-in data source UIDs. */ +#define E_SOURCE_BUILTIN_ADDRESS_BOOK_UID "system-address-book" +#define E_SOURCE_BUILTIN_CALENDAR_UID "system-calendar" +#define E_SOURCE_BUILTIN_MAIL_ACCOUNT_UID "local" +#define E_SOURCE_BUILTIN_MEMO_LIST_UID "system-memo-list" +#define E_SOURCE_BUILTIN_TASK_LIST_UID "system-task-list" + +/* GSettings keys for default data sources. */ +#define E_SETTINGS_DEFAULT_ADDRESS_BOOK_KEY "default-address-book" +#define E_SETTINGS_DEFAULT_CALENDAR_KEY "default-calendar" +#define E_SETTINGS_DEFAULT_MAIL_ACCOUNT_KEY "default-mail-account" +#define E_SETTINGS_DEFAULT_MAIL_IDENTITY_KEY "default-mail-identity" +#define E_SETTINGS_DEFAULT_MEMO_LIST_KEY "default-memo-list" +#define E_SETTINGS_DEFAULT_TASK_LIST_KEY "default-task-list" + +typedef struct _AsyncContext AsyncContext; +typedef struct _AuthContext AuthContext; +typedef struct _SourceClosure SourceClosure; +typedef struct _ThreadClosure ThreadClosure; + +struct _ESourceRegistryPrivate { + GMainContext *main_context; + + GThread *manager_thread; + ThreadClosure *thread_closure; + + GDBusObjectManager *dbus_object_manager; + EDBusSourceManager *dbus_source_manager; + + GHashTable *object_path_table; + GMutex *object_path_table_lock; + + GHashTable *sources; + GMutex *sources_lock; + + GSettings *settings; +}; + +struct _AsyncContext { + ESource *source; + GList *list_of_sources; + ESourceAuthenticator *auth; +}; + +/* Used in e_source_registry_authenticate_sync() */ +struct _AuthContext { + ESourceAuthenticator *auth; + EDBusAuthenticator *dbus_auth; + GCancellable *cancellable; + GMainLoop *main_loop; + ESourceAuthenticationResult auth_result; + GcrSecretExchange *secret_exchange; + gboolean authenticating; + gboolean success; + GError **error; +}; + +struct _SourceClosure { + ESourceRegistry *registry; + ESource *source; +}; + +struct _ThreadClosure { + ESourceRegistry *registry; + GMainContext *main_context; + GMainLoop *main_loop; + GCond *main_loop_cond; + GMutex *main_loop_mutex; +}; + +enum { + PROP_0, + PROP_DEFAULT_ADDRESS_BOOK, + PROP_DEFAULT_CALENDAR, + PROP_DEFAULT_MAIL_ACCOUNT, + PROP_DEFAULT_MAIL_IDENTITY, + PROP_DEFAULT_MEMO_LIST, + PROP_DEFAULT_TASK_LIST +}; + +enum { + SOURCE_ADDED, + SOURCE_CHANGED, + SOURCE_REMOVED, + SOURCE_ENABLED, + SOURCE_DISABLED, + LAST_SIGNAL +}; + +/* Forward Declarations */ +static void source_registry_add_source (ESourceRegistry *registry, + ESource *source); +static void e_source_registry_initable_init (GInitableIface *interface); + +static guint signals[LAST_SIGNAL]; + +/* By default, the GAsyncInitable interface calls GInitable.init() + * from a separate thread, so we only have to override GInitable. */ +G_DEFINE_TYPE_WITH_CODE ( + ESourceRegistry, + e_source_registry, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE ( + G_TYPE_INITABLE, e_source_registry_initable_init) + G_IMPLEMENT_INTERFACE ( + G_TYPE_ASYNC_INITABLE, NULL)) + +static void +async_context_free (AsyncContext *async_context) +{ + if (async_context->source != NULL) + g_object_unref (async_context->source); + + g_list_free_full ( + async_context->list_of_sources, + (GDestroyNotify) g_object_unref); + + if (async_context->auth != NULL) + g_object_unref (async_context->auth); + + g_slice_free (AsyncContext, async_context); +} + +static void +auth_context_free (AuthContext *auth_context) +{ + if (auth_context->auth != NULL) + g_object_unref (auth_context->auth); + + if (auth_context->dbus_auth != NULL) + g_object_unref (auth_context->dbus_auth); + + if (auth_context->cancellable != NULL) + g_object_unref (auth_context->cancellable); + + if (auth_context->main_loop != NULL) + g_main_loop_unref (auth_context->main_loop); + + if (auth_context->secret_exchange != NULL) + g_object_unref (auth_context->secret_exchange); + + g_slice_free (AuthContext, auth_context); +} + +static void +source_closure_free (SourceClosure *closure) +{ + g_object_unref (closure->registry); + g_object_unref (closure->source); + + g_slice_free (SourceClosure, closure); +} + +static void +thread_closure_free (ThreadClosure *closure) +{ + /* The registry member is not referenced. */ + + g_main_context_unref (closure->main_context); + g_main_loop_unref (closure->main_loop); + g_cond_free (closure->main_loop_cond); + g_mutex_free (closure->main_loop_mutex); + + g_slice_free (ThreadClosure, closure); +} + +static void +source_registry_object_path_table_insert (ESourceRegistry *registry, + const gchar *object_path, + ESource *source) +{ + g_return_if_fail (object_path != NULL); + g_return_if_fail (E_IS_SOURCE (source)); + + g_mutex_lock (registry->priv->object_path_table_lock); + + g_hash_table_insert ( + registry->priv->object_path_table, + g_strdup (object_path), + g_object_ref (source)); + + g_mutex_unlock (registry->priv->object_path_table_lock); +} + +static ESource * +source_registry_object_path_table_lookup (ESourceRegistry *registry, + const gchar *object_path) +{ + ESource *source; + + g_return_val_if_fail (object_path != NULL, NULL); + + g_mutex_lock (registry->priv->object_path_table_lock); + + source = g_hash_table_lookup ( + registry->priv->object_path_table, object_path); + if (source != NULL) + g_object_ref (source); + + g_mutex_unlock (registry->priv->object_path_table_lock); + + return source; +} + +static gboolean +source_registry_object_path_table_remove (ESourceRegistry *registry, + const gchar *object_path) +{ + gboolean removed; + + g_return_val_if_fail (object_path != NULL, FALSE); + + g_mutex_lock (registry->priv->object_path_table_lock); + + removed = g_hash_table_remove ( + registry->priv->object_path_table, object_path); + + g_mutex_unlock (registry->priv->object_path_table_lock); + + return removed; +} + +static void +source_registry_sources_insert (ESourceRegistry *registry, + ESource *source) +{ + const gchar *uid; + + uid = e_source_get_uid (source); + g_return_if_fail (uid != NULL); + + g_mutex_lock (registry->priv->sources_lock); + + g_hash_table_insert ( + registry->priv->sources, + g_strdup (uid), g_object_ref (source)); + + g_mutex_unlock (registry->priv->sources_lock); +} + +static gboolean +source_registry_sources_remove (ESourceRegistry *registry, + ESource *source) +{ + const gchar *uid; + gboolean removed; + + uid = e_source_get_uid (source); + g_return_val_if_fail (uid != NULL, FALSE); + + g_mutex_lock (registry->priv->sources_lock); + + removed = g_hash_table_remove (registry->priv->sources, uid); + + g_mutex_unlock (registry->priv->sources_lock); + + return removed; +} + +static ESource * +source_registry_sources_lookup (ESourceRegistry *registry, + const gchar *uid) +{ + ESource *source; + + g_return_val_if_fail (uid != NULL, NULL); + + g_mutex_lock (registry->priv->sources_lock); + + source = g_hash_table_lookup (registry->priv->sources, uid); + + if (source != NULL) + g_object_ref (source); + + g_mutex_unlock (registry->priv->sources_lock); + + return source; +} + +static GList * +source_registry_sources_get_values (ESourceRegistry *registry) +{ + GList *values; + + g_mutex_lock (registry->priv->sources_lock); + + values = g_hash_table_get_values (registry->priv->sources); + + g_list_foreach (values, (GFunc) g_object_ref, NULL); + + g_mutex_unlock (registry->priv->sources_lock); + + return values; +} + +static GNode * +source_registry_sources_build_tree (ESourceRegistry *registry) +{ + GNode *root; + GHashTable *index; + GHashTableIter iter; + gpointer key, value; + + g_mutex_lock (registry->priv->sources_lock); + + root = g_node_new (NULL); + index = g_hash_table_new (g_str_hash, g_str_equal); + + /* Add a GNode for each ESource to the index. */ + g_hash_table_iter_init (&iter, registry->priv->sources); + while (g_hash_table_iter_next (&iter, &key, &value)) { + ESource *source = g_object_ref (value); + g_hash_table_insert (index, key, g_node_new (source)); + } + + /* Traverse the index and link the nodes together. */ + g_hash_table_iter_init (&iter, index); + while (g_hash_table_iter_next (&iter, NULL, &value)) { + ESource *source; + GNode *source_node; + GNode *parent_node; + const gchar *parent_uid; + + source_node = (GNode *) value; + source = E_SOURCE (source_node->data); + parent_uid = e_source_get_parent (source); + + if (parent_uid == NULL || *parent_uid == '\0') { + parent_node = root; + } else { + parent_node = g_hash_table_lookup (index, parent_uid); + g_warn_if_fail (parent_node != NULL); + } + + /* Should never be NULL, but just to be safe. */ + if (parent_node != NULL) + g_node_append (parent_node, source_node); + } + + g_hash_table_destroy (index); + + g_mutex_unlock (registry->priv->sources_lock); + + return root; +} + +static void +source_registry_settings_changed_cb (GSettings *settings, + const gchar *key, + ESourceRegistry *registry) +{ + /* We define a property name that matches every key in + * the "org.gnome.Evolution.DefaultSources" schema. */ + g_object_notify (G_OBJECT (registry), key); +} + +static gboolean +source_registry_source_changed_idle_cb (gpointer user_data) +{ + SourceClosure *closure = user_data; + + g_signal_emit ( + closure->registry, + signals[SOURCE_CHANGED], 0, + closure->source); + + return FALSE; +} + +static gboolean +source_registry_source_notify_enabled_idle_cb (gpointer user_data) +{ + SourceClosure *closure = user_data; + + if (e_source_get_enabled (closure->source)) + g_signal_emit ( + closure->registry, + signals[SOURCE_ENABLED], 0, + closure->source); + else + g_signal_emit ( + closure->registry, + signals[SOURCE_DISABLED], 0, + closure->source); + + return FALSE; +} + +static void +source_registry_source_changed_cb (ESource *source, + ESourceRegistry *registry) +{ + GSource *idle_source; + SourceClosure *closure; + + closure = g_slice_new0 (SourceClosure); + closure->registry = g_object_ref (registry); + closure->source = g_object_ref (source); + + idle_source = g_idle_source_new (); + g_source_set_callback ( + idle_source, + source_registry_source_changed_idle_cb, + closure, (GDestroyNotify) source_closure_free); + g_source_attach (idle_source, registry->priv->main_context); + g_source_unref (idle_source); +} + +static void +source_registry_source_notify_enabled_cb (ESource *source, + GParamSpec *pspec, + ESourceRegistry *registry) +{ + GSource *idle_source; + SourceClosure *closure; + + closure = g_slice_new0 (SourceClosure); + closure->registry = g_object_ref (registry); + closure->source = g_object_ref (source); + + idle_source = g_idle_source_new (); + g_source_set_callback ( + idle_source, + source_registry_source_notify_enabled_idle_cb, + closure, (GDestroyNotify) source_closure_free); + g_source_attach (idle_source, registry->priv->main_context); + g_source_unref (idle_source); +} + +static void +source_registry_unref_source (ESource *source) +{ + g_signal_handlers_disconnect_matched ( + source, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + source_registry_source_changed_cb, NULL); + + g_signal_handlers_disconnect_matched ( + source, G_SIGNAL_MATCH_FUNC, 0, 0, NULL, + source_registry_source_notify_enabled_cb, NULL); + + g_object_unref (source); +} + +static void +source_registry_add_source (ESourceRegistry *registry, + ESource *source) +{ + const gchar *uid; + + uid = e_source_get_uid (source); + g_return_if_fail (uid != NULL); + + g_mutex_lock (registry->priv->sources_lock); + + /* Check if we already have this source in the registry. */ + if (g_hash_table_lookup (registry->priv->sources, uid) != NULL) { + g_mutex_unlock (registry->priv->sources_lock); + return; + } + + g_signal_connect ( + source, "changed", + G_CALLBACK (source_registry_source_changed_cb), + registry); + + g_signal_connect ( + source, "notify::enabled", + G_CALLBACK (source_registry_source_notify_enabled_cb), + registry); + + g_mutex_unlock (registry->priv->sources_lock); + + source_registry_sources_insert (registry, source); + + g_signal_emit (registry, signals[SOURCE_ADDED], 0, source); +} + +static void +source_registry_remove_source (ESourceRegistry *registry, + ESource *source) +{ + g_object_ref (source); + + if (source_registry_sources_remove (registry, source)) + g_signal_emit (registry, signals[SOURCE_REMOVED], 0, source); + + g_object_unref (source); +} + +static gboolean +source_registry_object_added_idle_cb (gpointer user_data) +{ + SourceClosure *closure = user_data; + + source_registry_add_source (closure->registry, closure->source); + + return FALSE; +} + +static void +source_registry_object_added_cb (GDBusObjectManager *object_manager, + GDBusObject *dbus_object, + ESourceRegistry *registry) +{ + SourceClosure *closure; + GMainContext *main_context; + GSource *idle_source; + ESource *source; + const gchar *object_path; + GError *error = NULL; + + g_return_if_fail (E_DBUS_IS_OBJECT (dbus_object)); + + /* We don't want the ESource emitting "changed" signals from + * the manager thread, so we pass it the same main context the + * registry uses for scheduling signal emissions. */ + main_context = registry->priv->main_context; + source = e_source_new (dbus_object, main_context, &error); + object_path = g_dbus_object_get_object_path (dbus_object); + + /* The likelihood of an error here is slim, so it's + * sufficient to just print a warning if one occurs. */ + if (error != NULL) { + g_warn_if_fail (source == NULL); + g_critical ( + "ESourceRegistry: Failed to create a " + "data source object for path '%s': %s", + object_path, error->message); + g_error_free (error); + return; + } + + g_return_if_fail (E_IS_SOURCE (source)); + + /* Add the ESource to the object path table immediately. */ + source_registry_object_path_table_insert ( + registry, object_path, source); + + /* Schedule a callback on the ESourceRegistry's GMainContext. */ + + closure = g_slice_new0 (SourceClosure); + closure->registry = g_object_ref (registry); + closure->source = g_object_ref (source); + + idle_source = g_idle_source_new (); + g_source_set_callback ( + idle_source, + source_registry_object_added_idle_cb, + closure, (GDestroyNotify) source_closure_free); + g_source_attach (idle_source, registry->priv->main_context); + g_source_unref (idle_source); + + g_object_unref (source); +} + +static gboolean +source_registry_object_removed_idle_cb (gpointer user_data) +{ + SourceClosure *closure = user_data; + + source_registry_remove_source (closure->registry, closure->source); + + return FALSE; +} + +static void +source_registry_object_removed_cb (GDBusObjectManager *manager, + GDBusObject *dbus_object, + ESourceRegistry *registry) +{ + SourceClosure *closure; + GSource *idle_source; + ESource *source; + const gchar *object_path; + + /* Find the corresponding ESource in the object path table. + * Note that the lookup returns a new ESource reference. */ + object_path = g_dbus_object_get_object_path (dbus_object); + source = source_registry_object_path_table_lookup ( + registry, object_path); + g_return_if_fail (E_IS_SOURCE (source)); + + /* Remove the ESource from the object path table immediately. */ + source_registry_object_path_table_remove (registry, object_path); + + /* Schedule a callback on the ESourceRegistry's GMainContext. */ + + closure = g_slice_new0 (SourceClosure); + closure->registry = g_object_ref (registry); + closure->source = g_object_ref (source); + + idle_source = g_idle_source_new (); + g_source_set_callback ( + idle_source, + source_registry_object_removed_idle_cb, + closure, (GDestroyNotify) source_closure_free); + g_source_attach (idle_source, registry->priv->main_context); + g_source_unref (idle_source); + + g_object_unref (source); +} + +static gboolean +source_registry_object_manager_running (gpointer data) +{ + ThreadClosure *closure = data; + + g_mutex_lock (closure->main_loop_mutex); + g_cond_broadcast (closure->main_loop_cond); + g_mutex_unlock (closure->main_loop_mutex); + + return FALSE; +} + +static gpointer +source_registry_object_manager_thread (gpointer data) +{ + GDBusObjectManager *object_manager; + ThreadClosure *closure = data; + GSource *idle_source; + GList *list, *link; + gulong object_added_id; + gulong object_removed_id; + GError *error = NULL; + + /* GDBusObjectManagerClient grabs the thread-default GMainContext + * at creation time and only emits signals from that GMainContext. + * Running it in a separate thread prevents its signal emissions + * from being inhibited by someone overriding the thread-default + * GMainContext. */ + + /* This becomes the GMainContext that GDBusObjectManagerClient + * will emit signals from. Make it the thread-default context + * for this thread before creating the client. */ + g_main_context_push_thread_default (closure->main_context); + + object_manager = e_dbus_object_manager_client_new_for_bus_sync ( + G_BUS_TYPE_SESSION, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, + SOURCES_DBUS_SERVICE_NAME, + DBUS_OBJECT_PATH, + NULL, &error); + + /* If this fails there's really no point in continuing + * since we rely on the object manager to populate the + * registry. Abort the process with a fatal error. */ + if (error != NULL) { + g_error ("%s", error->message); + g_assert_not_reached (); + } + + /* Give the registry a handle to the object manager. */ + closure->registry->priv->dbus_object_manager = + g_object_ref (object_manager); + + /* Now populate the registry with an initial set of ESources. */ + + list = g_dbus_object_manager_get_objects (object_manager); + + for (link = list; link != NULL; link = g_list_next (link)) + source_registry_object_added_cb ( + object_manager, + G_DBUS_OBJECT (link->data), + closure->registry); + + g_list_free_full (list, (GDestroyNotify) g_object_unref); + + /* Schedule a one-time idle callback to broadcast through a + * condition variable that our main loop is up and running. */ + + idle_source = g_idle_source_new (); + g_source_set_callback ( + idle_source, + source_registry_object_manager_running, + closure, (GDestroyNotify) NULL); + g_source_attach (idle_source, closure->main_context); + g_source_unref (idle_source); + + /* Listen for D-Bus object additions and removals. */ + + object_added_id = g_signal_connect ( + object_manager, "object-added", + G_CALLBACK (source_registry_object_added_cb), + closure->registry); + + object_removed_id = g_signal_connect ( + object_manager, "object-removed", + G_CALLBACK (source_registry_object_removed_cb), + closure->registry); + + /* Now we mostly idle here for the rest of the session. */ + + g_main_loop_run (closure->main_loop); + + /* Clean up and exit. */ + + g_signal_handler_disconnect (object_manager, object_added_id); + g_signal_handler_disconnect (object_manager, object_removed_id); + + g_object_unref (object_manager); + + g_main_context_pop_thread_default (closure->main_context); + + return NULL; +} + +static void +source_registry_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_DEFAULT_ADDRESS_BOOK: + e_source_registry_set_default_address_book ( + E_SOURCE_REGISTRY (object), + g_value_get_object (value)); + return; + + case PROP_DEFAULT_CALENDAR: + e_source_registry_set_default_calendar ( + E_SOURCE_REGISTRY (object), + g_value_get_object (value)); + return; + + case PROP_DEFAULT_MAIL_ACCOUNT: + e_source_registry_set_default_mail_account ( + E_SOURCE_REGISTRY (object), + g_value_get_object (value)); + return; + + case PROP_DEFAULT_MAIL_IDENTITY: + e_source_registry_set_default_mail_identity ( + E_SOURCE_REGISTRY (object), + g_value_get_object (value)); + return; + + case PROP_DEFAULT_MEMO_LIST: + e_source_registry_set_default_memo_list ( + E_SOURCE_REGISTRY (object), + g_value_get_object (value)); + return; + + case PROP_DEFAULT_TASK_LIST: + e_source_registry_set_default_task_list ( + E_SOURCE_REGISTRY (object), + g_value_get_object (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_registry_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_DEFAULT_ADDRESS_BOOK: + g_value_take_object ( + value, + e_source_registry_ref_default_address_book ( + E_SOURCE_REGISTRY (object))); + return; + + case PROP_DEFAULT_CALENDAR: + g_value_take_object ( + value, + e_source_registry_ref_default_calendar ( + E_SOURCE_REGISTRY (object))); + return; + + case PROP_DEFAULT_MAIL_ACCOUNT: + g_value_take_object ( + value, + e_source_registry_ref_default_mail_account ( + E_SOURCE_REGISTRY (object))); + return; + + case PROP_DEFAULT_MAIL_IDENTITY: + g_value_take_object ( + value, + e_source_registry_ref_default_mail_identity ( + E_SOURCE_REGISTRY (object))); + return; + + case PROP_DEFAULT_MEMO_LIST: + g_value_take_object ( + value, + e_source_registry_ref_default_memo_list ( + E_SOURCE_REGISTRY (object))); + return; + + case PROP_DEFAULT_TASK_LIST: + g_value_take_object ( + value, + e_source_registry_ref_default_task_list ( + E_SOURCE_REGISTRY (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_registry_dispose (GObject *object) +{ + ESourceRegistryPrivate *priv; + + priv = E_SOURCE_REGISTRY_GET_PRIVATE (object); + + /* Terminate the manager thread first. */ + if (priv->manager_thread != NULL) { + g_main_loop_quit (priv->thread_closure->main_loop); + g_thread_join (priv->manager_thread); + thread_closure_free (priv->thread_closure); + priv->manager_thread = NULL; + priv->thread_closure = NULL; + } + + if (priv->main_context != NULL) { + g_main_context_unref (priv->main_context); + priv->main_context = NULL; + } + + if (priv->dbus_object_manager != NULL) { + g_object_unref (priv->dbus_object_manager); + priv->dbus_object_manager = NULL; + } + + if (priv->dbus_source_manager != NULL) { + g_object_unref (priv->dbus_source_manager); + priv->dbus_source_manager = NULL; + } + + g_hash_table_remove_all (priv->object_path_table); + + g_hash_table_remove_all (priv->sources); + + if (priv->settings != NULL) { + g_object_unref (priv->settings); + priv->settings = NULL; + } + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_registry_parent_class)->dispose (object); +} + +static void +source_registry_finalize (GObject *object) +{ + ESourceRegistryPrivate *priv; + + priv = E_SOURCE_REGISTRY_GET_PRIVATE (object); + + g_hash_table_destroy (priv->object_path_table); + g_mutex_free (priv->object_path_table_lock); + + g_hash_table_destroy (priv->sources); + g_mutex_free (priv->sources_lock); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_registry_parent_class)->finalize (object); +} + +static gboolean +source_registry_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + ESourceRegistry *registry; + ThreadClosure *closure; + + registry = E_SOURCE_REGISTRY (initable); + + closure = g_slice_new0 (ThreadClosure); + closure->registry = registry; /* do not reference */ + closure->main_context = g_main_context_new (); + /* It's important to pass 'is_running=FALSE' here because + * we wait for the main loop to start running as a way of + * synchronizing with the manager thread. */ + closure->main_loop = g_main_loop_new (closure->main_context, FALSE); + closure->main_loop_cond = g_cond_new (); + closure->main_loop_mutex = g_mutex_new (); + + registry->priv->thread_closure = closure; + + registry->priv->manager_thread = g_thread_create ( + source_registry_object_manager_thread, + closure, TRUE /* joinable */, error); + + if (registry->priv->manager_thread == NULL) + return FALSE; + + /* Wait for notification that the manager + * thread's main loop has been started. */ + g_mutex_lock (closure->main_loop_mutex); + while (!g_main_loop_is_running (closure->main_loop)) + g_cond_wait ( + closure->main_loop_cond, + closure->main_loop_mutex); + g_mutex_unlock (closure->main_loop_mutex); + + /* We should now have a GDBusObjectManagerClient available. */ + g_return_val_if_fail ( + G_IS_DBUS_OBJECT_MANAGER_CLIENT ( + registry->priv->dbus_object_manager), FALSE); + + /* The manager thread will have queued up a bunch of idle + * sources on our GMainContext to populate the registry. + * Iterate our GMainContext until they get dispatched. */ + while (g_hash_table_size (registry->priv->sources) == 0) + g_main_context_iteration (registry->priv->main_context, TRUE); + + /* The EDBusSourceManagerProxy is just another D-Bus interface + * that resides at the same object path. It's unrelated to the + * GDBusObjectManagerClient and doesn't need its own thread. */ + registry->priv->dbus_source_manager = + e_dbus_source_manager_proxy_new_for_bus_sync ( + G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + SOURCES_DBUS_SERVICE_NAME, + DBUS_OBJECT_PATH, + cancellable, error); + + if (registry->priv->dbus_source_manager == NULL) + return FALSE; + + return TRUE; +} + +static void +e_source_registry_class_init (ESourceRegistryClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (ESourceRegistryPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_registry_set_property; + object_class->get_property = source_registry_get_property; + object_class->dispose = source_registry_dispose; + object_class->finalize = source_registry_finalize; + + /* The property names correspond to the key names in the + * "org.gnome.Evolution.DefaultSources" GSettings schema. */ + + /** + * ESourceRegistry:default-address-book + * + * The default address book #ESource. + **/ + g_object_class_install_property ( + object_class, + PROP_DEFAULT_ADDRESS_BOOK, + g_param_spec_object ( + "default-address-book", + "Default Address Book", + "The default address book ESource", + E_TYPE_SOURCE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * ESourceRegistry:default-calendar + * + * The default calendar #ESource. + **/ + g_object_class_install_property ( + object_class, + PROP_DEFAULT_CALENDAR, + g_param_spec_object ( + "default-calendar", + "Default Calendar", + "The default calendar ESource", + E_TYPE_SOURCE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * ESourceRegistry:default-mail-account + * + * The default mail account #ESource. + **/ + g_object_class_install_property ( + object_class, + PROP_DEFAULT_MAIL_ACCOUNT, + g_param_spec_object ( + "default-mail-account", + "Default Mail Account", + "The default mail account ESource", + E_TYPE_SOURCE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * ESourceRegistry:default-mail-identity + * + * The default mail identity #ESource. + **/ + g_object_class_install_property ( + object_class, + PROP_DEFAULT_MAIL_IDENTITY, + g_param_spec_object ( + "default-mail-identity", + "Default Mail Identity", + "The default mail identity ESource", + E_TYPE_SOURCE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * ESourceRegistry:default-memo-list + * + * The default memo list #ESource. + **/ + g_object_class_install_property ( + object_class, + PROP_DEFAULT_MEMO_LIST, + g_param_spec_object ( + "default-memo-list", + "Default Memo List", + "The default memo list ESource", + E_TYPE_SOURCE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * ESourceRegistry:default-task-list + * + * The default task list #ESource. + **/ + g_object_class_install_property ( + object_class, + PROP_DEFAULT_TASK_LIST, + g_param_spec_object ( + "default-task-list", + "Default Task List", + "The default task list ESource", + E_TYPE_SOURCE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); + + /** + * ESourceRegistry::source-added: + * @registry: the #ESourceRegistry which emitted the signal + * @source: the newly-added #ESource + * + * Emitted when an #ESource is added to @registry. + **/ + signals[SOURCE_ADDED] = g_signal_new ( + "source-added", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ESourceRegistryClass, source_added), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_SOURCE); + + /** + * ESourceRegistry::source-changed + * @registry: the #ESourceRegistry which emitted the signal + * @source: the #ESource that changed + * + * Emitted when an #ESource registered with @registry emits + * its #ESource::changed signal. + **/ + signals[SOURCE_CHANGED] = g_signal_new ( + "source-changed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ESourceRegistryClass, source_changed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_SOURCE); + + /** + * ESourceRegistry::source-removed: + * @registry: the #ESourceRegistry which emitted the signal + * @source: the #ESource that got removed + * + * Emitted when an #ESource is removed from @registry. + **/ + signals[SOURCE_REMOVED] = g_signal_new ( + "source-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ESourceRegistryClass, source_removed), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_SOURCE); + + /** + * ESourceRegistry::source-enabled: + * @registry: the #ESourceRegistry which emitted the signal + * @source: the #ESource that got enabled + * + * Emitted when an #ESource #ESource:enabled property becomes %TRUE. + **/ + signals[SOURCE_ENABLED] = g_signal_new ( + "source-enabled", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ESourceRegistryClass, source_enabled), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_SOURCE); + + /** + * ESourceRegistry::source-disabled: + * @registry: the #ESourceRegistry which emitted the signal + * @source: the #ESource that got disabled + * + * Emitted when an #ESource #ESource:enabled property becomes %FALSE. + **/ + signals[SOURCE_DISABLED] = g_signal_new ( + "source-disabled", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (ESourceRegistryClass, source_disabled), + NULL, NULL, + g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, + E_TYPE_SOURCE); +} + +static void +e_source_registry_initable_init (GInitableIface *interface) +{ + interface->init = source_registry_initable_init; +} + +static void +e_source_registry_init (ESourceRegistry *registry) +{ + registry->priv = E_SOURCE_REGISTRY_GET_PRIVATE (registry); + + /* This is so the object manager thread can schedule signal + * emissions on the thread-default context for this thread. */ + registry->priv->main_context = g_main_context_get_thread_default (); + if (registry->priv->main_context != NULL) + g_main_context_ref (registry->priv->main_context); + + /* D-Bus object path -> ESource */ + registry->priv->object_path_table = + g_hash_table_new_full ( + (GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_object_unref); + + registry->priv->object_path_table_lock = g_mutex_new (); + + /* UID string -> ESource */ + registry->priv->sources = g_hash_table_new_full ( + (GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) source_registry_unref_source); + + registry->priv->sources_lock = g_mutex_new (); + + registry->priv->settings = g_settings_new (GSETTINGS_SCHEMA); + + g_signal_connect ( + registry->priv->settings, "changed", + G_CALLBACK (source_registry_settings_changed_cb), registry); +} + +/** + * e_source_registry_new_sync: + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @error: return location for a #GError, or %NULL + * + * Creates a new #ESourceRegistry front-end for the registry D-Bus service. + * If an error occurs in connecting to the D-Bus service, the function sets + * @error and returns %NULL. + * + * Returns: a new #ESourceRegistry, or %NULL + * + * Since: 3.6 + **/ +ESourceRegistry * +e_source_registry_new_sync (GCancellable *cancellable, + GError **error) +{ + return g_initable_new ( + E_TYPE_SOURCE_REGISTRY, + cancellable, error, NULL); +} + +/** + * e_source_registry_new: + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @callback: (scope async): a #GAsyncReadyCallback to call when the request + * is satisfied + * @user_data: (closure): data to pass to the callback function + * + * Asynchronously creates a new #ESourceRegistry front-end for the registry + * D-Bus service. + * + * When the operation is finished, @callback will be called. You can then + * call e_source_registry_new_finish() to get the result of the operation. + * + * Since: 3.6 + **/ +void +e_source_registry_new (GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_async_initable_new_async ( + E_TYPE_SOURCE_REGISTRY, + G_PRIORITY_DEFAULT, cancellable, + callback, user_data, NULL); +} + +/** + * e_source_registry_new_finish: + * @result: a #GAsyncResult + * @error: return location for a #GError, or %NULL + * + * Finishes the operation started with e_source_registry_new_finish(). + * If an error occurs in connecting to the D-Bus service, the function + * sets @error and returns %NULL. + * + * Returns: a new #ESourceRegistry, or %NULL + * + * Since: 3.6 + **/ +ESourceRegistry * +e_source_registry_new_finish (GAsyncResult *result, + GError **error) +{ + GObject *source_object; + GObject *object; + + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL); + + source_object = g_async_result_get_source_object (result); + g_return_val_if_fail (source_object != NULL, NULL); + + object = g_async_initable_new_finish ( + G_ASYNC_INITABLE (source_object), result, error); + + g_object_unref (source_object); + + return (object != NULL) ? E_SOURCE_REGISTRY (object) : NULL; +} + +/* Helper for e_source_registry_authenticate() */ +static void +source_registry_authenticate_thread (GSimpleAsyncResult *simple, + GObject *object, + GCancellable *cancellable) +{ + AsyncContext *async_context; + GError *error = NULL; + + async_context = g_simple_async_result_get_op_res_gpointer (simple); + + e_source_registry_authenticate_sync ( + E_SOURCE_REGISTRY (object), + async_context->source, + async_context->auth, + cancellable, &error); + + if (error != NULL) + g_simple_async_result_take_error (simple, error); +} + +/* Helper for e_source_registry_authenticate_sync() */ +static gboolean +source_registry_authenticate_respond_cb (AuthContext *auth_context) +{ + ESourceAuthenticationResult auth_result; + GError *non_fatal_error = NULL; + + g_return_val_if_fail (auth_context->authenticating, FALSE); + + auth_result = auth_context->auth_result; + + /* Allow the next authentication attempt to proceed. */ + auth_context->authenticating = FALSE; + + /* Send the server a status update based on the authentication + * result. Note, we don't really care if the D-Bus message gets + * through to the server at this point. If it doesn't, the auth + * session will either time out on its own or the authentication + * dialog will eventually be dismissed by the user. */ + + /* If an error occurred while attempting to authenticate, + * tell the server to cancel the authentication session. */ + if (auth_result == E_SOURCE_AUTHENTICATION_ERROR) { + e_dbus_authenticator_call_cancel_sync ( + auth_context->dbus_auth, + auth_context->cancellable, + &non_fatal_error); + g_main_loop_quit (auth_context->main_loop); + auth_context->success = FALSE; + + /* If the password was accepted, let the server know so it + * can close any authentication dialogs and save the user + * provided password to the keyring. */ + } else if (auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) { + e_dbus_authenticator_call_accepted_sync ( + auth_context->dbus_auth, + auth_context->cancellable, + &non_fatal_error); + g_main_loop_quit (auth_context->main_loop); + auth_context->success = TRUE; + + /* If the password was rejected, let the server know so it can + * indicate failure and request a different password, and then + * wait for the next "response" signal. */ + } else { + e_dbus_authenticator_call_rejected_sync ( + auth_context->dbus_auth, + auth_context->cancellable, + &non_fatal_error); + } + + /* Leave breadcrumbs if something went wrong, + * but don't fail the whole operation over it. */ + if (non_fatal_error != NULL) { + g_warning ("%s: %s", G_STRFUNC, non_fatal_error->message); + g_error_free (non_fatal_error); + } + + return FALSE; +} + +/* Helper for e_source_registry_authenticate_sync() */ +static void +source_registry_authenticate_authenticate_cb (EDBusAuthenticator *dbus_auth, + const gchar *encrypted_secret, + AuthContext *auth_context) +{ + GSource *idle_source; + GMainContext *main_context; + GString *password; + gboolean valid_secret; + + /* We should only get one secret at a time. */ + g_return_if_fail (!auth_context->authenticating); + + valid_secret = gcr_secret_exchange_receive ( + auth_context->secret_exchange, encrypted_secret); + g_return_if_fail (valid_secret); + + auth_context->authenticating = TRUE; + + /* This avoids revealing the password in a stack trace. */ + password = g_string_new ( + gcr_secret_exchange_get_secret ( + auth_context->secret_exchange, NULL)); + + /* Try authenticating with the given password. We have to + * call this synchronously because some authenticators use + * mutexes to serialize I/O operations and are not prepared + * to make authentication attempts from a different thread. + * + * Unfortunately this means we won't notice server-side + * dismissals while the main loop is blocked. We respond + * to the server from a low-priority idle callback so that + * any pending "dismissed" signals get handled first. */ + + auth_context->auth_result = + e_source_authenticator_try_password_sync ( + auth_context->auth, password, + auth_context->cancellable, + auth_context->error); + + idle_source = g_idle_source_new (); + main_context = g_main_context_get_thread_default (); + g_source_set_callback ( + idle_source, (GSourceFunc) + source_registry_authenticate_respond_cb, + auth_context, NULL); + g_source_attach (idle_source, main_context); + g_source_unref (idle_source); + + g_string_free (password, TRUE); +} + +/* Helper for e_source_registry_authenticate_sync() */ +static void +source_registry_authenticate_dismissed_cb (EDBusAuthenticator *dbus_auth, + AuthContext *auth_context) +{ + /* Be careful not to overwrite an existing error in case this + * is called after e_source_authenticator_try_password_sync() + * but prior to the idle callback. */ + if (auth_context->auth_result != E_SOURCE_AUTHENTICATION_ERROR) { + /* XXX Use a separate error code for dismissals? */ + g_set_error_literal ( + auth_context->error, + G_IO_ERROR, G_IO_ERROR_CANCELLED, + _("The user declined to authenticate")); + auth_context->auth_result = E_SOURCE_AUTHENTICATION_ERROR; + } + + g_main_loop_quit (auth_context->main_loop); + auth_context->success = FALSE; +} + +/* Helper for e_source_registry_authenticate_sync() */ +static gboolean +source_registry_call_authenticate_for_source (ESourceRegistry *registry, + ESourceAuthenticator *auth, + ESource *source, + gchar **out_object_path, + GCancellable *cancellable, + GError **error) +{ + ESource *collection; + const gchar *uid; + gchar *prompt_title = NULL; + gchar *prompt_message = NULL; + gchar *prompt_description = NULL; + gboolean success; + + /* If the source is a member of a collection, we want to store + * the password under the UID of the "collection" source so it + * will apply to the entire collection. + * + * XXX This assumes all sources in a collection share a single + * password. If that turns out not to be true in all cases + * we could maybe add a "SharedPassword: true/false" key to + * [Collection] and apply it here. + */ + collection = e_source_registry_find_extension ( + registry, source, E_SOURCE_EXTENSION_COLLECTION); + if (collection != NULL) + source = collection; + else + g_object_ref (source); + + uid = e_source_get_uid (source); + + e_source_authenticator_get_prompt_strings ( + auth, source, + &prompt_title, + &prompt_message, + &prompt_description); + + success = e_dbus_source_manager_call_authenticate_sync ( + registry->priv->dbus_source_manager, uid, + prompt_title, prompt_message, prompt_description, + out_object_path, cancellable, error); + + g_free (prompt_title); + g_free (prompt_message); + g_free (prompt_description); + + g_object_unref (source); + + return success; +} + +/** + * e_source_registry_authenticate_sync: + * @registry: an #ESourceRegistry + * @source: an #ESource + * @auth: an #ESourceAuthenticator + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @error: return location for a #GError, or %NULL + * + * Authenticates @source, using @auth to handle the authentication + * attempts. The operation loops until authentication is successful or + * the user aborts further authentication attempts. If an error occurs, + * the function will set @error and return %FALSE. + * + * Note that @source need not have a #GDBusObject, which means this + * function can test authentication on a scratch #ESource. + * + * Only backend implementations and data source editors should call this + * function. The intent is for basic client applications to not have to + * deal with authentication at all. + * + * Returns: %TRUE on success, %FALSE on failure + * + * Since: 3.6 + **/ +gboolean +e_source_registry_authenticate_sync (ESourceRegistry *registry, + ESource *source, + ESourceAuthenticator *auth, + GCancellable *cancellable, + GError **error) +{ + AuthContext *auth_context; + GMainContext *main_context; + EDBusAuthenticator *dbus_auth; + gchar *encryption_key; + gchar *object_path = NULL; + gboolean success; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE); + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth), FALSE); + + /* This extracts authentication prompt details for the ESource + * before initiating an authentication session with the server, + * so split it out of the main algorithm for clarity's sake. */ + success = source_registry_call_authenticate_for_source ( + registry, auth, source, &object_path, cancellable, error); + + if (!success) { + g_warn_if_fail (object_path == NULL); + return FALSE; + } + + g_return_val_if_fail (object_path != NULL, FALSE); + + main_context = g_main_context_new (); + g_main_context_push_thread_default (main_context); + + dbus_auth = e_dbus_authenticator_proxy_new_for_bus_sync ( + G_BUS_TYPE_SESSION, + G_DBUS_PROXY_FLAGS_NONE, + SOURCES_DBUS_SERVICE_NAME, + object_path, cancellable, error); + + g_free (object_path); + + if (dbus_auth == NULL) { + success = FALSE; + goto exit; + } + + auth_context = g_slice_new0 (AuthContext); + auth_context->auth = g_object_ref (auth); + auth_context->dbus_auth = dbus_auth; /* takes ownership */ + auth_context->main_loop = g_main_loop_new (main_context, FALSE); + auth_context->error = error; + + /* This just needs to be something other than + * E_SOURCE_AUTHENTICATION_ERROR so we don't trip + * up source_registry_authenticate_dismissed_cb(). */ + auth_context->auth_result = E_SOURCE_AUTHENTICATION_REJECTED; + + if (G_IS_CANCELLABLE (cancellable)) + auth_context->cancellable = g_object_ref (cancellable); + + auth_context->secret_exchange = + gcr_secret_exchange_new (GCR_SECRET_EXCHANGE_PROTOCOL_1); + + g_signal_connect ( + dbus_auth, "authenticate", + G_CALLBACK (source_registry_authenticate_authenticate_cb), + auth_context); + + g_signal_connect ( + dbus_auth, "dismissed", + G_CALLBACK (source_registry_authenticate_dismissed_cb), + auth_context); + + encryption_key = gcr_secret_exchange_begin ( + auth_context->secret_exchange); + + /* Signal the D-Bus server that we're ready to begin the + * authentication session. This must happen AFTER we've + * connected to the response signal since the server may + * already have a response ready and waiting for us. */ + success = e_dbus_authenticator_call_ready_sync ( + dbus_auth, encryption_key, cancellable, error); + + g_free (encryption_key); + + if (success) { + g_main_loop_run (auth_context->main_loop); + success = auth_context->success; + } + + auth_context_free (auth_context); + +exit: + g_main_context_pop_thread_default (main_context); + g_main_context_unref (main_context); + + return success; +} + +/** + * e_source_registry_authenticate: + * @registry: an #ESourceRegistry + * @source: an #ESource + * @auth: an #ESourceAuthenticator + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @callback: (scope async): a #GAsyncReadyCallback to call when the request + * is satisfied + * @user_data: (closure): data to pass to the callback function + * + * Asynchronously authenticates @source, using @auth to handle the + * authentication attempts. The operation loops until authentication + * is successful or the user aborts further authentication attempts. + * + * Note that @source need not have a #GDBusObject, which means this + * function can test authentication on a scratch #ESource. + * + * When the operation is finished, @callback will be called. You can then + * call e_source_registry_authenticate_finish() to get the result of the + * operation. + * + * Only backend implementations and data source editors should call this + * function. The intent is for basic client applications to not have to + * deal with authentication at all. + * + * Since: 3.6 + **/ +void +e_source_registry_authenticate (ESourceRegistry *registry, + ESource *source, + ESourceAuthenticator *auth, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + AsyncContext *async_context; + + g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); + g_return_if_fail (E_IS_SOURCE (source)); + g_return_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth)); + + async_context = g_slice_new0 (AsyncContext); + async_context->source = g_object_ref (source); + async_context->auth = g_object_ref (auth); + + simple = g_simple_async_result_new ( + G_OBJECT (registry), callback, user_data, + e_source_registry_authenticate); + + g_simple_async_result_set_check_cancellable (simple, cancellable); + + g_simple_async_result_set_op_res_gpointer ( + simple, async_context, (GDestroyNotify) async_context_free); + + g_simple_async_result_run_in_thread ( + simple, source_registry_authenticate_thread, + G_PRIORITY_DEFAULT, cancellable); + + g_object_unref (simple); +} + +/** + * e_source_registry_authenticate_finish: + * @registry: an #ESourceRegistry + * @result: a #GAsyncResult + * @error: return location for a #GError, or %NULL + * + * Finishes the operation started with e_source_registry_authenticate(). + * If an error occurred, the function will set @error and return %FALSE. + * + * Returns: %TRUE on success, %FALSE on failure + * + * Since: 3.6 + **/ +gboolean +e_source_registry_authenticate_finish (ESourceRegistry *registry, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail ( + g_simple_async_result_is_valid ( + result, G_OBJECT (registry), + e_source_registry_authenticate), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + /* Assume success unless a GError is set. */ + return !g_simple_async_result_propagate_error (simple, error); +} + +/* Helper for e_source_registry_commit_source() */ +static void +source_registry_commit_source_thread (GSimpleAsyncResult *simple, + GObject *object, + GCancellable *cancellable) +{ + AsyncContext *async_context; + GError *error = NULL; + + async_context = g_simple_async_result_get_op_res_gpointer (simple); + + e_source_registry_commit_source_sync ( + E_SOURCE_REGISTRY (object), + async_context->source, + cancellable, &error); + + if (error != NULL) + g_simple_async_result_take_error (simple, error); +} + +/** + * e_source_registry_commit_source_sync: + * @registry: an #ESourceRegistry + * @source: an #ESource with changes to commit + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @error: return location for #GError, or %NULL + * + * This is a convenience function intended for use with graphical + * #ESource editors. Call this function when the user is finished + * making changes to @source. + * + * If @source has a #GDBusObject, its contents are submitted to the D-Bus + * service through e_source_write_sync(). + * + * If @source does NOT have a #GDBusObject (implying it's a scratch + * #ESource), its contents are submitted to the D-Bus service through + * e_source_registry_create_sources_sync(). + * + * If an error occurs, the function will set @error and return %FALSE. + * + * Returns: %TRUE on success, %FALSE on failure + * + * Since: 3.6 + **/ +gboolean +e_source_registry_commit_source_sync (ESourceRegistry *registry, + ESource *source, + GCancellable *cancellable, + GError **error) +{ + GDBusObject *dbus_object; + gboolean success; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE); + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + + dbus_object = e_source_ref_dbus_object (source); + + if (dbus_object != NULL) { + success = e_source_write_sync (source, cancellable, error); + g_object_unref (dbus_object); + } else { + GList *list = g_list_prepend (NULL, source); + success = e_source_registry_create_sources_sync ( + registry, list, cancellable, error); + g_list_free (list); + } + + return success; +} + +/** + * e_source_registry_commit_source: + * @registry: an #ESourceRegistry + * @source: an #ESource with changes to commit + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @callback: (scope async): a #GAsyncReadyCallback to call when the request + * is satisfied + * @user_data: (closure): data to pass to the callback function + * + * See e_source_registry_commit_source_sync() for details. + * + * When the operation is finished, @callback will be called. You can then + * call e_source_registry_commit_source_finish() to get the result of the + * operation. + * + * Since: 3.6 + **/ +void +e_source_registry_commit_source (ESourceRegistry *registry, + ESource *source, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + AsyncContext *async_context; + + g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); + g_return_if_fail (E_IS_SOURCE (source)); + + async_context = g_slice_new0 (AsyncContext); + async_context->source = g_object_ref (source); + + simple = g_simple_async_result_new ( + G_OBJECT (registry), callback, user_data, + e_source_registry_commit_source); + + g_simple_async_result_set_check_cancellable (simple, cancellable); + + g_simple_async_result_set_op_res_gpointer ( + simple, async_context, (GDestroyNotify) async_context_free); + + g_simple_async_result_run_in_thread ( + simple, source_registry_commit_source_thread, + G_PRIORITY_DEFAULT, cancellable); + + g_object_unref (simple); +} + +/** + * e_source_registry_commit_source_finish: + * @registry: an #ESourceRegistry + * @result: a #GAsyncResult + * @error: return location for a #GError, or %NULL + * + * Finishes the operation started with e_source_registry_commit_source(). + * + * If an error occurred, the function will set @error and return %FALSE. + * + * Returns: %TRUE on success, %FALSE on failure + * + * Since: 3.6 + **/ +gboolean +e_source_registry_commit_source_finish (ESourceRegistry *registry, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail ( + g_simple_async_result_is_valid ( + result, G_OBJECT (registry), + e_source_registry_commit_source), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + /* Assume success unless a GError is set. */ + return !g_simple_async_result_propagate_error (simple, error); +} + +/* Helper for e_source_registry_create_sources() */ +static void +source_registry_create_sources_thread (GSimpleAsyncResult *simple, + GObject *object, + GCancellable *cancellable) +{ + AsyncContext *async_context; + GError *error = NULL; + + async_context = g_simple_async_result_get_op_res_gpointer (simple); + + e_source_registry_create_sources_sync ( + E_SOURCE_REGISTRY (object), + async_context->list_of_sources, + cancellable, &error); + + if (error != NULL) + g_simple_async_result_take_error (simple, error); +} + +/** + * e_source_registry_create_sources_sync: + * @registry: an #ESourceRegistry + * @list_of_sources: a list of #ESource instances with no #GDBusObject + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @error: return location for a #GError, or %NULL + * + * Requests the D-Bus service create new key files for each #ESource in + * @list_of_sources. Each list element must be a scratch #ESource with + * no #GDBusObject. + * + * If an error occurs, the function will set @error and return %FALSE. + * + * Returns: %TRUE on success, %FALSE on failure + * + * Since: 3.6 + **/ +gboolean +e_source_registry_create_sources_sync (ESourceRegistry *registry, + GList *list_of_sources, + GCancellable *cancellable, + GError **error) +{ + GVariantBuilder builder; + GVariant *variant; + GList *link; + gboolean success; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE); + + /* Verify the list elements are all ESources. */ + for (link = list_of_sources; link != NULL; link = g_list_next (link)) + g_return_val_if_fail (E_IS_SOURCE (link->data), FALSE); + + g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); + + for (link = list_of_sources; link != NULL; link = g_list_next (link)) { + ESource *source; + const gchar *uid; + gchar *source_data; + + source = E_SOURCE (link->data); + uid = e_source_get_uid (source); + + source_data = e_source_to_string (source, NULL); + g_variant_builder_add (&builder, "{ss}", uid, source_data); + g_free (source_data); + } + + variant = g_variant_builder_end (&builder); + + /* This function sinks the floating GVariant reference. */ + success = e_dbus_source_manager_call_create_sources_sync ( + registry->priv->dbus_source_manager, + variant, cancellable, error); + + g_variant_builder_clear (&builder); + + return success; +} + +/** + * e_source_registry_create_sources: + * @registry: an #ESourceRegistry + * @list_of_sources: a list of #ESource instances with no #GDBusObject + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @callback: (scope async): a #GAsyncReadyCallback to call when the request + * is satisfied + * @user_data: (closure): data to pass to the callback function + * + * Asynchronously requests the D-Bus service create new key files for each + * #ESource in @list_of_sources. Each list element must be a scratch + * #ESource with no #GDBusObject. + * + * When the operation is finished, @callback will be called. You can then + * call e_source_registry_create_sources_finish() to get the result of the + * operation. + * + * Since: 3.6 + **/ +void +e_source_registry_create_sources (ESourceRegistry *registry, + GList *list_of_sources, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + AsyncContext *async_context; + GList *link; + + g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); + + /* Verify the list elements are all ESources. */ + for (link = list_of_sources; link != NULL; link = g_list_next (link)) + g_return_if_fail (E_IS_SOURCE (link->data)); + + async_context = g_slice_new0 (AsyncContext); + async_context->list_of_sources = g_list_copy (list_of_sources); + + g_list_foreach ( + async_context->list_of_sources, + (GFunc) g_object_ref, NULL); + + simple = g_simple_async_result_new ( + G_OBJECT (registry), callback, user_data, + e_source_registry_create_sources); + + g_simple_async_result_set_check_cancellable (simple, cancellable); + + g_simple_async_result_set_op_res_gpointer ( + simple, async_context, (GDestroyNotify) async_context_free); + + g_simple_async_result_run_in_thread ( + simple, source_registry_create_sources_thread, + G_PRIORITY_DEFAULT, cancellable); + + g_object_unref (simple); +} + +/** + * e_source_registry_create_sources_finish: + * @registry: an #ESourceRegistry + * @result: a #GAsyncResult + * @error: return location for a #GError, or %NULL + * + * Finishes the operation started with e_source_registry_create_sources(). + * + * If an error occurred, the function will set @error and return %FALSE. + * + * Returns: %TRUE on success, %FALSE on failure + * + * Since: 3.6 + **/ +gboolean +e_source_registry_create_sources_finish (ESourceRegistry *registry, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail ( + g_simple_async_result_is_valid ( + result, G_OBJECT (registry), + e_source_registry_create_sources), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + /* Assume success unless a GError is set. */ + return !g_simple_async_result_propagate_error (simple, error); +} + +/** + * e_source_registry_ref_source: + * @registry: an #ESourceRegistry + * @uid: a unique identifier string + * + * Looks up an #ESource in @registry by its unique identifier string. + * + * The returned #ESource is referenced for thread-safety and must be + * unreferenced with g_object_unref() when finished with it. + * + * Returns: (transfer full): an #ESource, or %NULL if no match was found + * + * Since: 3.6 + **/ +ESource * +e_source_registry_ref_source (ESourceRegistry *registry, + const gchar *uid) +{ + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + g_return_val_if_fail (uid != NULL, NULL); + + return source_registry_sources_lookup (registry, uid); +} + +/** + * e_source_registry_list_sources: + * @registry: an #ESourceRegistry + * @extension_name: (allow-none): an extension name, or %NULL + * + * Returns a list of registered sources, sorted by display name. If + * @extension_name is given, restrict the list to sources having that + * extension name. + * + * The sources returned in the list are referenced for thread-safety. + * They must each be unreferenced with g_object_unref() when finished + * when them. Free the returned list itself with g_list_free(). + * + * An easy way to free the list properly in one step is as follows: + * + * |[ + * g_list_free_full (list, g_object_unref); + * ]| + * + * Returns: (transfer full): a sorted list of sources + * + * Since: 3.6 + **/ +GList * +e_source_registry_list_sources (ESourceRegistry *registry, + const gchar *extension_name) +{ + GList *list, *link; + GQueue trash = G_QUEUE_INIT; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + + list = g_list_sort ( + source_registry_sources_get_values (registry), + (GCompareFunc) e_source_compare_by_display_name); + + if (extension_name == NULL) + return list; + + for (link = list; link != NULL; link = g_list_next (link)) { + ESource *source = E_SOURCE (link->data); + + if (!e_source_has_extension (source, extension_name)) { + g_queue_push_tail (&trash, link); + g_object_unref (source); + } + } + + /* We do want pop_head() here, not pop_head_link(). */ + while ((link = g_queue_pop_head (&trash)) != NULL) + list = g_list_delete_link (list, link); + + return list; +} + +/** + * e_source_registry_find_extension: + * @registry: an #ESourceRegistry + * @source: an #ESource + * @extension_name: the extension name to find + * + * Examines @source and its ancestors and returns the "deepest" #ESource + * having an #ESourceExtension with the given @extension_name. If neither + * @source nor any of its ancestors have such an extension, the function + * returns %NULL. + * + * This function is useful in cases when an #ESourceExtension is meant to + * apply to both the #ESource it belongs to and the #ESource's descendants. + * + * A common example is the #ESourceCollection extension, where descendants + * of an #ESource having an #ESourceCollection extension are implied to be + * members of that collection. In that example, this function can be used + * to test whether @source is a member of a collection. + * + * The returned #ESource is referenced for thread-safety and must be + * unreferenced with g_object_unref() when finished with it. + * + * Note the function returns the #ESource containing the #ESourceExtension + * instead of the #ESourceExtension itself because extension instances are + * not to be referenced directly (see e_source_get_extension()). + * + * Returns: (transfer full): an #ESource, or %NULL if no match was found + * + * Since: 3.6 + **/ +ESource * +e_source_registry_find_extension (ESourceRegistry *registry, + ESource *source, + const gchar *extension_name) +{ + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + g_return_val_if_fail (E_IS_SOURCE (source), NULL); + g_return_val_if_fail (extension_name != NULL, NULL); + + g_object_ref (source); + + while (!e_source_has_extension (source, extension_name)) { + gchar *uid; + + uid = e_source_dup_parent (source); + + g_object_unref (source); + source = NULL; + + if (uid != NULL) { + source = e_source_registry_ref_source (registry, uid); + g_free (uid); + } + + if (source == NULL) + break; + } + + return source; +} + +/* Helper for e_source_registry_build_display_tree() */ +static gint +source_registry_compare_nodes (GNode *node_a, + GNode *node_b) +{ + ESource *source_a = E_SOURCE (node_a->data); + ESource *source_b = E_SOURCE (node_b->data); + const gchar *uid_a, *uid_b; + + uid_a = e_source_get_uid (source_a); + uid_b = e_source_get_uid (source_b); + + /* Sanity check, with runtime warnings. */ + if (uid_a == NULL) { + g_warn_if_reached (); + uid_a = ""; + } + if (uid_b == NULL) { + g_warn_if_reached (); + uid_b = ""; + } + + /* The built-in "local-stub" source comes first at depth 1. */ + + if (g_strcmp0 (uid_a, "local-stub") == 0) + return -1; + + if (g_strcmp0 (uid_b, "local-stub") == 0) + return 1; + + /* The built-in "system-*" sources come first at depth 2. */ + + if (g_str_has_prefix (uid_a, "system-")) + return -1; + + if (g_str_has_prefix (uid_b, "system-")) + return 1; + + return e_source_compare_by_display_name (source_a, source_b); +} + +/* Helper for e_source_registry_build_display_tree() */ +static gboolean +source_registry_prune_nodes (GNode *node, + const gchar *extension_name) +{ + GQueue queue = G_QUEUE_INIT; + GNode *child; + + /* Unlink all the child nodes and place them in a queue. */ + while ((child = g_node_first_child (node)) != NULL) { + g_node_unlink (child); + g_queue_push_tail (&queue, child); + } + + /* Sort the queue by source name. */ + g_queue_sort ( + &queue, (GCompareDataFunc) + source_registry_compare_nodes, NULL); + + /* Pop nodes off the head of the queue until the queue is empty. + * If the node has either its own children or the given extension + * name, put it back under the parent node (preserving the sorted + * order). Otherwise delete the node. */ + while ((child = g_queue_pop_head (&queue)) != NULL) { + ESource *source = E_SOURCE (child->data); + + if (extension_name == NULL) { + if (e_source_get_enabled (source)) { + g_node_append (node, child); + } else { + g_object_unref (source); + g_node_destroy (child); + } + + } else if (e_source_has_extension (source, extension_name)) { + if (e_source_get_enabled (source)) { + g_node_append (node, child); + } else { + g_object_unref (source); + g_node_destroy (child); + } + + } else if (g_node_first_child (child) != NULL) { + g_node_append (node, child); + + } else { + g_object_unref (source); + g_node_destroy (child); + } + } + + return FALSE; +} + +/** + * e_source_registry_build_display_tree: + * @registry: an #ESourceRegistry + * @extension_name: (allow-none): an extension name, or %NULL + * + * Returns a single #GNode tree of registered sources that can be used to + * populate a #GtkTreeModel. (The root #GNode is just an empty placeholder.) + * + * Similar to e_source_registry_list_sources(), an @extension_name can be + * given to restrict the tree to sources having that extension name. Parents + * of matched sources are included in the tree regardless of whether they have + * an extension named @extension_name. + * + * Disabled leaf nodes are automatically excluded from the #GNode tree. + * + * The sources returned in the tree are referenced for thread-safety. + * They must each be unreferenced with g_object_unref() when finished + * with them. Free the returned tree itself with g_node_destroy(). + * For convenience, e_source_registry_free_display_tree() does all + * that in one step. + * + * Returns: (transfer full): a tree of sources, arranged for display + * + * Since: 3.6 + **/ +GNode * +e_source_registry_build_display_tree (ESourceRegistry *registry, + const gchar *extension_name) +{ + GNode *root; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + + /* Assemble all data sources into a tree. */ + root = source_registry_sources_build_tree (registry); + + /* Prune unwanted nodes from the copied source trees. + * This must be done in "post" order (children first) + * since it reorders and deletes child nodes. */ + g_node_traverse ( + root, G_POST_ORDER, G_TRAVERSE_ALL, -1, + (GNodeTraverseFunc) source_registry_prune_nodes, + (gpointer) extension_name); + + return root; +} + +/* Helper for e_source_registry_free_display_tree() */ +static void +source_registry_unref_nodes (GNode *node) +{ + while (node != NULL) { + if (node->children != NULL) + source_registry_unref_nodes (node->children); + if (node->data != NULL) + g_object_unref (node->data); + node = node->next; + } +} + +/** + * e_source_registry_free_display_tree: + * @display_tree: a tree of sources, arranged for display + * + * Convenience function to free a #GNode tree of registered + * sources created by e_source_registry_build_display_tree(). + * + * Since: 3.6 + **/ +void +e_source_registry_free_display_tree (GNode *display_tree) +{ + g_return_if_fail (display_tree != NULL); + + /* XXX This would be easier if GLib had something like + * g_node_destroy_full() which took a GDestroyNotify. + * Then the tree would not have to be traversed twice. */ + + source_registry_unref_nodes (display_tree); + g_node_destroy (display_tree); +} + +/* Helper for e_source_registry_debug_dump() */ +static gboolean +source_registry_debug_dump_cb (GNode *node) +{ + guint ii, depth; + + /* Root node is an empty placeholder. */ + if (G_NODE_IS_ROOT (node)) + return FALSE; + + depth = g_node_depth (node); + for (ii = 2; ii < depth; ii++) + g_print (" "); + + if (E_IS_SOURCE (node->data)) { + ESource *source = E_SOURCE (node->data); + g_print ("\"%s\" ", e_source_get_display_name (source)); + g_print ("(%s)", e_source_get_uid (source)); + } + + g_print ("\n"); + + return FALSE; +} + +/** + * e_source_registry_debug_dump: + * @registry: an #ESourceRegistry + * @extension_name: (allow-none): an extension name, or %NULL + * + * Handy debugging function that uses e_source_registry_build_display_tree() + * to print a tree of registered sources to standard output. + * + * Since: 3.6 + **/ +void +e_source_registry_debug_dump (ESourceRegistry *registry, + const gchar *extension_name) +{ + GNode *root; + + g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); + + root = e_source_registry_build_display_tree (registry, extension_name); + + g_node_traverse ( + root, G_PRE_ORDER, G_TRAVERSE_ALL, -1, + (GNodeTraverseFunc) source_registry_debug_dump_cb, NULL); + + e_source_registry_free_display_tree (root); +} + +/** + * e_source_registry_ref_builtin_address_book: + * @registry: an #ESourceRegistry + * + * Returns the built-in address book #ESource. + * + * This #ESource is always present and makes for a safe fallback. + * + * The returned #ESource is referenced for thread-safety and must be + * unreferenced with g_object_unref() when finished with it. + * + * Returns: (transfer full): the built-in address book #ESource + * + * Since: 3.6 + **/ +ESource * +e_source_registry_ref_builtin_address_book (ESourceRegistry *registry) +{ + ESource *source; + const gchar *uid; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + + uid = E_SOURCE_BUILTIN_ADDRESS_BOOK_UID; + source = e_source_registry_ref_source (registry, uid); + g_return_val_if_fail (source != NULL, NULL); + + return source; +} + +/** + * e_source_registry_ref_default_address_book: + * @registry: an #ESourceRegistry + * + * Returns the #ESource most recently passed to + * e_source_registry_set_default_address_book() either in this session + * or a previous session, or else falls back to the built-in address book. + * + * The returned #ESource is referenced for thread-safety and must be + * unreferenced with g_object_unref() when finished with it. + * + * Returns: (transfer full): the default address book #ESource + * + * Since: 3.6 + **/ +ESource * +e_source_registry_ref_default_address_book (ESourceRegistry *registry) +{ + const gchar *key; + ESource *source; + gchar *uid; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + + key = E_SETTINGS_DEFAULT_ADDRESS_BOOK_KEY; + uid = g_settings_get_string (registry->priv->settings, key); + source = e_source_registry_ref_source (registry, uid); + g_free (uid); + + /* The built-in source is always present. */ + if (source == NULL) + source = e_source_registry_ref_builtin_address_book (registry); + + g_return_val_if_fail (E_IS_SOURCE (source), NULL); + + return source; +} + +/** + * e_source_registry_set_default_address_book: + * @registry: an #ESourceRegistry + * @default_source: (allow-none): an address book #ESource, or %NULL + * + * Sets @default_source as the default address book. If @default_source + * is %NULL, the default address book is reset to the built-in address book. + * This setting will persist across sessions until changed. + * + * Since: 3.6 + **/ +void +e_source_registry_set_default_address_book (ESourceRegistry *registry, + ESource *default_source) +{ + const gchar *key; + const gchar *uid; + + g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); + + if (default_source != NULL) { + g_return_if_fail (E_IS_SOURCE (default_source)); + uid = e_source_get_uid (default_source); + } else { + uid = E_SOURCE_BUILTIN_ADDRESS_BOOK_UID; + } + + key = E_SETTINGS_DEFAULT_ADDRESS_BOOK_KEY; + g_settings_set_string (registry->priv->settings, key, uid); + + /* The GSettings::changed signal will trigger a "notify" signal + * from the registry, so no need to call g_object_notify() here. */ +} + +/** + * e_source_registry_ref_builtin_calendar: + * @registry: an #ESourceRegistry + * + * Returns the built-in calendar #ESource. + * + * This #ESource is always present and makes for a safe fallback. + * + * The returned #ESource is referenced for thread-safety and must be + * unreferenced with g_object_unref() when finished with it. + * + * Returns: (transfer full): the built-in calendar #ESource + * + * Since: 3.6 + **/ +ESource * +e_source_registry_ref_builtin_calendar (ESourceRegistry *registry) +{ + ESource *source; + const gchar *uid; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + + uid = E_SOURCE_BUILTIN_CALENDAR_UID; + source = e_source_registry_ref_source (registry, uid); + g_return_val_if_fail (source != NULL, NULL); + + return source; +} + +/** + * e_source_registry_ref_default_calendar: + * @registry: an #ESourceRegistry + * + * Returns the #ESource most recently passed to + * e_source_registry_set_default_calendar() either in this session + * or a previous session, or else falls back to the built-in calendar. + * + * The returned #ESource is referenced for thread-safety and must be + * unreferenced with g_object_unref() when finished with it. + * + * Returns: (transfer full): the default calendar #ESource + * + * Since: 3.6 + **/ +ESource * +e_source_registry_ref_default_calendar (ESourceRegistry *registry) +{ + const gchar *key; + ESource *source; + gchar *uid; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + + key = E_SETTINGS_DEFAULT_CALENDAR_KEY; + uid = g_settings_get_string (registry->priv->settings, key); + source = e_source_registry_ref_source (registry, uid); + g_free (uid); + + /* The built-in source is always present. */ + if (source == NULL) + source = e_source_registry_ref_builtin_calendar (registry); + + g_return_val_if_fail (E_IS_SOURCE (source), NULL); + + return source; +} + +/** + * e_source_registry_set_default_calendar: + * @registry: an #ESourceRegistry + * @default_source: (allow-none): a calendar #ESource, or %NULL + * + * Sets @default_source as the default calendar. If @default_source + * is %NULL, the default calendar is reset to the built-in calendar. + * This setting will persist across sessions until changed. + * + * Since: 3.6 + **/ +void +e_source_registry_set_default_calendar (ESourceRegistry *registry, + ESource *default_source) +{ + const gchar *key; + const gchar *uid; + + g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); + + if (default_source != NULL) { + g_return_if_fail (E_IS_SOURCE (default_source)); + uid = e_source_get_uid (default_source); + } else { + uid = E_SOURCE_BUILTIN_CALENDAR_UID; + } + + key = E_SETTINGS_DEFAULT_CALENDAR_KEY; + g_settings_set_string (registry->priv->settings, key, uid); + + /* The GSettings::changed signal will trigger a "notify" signal + * from the registry, so no need to call g_object_notify() here. */ +} + +/** + * e_source_registry_ref_builtin_mail_account: + * @registry: an #ESourceRegistry + * + * Returns the built-in mail account #ESource. + * + * This #ESource is always present and makes for a safe fallback. + * + * The returned #ESource is referenced for thread-safety and must be + * unreferenced with g_object_unref() when finished with it. + * + * Returns: (transfer full): the built-in mail account #ESource + * + * Since: 3.6 + **/ +ESource * +e_source_registry_ref_builtin_mail_account (ESourceRegistry *registry) +{ + ESource *source; + const gchar *uid; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + + uid = E_SOURCE_BUILTIN_MAIL_ACCOUNT_UID; + source = e_source_registry_ref_source (registry, uid); + g_return_val_if_fail (source != NULL, NULL); + + return source; +} + +/** + * e_source_registry_ref_default_mail_account: + * @registry: an #ESourceRegistry + * + * Returns the #ESource most recently passed to + * e_source_registry_set_default_mail_account() either in this session + * or a previous session, or else falls back to the built-in mail account. + * + * The returned #ESource is referenced for thread-safety and must be + * unreferenced with g_object_unref() when finished with it. + * + * Returns: (transfer full): the default mail account #ESource + * + * Since: 3.6 + **/ +ESource * +e_source_registry_ref_default_mail_account (ESourceRegistry *registry) +{ + const gchar *key; + ESource *source; + gchar *uid; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + + key = E_SETTINGS_DEFAULT_MAIL_ACCOUNT_KEY; + uid = g_settings_get_string (registry->priv->settings, key); + source = e_source_registry_ref_source (registry, uid); + g_free (uid); + + /* The built-in source is always present. */ + if (source == NULL) + source = e_source_registry_ref_builtin_mail_account (registry); + + g_return_val_if_fail (E_IS_SOURCE (source), NULL); + + return source; +} + +/** + * e_source_registry_set_default_mail_account: + * @registry: an #ESourceRegistry + * @default_source: (allow-none): a mail account #ESource, or %NULL + * + * Sets @default_source as the default mail account. If @default_source + * is %NULL, the default mail account is reset to the built-in mail account. + * This setting will persist across sessions until changed. + * + * Since: 3.6 + **/ +void +e_source_registry_set_default_mail_account (ESourceRegistry *registry, + ESource *default_source) +{ + const gchar *key; + const gchar *uid; + + g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); + + if (default_source != NULL) { + g_return_if_fail (E_IS_SOURCE (default_source)); + uid = e_source_get_uid (default_source); + } else { + uid = E_SOURCE_BUILTIN_MAIL_ACCOUNT_UID; + } + + key = E_SETTINGS_DEFAULT_MAIL_ACCOUNT_KEY; + g_settings_set_string (registry->priv->settings, key, uid); + + /* The GSettings::changed signal will trigger a "notify" signal + * from the registry, so no need to call g_object_notify() here. */ +} + +/* Helper for e_source_registry_ref_default_mail_identity() */ +static ESource * +source_registry_ref_any_mail_identity (ESourceRegistry *registry) +{ + ESource *source; + GList *list, *link; + const gchar *extension_name; + gchar *uid = NULL; + + /* First fallback: Return the mail identity named + * by the default mail account. */ + + source = e_source_registry_ref_default_mail_account (registry); + + /* This should never be NULL, but just to be safe. */ + if (source != NULL) { + ESourceMailAccount *extension; + + extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT; + extension = e_source_get_extension (source, extension_name); + uid = e_source_mail_account_dup_identity_uid (extension); + + g_object_unref (source); + source = NULL; + } + + if (uid != NULL) { + source = e_source_registry_ref_source (registry, uid); + g_free (uid); + } + + if (source != NULL) + return source; + + /* Second fallback: Pick any available mail identity, + * preferring enabled identities. */ + + extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY; + list = e_source_registry_list_sources (registry, extension_name); + + for (link = list; link != NULL; link = g_list_next (link)) { + ESource *candidate = E_SOURCE (link->data); + + if (e_source_get_enabled (candidate)) { + source = g_object_ref (candidate); + break; + } + } + + if (source == NULL && list != NULL) + source = g_object_ref (link->data); + + g_list_free_full (list, (GDestroyNotify) g_object_unref); + + return source; +} + +/** + * e_source_registry_ref_default_mail_identity: + * @registry: an #ESourceRegistry + * + * Returns the #ESource most recently passed to + * e_source_registry_set_default_mail_identity() either in this session + * or a previous session, or else falls back to the mail identity named + * by the default mail account. If even that fails it returns any mail + * identity from @registry, or %NULL if there are none. + * + * The returned #ESource is referenced for thread-safety and must be + * unreferenced with g_object_unref() when finished with it. + * + * Returns: (transfer full): the default mail identity #ESource, or %NULL + * + * Since: 3.6 + **/ +ESource * +e_source_registry_ref_default_mail_identity (ESourceRegistry *registry) +{ + const gchar *key; + ESource *source; + gchar *uid; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + + key = E_SETTINGS_DEFAULT_MAIL_IDENTITY_KEY; + uid = g_settings_get_string (registry->priv->settings, key); + source = e_source_registry_ref_source (registry, uid); + g_free (uid); + + if (source == NULL) + source = source_registry_ref_any_mail_identity (registry); + + return source; +} + +/** + * e_source_registry_set_default_mail_identity: + * @registry: an #ESourceRegistry + * @default_source: (allow-none): a mail identity #ESource, or %NULL + * + * Sets @default_source as the default mail identity. If @default_source + * is %NULL, the next request for the default mail identity will use the + * fallbacks described in e_source_registry_get_default_mail_identity(). + * + * Since: 3.6 + **/ +void +e_source_registry_set_default_mail_identity (ESourceRegistry *registry, + ESource *default_source) +{ + const gchar *key; + const gchar *uid; + + g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); + + if (default_source != NULL) { + g_return_if_fail (E_IS_SOURCE (default_source)); + uid = e_source_get_uid (default_source); + } else { + uid = ""; /* no built-in mail identity */ + } + + key = E_SETTINGS_DEFAULT_MAIL_IDENTITY_KEY; + g_settings_set_string (registry->priv->settings, key, uid); + + /* The GSettings::changed signal will trigger a "notify" signal + * from the registry, so no need to call g_object_notify() here. */ +} + +/** + * e_source_registry_ref_builtin_memo_list: + * @registry: an #ESourceRegistry + * + * Returns the built-in memo list #ESource. + * + * This #ESource is always present and makes for a safe fallback. + * + * The returned #ESource is referenced for thread-safety and must be + * unreferenced with g_object_unref() when finished with it. + * + * Returns: (transfer full): the built-in memo list #ESource + * + * Since: 3.6 + **/ +ESource * +e_source_registry_ref_builtin_memo_list (ESourceRegistry *registry) +{ + ESource *source; + const gchar *uid; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + + uid = E_SOURCE_BUILTIN_MEMO_LIST_UID; + source = e_source_registry_ref_source (registry, uid); + g_return_val_if_fail (source != NULL, NULL); + + return source; +} + +/** + * e_source_registry_ref_default_memo_list: + * @registry: an #ESourceRegistry + * + * Returns the #ESource most recently passed to + * e_source_registry_set_default_memo_list() either in this session + * or a previous session, or else falls back to the built-in memo list. + * + * The returned #ESource is referenced for thread-safety and must be + * unreferenced with g_object_unref() when finished with it. + * + * Returns: (transfer full): the default memo list #ESource + * + * Since: 3.6 + **/ +ESource * +e_source_registry_ref_default_memo_list (ESourceRegistry *registry) +{ + const gchar *key; + ESource *source; + gchar *uid; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + + key = E_SETTINGS_DEFAULT_MEMO_LIST_KEY; + uid = g_settings_get_string (registry->priv->settings, key); + source = e_source_registry_ref_source (registry, uid); + g_free (uid); + + /* The built-in source is always present. */ + if (source == NULL) + source = e_source_registry_ref_builtin_memo_list (registry); + + g_return_val_if_fail (E_IS_SOURCE (source), NULL); + + return source; +} + +/** + * e_source_registry_set_default_memo_list: + * @registry: an #ESourceRegistry + * @default_source: (allow-none): a memo list #ESource, or %NULL + * + * Sets @default_source as the default memo list. If @default_source + * is %NULL, the default memo list is reset to the built-in memo list. + * This setting will persist across sessions until changed. + * + * Since: 3.6 + **/ +void +e_source_registry_set_default_memo_list (ESourceRegistry *registry, + ESource *default_source) +{ + const gchar *key; + const gchar *uid; + + g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); + + if (default_source != NULL) { + g_return_if_fail (E_IS_SOURCE (default_source)); + uid = e_source_get_uid (default_source); + } else { + uid = E_SOURCE_BUILTIN_MEMO_LIST_UID; + } + + key = E_SETTINGS_DEFAULT_MEMO_LIST_KEY; + g_settings_set_string (registry->priv->settings, key, uid); + + /* The GSettings::changed signal will trigger a "notify" signal + * from the registry, so no need to call g_object_notify() here. */ +} + +/** + * e_source_registry_ref_builtin_task_list: + * @registry: an #ESourceRegistry + * + * Returns the built-in task list #ESource. + * + * This #ESource is always present and makes for a safe fallback. + * + * The returned #ESource is referenced for thread-safety and must be + * unreferenced with g_object_unref() when finished with it. + * + * Returns: (transfer full): the built-in task list #ESource + * + * Since: 3.6 + **/ +ESource * +e_source_registry_ref_builtin_task_list (ESourceRegistry *registry) +{ + ESource *source; + const gchar *uid; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + + uid = E_SOURCE_BUILTIN_TASK_LIST_UID; + source = e_source_registry_ref_source (registry, uid); + g_return_val_if_fail (source != NULL, NULL); + + return source; +} + +/** + * e_source_registry_ref_default_task_list: + * @registry: an #ESourceRegistry + * + * Returns the #ESource most recently passed to + * e_source_registry_set_default_task_list() either in this session + * or a previous session, or else falls back to the built-in task list. + * + * The returned #ESource is referenced for thread-safety and must be + * unreferenced with g_object_unref() when finished with it. + * + * Returns: (transfer full): the default task list #ESource + * + * Since: 3.6 + **/ +ESource * +e_source_registry_ref_default_task_list (ESourceRegistry *registry) +{ + const gchar *key; + ESource *source; + gchar *uid; + + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + + key = E_SETTINGS_DEFAULT_TASK_LIST_KEY; + uid = g_settings_get_string (registry->priv->settings, key); + source = e_source_registry_ref_source (registry, uid); + g_free (uid); + + /* The built-in source is always present. */ + if (source == NULL) + source = e_source_registry_ref_builtin_task_list (registry); + + g_return_val_if_fail (E_IS_SOURCE (source), NULL); + + return source; +} + +/** + * e_source_registry_set_default_task_list: + * @registry: an #ESourceRegistry + * @default_source: (allow-none): a task list #ESource, or %NULL + * + * Sets @default_source as the default task list. If @default_source + * is %NULL, the default task list is reset to the built-in task list. + * This setting will persist across sessions until changed. + * + * Since: 3.6 + **/ +void +e_source_registry_set_default_task_list (ESourceRegistry *registry, + ESource *default_source) +{ + const gchar *key; + const gchar *uid; + + g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); + + if (default_source != NULL) { + g_return_if_fail (E_IS_SOURCE (default_source)); + uid = e_source_get_uid (default_source); + } else { + uid = E_SOURCE_BUILTIN_TASK_LIST_UID; + } + + key = E_SETTINGS_DEFAULT_TASK_LIST_KEY; + g_settings_set_string (registry->priv->settings, key, uid); + + /* The GSettings::changed signal will trigger a "notify" signal + * from the registry, so no need to call g_object_notify() here. */ +} + +/** + * e_source_registry_ref_default_for_extension_name: + * @registry: an #ESourceRegistry + * @extension_name: an extension_name + * + * This is a convenience function to return a default #ESource based on + * @extension_name. This only works with a subset of extension names. + * + * If @extension_name is #E_SOURCE_EXTENSION_ADDRESS_BOOK, the function + * returns the current default address book, or else falls back to the + * built-in address book. + * + * If @extension_name is #E_SOURCE_EXTENSION_CALENDAR, the function returns + * the current default calendar, or else falls back to the built-in calendar. + * + * If @extension_name is #E_SOURCE_EXTENSION_MAIL_ACCOUNT, the function + * returns the current default mail account, or else falls back to the + * built-in mail account. + * + * If @extension_name is #E_SOURCE_EXTENSION_MAIL_IDENTITY, the function + * returns the current default mail identity, or else falls back to the + * mail identity named by the current default mail account. + * + * If @extension_name is #E_SOURCE_EXTENSION_MEMO_LIST, the function returns + * the current default memo list, or else falls back to the built-in memo list. + * + * If @extension_name is #E_SOURCE_EXTENSION_TASK_LIST, the function returns + * the current default task list, or else falls back to the built-in task list. + * + * For all other values of @extension_name, the function returns %NULL. + * + * The returned #ESource is referenced for thread-safety and must be + * unreferenced with g_object_unref() when finished with it. + * + * Returns: (transfer full): the default #ESource based on @extension_name + * + * Since: 3.6 + **/ +ESource * +e_source_registry_ref_default_for_extension_name (ESourceRegistry *registry, + const gchar *extension_name) +{ + g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); + g_return_val_if_fail (extension_name != NULL, NULL); + + if (strcmp (extension_name, E_SOURCE_EXTENSION_ADDRESS_BOOK) == 0) + return e_source_registry_ref_default_address_book (registry); + + if (strcmp (extension_name, E_SOURCE_EXTENSION_CALENDAR) == 0) + return e_source_registry_ref_default_calendar (registry); + + if (strcmp (extension_name, E_SOURCE_EXTENSION_MAIL_ACCOUNT) == 0) + return e_source_registry_ref_default_mail_account (registry); + + if (strcmp (extension_name, E_SOURCE_EXTENSION_MAIL_IDENTITY) == 0) + return e_source_registry_ref_default_mail_identity (registry); + + if (strcmp (extension_name, E_SOURCE_EXTENSION_MEMO_LIST) == 0) + return e_source_registry_ref_default_memo_list (registry); + + if (strcmp (extension_name, E_SOURCE_EXTENSION_TASK_LIST) == 0) + return e_source_registry_ref_default_task_list (registry); + + return NULL; +} + +/** + * e_source_registry_set_default_for_extension_name: + * @registry: an #ESourceRegistry + * @extension_name: an extension name + * @default_source: (allow-none): an #ESource, or %NULL + * + * This is a convenience function to set a default #ESource based on + * @extension_name. This only works with a subset of extension names. + * + * If @extension_name is #E_SOURCE_EXTENSION_ADDRESS_BOOK, the function + * sets @default_source as the default address book. If @default_source + * is %NULL, the default address book is reset to the built-in address book. + * + * If @extension_name is #E_SOURCE_EXTENSION_CALENDAR, the function sets + * @default_source as the default calendar. If @default_source is %NULL, + * the default calendar is reset to the built-in calendar. + * + * If @extension_name is #E_SOURCE_EXTENSION_MAIL_ACCOUNT, the function + * sets @default_source as the default mail account. If @default_source + * is %NULL, the default mail account is reset to the built-in mail account. + * + * If @extension_name is #E_SOURCE_EXTENSION_MAIL_IDENTITY, the function + * sets @default_source as the default mail identity. If @default_source + * is %NULL, the next request for the default mail identity will return + * the mail identity named by the default mail account. + * + * If @extension_name is #E_SOURCE_EXTENSION_MEMO_LIST, the function sets + * @default_source as the default memo list. If @default_source is %NULL, + * the default memo list is reset to the built-in memo list. + * + * If @extension_name is #E_SOURCE_EXTENSION_TASK_LIST, the function sets + * @default_source as the default task list. If @default_source is %NULL, + * the default task list is reset to the built-in task list. + * + * For all other values of @extension_name, the function does nothing. + * + * Since: 3.6 + **/ +void +e_source_registry_set_default_for_extension_name (ESourceRegistry *registry, + const gchar *extension_name, + ESource *default_source) +{ + g_return_if_fail (E_IS_SOURCE_REGISTRY (registry)); + g_return_if_fail (extension_name != NULL); + + if (strcmp (extension_name, E_SOURCE_EXTENSION_ADDRESS_BOOK) == 0) + e_source_registry_set_default_address_book ( + registry, default_source); + + if (strcmp (extension_name, E_SOURCE_EXTENSION_CALENDAR) == 0) + e_source_registry_set_default_calendar ( + registry, default_source); + + if (strcmp (extension_name, E_SOURCE_EXTENSION_MAIL_ACCOUNT) == 0) + e_source_registry_set_default_mail_account ( + registry, default_source); + + if (strcmp (extension_name, E_SOURCE_EXTENSION_MAIL_IDENTITY) == 0) + e_source_registry_set_default_mail_identity ( + registry, default_source); + + if (strcmp (extension_name, E_SOURCE_EXTENSION_MEMO_LIST) == 0) + e_source_registry_set_default_memo_list ( + registry, default_source); + + if (strcmp (extension_name, E_SOURCE_EXTENSION_TASK_LIST) == 0) + e_source_registry_set_default_task_list ( + registry, default_source); +} + diff --git a/libedataserver/e-source-registry.h b/libedataserver/e-source-registry.h new file mode 100644 index 0000000..c675fa7 --- /dev/null +++ b/libedataserver/e-source-registry.h @@ -0,0 +1,204 @@ +/* + * e-source-registry.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_REGISTRY_H +#define E_SOURCE_REGISTRY_H + +#include +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_REGISTRY \ + (e_source_registry_get_type ()) +#define E_SOURCE_REGISTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_REGISTRY, ESourceRegistry)) +#define E_SOURCE_REGISTRY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_REGISTRY, ESourceRegistryClass)) +#define E_IS_SOURCE_REGISTRY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_REGISTRY)) +#define E_IS_SOURCE_REGISTRY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_REGISTRY)) +#define E_SOURCE_REGISTRY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_REGISTRY, ESourceRegistryClass)) + +G_BEGIN_DECLS + +typedef struct _ESourceRegistry ESourceRegistry; +typedef struct _ESourceRegistryClass ESourceRegistryClass; +typedef struct _ESourceRegistryPrivate ESourceRegistryPrivate; + +/** + * ESourceRegistry: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceRegistry { + GObject parent; + ESourceRegistryPrivate *priv; +}; + +struct _ESourceRegistryClass { + GObjectClass parent_class; + + /* Signals */ + void (*source_added) (ESourceRegistry *registry, + ESource *source); + void (*source_changed) (ESourceRegistry *registry, + ESource *source); + void (*source_removed) (ESourceRegistry *registry, + ESource *source); + void (*source_enabled) (ESourceRegistry *registry, + ESource *source); + void (*source_disabled) (ESourceRegistry *registry, + ESource *source); +}; + +GType e_source_registry_get_type (void) G_GNUC_CONST; +ESourceRegistry * + e_source_registry_new_sync (GCancellable *cancellable, + GError **error); +void e_source_registry_new (GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +ESourceRegistry * + e_source_registry_new_finish (GAsyncResult *result, + GError **error); +gboolean e_source_registry_authenticate_sync + (ESourceRegistry *registry, + ESource *source, + ESourceAuthenticator *auth, + GCancellable *cancellable, + GError **error); +void e_source_registry_authenticate (ESourceRegistry *registry, + ESource *source, + ESourceAuthenticator *auth, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean e_source_registry_authenticate_finish + (ESourceRegistry *registry, + GAsyncResult *result, + GError **error); +gboolean e_source_registry_commit_source_sync + (ESourceRegistry *registry, + ESource *source, + GCancellable *cancellable, + GError **error); +void e_source_registry_commit_source (ESourceRegistry *registry, + ESource *source, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean e_source_registry_commit_source_finish + (ESourceRegistry *registry, + GAsyncResult *result, + GError **error); +gboolean e_source_registry_create_sources_sync + (ESourceRegistry *registry, + GList *list_of_sources, + GCancellable *cancellable, + GError **error); +void e_source_registry_create_sources + (ESourceRegistry *registry, + GList *list_of_sources, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean e_source_registry_create_sources_finish + (ESourceRegistry *registry, + GAsyncResult *result, + GError **error); +ESource * e_source_registry_ref_source (ESourceRegistry *registry, + const gchar *uid); +GList * e_source_registry_list_sources (ESourceRegistry *registry, + const gchar *extension_name); +ESource * e_source_registry_find_extension + (ESourceRegistry *registry, + ESource *source, + const gchar *extension_name); +GNode * e_source_registry_build_display_tree + (ESourceRegistry *registry, + const gchar *extension_name); +void e_source_registry_free_display_tree + (GNode *display_tree); +void e_source_registry_debug_dump (ESourceRegistry *registry, + const gchar *extension_name); + +/* The following is a front-end for the "org.gnome.Evolution.DefaultSources" + * GSettings schema, except that it gets and sets ESource objects instead of + * ESource UID strings. */ + +ESource * e_source_registry_ref_builtin_address_book + (ESourceRegistry *registry); +ESource * e_source_registry_ref_default_address_book + (ESourceRegistry *registry); +void e_source_registry_set_default_address_book + (ESourceRegistry *registry, + ESource *default_source); +ESource * e_source_registry_ref_builtin_calendar + (ESourceRegistry *registry); +ESource * e_source_registry_ref_default_calendar + (ESourceRegistry *registry); +void e_source_registry_set_default_calendar + (ESourceRegistry *registry, + ESource *default_source); +ESource * e_source_registry_ref_builtin_mail_account + (ESourceRegistry *registry); +ESource * e_source_registry_ref_default_mail_account + (ESourceRegistry *registry); +void e_source_registry_set_default_mail_account + (ESourceRegistry *registry, + ESource *default_source); +ESource * e_source_registry_ref_default_mail_identity + (ESourceRegistry *registry); +void e_source_registry_set_default_mail_identity + (ESourceRegistry *registry, + ESource *default_source); +ESource * e_source_registry_ref_builtin_memo_list + (ESourceRegistry *registry); +ESource * e_source_registry_ref_default_memo_list + (ESourceRegistry *registry); +void e_source_registry_set_default_memo_list + (ESourceRegistry *registry, + ESource *default_source); +ESource * e_source_registry_ref_builtin_task_list + (ESourceRegistry *registry); +ESource * e_source_registry_ref_default_task_list + (ESourceRegistry *registry); +void e_source_registry_set_default_task_list + (ESourceRegistry *registry, + ESource *default_source); +ESource * e_source_registry_ref_default_for_extension_name + (ESourceRegistry *registry, + const gchar *extension_name); +void e_source_registry_set_default_for_extension_name + (ESourceRegistry *registry, + const gchar *extension_name, + ESource *default_source); + +G_END_DECLS + +#endif /* E_SOURCE_REGISTRY_H */ diff --git a/libedataserver/e-source-security.c b/libedataserver/e-source-security.c new file mode 100644 index 0000000..6bc916d --- /dev/null +++ b/libedataserver/e-source-security.c @@ -0,0 +1,316 @@ +/* + * e-source-security.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-security + * @include: libedataserver/e-source-security.h + * @short_description: #ESource extension for security settings + * + * The #ESourceSecurity extension tracks settings for establishing a + * secure connection with a remote server. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceSecurity *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_SECURITY); + * ]| + **/ + +#include "e-source-security.h" + +#include + +#define E_SOURCE_SECURITY_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_SECURITY, ESourceSecurityPrivate)) + +#define SECURE_METHOD "tls" + +struct _ESourceSecurityPrivate { + GMutex *property_lock; + gchar *method; +}; + +enum { + PROP_0, + PROP_METHOD, + PROP_SECURE +}; + +G_DEFINE_TYPE ( + ESourceSecurity, + e_source_security, + E_TYPE_SOURCE_EXTENSION) + +static void +source_security_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_METHOD: + e_source_security_set_method ( + E_SOURCE_SECURITY (object), + g_value_get_string (value)); + return; + + case PROP_SECURE: + e_source_security_set_secure ( + E_SOURCE_SECURITY (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_security_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_METHOD: + g_value_take_string ( + value, + e_source_security_dup_method ( + E_SOURCE_SECURITY (object))); + return; + + case PROP_SECURE: + g_value_set_boolean ( + value, + e_source_security_get_secure ( + E_SOURCE_SECURITY (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_security_finalize (GObject *object) +{ + ESourceSecurityPrivate *priv; + + priv = E_SOURCE_SECURITY_GET_PRIVATE (object); + + g_mutex_free (priv->property_lock); + + g_free (priv->method); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_security_parent_class)->finalize (object); +} + +static void +e_source_security_class_init (ESourceSecurityClass *class) +{ + GObjectClass *object_class; + ESourceExtensionClass *extension_class; + + g_type_class_add_private (class, sizeof (ESourceSecurityPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_security_set_property; + object_class->get_property = source_security_get_property; + object_class->finalize = source_security_finalize; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_SECURITY; + + g_object_class_install_property ( + object_class, + PROP_METHOD, + g_param_spec_string ( + "method", + "Method", + "Security method", + "none", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_SECURE, + g_param_spec_boolean ( + "secure", + "Secure", + "Secure the network connection", + FALSE, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS)); +} + +static void +e_source_security_init (ESourceSecurity *extension) +{ + extension->priv = E_SOURCE_SECURITY_GET_PRIVATE (extension); + extension->priv->property_lock = g_mutex_new (); +} + +/** + * e_source_security_get_method: + * @extension: an #ESourceSecurity + * + * Returns the method used to establish a secure network connection to a + * remote account. There are no pre-defined method names; backends are + * free to set this however they wish. If a secure connection is not + * desired, the convention is to set #ESourceSecurity:method to "none". + * + * Returns: the method used to establish a secure network connection + * + * Since: 3.6 + **/ +const gchar * +e_source_security_get_method (ESourceSecurity *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_SECURITY (extension), NULL); + + return extension->priv->method; +} + +/** + * e_source_security_dup_method: + * @extension: an #ESourceSecurity + * + * Thread-safe variation of e_source_security_get_method(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceSecurity:method + * + * Since: 3.6 + **/ +gchar * +e_source_security_dup_method (ESourceSecurity *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_SECURITY (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_security_get_method (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_security_set_method: + * @extension: an #ESourceSecurity + * @method: (allow-none): security method, or %NULL + * + * Sets the method used to establish a secure network connection to a + * remote account. There are no pre-defined method names; backends are + * free to set this however they wish. If a secure connection is not + * desired, the convention is to set #ESourceSecurity:method to "none". + * In keeping with that convention, #ESourceSecurity:method will be set + * to "none" if @method is %NULL or an empty string. + * + * Since: 3.6 + **/ +void +e_source_security_set_method (ESourceSecurity *extension, + const gchar *method) +{ + GObject *object; + + g_return_if_fail (E_IS_SOURCE_SECURITY (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->method); + extension->priv->method = e_util_strdup_strip (method); + + if (extension->priv->method == NULL) + extension->priv->method = g_strdup ("none"); + + g_mutex_unlock (extension->priv->property_lock); + + object = G_OBJECT (extension); + g_object_freeze_notify (object); + g_object_notify (object, "method"); + g_object_notify (object, "secure"); + g_object_thaw_notify (object); +} + +/** + * e_source_security_get_secure: + * @extension: an #ESourceSecurity + * + * This is a convenience function which returns whether a secure network + * connection is desired, regardless of the method used. This relies on + * the convention of setting #ESourceSecurity:method to "none" when a + * secure network connection is not desired. + * + * Returns: whether a secure network connection is desired + * + * Since: 3.6 + **/ +gboolean +e_source_security_get_secure (ESourceSecurity *extension) +{ + const gchar *method; + + g_return_val_if_fail (E_IS_SOURCE_SECURITY (extension), FALSE); + + method = e_source_security_get_method (extension); + g_return_val_if_fail (method != NULL, FALSE); + + return (g_strcmp0 (method, "none") != 0); +} + +/** + * e_source_security_set_secure: + * @extension: an #ESourceSecurity + * @secure: whether a secure network connection is desired + * + * This function provides a simpler way to set #ESourceSecurity:method + * when using a secure network connection is a yes or no option and the + * exact method name is unimportant. If @secure is %FALSE, the + * #ESourceSecurity:method property is set to "none". If @secure is + * %TRUE, the function assumes the backend will use Transport Layer + * Security and sets the #ESourceSecurity:method property to "tls". + * + * Since: 3.6 + **/ +void +e_source_security_set_secure (ESourceSecurity *extension, + gboolean secure) +{ + const gchar *method; + + g_return_if_fail (E_IS_SOURCE_SECURITY (extension)); + + method = secure ? SECURE_METHOD : "none"; + e_source_security_set_method (extension, method); +} diff --git a/libedataserver/e-source-security.h b/libedataserver/e-source-security.h new file mode 100644 index 0000000..28e2e85 --- /dev/null +++ b/libedataserver/e-source-security.h @@ -0,0 +1,87 @@ +/* + * e-source-security.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_SECURITY_H +#define E_SOURCE_SECURITY_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_SECURITY \ + (e_source_security_get_type ()) +#define E_SOURCE_SECURITY(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_SECURITY, ESourceSecurity)) +#define E_SOURCE_SECURITY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_SECURITY, ESourceSecurityClass)) +#define E_IS_SOURCE_SECURITY(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_SECURITY)) +#define E_IS_SOURCE_SECURITY_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_SECURITY)) +#define E_SOURCE_SECURITY_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_SECURITY, ESourceSecurityClass)) + +/** + * E_SOURCE_EXTENSION_SECURITY: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceSecurity. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_SECURITY "Security" + +G_BEGIN_DECLS + +typedef struct _ESourceSecurity ESourceSecurity; +typedef struct _ESourceSecurityClass ESourceSecurityClass; +typedef struct _ESourceSecurityPrivate ESourceSecurityPrivate; + +/** + * ESourceSecurity: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceSecurity { + ESourceExtension parent; + ESourceSecurityPrivate *priv; +}; + +struct _ESourceSecurityClass { + ESourceExtensionClass parent_class; +}; + +GType e_source_security_get_type (void) G_GNUC_CONST; +const gchar * e_source_security_get_method (ESourceSecurity *extension); +gchar * e_source_security_dup_method (ESourceSecurity *extension); +void e_source_security_set_method (ESourceSecurity *extension, + const gchar *method); +gboolean e_source_security_get_secure (ESourceSecurity *extension); +void e_source_security_set_secure (ESourceSecurity *extension, + gboolean secure); + +G_END_DECLS + +#endif /* E_SOURCE_SECURITY_H */ diff --git a/libedataserver/e-source-selectable.c b/libedataserver/e-source-selectable.c new file mode 100644 index 0000000..5645387 --- /dev/null +++ b/libedataserver/e-source-selectable.c @@ -0,0 +1,290 @@ +/* + * e-source-selectable.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-selectable + * @include: libedataserver/e-source-selectable.h + * @short_description: Base class for selectable data sources + * @see_also: #ESourceAddressBook, #ESourceCalendar, #ESourceMemoList, + * #ESourceTaskList + * + * #ESourceSelectable is an abstract base class for data sources + * that can be selected in an #ESourceSelector or similar widget. + **/ + +#include "e-source-selectable.h" + +#include + +#define E_SOURCE_SELECTABLE_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_SELECTABLE, ESourceSelectablePrivate)) + +struct _ESourceSelectablePrivate { + GMutex *property_lock; + gchar *color; + gboolean selected; +}; + +enum { + PROP_0, + PROP_COLOR, + PROP_SELECTED +}; + +G_DEFINE_ABSTRACT_TYPE ( + ESourceSelectable, + e_source_selectable, + E_TYPE_SOURCE_BACKEND) + +static void +source_selectable_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_COLOR: + e_source_selectable_set_color ( + E_SOURCE_SELECTABLE (object), + g_value_get_string (value)); + return; + + case PROP_SELECTED: + e_source_selectable_set_selected ( + E_SOURCE_SELECTABLE (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_selectable_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_COLOR: + g_value_take_string ( + value, + e_source_selectable_dup_color ( + E_SOURCE_SELECTABLE (object))); + return; + + case PROP_SELECTED: + g_value_set_boolean ( + value, + e_source_selectable_get_selected ( + E_SOURCE_SELECTABLE (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_selectable_finalize (GObject *object) +{ + ESourceSelectablePrivate *priv; + + priv = E_SOURCE_SELECTABLE_GET_PRIVATE (object); + + g_mutex_free (priv->property_lock); + + g_free (priv->color); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_selectable_parent_class)->finalize (object); +} + +static void +e_source_selectable_class_init (ESourceSelectableClass *class) +{ + GObjectClass *object_class; + + g_type_class_add_private (class, sizeof (ESourceSelectablePrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_selectable_set_property; + object_class->get_property = source_selectable_get_property; + object_class->finalize = source_selectable_finalize; + + /* We do not provide an extension name, + * which is why the class is abstract. */ + + g_object_class_install_property ( + object_class, + PROP_COLOR, + g_param_spec_string ( + "color", + "Color", + "Textual specification of a color", + "#becedd", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_SELECTED, + g_param_spec_boolean ( + "selected", + "Selected", + "Whether the data source is selected", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); +} + +static void +e_source_selectable_init (ESourceSelectable *extension) +{ + extension->priv = E_SOURCE_SELECTABLE_GET_PRIVATE (extension); + extension->priv->property_lock = g_mutex_new (); +} + +/** + * e_source_selectable_get_color: + * @extension: an #ESourceSelectable + * + * Returns the color specification for the #ESource to which @extension + * belongs. A colored block is often displayed next to the data source's + * display name in user interfaces. + * + * Returns: the color specification for the #ESource + * + * Since: 3.6 + **/ +const gchar * +e_source_selectable_get_color (ESourceSelectable *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_SELECTABLE (extension), NULL); + + return extension->priv->color; +} + +/** + * e_source_selectable_dup_color: + * @extension: an #ESourceSelectable + * + * Thread-safe variation of e_source_selectable_get_color(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceSelectable:color + * + * Since: 3.6 + **/ +gchar * +e_source_selectable_dup_color (ESourceSelectable *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_SELECTABLE (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_selectable_get_color (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_selectable_set_color: + * @extension: an #ESourceSelectable + * @color: (allow-none): a color specification, or %NULL + * + * Sets the color specification for the #ESource to which @extension + * belongs. A colored block is often displayed next to the data source's + * display name in user interfaces. + * + * The internal copy of @color is automatically stripped of leading and + * trailing whitespace. If the resulting string is empty, %NULL is set + * instead. + * + * Since: 3.6 + **/ +void +e_source_selectable_set_color (ESourceSelectable *extension, + const gchar *color) +{ + g_return_if_fail (E_IS_SOURCE_SELECTABLE (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->color); + extension->priv->color = e_util_strdup_strip (color); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "color"); +} + +/** + * e_source_selectable_get_selected: + * @extension: an #ESourceSelectable + * + * Returns the selected state of the #ESource to which @extension belongs. + * The selected state is often represented as a checkbox next to the data + * source's display name in user interfaces. + * + * Returns: the selected state for the #ESource + * + * Since: 3.6 + **/ +gboolean +e_source_selectable_get_selected (ESourceSelectable *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_SELECTABLE (extension), FALSE); + + return extension->priv->selected; +} + +/** + * e_source_selectable_set_selected: + * @extension: an #ESourceSelectable + * @selected: selected state + * + * Sets the selected state for the #ESource to which @extension belongs. + * The selected state is often represented as a checkbox next to the data + * source's display name in user interfaces. + * + * Since: 3.6 + **/ +void +e_source_selectable_set_selected (ESourceSelectable *extension, + gboolean selected) +{ + g_return_if_fail (E_IS_SOURCE_SELECTABLE (extension)); + + extension->priv->selected = selected; + + g_object_notify (G_OBJECT (extension), "selected"); +} + diff --git a/libedataserver/e-source-selectable.h b/libedataserver/e-source-selectable.h new file mode 100644 index 0000000..07d43b0 --- /dev/null +++ b/libedataserver/e-source-selectable.h @@ -0,0 +1,79 @@ +/* + * e-source-selectable.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_SELECTABLE_H +#define E_SOURCE_SELECTABLE_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_SELECTABLE \ + (e_source_selectable_get_type ()) +#define E_SOURCE_SELECTABLE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_SELECTABLE, ESourceSelectable)) +#define E_SOURCE_SELECTABLE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_SELECTABLE, ESourceSelectableClass)) +#define E_IS_SOURCE_SELECTABLE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_SELECTABLE)) +#define E_IS_SOURCE_SELECTABLE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_SELECTABLE)) +#define E_SOURCE_SELECTABLE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_SELECTABLE, ESourceSelectableClass)) + +G_BEGIN_DECLS + +typedef struct _ESourceSelectable ESourceSelectable; +typedef struct _ESourceSelectableClass ESourceSelectableClass; +typedef struct _ESourceSelectablePrivate ESourceSelectablePrivate; + +/** + * ESourceSelectable: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceSelectable { + ESourceBackend parent; + ESourceSelectablePrivate *priv; +}; + +struct _ESourceSelectableClass { + ESourceBackendClass parent_class; +}; + +GType e_source_selectable_get_type (void) G_GNUC_CONST; +const gchar * e_source_selectable_get_color (ESourceSelectable *extension); +gchar * e_source_selectable_dup_color (ESourceSelectable *extension); +void e_source_selectable_set_color (ESourceSelectable *extension, + const gchar *color); +gboolean e_source_selectable_get_selected + (ESourceSelectable *extension); +void e_source_selectable_set_selected + (ESourceSelectable *extension, + gboolean selected); + +G_END_DECLS + +#endif /* E_SOURCE_SELECTABLE_H */ diff --git a/libedataserver/e-source-smime.c b/libedataserver/e-source-smime.c new file mode 100644 index 0000000..7bb7fb6 --- /dev/null +++ b/libedataserver/e-source-smime.c @@ -0,0 +1,650 @@ +/* + * e-source-smime.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-smime + * @include: libedataserver/e-source-smime.h + * @short_description: #ESource extension for S/MIME settings + * + * The #ESourceSMIME extension tracks Secure/Multipurpose Internet Mail + * Extensions (S/MIME) settings to be applied to outgoing mail messages. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceSMIME *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_SMIME); + * ]| + **/ + +#include "e-source-smime.h" + +#include + +#define E_SOURCE_SMIME_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_SMIME, ESourceSMIMEPrivate)) + +struct _ESourceSMIMEPrivate { + GMutex *property_lock; + gchar *encryption_certificate; + gchar *signing_algorithm; + gchar *signing_certificate; + + gboolean encrypt_by_default; + gboolean encrypt_to_self; + gboolean sign_by_default; +}; + +enum { + PROP_0, + PROP_ENCRYPTION_CERTIFICATE, + PROP_ENCRYPT_BY_DEFAULT, + PROP_ENCRYPT_TO_SELF, + PROP_SIGNING_ALGORITHM, + PROP_SIGNING_CERTIFICATE, + PROP_SIGN_BY_DEFAULT +}; + +G_DEFINE_TYPE ( + ESourceSMIME, + e_source_smime, + E_TYPE_SOURCE_EXTENSION) + +static void +source_smime_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ENCRYPTION_CERTIFICATE: + e_source_smime_set_encryption_certificate ( + E_SOURCE_SMIME (object), + g_value_get_string (value)); + return; + + case PROP_ENCRYPT_BY_DEFAULT: + e_source_smime_set_encrypt_by_default ( + E_SOURCE_SMIME (object), + g_value_get_boolean (value)); + return; + + case PROP_ENCRYPT_TO_SELF: + e_source_smime_set_encrypt_to_self ( + E_SOURCE_SMIME (object), + g_value_get_boolean (value)); + return; + + case PROP_SIGNING_ALGORITHM: + e_source_smime_set_signing_algorithm ( + E_SOURCE_SMIME (object), + g_value_get_string (value)); + return; + + case PROP_SIGNING_CERTIFICATE: + e_source_smime_set_signing_certificate ( + E_SOURCE_SMIME (object), + g_value_get_string (value)); + return; + + case PROP_SIGN_BY_DEFAULT: + e_source_smime_set_sign_by_default ( + E_SOURCE_SMIME (object), + g_value_get_boolean (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_smime_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_ENCRYPTION_CERTIFICATE: + g_value_take_string ( + value, + e_source_smime_dup_encryption_certificate ( + E_SOURCE_SMIME (object))); + return; + + case PROP_ENCRYPT_BY_DEFAULT: + g_value_set_boolean ( + value, + e_source_smime_get_encrypt_by_default ( + E_SOURCE_SMIME (object))); + return; + + case PROP_ENCRYPT_TO_SELF: + g_value_set_boolean ( + value, + e_source_smime_get_encrypt_to_self ( + E_SOURCE_SMIME (object))); + return; + + case PROP_SIGNING_ALGORITHM: + g_value_take_string ( + value, + e_source_smime_dup_signing_algorithm ( + E_SOURCE_SMIME (object))); + return; + + case PROP_SIGNING_CERTIFICATE: + g_value_take_string ( + value, + e_source_smime_dup_signing_certificate ( + E_SOURCE_SMIME (object))); + return; + + case PROP_SIGN_BY_DEFAULT: + g_value_set_boolean ( + value, + e_source_smime_get_sign_by_default ( + E_SOURCE_SMIME (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_smime_finalize (GObject *object) +{ + ESourceSMIMEPrivate *priv; + + priv = E_SOURCE_SMIME_GET_PRIVATE (object); + + g_mutex_free (priv->property_lock); + + g_free (priv->encryption_certificate); + g_free (priv->signing_algorithm); + g_free (priv->signing_certificate); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_smime_parent_class)->finalize (object); +} + +static void +e_source_smime_class_init (ESourceSMIMEClass *class) +{ + GObjectClass *object_class; + ESourceExtensionClass *extension_class; + + g_type_class_add_private (class, sizeof (ESourceSMIMEPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_smime_set_property; + object_class->get_property = source_smime_get_property; + object_class->finalize = source_smime_finalize; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_SMIME; + + g_object_class_install_property ( + object_class, + PROP_ENCRYPTION_CERTIFICATE, + g_param_spec_string ( + "encryption-certificate", + "Encryption Certificate", + "S/MIME certificate for encrypting messages", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_ENCRYPT_BY_DEFAULT, + g_param_spec_boolean ( + "encrypt-by-default", + "Encrypt By Default", + "Encrypt outgoing messages by default", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_ENCRYPT_TO_SELF, + g_param_spec_boolean ( + "encrypt-to-self", + "Encrypt To Self", + "Always encrypt to myself", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_SIGNING_ALGORITHM, + g_param_spec_string ( + "signing-algorithm", + "Signing Algorithm", + "Hash algorithm used to sign messages", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_SIGNING_CERTIFICATE, + g_param_spec_string ( + "signing-certificate", + "Signing Certificate", + "S/MIME certificate for signing messages", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_SIGN_BY_DEFAULT, + g_param_spec_boolean ( + "sign-by-default", + "Sign By Default", + "Sign outgoing messages by default", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); +} + +static void +e_source_smime_init (ESourceSMIME *extension) +{ + extension->priv = E_SOURCE_SMIME_GET_PRIVATE (extension); + extension->priv->property_lock = g_mutex_new (); +} + +/** + * e_source_smime_get_encryption_certificate: + * @extension: an #ESourceSMIME + * + * Returns the S/MIME certificate name used to encrypt messages. + * + * Returns: the certificate name used to encrypt messages + * + * Since: 3.6 + **/ +const gchar * +e_source_smime_get_encryption_certificate (ESourceSMIME *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), NULL); + + return extension->priv->encryption_certificate; +} + +/** + * e_source_smime_dup_encryption_certificate: + * @extension: an #ESourceSMIME + * + * Thread-safe variation of e_source_smime_get_encryption_certificate(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceSMIME:encryption-certificate + * + * Since: 3.6 + **/ +gchar * +e_source_smime_dup_encryption_certificate (ESourceSMIME *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_smime_get_encryption_certificate (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_smime_set_encryption_certificate: + * @extension: an #ESourceSMIME + * @encryption_certificate: (allow-none): the certificate name used to encrypt + * messages, or %NULL + * + * Sets the certificate name used to encrypt messages. + * + * The internal copy of @encryption_certificate is automatically stripped + * of leading and trailing whitespace. If the resulting string is empty, + * %NULL is set instead. + * + * Since: 3.6 + **/ +void +e_source_smime_set_encryption_certificate (ESourceSMIME *extension, + const gchar *encryption_certificate) +{ + g_return_if_fail (E_IS_SOURCE_SMIME (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->encryption_certificate); + extension->priv->encryption_certificate = + e_util_strdup_strip (encryption_certificate); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "encryption-certificate"); +} + +/** + * e_source_smime_get_encrypt_by_default: + * @extension: an #ESourceSMIME + * + * Returns whether to encrypt outgoing messages by default using S/MIME + * software such as Mozilla Network Security Services (NSS). + * + * Returns: whether to encrypt outgoing messages by default + * + * Since: 3.6 + **/ +gboolean +e_source_smime_get_encrypt_by_default (ESourceSMIME *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), FALSE); + + return extension->priv->encrypt_by_default; +} + +/** + * e_source_smime_set_encrypt_by_default: + * @extension: an #ESourceSMIME + * @encrypt_by_default: whether to encrypt outgoing messages by default + * + * Sets whether to encrypt outgoing messages by default using S/MIME + * software such as Mozilla Network Security Services (NSS). + * + * Since: 3.6 + **/ +void +e_source_smime_set_encrypt_by_default (ESourceSMIME *extension, + gboolean encrypt_by_default) +{ + g_return_if_fail (E_IS_SOURCE_SMIME (extension)); + + extension->priv->encrypt_by_default = encrypt_by_default; + + g_object_notify (G_OBJECT (extension), "encrypt-by-default"); +} + +/** + * e_source_smime_get_encrypt_to_self: + * @extension: an #ESourceSMIME + * + * Returns whether to "encrypt-to-self" when sending encrypted messages. + * + * Returns: whether to "encrypt-to-self" + * + * Since: 3.6 + **/ +gboolean +e_source_smime_get_encrypt_to_self (ESourceSMIME *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), FALSE); + + return extension->priv->encrypt_to_self; +} + +/** + * e_source_smime_set_encrypt_to_self: + * @extension: an #ESourceSMIME + * @encrypt_to_self: whether to "encrypt-to-self" + * + * Sets whether to "encrypt-to-self" when sending encrypted messages. + * + * Since: 3.6 + **/ +void +e_source_smime_set_encrypt_to_self (ESourceSMIME *extension, + gboolean encrypt_to_self) +{ + g_return_if_fail (E_IS_SOURCE_SMIME (extension)); + + extension->priv->encrypt_to_self = encrypt_to_self; + + g_object_notify (G_OBJECT (extension), "encrypt-to-self"); +} + +/** + * e_source_smime_get_signing_algorithm: + * @extension: an #ESourceSMIME + * + * Returns the name of the hash algorithm used to digitally sign outgoing + * messages. + * + * Returns: the signing algorithm for outgoing messages + * + * Since: 3.6 + **/ +const gchar * +e_source_smime_get_signing_algorithm (ESourceSMIME *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), NULL); + + return extension->priv->signing_algorithm; +} + +/** + * e_source_smime_dup_signing_algorithm: + * @extension: an #ESourceSMIME + * + * Thread-safe variation of e_source_smime_get_signing_algorithm(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceSMIME:signing-algorithm + * + * Since: 3.6 + **/ +gchar * +e_source_smime_dup_signing_algorithm (ESourceSMIME *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_smime_get_signing_algorithm (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_smime_set_signing_algorithm: + * @extension: an #ESourceSMIME + * @signing_algorithm: (allow-none): the signing algorithm for outgoing + * messages, or %NULL + * + * Sets the name of the hash algorithm used to digitally sign outgoing + * messages. + * + * The internal copy of @signing_algorithm is automatically stripped of + * leading and trailing whitespace. If the resulting string is empty, + * %NULL is set instead. + * + * Since: 3.6 + **/ +void +e_source_smime_set_signing_algorithm (ESourceSMIME *extension, + const gchar *signing_algorithm) +{ + g_return_if_fail (E_IS_SOURCE_SMIME (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->signing_algorithm); + extension->priv->signing_algorithm = + e_util_strdup_strip (signing_algorithm); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "signing-algorithm"); +} + +/** + * e_source_smime_get_signing_certificate: + * @extension: an #ESourceSMIME + * + * Returns the S/MIME certificate name used to sign messages. + * + * Returns: the certificate name used to sign messages + * + * Since: 3.6 + **/ +const gchar * +e_source_smime_get_signing_certificate (ESourceSMIME *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), NULL); + + return extension->priv->signing_certificate; +} + +/** + * e_source_smime_dup_signing_certificate: + * @extension: an #ESourceSMIME + * + * Thread-safe variation of e_source_smime_get_signing_certificate(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceSMIME:signing-certificate + * + * Since: 3.6 + **/ +gchar * +e_source_smime_dup_signing_certificate (ESourceSMIME *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_smime_get_signing_certificate (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_smime_set_signing_certificate: + * @extension: an #ESourceSMIME + * @signing_certificate: (allow-none): the certificate name used to sign + * messages, or %NULL + * + * Sets the S/MIME certificate name used to sign messages. + * + * The internal copy of @signing_certificate is automatically stripped + * of leading and trailing whitespace. If the resulting string is empty, + * %NULL is set instead. + * + * Since: 3.6 + **/ +void +e_source_smime_set_signing_certificate (ESourceSMIME *extension, + const gchar *signing_certificate) +{ + g_return_if_fail (E_IS_SOURCE_SMIME (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->signing_certificate); + extension->priv->signing_certificate = + e_util_strdup_strip (signing_certificate); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "signing-certificate"); +} + +/** + * e_source_smime_get_sign_by_default: + * @extension: an #ESourceSMIME + * + * Returns whether to digitally sign outgoing messages by default using + * S/MIME software such as Mozilla Network Security Services (NSS). + * + * Returns: whether to sign outgoing messages by default + * + * Since: 3.6 + **/ +gboolean +e_source_smime_get_sign_by_default (ESourceSMIME *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_SMIME (extension), FALSE); + + return extension->priv->sign_by_default; +} + +/** + * e_source_smime_set_sign_by_default: + * @extension: an #ESourceSMIME + * @sign_by_default: whether to sign outgoing messages by default + * + * Sets whether to digitally sign outgoing messages by default using + * S/MIME software such as Mozilla Network Security Services (NSS). + * + * Since: 3.6 + **/ +void +e_source_smime_set_sign_by_default (ESourceSMIME *extension, + gboolean sign_by_default) +{ + g_return_if_fail (E_IS_SOURCE_SMIME (extension)); + + extension->priv->sign_by_default = sign_by_default; + + g_object_notify (G_OBJECT (extension), "sign-by-default"); +} + diff --git a/libedataserver/e-source-smime.h b/libedataserver/e-source-smime.h new file mode 100644 index 0000000..841727f --- /dev/null +++ b/libedataserver/e-source-smime.h @@ -0,0 +1,117 @@ +/* + * e-source-smime.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_SMIME_H +#define E_SOURCE_SMIME_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_SMIME \ + (e_source_smime_get_type ()) +#define E_SOURCE_SMIME(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_SMIME, ESourceSMIME)) +#define E_SOURCE_SMIME_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_SMIME, ESourceSMIMEClass)) +#define E_IS_SOURCE_SMIME(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_SMIME)) +#define E_IS_SOURCE_SMIME_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_SMIME)) +#define E_SOURCE_SMIME_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_SMIME, ESourceSMIMEClass)) + +/** + * E_SOURCE_EXTENSION_SMIME: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceSMIME. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_SMIME "Secure MIME (S/MIME)" + +G_BEGIN_DECLS + +typedef struct _ESourceSMIME ESourceSMIME; +typedef struct _ESourceSMIMEClass ESourceSMIMEClass; +typedef struct _ESourceSMIMEPrivate ESourceSMIMEPrivate; + +/** + * ESourceSMIME: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceSMIME { + ESourceExtension parent; + ESourceSMIMEPrivate *priv; +}; + +struct _ESourceSMIMEClass { + ESourceExtensionClass parent_class; +}; + +GType e_source_smime_get_type (void) G_GNUC_CONST; +const gchar * e_source_smime_get_encryption_certificate + (ESourceSMIME *extension); +gchar * e_source_smime_dup_encryption_certificate + (ESourceSMIME *extension); +void e_source_smime_set_encryption_certificate + (ESourceSMIME *extension, + const gchar *encryption_certificate); +gboolean e_source_smime_get_encrypt_by_default + (ESourceSMIME *extension); +void e_source_smime_set_encrypt_by_default + (ESourceSMIME *extension, + gboolean encrypt_by_default); +gboolean e_source_smime_get_encrypt_to_self + (ESourceSMIME *extension); +void e_source_smime_set_encrypt_to_self + (ESourceSMIME *extension, + gboolean encrypt_to_self); +const gchar * e_source_smime_get_signing_algorithm + (ESourceSMIME *extension); +gchar * e_source_smime_dup_signing_algorithm + (ESourceSMIME *extension); +void e_source_smime_set_signing_algorithm + (ESourceSMIME *extension, + const gchar *signing_algorithm); +const gchar * e_source_smime_get_signing_certificate + (ESourceSMIME *extension); +gchar * e_source_smime_dup_signing_certificate + (ESourceSMIME *extension); +void e_source_smime_set_signing_certificate + (ESourceSMIME *extension, + const gchar *signing_certificate); +gboolean e_source_smime_get_sign_by_default + (ESourceSMIME *extension); +void e_source_smime_set_sign_by_default + (ESourceSMIME *extension, + gboolean sign_by_default); + +G_END_DECLS + +#endif /* E_SOURCE_SMIME_H */ + diff --git a/libedataserver/e-source-webdav.c b/libedataserver/e-source-webdav.c new file mode 100644 index 0000000..917a568 --- /dev/null +++ b/libedataserver/e-source-webdav.c @@ -0,0 +1,1049 @@ +/* + * e-source-webdav.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/** + * SECTION: e-source-webdav + * @include: libedataserver/e-source-webdav.h + * @short_description: #ESource extension for WebDAV settings + * + * The #ESourceWebdav extension tracks settings for accessing resources + * on a remote WebDAV server. + * + * This class exists in libedataserver because we have several + * WebDAV-based backends. Each of these backends is free to use + * this class directly or subclass it with additional settings. + * Subclasses should override the extension name. + * + * The #SoupURI is parsed into components and distributed across + * several other built-in extensions such as #ESourceAuthentication + * and #ESourceSecurity. + * + * Access the extension as follows: + * + * |[ + * #include + * + * ESourceWebdav *extension; + * + * extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND); + * ]| + **/ + +#include "e-source-webdav.h" + +#include +#include +#include + +#define E_SOURCE_WEBDAV_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), E_TYPE_SOURCE_WEBDAV, ESourceWebdavPrivate)) + +struct _ESourceWebdavPrivate { + GMutex *property_lock; + gchar *display_name; + gchar *email_address; + gchar *resource_path; + gboolean avoid_ifmatch; + gboolean calendar_auto_schedule; + gboolean ignore_invalid_cert; + SoupURI *uri; +}; + +enum { + PROP_0, + PROP_AVOID_IFMATCH, + PROP_CALENDAR_AUTO_SCHEDULE, + PROP_DISPLAY_NAME, + PROP_EMAIL_ADDRESS, + PROP_IGNORE_INVALID_CERT, + PROP_RESOURCE_PATH, + PROP_SOUP_URI +}; + +G_DEFINE_TYPE ( + ESourceWebdav, + e_source_webdav, + E_TYPE_SOURCE_EXTENSION) + +static gboolean +source_webdav_host_to_soup_uri (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GObject *target; + SoupURI *soup_uri; + const gchar *host; + + host = g_value_get_string (source_value); + + target = g_binding_get_target (binding); + g_return_val_if_fail (E_IS_SOURCE_WEBDAV (target), FALSE); + + soup_uri = e_source_webdav_dup_soup_uri (E_SOURCE_WEBDAV (target)); + soup_uri_set_host (soup_uri, host); + g_value_take_boxed (target_value, soup_uri); + + return TRUE; +} + +static gboolean +source_webdav_soup_uri_to_host (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + SoupURI *soup_uri; + + soup_uri = g_value_get_boxed (source_value); + g_value_set_string (target_value, soup_uri->host); + + return TRUE; +} + +static gboolean +source_webdav_path_to_soup_uri (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GObject *target; + SoupURI *soup_uri; + const gchar *path; + + path = g_value_get_string (source_value); + + /* soup_uri_set_path() warns on NULL. */ + if (path == NULL) + path = ""; + + target = g_binding_get_target (binding); + g_return_val_if_fail (E_IS_SOURCE_WEBDAV (target), FALSE); + + soup_uri = e_source_webdav_dup_soup_uri (E_SOURCE_WEBDAV (target)); + soup_uri_set_path (soup_uri, path); + g_value_take_boxed (target_value, soup_uri); + + return TRUE; +} + +static gboolean +source_webdav_soup_uri_to_path (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + SoupURI *soup_uri; + + soup_uri = g_value_get_boxed (source_value); + g_value_set_string (target_value, soup_uri->path); + + return TRUE; +} + +static gboolean +source_webdav_port_to_soup_uri (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GObject *target; + SoupURI *soup_uri; + guint port; + + port = g_value_get_uint (source_value); + + target = g_binding_get_target (binding); + g_return_val_if_fail (E_IS_SOURCE_WEBDAV (target), FALSE); + + soup_uri = e_source_webdav_dup_soup_uri (E_SOURCE_WEBDAV (target)); + soup_uri_set_port (soup_uri, port); + g_value_take_boxed (target_value, soup_uri); + + return TRUE; +} + +static gboolean +source_webdav_soup_uri_to_port (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + SoupURI *soup_uri; + + soup_uri = g_value_get_boxed (source_value); + g_value_set_uint (target_value, soup_uri->port); + + return TRUE; +} + +static gboolean +source_webdav_secure_to_soup_uri (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GObject *target; + SoupURI *soup_uri; + gboolean secure; + + secure = g_value_get_boolean (source_value); + + target = g_binding_get_target (binding); + g_return_val_if_fail (E_IS_SOURCE_WEBDAV (target), FALSE); + + soup_uri = e_source_webdav_dup_soup_uri (E_SOURCE_WEBDAV (target)); + if (secure) + soup_uri_set_scheme (soup_uri, SOUP_URI_SCHEME_HTTPS); + else + soup_uri_set_scheme (soup_uri, SOUP_URI_SCHEME_HTTP); + g_value_take_boxed (target_value, soup_uri); + + return TRUE; +} + +static gboolean +source_webdav_soup_uri_to_secure (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + SoupURI *soup_uri; + gboolean secure; + + soup_uri = g_value_get_boxed (source_value); + secure = (soup_uri->scheme == SOUP_URI_SCHEME_HTTPS); + g_value_set_boolean (target_value, secure); + + return TRUE; +} + +static gboolean +source_webdav_user_to_soup_uri (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + GObject *target; + SoupURI *soup_uri; + const gchar *user; + + user = g_value_get_string (source_value); + + target = g_binding_get_target (binding); + g_return_val_if_fail (E_IS_SOURCE_WEBDAV (target), FALSE); + + soup_uri = e_source_webdav_dup_soup_uri (E_SOURCE_WEBDAV (target)); + soup_uri_set_user (soup_uri, user); + g_value_take_boxed (target_value, soup_uri); + + return TRUE; +} + +static gboolean +source_webdav_soup_uri_to_user (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + SoupURI *soup_uri; + + soup_uri = g_value_get_boxed (source_value); + g_value_set_string (target_value, soup_uri->user); + + return TRUE; +} + +static gboolean +source_webdav_user_to_method (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer user_data) +{ + const gchar *user; + + user = g_value_get_string (source_value); + if (user == NULL || *user == '\0') + g_value_set_string (target_value, "none"); + else + g_value_set_string (target_value, "plain/password"); + + return TRUE; +} + +static void +source_webdav_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_AVOID_IFMATCH: + e_source_webdav_set_avoid_ifmatch ( + E_SOURCE_WEBDAV (object), + g_value_get_boolean (value)); + return; + + case PROP_CALENDAR_AUTO_SCHEDULE: + e_source_webdav_set_calendar_auto_schedule ( + E_SOURCE_WEBDAV (object), + g_value_get_boolean (value)); + return; + + case PROP_DISPLAY_NAME: + e_source_webdav_set_display_name ( + E_SOURCE_WEBDAV (object), + g_value_get_string (value)); + return; + + case PROP_EMAIL_ADDRESS: + e_source_webdav_set_email_address ( + E_SOURCE_WEBDAV (object), + g_value_get_string (value)); + return; + + case PROP_IGNORE_INVALID_CERT: + e_source_webdav_set_ignore_invalid_cert ( + E_SOURCE_WEBDAV (object), + g_value_get_boolean (value)); + return; + + case PROP_RESOURCE_PATH: + e_source_webdav_set_resource_path ( + E_SOURCE_WEBDAV (object), + g_value_get_string (value)); + return; + + case PROP_SOUP_URI: + e_source_webdav_set_soup_uri ( + E_SOURCE_WEBDAV (object), + g_value_get_boxed (value)); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_webdav_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_AVOID_IFMATCH: + g_value_set_boolean ( + value, + e_source_webdav_get_avoid_ifmatch ( + E_SOURCE_WEBDAV (object))); + return; + + case PROP_CALENDAR_AUTO_SCHEDULE: + g_value_set_boolean ( + value, + e_source_webdav_get_calendar_auto_schedule ( + E_SOURCE_WEBDAV (object))); + return; + + case PROP_DISPLAY_NAME: + g_value_take_string ( + value, + e_source_webdav_dup_display_name ( + E_SOURCE_WEBDAV (object))); + return; + + case PROP_EMAIL_ADDRESS: + g_value_take_string ( + value, + e_source_webdav_dup_email_address ( + E_SOURCE_WEBDAV (object))); + return; + + case PROP_IGNORE_INVALID_CERT: + g_value_set_boolean ( + value, + e_source_webdav_get_ignore_invalid_cert ( + E_SOURCE_WEBDAV (object))); + return; + + case PROP_RESOURCE_PATH: + g_value_take_string ( + value, + e_source_webdav_dup_resource_path ( + E_SOURCE_WEBDAV (object))); + return; + + case PROP_SOUP_URI: + g_value_take_boxed ( + value, + e_source_webdav_dup_soup_uri ( + E_SOURCE_WEBDAV (object))); + return; + } + + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} + +static void +source_webdav_finalize (GObject *object) +{ + ESourceWebdavPrivate *priv; + + priv = E_SOURCE_WEBDAV_GET_PRIVATE (object); + + g_mutex_free (priv->property_lock); + + g_free (priv->display_name); + g_free (priv->email_address); + g_free (priv->resource_path); + + soup_uri_free (priv->uri); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_webdav_parent_class)->finalize (object); +} + +static void +source_webdav_constructed (GObject *object) +{ + ESource *source; + ESourceExtension *this_extension; + ESourceExtension *other_extension; + const gchar *extension_name; + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_source_webdav_parent_class)->constructed (object); + + this_extension = E_SOURCE_EXTENSION (object); + source = e_source_extension_get_source (this_extension); + + g_object_bind_property_full ( + this_extension, "resource-path", + this_extension, "soup-uri", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE, + source_webdav_path_to_soup_uri, + source_webdav_soup_uri_to_path, + NULL, (GDestroyNotify) NULL); + + /* Bind to properties of other extensions for convenience. */ + + extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; + other_extension = e_source_get_extension (source, extension_name); + + g_object_bind_property_full ( + other_extension, "host", + this_extension, "soup-uri", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE, + source_webdav_host_to_soup_uri, + source_webdav_soup_uri_to_host, + NULL, (GDestroyNotify) NULL); + + g_object_bind_property_full ( + other_extension, "port", + this_extension, "soup-uri", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE, + source_webdav_port_to_soup_uri, + source_webdav_soup_uri_to_port, + NULL, (GDestroyNotify) NULL); + + g_object_bind_property_full ( + other_extension, "user", + this_extension, "soup-uri", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE, + source_webdav_user_to_soup_uri, + source_webdav_soup_uri_to_user, + NULL, (GDestroyNotify) NULL); + + g_object_bind_property_full ( + other_extension, "user", + other_extension, "method", + G_BINDING_SYNC_CREATE, + source_webdav_user_to_method, + NULL, + NULL, (GDestroyNotify) NULL); + + extension_name = E_SOURCE_EXTENSION_SECURITY; + other_extension = e_source_get_extension (source, extension_name); + + g_object_bind_property_full ( + other_extension, "secure", + this_extension, "soup-uri", + G_BINDING_BIDIRECTIONAL | + G_BINDING_SYNC_CREATE, + source_webdav_secure_to_soup_uri, + source_webdav_soup_uri_to_secure, + NULL, (GDestroyNotify) NULL); +} + +static void +e_source_webdav_class_init (ESourceWebdavClass *class) +{ + GObjectClass *object_class; + ESourceExtensionClass *extension_class; + + g_type_class_add_private (class, sizeof (ESourceWebdavPrivate)); + + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_webdav_set_property; + object_class->get_property = source_webdav_get_property; + object_class->finalize = source_webdav_finalize; + object_class->constructed = source_webdav_constructed; + + extension_class = E_SOURCE_EXTENSION_CLASS (class); + extension_class->name = E_SOURCE_EXTENSION_WEBDAV_BACKEND; + + g_object_class_install_property ( + object_class, + PROP_AVOID_IFMATCH, + g_param_spec_boolean ( + "avoid-ifmatch", + "Avoid If-Match", + "Work around a bug in old Apache servers", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_CALENDAR_AUTO_SCHEDULE, + g_param_spec_boolean ( + "calendar-auto-schedule", + "Calendar Auto-Schedule", + "Whether the server handles meeting " + "invitations (CalDAV-only)", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_DISPLAY_NAME, + g_param_spec_string ( + "display-name", + "Display Name", + "Display name of the WebDAV resource", + "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_EMAIL_ADDRESS, + g_param_spec_string ( + "email-address", + "Email Address", + "The user's email address", + "", + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_IGNORE_INVALID_CERT, + g_param_spec_boolean ( + "ignore-invalid-cert", + "Ignore Invalid Cert", + "Ignore invalid SSL certificates", + FALSE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_RESOURCE_PATH, + g_param_spec_string ( + "resource-path", + "Resource Path", + "Absolute path to a WebDAV resource", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_SOUP_URI, + g_param_spec_boxed ( + "soup-uri", + "SoupURI", + "WebDAV service as a SoupURI", + SOUP_TYPE_URI, + G_PARAM_READWRITE)); +} + +static void +e_source_webdav_init (ESourceWebdav *extension) +{ + extension->priv = E_SOURCE_WEBDAV_GET_PRIVATE (extension); + extension->priv->property_lock = g_mutex_new (); + + /* Initialize this enough for SOUP_URI_IS_VALID() to pass. */ + extension->priv->uri = soup_uri_new (NULL); + extension->priv->uri->scheme = SOUP_URI_SCHEME_HTTP; + extension->priv->uri->path = g_strdup (""); +} + +/** + * e_source_webdav_get_avoid_ifmatch: + * @extension: an #ESourceWebdav + * + * This setting works around a + * + * bug in older Apache mod_dav versions. + * + * + * + * We may deprecate this once Apache 2.2.8 or newer becomes + * sufficiently ubiquitous, or we figure out a way to detect + * and work around the bug automatically. + * + * + * + * Returns: whether the WebDAV server is known to exhibit the bug + * + * Since: 3.6 + **/ +gboolean +e_source_webdav_get_avoid_ifmatch (ESourceWebdav *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), FALSE); + + return extension->priv->avoid_ifmatch; +} + +/** + * e_source_webdav_set_avoid_ifmatch: + * @extension: an #ESourceWebdav + * @avoid_ifmatch: whether the WebDAV server is known to exhibit the bug + * + * This setting works around a + * + * bug in older Apache mod_dav versions. + * + * + * + * We may deprecate this once Apache 2.2.8 or newer becomes + * sufficiently ubiquitous, or we figure out a way to detect + * and work around the bug automatically. + * + * + * + * Since: 3.6 + **/ +void +e_source_webdav_set_avoid_ifmatch (ESourceWebdav *extension, + gboolean avoid_ifmatch) +{ + g_return_if_fail (E_IS_SOURCE_WEBDAV (extension)); + + extension->priv->avoid_ifmatch = avoid_ifmatch; + + g_object_notify (G_OBJECT (extension), "avoid-ifmatch"); +} + +/** + * e_source_webdav_get_calendar_auto_schedule: + * @extension: an #ESourceWebdav + * + * FIXME Document me! + * + * Since: 3.6 + **/ +gboolean +e_source_webdav_get_calendar_auto_schedule (ESourceWebdav *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), FALSE); + + return extension->priv->calendar_auto_schedule; +} + +/** + * e_source_webdav_set_calendar_auto_schedule: + * @extension: an #ESourceWebdav + * @calendar_auto_schedule: whether the server supports the + * "calendar-auto-schedule" feature of CalDAV + * + * FIXME Document me! + * + * Since: 3.6 + **/ +void +e_source_webdav_set_calendar_auto_schedule (ESourceWebdav *extension, + gboolean calendar_auto_schedule) +{ + g_return_if_fail (E_IS_SOURCE_WEBDAV (extension)); + + extension->priv->calendar_auto_schedule = calendar_auto_schedule; + + g_object_notify (G_OBJECT (extension), "calendar-auto-schedule"); +} + +/** + * e_source_webdav_get_display_name: + * @extension: an #ESourceWebdav + * + * Returns the last known display name of a WebDAV resource, which may + * differ from the #ESource:display-name property of the #ESource to which + * @extension belongs. + * + * Returns: the display name of the WebDAV resource + * + * Since: 3.6 + **/ +const gchar * +e_source_webdav_get_display_name (ESourceWebdav *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); + + return extension->priv->display_name; +} + +/** + * e_source_webdav_dup_display_name: + * @extension: an #ESourceWebdav + * + * Thread-safe variation of e_source_webdav_get_display_name(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESourceWebdav:display-name + * + * Since: 3.6 + **/ +gchar * +e_source_webdav_dup_display_name (ESourceWebdav *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_webdav_get_display_name (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_webdav_set_display_name: + * @extension: an #ESourceWebdav + * @display_name: (allow-none): the display name of the WebDAV resource, + * or %NULL + * + * Updates the last known display name of a WebDAV resource, which may + * differ from the #ESource:display-name property of the #ESource to which + * @extension belongs. + * + * The internal copy of @display_name is automatically stripped of leading + * and trailing whitespace. If the resulting string is empty, %NULL is set + * instead. + * + * Since: 3.6 + **/ +void +e_source_webdav_set_display_name (ESourceWebdav *extension, + const gchar *display_name) +{ + g_return_if_fail (E_IS_SOURCE_WEBDAV (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->display_name); + extension->priv->display_name = e_util_strdup_strip (display_name); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "display-name"); +} + +/** + * e_source_webdav_get_email_address: + * @extension: an #ESourceWebdav + * + * Returns the user's email address which can be passed to a CalDAV server + * if the user wishes to receive scheduling messages. + * + * Returns: the user's email address + * + * Since: 3.6 + **/ +const gchar * +e_source_webdav_get_email_address (ESourceWebdav *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); + + return extension->priv->email_address; +} + +/** + * e_source_webdav_dup_email_address: + * @extension: an #ESourceWebdav + * + * Thread-safe variation of e_source_webdav_get_email_address(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: the newly-allocated copy of #ESourceWebdav:email-address + * + * Since: 3.6 + **/ +gchar * +e_source_webdav_dup_email_address (ESourceWebdav *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_webdav_get_email_address (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_webdav_set_email_address: + * @extension: an #ESourceWebdav + * @email_address: (allow-none): the user's email address, or %NULL + * + * Sets the user's email address which can be passed to a CalDAV server if + * the user wishes to receive scheduling messages. + * + * The internal copy of @email_address is automatically stripped of leading + * and trailing whitespace. If the resulting string is empty, %NULL is set + * instead. + * + * Since: 3.6 + **/ +void +e_source_webdav_set_email_address (ESourceWebdav *extension, + const gchar *email_address) +{ + g_return_if_fail (E_IS_SOURCE_WEBDAV (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->email_address); + extension->priv->email_address = e_util_strdup_strip (email_address); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "email-address"); +} + +/** + * e_source_webdav_get_ignore_invalid_cert: + * @extension: an #ESourceWebdav + * + * Returns %TRUE if invalid SSL certificates should be ignored. + * + * This option allows SSL certificates to be accepted even if they have + * signed by an unrecognized Certificate Authority. + * + * Returns: whether invalid SSL certificates should be ignored + * + * Since: 3.6 + **/ +gboolean +e_source_webdav_get_ignore_invalid_cert (ESourceWebdav *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), FALSE); + + return extension->priv->ignore_invalid_cert; +} + +/** + * e_source_webdav_set_ignore_invalid_cert: + * @extension: an #ESourceWebdav + * @ignore_invalid_cert: whether invalid SSL certificates should be ignored + * + * Sets whether invalid SSL certificates should be ignored. + * + * This option allows SSL certificates to be accepted even if they have + * signed by an unrecognized Certificate Authority. + * + * Since: 3.6 + **/ +void +e_source_webdav_set_ignore_invalid_cert (ESourceWebdav *extension, + gboolean ignore_invalid_cert) +{ + g_return_if_fail (E_IS_SOURCE_WEBDAV (extension)); + + extension->priv->ignore_invalid_cert = ignore_invalid_cert; + + g_object_notify (G_OBJECT (extension), "ignore-invalid-cert"); +} + +/** + * e_source_webdav_get_resource_path: + * @extension: an #ESourceWebdav + * + * Returns the absolute path to a resource on a WebDAV server. + * + * Returns: the absolute path to a WebDAV resource + * + * Since: 3.6 + **/ +const gchar * +e_source_webdav_get_resource_path (ESourceWebdav *extension) +{ + g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); + + return extension->priv->resource_path; +} + +/** + * e_source_webdav_dup_resource_path: + * @extension: an #ESourceWebdav + * + * Thread-safe variation of e_source_webdav_get_resource_path(). + * Use this function when accessing @extension from multiple threads. + * + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: the newly-allocated copy of #ESourceWebdav:resource-path + * + * Since: 3.6 + **/ +gchar * +e_source_webdav_dup_resource_path (ESourceWebdav *extension) +{ + const gchar *protected; + gchar *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + protected = e_source_webdav_get_resource_path (extension); + duplicate = g_strdup (protected); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_webdav_set_resource_path: + * @extension: an #ESourceWebdav + * @resource_path: (allow-none): the absolute path to a WebDAV resource, + * or %NULL + * + * Sets the absolute path to a resource on a WebDAV server. + * + * The internal copy of @resource_path is automatically stripped of leading + * and trailing whitespace. If the resulting string is empty, %NULL is set + * instead. + * + * Since: 3.6 + **/ +void +e_source_webdav_set_resource_path (ESourceWebdav *extension, + const gchar *resource_path) +{ + g_return_if_fail (E_IS_SOURCE_WEBDAV (extension)); + + g_mutex_lock (extension->priv->property_lock); + + g_free (extension->priv->resource_path); + extension->priv->resource_path = e_util_strdup_strip (resource_path); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "resource-path"); +} + +/** + * e_source_webdav_dup_soup_uri: + * @extension: an #ESourceWebdav + * + * This is a convenience function which returns a newly-allocated + * #SoupURI, its contents assembled from the #ESourceAuthentication + * extension, the #ESourceSecurity extension, and @extension itself. + * Free the returned #SoupURI with soup_uri_free(). + * + * Returns: (transfer full): a newly-allocated #SoupURI + * + * Since: 3.6 + **/ +SoupURI * +e_source_webdav_dup_soup_uri (ESourceWebdav *extension) +{ + SoupURI *duplicate; + + g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL); + + g_mutex_lock (extension->priv->property_lock); + + duplicate = soup_uri_copy (extension->priv->uri); + + g_mutex_unlock (extension->priv->property_lock); + + return duplicate; +} + +/** + * e_source_webdav_set_soup_uri: + * @extension: an #ESourceWebdav + * @uri: a #SoupURI + * + * This is a convenience function which propagates the components of + * @uri to the #ESourceAuthentication extension, the #ESourceSecurity + * extension, and @extension itself. (The "query" and "fragment" + * components of @uri are ignored.) + * + * Since: 3.6 + **/ +void +e_source_webdav_set_soup_uri (ESourceWebdav *extension, + SoupURI *uri) +{ + g_return_if_fail (E_IS_SOURCE_WEBDAV (extension)); + g_return_if_fail (SOUP_URI_IS_VALID (uri)); + + g_mutex_lock (extension->priv->property_lock); + + soup_uri_free (extension->priv->uri); + extension->priv->uri = soup_uri_copy (uri); + + g_mutex_unlock (extension->priv->property_lock); + + g_object_notify (G_OBJECT (extension), "soup-uri"); +} + diff --git a/libedataserver/e-source-webdav.h b/libedataserver/e-source-webdav.h new file mode 100644 index 0000000..3871277 --- /dev/null +++ b/libedataserver/e-source-webdav.h @@ -0,0 +1,120 @@ +/* + * e-source-webdav.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SOURCE_WEBDAV_H +#define E_SOURCE_WEBDAV_H + +#include +#include + +/* Standard GObject macros */ +#define E_TYPE_SOURCE_WEBDAV \ + (e_source_webdav_get_type ()) +#define E_SOURCE_WEBDAV(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SOURCE_WEBDAV, ESourceWebdav)) +#define E_SOURCE_WEBDAV_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SOURCE_WEBDAV, ESourceWebdavClass)) +#define E_IS_SOURCE_WEBDAV(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SOURCE_WEBDAV)) +#define E_IS_SOURCE_WEBDAV_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SOURCE_WEBDAV)) +#define E_SOURCE_WEBDAV_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE_WEBDAV, ESourceWebdavClass)) + +/** + * E_SOURCE_EXTENSION_WEBDAV_BACKEND: + * + * Pass this extension name to e_source_get_extension() to access + * #ESourceWebdav. This is also used as a group name in key files. + * + * Since: 3.6 + **/ +#define E_SOURCE_EXTENSION_WEBDAV_BACKEND "WebDAV Backend" + +G_BEGIN_DECLS + +typedef struct _ESourceWebdav ESourceWebdav; +typedef struct _ESourceWebdavClass ESourceWebdavClass; +typedef struct _ESourceWebdavPrivate ESourceWebdavPrivate; + +/** + * ESourceWebdav: + * + * Contains only private data that should be read and manipulated using the + * functions below. + * + * Since: 3.6 + **/ +struct _ESourceWebdav { + ESourceExtension parent; + ESourceWebdavPrivate *priv; +}; + +struct _ESourceWebdavClass { + ESourceExtensionClass parent_class; +}; + +GType e_source_webdav_get_type (void) G_GNUC_CONST; +gboolean e_source_webdav_get_avoid_ifmatch + (ESourceWebdav *extension); +void e_source_webdav_set_avoid_ifmatch + (ESourceWebdav *extension, + gboolean avoid_ifmatch); +gboolean e_source_webdav_get_calendar_auto_schedule + (ESourceWebdav *extension); +void e_source_webdav_set_calendar_auto_schedule + (ESourceWebdav *extension, + gboolean calendar_auto_schedule); +const gchar * e_source_webdav_get_display_name + (ESourceWebdav *extension); +gchar * e_source_webdav_dup_display_name + (ESourceWebdav *extension); +void e_source_webdav_set_display_name + (ESourceWebdav *extension, + const gchar *display_name); +const gchar * e_source_webdav_get_email_address + (ESourceWebdav *extension); +gchar * e_source_webdav_dup_email_address + (ESourceWebdav *extension); +void e_source_webdav_set_email_address + (ESourceWebdav *extension, + const gchar *email_address); +gboolean e_source_webdav_get_ignore_invalid_cert + (ESourceWebdav *extension); +void e_source_webdav_set_ignore_invalid_cert + (ESourceWebdav *extension, + gboolean ignore_invalid_cert); +const gchar * e_source_webdav_get_resource_path + (ESourceWebdav *extension); +gchar * e_source_webdav_dup_resource_path + (ESourceWebdav *extension); +void e_source_webdav_set_resource_path + (ESourceWebdav *extension, + const gchar *resource_path); +SoupURI * e_source_webdav_dup_soup_uri (ESourceWebdav *extension); +void e_source_webdav_set_soup_uri (ESourceWebdav *extension, + SoupURI *uri); + +G_END_DECLS + +#endif /* E_SOURCE_WEBDAV_H */ diff --git a/libedataserver/e-source.c b/libedataserver/e-source.c index 4b96575..62a7a9a 100644 --- a/libedataserver/e-source.c +++ b/libedataserver/e-source.c @@ -1,50 +1,144 @@ -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ -/* e-source.c - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) +/* + * e-source.c * * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU Lesser General Public - * License as published by the Free Software Foundation. + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. * * 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. + * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. + * License along with the program; if not, see * - * Author: Ettore Perazzoli */ +/** + * SECTION: e-source + * @include: libedataserver/e-source.h + * @short_description: Hierarchical data sources + * + * An #ESource (or "data source") is a description of a file or network + * location where data can be obtained (such as a mail account), or a + * description of a resource at that location (such as a mail folder). + * + * In more concrete terms, it's an interface for a key file. All such + * key files have a main group named [Data Source]. The keys in a + * [Data Source] group map to #GObject properties in an #ESource. + * + * Additional groups in the key file are referred to as "extensions". + * #ESourceExtension serves as the base class for writing interfaces + * for these additional key file groups. The keys in one of these + * key file groups map to #GObject properties in some custom subclass + * of #ESourceExtension which was written specifically for that key + * file group. For example, a key file might include a group named + * [Calendar], whose keys map to #GObject properties in an extension + * class named #ESourceCalendar. + * + * Each #ESource contains an internal dictionary of extension objects, + * accessible by their key file group name. e_source_get_extension() + * can look up extension objects by name. + * + * An #ESource is identified by a unique identifier string, or "UID", + * which is also the basename of the corresponding key file. Additional + * files related to the #ESource, such as cache files, are usually kept + * in a directory named after the UID of the #ESource. Similarly, the + * password for an account described by an #ESource is kept in GNOME + * Keyring under the UID of the #ESource. This makes finding these + * additional resources simple. + * + * Several extensions for common information such as authentication + * details are built into libedataserver (#ESourceAuthentication, for + * example). Backend modules may also define their own extensions for + * information and settings unique to the backend. #ESourceExtension + * subclasses written for specific backends are generally not available + * to applications and shared libraries. This is by design, to try and + * keep backend-specific knowledge from creeping into places it doesn't + * belong. + **/ + #include "e-source.h" #include #include - -#include -#include +#include + +/* Private D-Bus classes. */ +#include + +#include "e-data-server-util.h" +#include "e-source-extension.h" +#include "e-uid.h" + +/* built-in extension types */ +#include "e-source-address-book.h" +#include "e-source-alarms.h" +#include "e-source-authentication.h" +#include "e-source-autocomplete.h" +#include "e-source-calendar.h" +#include "e-source-camel.h" +#include "e-source-collection.h" +#include "e-source-goa.h" +#include "e-source-mail-account.h" +#include "e-source-mail-composition.h" +#include "e-source-mail-identity.h" +#include "e-source-mail-signature.h" +#include "e-source-mail-submission.h" +#include "e-source-mail-transport.h" +#include "e-source-mdn.h" +#include "e-source-offline.h" +#include "e-source-openpgp.h" +#include "e-source-refresh.h" +#include "e-source-security.h" +#include "e-source-selectable.h" +#include "e-source-smime.h" +#include "e-source-webdav.h" #define E_SOURCE_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ ((obj), E_TYPE_SOURCE, ESourcePrivate)) +/* This forces the GType to be registered in a way that + * avoids a "statement with no effect" compiler warning. */ +#define REGISTER_TYPE(type) \ + (g_type_class_unref (g_type_class_ref (type))) + +#define PRIMARY_GROUP_NAME "Data Source" + struct _ESourcePrivate { - ESourceGroup *group; + GDBusObject *dbus_object; + GMainContext *main_context; + + GSource *changed; + GMutex *changed_lock; + GMutex *property_lock; + gchar *display_name; + gchar *parent; gchar *uid; - gchar *name; - gchar *relative_uri; - gchar *absolute_uri; - gboolean readonly; + /* The lock guards the key file and hash table. */ - gchar *color_spec; + GKeyFile *key_file; + GStaticRecMutex lock; + GHashTable *extensions; - GHashTable *properties; + gboolean enabled; +}; + +enum { + PROP_0, + PROP_DBUS_OBJECT, + PROP_DISPLAY_NAME, + PROP_ENABLED, + PROP_MAIN_CONTEXT, + PROP_PARENT, + PROP_REMOVABLE, + PROP_UID, + PROP_WRITABLE }; enum { @@ -52,1197 +146,1976 @@ enum { LAST_SIGNAL }; -static guint signals[LAST_SIGNAL] = { 0 }; +static guint signals[LAST_SIGNAL]; + +/* Forward Declarations */ +static void e_source_initable_init (GInitableIface *interface); -/* Callbacks. */ +G_DEFINE_TYPE_WITH_CODE ( + ESource, + e_source, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE ( + G_TYPE_INITABLE, + e_source_initable_init)) static void -group_weak_notify (ESource *source, - GObject **where_the_object_was) +source_find_extension_classes_rec (GType parent_type, + GHashTable *hash_table) { - source->priv->group = NULL; + GType *children; + guint n_children, ii; - g_signal_emit (source, signals[CHANGED], 0); + children = g_type_children (parent_type, &n_children); + + for (ii = 0; ii < n_children; ii++) { + GType type = children[ii]; + ESourceExtensionClass *class; + gpointer key; + + /* Recurse over the child's children. */ + source_find_extension_classes_rec (type, hash_table); + + /* Skip abstract types. */ + if (G_TYPE_IS_ABSTRACT (type)) + continue; + + class = g_type_class_ref (type); + key = (gpointer) class->name; + + if (key != NULL) + g_hash_table_insert (hash_table, key, class); + else + g_type_class_unref (class); + } + + g_free (children); } -/* GObject methods. */ +static GHashTable * +source_find_extension_classes (void) +{ + GHashTable *hash_table; + + hash_table = g_hash_table_new_full ( + (GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) g_type_class_unref); -G_DEFINE_TYPE (ESource, e_source, G_TYPE_OBJECT) + source_find_extension_classes_rec ( + E_TYPE_SOURCE_EXTENSION, hash_table); + + return hash_table; +} static void -source_finalize (GObject *object) +source_localized_hack (GKeyFile *key_file, + const gchar *group_name, + const gchar *key, + const gchar *new_value) { - ESourcePrivate *priv; + const gchar * const *language_names; + gchar *localized_key; - priv = E_SOURCE_GET_PRIVATE (object); + /* XXX If we're changing a string key that has translations, + * set both "key" and "key[$CURRENT_LOCALE]" to the new + * value so g_key_file_get_locale_string() will pick it + * up. This is not a perfect solution however. When a + * different locale is used the value may revert to its + * original localized string. Good enough for now. */ - g_free (priv->uid); - g_free (priv->name); - g_free (priv->relative_uri); - g_free (priv->absolute_uri); - g_free (priv->color_spec); + language_names = g_get_language_names (); + localized_key = g_strdup_printf ("%s[%s]", key, language_names[0]); - g_hash_table_destroy (priv->properties); + if (g_key_file_has_key (key_file, group_name, localized_key, NULL)) + g_key_file_set_string ( + key_file, group_name, localized_key, new_value); - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (e_source_parent_class)->finalize (object); + g_free (localized_key); } static void -source_dispose (GObject *object) +source_set_key_file_from_property (GObject *object, + GParamSpec *pspec, + GKeyFile *key_file, + const gchar *group_name) { - ESourcePrivate *priv; - - priv = E_SOURCE_GET_PRIVATE (object); + GValue *pvalue; + GValue *svalue; + gchar *key; + + pvalue = g_slice_new0 (GValue); + g_value_init (pvalue, pspec->value_type); + g_object_get_property (object, pspec->name, pvalue); + + svalue = g_slice_new0 (GValue); + g_value_init (svalue, G_TYPE_STRING); + + key = e_source_parameter_to_key (pspec->name); + + /* For the most part we can just transform any supported + * property type to a string, with a couple exceptions. */ + + /* Transforming a boolean GValue to a string results in + * "TRUE" or "FALSE" (all uppercase), but GKeyFile only + * recognizes "true" or "false" (all lowercase). So we + * have to use g_key_file_set_boolean(). */ + if (G_VALUE_HOLDS_BOOLEAN (pvalue)) { + gboolean v_boolean = g_value_get_boolean (pvalue); + g_key_file_set_boolean (key_file, group_name, key, v_boolean); + + /* String GValues may contain characters that need escaping. */ + } else if (G_VALUE_HOLDS_STRING (pvalue)) { + const gchar *v_string = g_value_get_string (pvalue); + + if (v_string == NULL) + v_string = ""; + + /* Special case for localized "DisplayName" keys. */ + source_localized_hack (key_file, group_name, key, v_string); + g_key_file_set_string (key_file, group_name, key, v_string); + + /* Transforming an enum GValue to a string results in + * the GEnumValue name. We want the shorter nickname. */ + } else if (G_VALUE_HOLDS_ENUM (pvalue)) { + GParamSpecEnum *enum_pspec; + GEnumClass *enum_class; + GEnumValue *enum_value; + gint value; + + enum_pspec = G_PARAM_SPEC_ENUM (pspec); + enum_class = enum_pspec->enum_class; + + value = g_value_get_enum (pvalue); + enum_value = g_enum_get_value (enum_class, value); + + if (enum_value == NULL) { + value = enum_pspec->default_value; + enum_value = g_enum_get_value (enum_class, value); + } - if (priv->group != NULL) { - g_object_weak_unref (G_OBJECT (priv->group), (GWeakNotify) group_weak_notify, object); - priv->group = NULL; + if (enum_value != NULL) + g_key_file_set_string ( + key_file, group_name, key, + enum_value->value_nick); + + } else if (G_VALUE_HOLDS (pvalue, G_TYPE_STRV)) { + const gchar **strv = g_value_get_boxed (pvalue); + guint length = 0; + + if (strv != NULL) + length = g_strv_length ((gchar **) strv); + g_key_file_set_string_list ( + key_file, group_name, key, strv, length); + + /* For GValues holding a GFile object we save the URI. */ + } else if (G_VALUE_HOLDS (pvalue, G_TYPE_FILE)) { + GFile *file = g_value_get_object (pvalue); + gchar *uri = NULL; + + if (file != NULL) + uri = g_file_get_uri (file); + g_key_file_set_string ( + key_file, group_name, key, + (uri != NULL) ? uri : ""); + g_free (uri); + + } else if (g_value_transform (pvalue, svalue)) { + const gchar *value = g_value_get_string (svalue); + g_key_file_set_value (key_file, group_name, key, value); } - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (e_source_parent_class)->dispose (object); + g_free (key); + g_value_unset (pvalue); + g_value_unset (svalue); + g_slice_free (GValue, pvalue); + g_slice_free (GValue, svalue); } -/* Initialization. */ - static void -e_source_class_init (ESourceClass *class) +source_set_property_from_key_file (GObject *object, + GParamSpec *pspec, + GKeyFile *key_file, + const gchar *group_name) { - GObjectClass *object_class; + gchar *key; + GValue *value; + GError *error = NULL; + + value = g_slice_new0 (GValue); + key = e_source_parameter_to_key (pspec->name); + + if (G_IS_PARAM_SPEC_CHAR (pspec) || + G_IS_PARAM_SPEC_UCHAR (pspec) || + G_IS_PARAM_SPEC_INT (pspec) || + G_IS_PARAM_SPEC_UINT (pspec) || + G_IS_PARAM_SPEC_LONG (pspec) || + G_IS_PARAM_SPEC_ULONG (pspec)) { + gint v_int; + + v_int = g_key_file_get_integer ( + key_file, group_name, key, &error); + if (error == NULL) { + g_value_init (value, G_TYPE_INT); + g_value_set_int (value, v_int); + } - g_type_class_add_private (class, sizeof (ESourcePrivate)); + } else if (G_IS_PARAM_SPEC_BOOLEAN (pspec)) { + gboolean v_boolean; - object_class = G_OBJECT_CLASS (class); - object_class->dispose = source_dispose; - object_class->finalize = source_finalize; + v_boolean = g_key_file_get_boolean ( + key_file, group_name, key, &error); + if (error == NULL) { + g_value_init (value, G_TYPE_BOOLEAN); + g_value_set_boolean (value, v_boolean); + } - signals[CHANGED] = g_signal_new ( - "changed", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (ESourceClass, changed), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); + } else if (G_IS_PARAM_SPEC_ENUM (pspec)) { + gchar *nick; + + nick = g_key_file_get_string ( + key_file, group_name, key, &error); + if (error == NULL) { + GParamSpecEnum *enum_pspec; + GEnumValue *enum_value; + + enum_pspec = G_PARAM_SPEC_ENUM (pspec); + enum_value = g_enum_get_value_by_nick ( + enum_pspec->enum_class, nick); + if (enum_value != NULL) { + g_value_init (value, pspec->value_type); + g_value_set_enum (value, enum_value->value); + } + g_free (nick); + } + + } else if (G_IS_PARAM_SPEC_FLOAT (pspec) || + G_IS_PARAM_SPEC_DOUBLE (pspec)) { + gdouble v_double; + + v_double = g_key_file_get_double ( + key_file, group_name, key, &error); + if (error == NULL) { + g_value_init (value, G_TYPE_DOUBLE); + g_value_set_double (value, v_double); + } + + } else if (G_IS_PARAM_SPEC_STRING (pspec)) { + gchar *v_string; + + /* Get the localized string if present. */ + v_string = g_key_file_get_locale_string ( + key_file, group_name, key, NULL, &error); + if (error == NULL) { + g_value_init (value, G_TYPE_STRING); + g_value_take_string (value, v_string); + } + + } else if (g_type_is_a (pspec->value_type, G_TYPE_STRV)) { + gchar **strv; + + strv = g_key_file_get_string_list ( + key_file, group_name, key, NULL, &error); + if (error == NULL) { + g_value_init (value, G_TYPE_STRV); + g_value_take_boxed (value, strv); + } + + } else if (g_type_is_a (pspec->value_type, G_TYPE_FILE)) { + gchar *uri; + + /* Create the GFile from the URI string. */ + uri = g_key_file_get_locale_string ( + key_file, group_name, key, NULL, &error); + if (error == NULL) { + GFile *file = NULL; + if (uri != NULL && *uri != '\0') + file = g_file_new_for_uri (uri); + g_value_init (value, pspec->value_type); + g_value_take_object (value, file); + g_free (uri); + } + + } else { + g_warning ( + "No GKeyFile-to-GValue converter defined " + "for type '%s'", G_VALUE_TYPE_NAME (value)); + } + + /* If a value could not be retrieved from the key + * file, restore the property to its default value. */ + if (error != NULL) { + g_value_init (value, pspec->value_type); + g_param_value_set_default (pspec, value); + g_error_free (error); + } + + if (G_IS_VALUE (value)) { + g_object_set_property (object, pspec->name, value); + g_value_unset (value); + } + + g_slice_free (GValue, value); + g_free (key); } static void -e_source_init (ESource *source) +source_load_from_key_file (GObject *object, + GKeyFile *key_file, + const gchar *group_name) { - source->priv = E_SOURCE_GET_PRIVATE (source); + GObjectClass *class; + GParamSpec **properties; + guint n_properties, ii; - source->priv->properties = g_hash_table_new_full ( - (GHashFunc) g_str_hash, - (GEqualFunc) g_str_equal, - (GDestroyNotify) g_free, - (GDestroyNotify) g_free); + class = G_OBJECT_GET_CLASS (object); + properties = g_object_class_list_properties (class, &n_properties); + + g_object_freeze_notify (object); + + for (ii = 0; ii < n_properties; ii++) { + if (properties[ii]->flags & E_SOURCE_PARAM_SETTING) { + source_set_property_from_key_file ( + object, properties[ii], key_file, group_name); + } + } + + g_object_thaw_notify (object); + + g_free (properties); } -/* Private methods. */ +static void +source_save_to_key_file (GObject *object, + GKeyFile *key_file, + const gchar *group_name) +{ + GObjectClass *class; + GParamSpec **properties; + guint n_properties, ii; + + class = G_OBJECT_GET_CLASS (object); + properties = g_object_class_list_properties (class, &n_properties); + + for (ii = 0; ii < n_properties; ii++) { + if (properties[ii]->flags & E_SOURCE_PARAM_SETTING) { + source_set_key_file_from_property ( + object, properties[ii], key_file, group_name); + } + } + + g_free (properties); +} static gboolean -set_color_spec (ESource *source, - const gchar *color_spec) +source_parse_dbus_data (ESource *source, + GError **error) { - ESourcePrivate *priv = source->priv; - gboolean do_cmp; + GHashTableIter iter; + EDBusObject *dbus_object; + EDBusSource *dbus_source; + GKeyFile *key_file; + gpointer group_name; + gpointer extension; + gchar *data; + gboolean success; + + dbus_object = E_DBUS_OBJECT (source->priv->dbus_object); + + dbus_source = e_dbus_object_get_source (dbus_object); + data = e_dbus_source_dup_data (dbus_source); + g_object_unref (dbus_source); + + g_return_val_if_fail (data != NULL, FALSE); - if (color_spec == priv->color_spec) + key_file = source->priv->key_file; + + success = g_key_file_load_from_data ( + key_file, data, strlen (data), + G_KEY_FILE_KEEP_COMMENTS | + G_KEY_FILE_KEEP_TRANSLATIONS, + error); + + g_free (data); + data = NULL; + + if (!success) return FALSE; - do_cmp = (color_spec != NULL && priv->color_spec != NULL); - if (do_cmp && g_ascii_strcasecmp (color_spec, priv->color_spec) == 0) + /* Make sure the key file has a [Data Source] group. */ + if (!g_key_file_has_group (key_file, PRIMARY_GROUP_NAME)) { + g_set_error ( + error, G_KEY_FILE_ERROR, + G_KEY_FILE_ERROR_GROUP_NOT_FOUND, + _("Source file is missing a [%s] group"), + PRIMARY_GROUP_NAME); return FALSE; + } - g_free (priv->color_spec); - priv->color_spec = g_strdup (color_spec); + /* Load key file values from the [Data Source] group and from + * any other groups for which an extension object has already + * been created. Note that not all the extension classes may + * be registered at this point, so avoid attempting to create + * new extension objects here. Extension objects are created + * on-demand in e_source_get_extension(). */ + + source_load_from_key_file ( + G_OBJECT (source), key_file, PRIMARY_GROUP_NAME); + + g_hash_table_iter_init (&iter, source->priv->extensions); + while (g_hash_table_iter_next (&iter, &group_name, &extension)) + source_load_from_key_file (extension, key_file, group_name); return TRUE; } -/** - * e_source_new: - * @name: a display name for the source - * @relative_uri: a relative URI for the source - * - * Creates a new #ESource instance, and gives it a display name specified - * by @name and a relative URI specified by @relative_uri. - * - * Returns: a new #ESource - **/ -ESource * -e_source_new (const gchar *name, - const gchar *relative_uri) +static void +source_notify_dbus_data_cb (EDBusSource *dbus_source, + GParamSpec *pspec, + ESource *source) { - ESource *source; + GError *error = NULL; - g_return_val_if_fail (name != NULL, NULL); - g_return_val_if_fail (relative_uri != NULL, NULL); + g_static_rec_mutex_lock (&source->priv->lock); - source = g_object_new (E_TYPE_SOURCE, NULL); - source->priv->uid = e_uid_new (); - - e_source_set_name (source, name); - e_source_set_relative_uri (source, relative_uri); + /* Since the source data came from a GKeyFile structure on the + * server-side, this should never fail. But we'll print error + * messages to the terminal just in case. */ + if (!source_parse_dbus_data (source, &error)) { + g_return_if_fail (error != NULL); + g_warning ("%s", error->message); + g_error_free (error); + } - return source; + g_static_rec_mutex_unlock (&source->priv->lock); } -/** - * e_source_new_with_absolute_uri: - * @name: a display name for the source - * @absolute_uri: a custom absolute URI for the source - * - * Creates a new #ESource instance, and gives it a display name specified - * by @name and a custom absolute URI specified by @abolute_uri. - * - * Returns: a new #ESource - **/ -ESource * -e_source_new_with_absolute_uri (const gchar *name, - const gchar *absolute_uri) +static gboolean +source_idle_changed_cb (gpointer user_data) { - ESource *source; + ESource *source = E_SOURCE (user_data); + + g_mutex_lock (source->priv->changed_lock); + g_source_unref (source->priv->changed); + source->priv->changed = NULL; + g_mutex_unlock (source->priv->changed_lock); - g_return_val_if_fail (name != NULL, NULL); - g_return_val_if_fail (absolute_uri != NULL, NULL); + g_signal_emit (source, signals[CHANGED], 0); + + return FALSE; +} - source = g_object_new (E_TYPE_SOURCE, NULL); - source->priv->uid = e_uid_new (); +static void +source_set_dbus_object (ESource *source, + EDBusObject *dbus_object) +{ + /* D-Bus object will be NULL when configuring a new source. */ + if (dbus_object == NULL) + return; - e_source_set_name (source, name); - e_source_set_absolute_uri (source, absolute_uri); + g_return_if_fail (E_DBUS_IS_OBJECT (dbus_object)); + g_return_if_fail (source->priv->dbus_object == NULL); - return source; + source->priv->dbus_object = g_object_ref (dbus_object); } -/** - * e_source_new_from_xml_node: - * @node: a pointer to the XML node to parse - * - * Creates a new #ESource instance from the XML specification in @node. - * If the XML specification is invalid, the function returns %NULL. - * - * Returns: a new #ESource, or %NULL - **/ -ESource * -e_source_new_from_xml_node (xmlNodePtr node) +static void +source_set_main_context (ESource *source, + GMainContext *main_context) { - ESource *source; - xmlChar *uid; + g_return_if_fail (source->priv->main_context == NULL); - uid = xmlGetProp (node, (xmlChar *)"uid"); - if (uid == NULL) - return NULL; + source->priv->main_context = + (main_context != NULL) ? + g_main_context_ref (main_context) : + g_main_context_ref_thread_default (); +} - source = g_object_new (E_TYPE_SOURCE, NULL); +static void +source_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_DBUS_OBJECT: + source_set_dbus_object ( + E_SOURCE (object), + g_value_get_object (value)); + return; + + case PROP_DISPLAY_NAME: + e_source_set_display_name ( + E_SOURCE (object), + g_value_get_string (value)); + return; + + case PROP_ENABLED: + e_source_set_enabled ( + E_SOURCE (object), + g_value_get_boolean (value)); + return; + + case PROP_MAIN_CONTEXT: + source_set_main_context ( + E_SOURCE (object), + g_value_get_boxed (value)); + return; + + case PROP_PARENT: + e_source_set_parent ( + E_SOURCE (object), + g_value_get_string (value)); + return; + } - source->priv->uid = g_strdup ((gchar *) uid); - xmlFree (uid); + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); +} - if (e_source_update_from_xml_node (source, node, NULL)) - return source; +static void +source_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + switch (property_id) { + case PROP_DBUS_OBJECT: + g_value_take_object ( + value, e_source_ref_dbus_object ( + E_SOURCE (object))); + return; + + case PROP_DISPLAY_NAME: + g_value_take_string ( + value, e_source_dup_display_name ( + E_SOURCE (object))); + return; + + case PROP_ENABLED: + g_value_set_boolean ( + value, e_source_get_enabled ( + E_SOURCE (object))); + return; + + case PROP_MAIN_CONTEXT: + g_value_take_boxed ( + value, e_source_ref_main_context ( + E_SOURCE (object))); + return; + + case PROP_PARENT: + g_value_take_string ( + value, e_source_dup_parent ( + E_SOURCE (object))); + return; + + case PROP_REMOVABLE: + g_value_set_boolean ( + value, e_source_get_removable ( + E_SOURCE (object))); + return; + + case PROP_UID: + g_value_take_string ( + value, e_source_dup_uid ( + E_SOURCE (object))); + return; + + case PROP_WRITABLE: + g_value_set_boolean ( + value, e_source_get_writable ( + E_SOURCE (object))); + return; + } - g_object_unref (source); - return NULL; + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } static void -import_properties (ESource *source, - xmlNodePtr prop_root) +source_dispose (GObject *object) { - ESourcePrivate *priv = source->priv; - xmlNodePtr prop_node; + ESourcePrivate *priv; + + priv = E_SOURCE_GET_PRIVATE (object); - for (prop_node = prop_root->children; prop_node; prop_node = prop_node->next) { - xmlChar *name, *value; + if (priv->dbus_object != NULL) { + EDBusObject *dbus_object; + EDBusSource *dbus_source; - if (!prop_node->name || strcmp ((gchar *)prop_node->name, "property")) - continue; + dbus_object = E_DBUS_OBJECT (priv->dbus_object); - name = xmlGetProp (prop_node, (xmlChar *)"name"); - value = xmlGetProp (prop_node, (xmlChar *)"value"); + dbus_source = e_dbus_object_get_source (dbus_object); + if (dbus_source != NULL) { + g_signal_handlers_disconnect_matched ( + dbus_source, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, object); + g_object_unref (dbus_source); + } + + g_object_unref (priv->dbus_object); + priv->dbus_object = NULL; + } - if (name && value) - g_hash_table_insert (priv->properties, g_strdup ((gchar *) name), g_strdup ((gchar *) value)); + if (priv->main_context != NULL) { + g_main_context_unref (priv->main_context); + priv->main_context = NULL; + } - if (name) - xmlFree (name); - if (value) - xmlFree (value); + /* XXX Maybe not necessary to acquire the lock? */ + g_mutex_lock (priv->changed_lock); + if (priv->changed != NULL) { + g_source_destroy (priv->changed); + g_source_unref (priv->changed); + priv->changed = NULL; } + g_mutex_unlock (priv->changed_lock); + + g_hash_table_remove_all (priv->extensions); + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_source_parent_class)->dispose (object); } -typedef struct +static void +source_finalize (GObject *object) { - gboolean equal; - GHashTable *table2; -} hash_compare_data; + ESourcePrivate *priv; + + priv = E_SOURCE_GET_PRIVATE (object); + + g_mutex_free (priv->changed_lock); + g_mutex_free (priv->property_lock); + + g_free (priv->display_name); + g_free (priv->parent); + g_free (priv->uid); + + g_key_file_free (priv->key_file); + g_static_rec_mutex_free (&priv->lock); + g_hash_table_destroy (priv->extensions); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_source_parent_class)->finalize (object); +} static void -compare_str_hash (gpointer key, - gpointer value, - hash_compare_data *cd) +source_notify (GObject *object, + GParamSpec *pspec) { - gpointer value2 = g_hash_table_lookup (cd->table2, key); - if (value2 == NULL || g_str_equal (value, value2) == FALSE) - cd->equal = FALSE; + if ((pspec->flags & E_SOURCE_PARAM_SETTING) != 0) + e_source_changed (E_SOURCE (object)); } static gboolean -compare_str_hashes (GHashTable *table1, - GHashTable *table2) +source_remove_sync (ESource *source, + GCancellable *cancellable, + GError **error) { - hash_compare_data cd; + EDBusObject *dbus_object; + EDBusSourceRemovable *dbus_source; + gboolean success; + + dbus_object = E_DBUS_OBJECT (source->priv->dbus_object); - if (g_hash_table_size (table1) != g_hash_table_size (table2)) + dbus_source = e_dbus_object_get_source_removable (dbus_object); + + if (dbus_source == NULL) { + g_set_error ( + error, G_IO_ERROR, + G_IO_ERROR_PERMISSION_DENIED, + _("Data source '%s' is not removable"), + e_source_get_display_name (source)); return FALSE; + } - cd.equal = TRUE; - cd.table2 = table2; - g_hash_table_foreach (table1, (GHFunc) compare_str_hash, &cd); - return cd.equal; + success = e_dbus_source_removable_call_remove_sync ( + dbus_source, cancellable, error); + + g_object_unref (dbus_source); + + return success; } -/** - * e_source_update_from_xml_node: - * @source: an #ESource. - * @node: a pointer to the XML node to parse - * @changed_return: return location for change confirmation, or %NULL - * - * Update the #ESource attributes from @node. If @changed_return is - * non-%NULL, it will be set to %TRUE if any attributes were actually - * changed in the course of the update. This will also emit the - * #ESource::changed signal if any attributes were actually changed. - * - * Returns: %TRUE if the data in @node was recognized and parsed into - * acceptable values for @source, %FALSE otherwise - **/ -gboolean -e_source_update_from_xml_node (ESource *source, - xmlNodePtr node, - gboolean *changed_return) +/* Helper for source_remove() */ +static void +source_remove_thread (GSimpleAsyncResult *simple, + GObject *object, + GCancellable *cancellable) { - xmlChar *name; - xmlChar *relative_uri; - xmlChar *absolute_uri; - xmlChar *color_spec; - xmlChar *color; - gboolean retval = FALSE; - gboolean changed = FALSE; - - name = xmlGetProp (node, (xmlChar *)"name"); - relative_uri = xmlGetProp (node, (xmlChar *)"relative_uri"); - absolute_uri = xmlGetProp (node, (xmlChar *)"uri"); - color_spec = xmlGetProp (node, (xmlChar *)"color_spec"); - color = xmlGetProp (node, (xmlChar *)"color"); /* obsolete */ - - if (name == NULL || (relative_uri == NULL && absolute_uri == NULL)) - goto done; - - if (color_spec != NULL && color != NULL) - goto done; - - if (source->priv->name == NULL - || strcmp ((gchar *) name, source->priv->name) != 0 - || (source->priv->relative_uri == NULL && relative_uri != NULL) - || (source->priv->relative_uri != NULL && relative_uri == NULL) - || (relative_uri && source->priv->relative_uri && strcmp ((gchar *) relative_uri, source->priv->relative_uri) != 0)) { - gchar *abs_uri = NULL; - - g_free (source->priv->name); - source->priv->name = g_strdup ((gchar *) name); - - if (source->priv->group) { - abs_uri = e_source_build_absolute_uri (source); - } + GError *error = NULL; - if (abs_uri && source->priv->absolute_uri && g_str_equal (abs_uri, source->priv->absolute_uri)) { - /* reset the absolute uri to NULL to be regenerated when asked for, - * but only when it was generated also before */ - g_free (source->priv->absolute_uri); - source->priv->absolute_uri = NULL; - } else if (source->priv->absolute_uri && - source->priv->relative_uri && - g_str_has_suffix (source->priv->absolute_uri, source->priv->relative_uri)) { - gchar *tmp = source->priv->absolute_uri; + e_source_remove_sync (E_SOURCE (object), cancellable, &error); - tmp[strlen (tmp) - strlen (source->priv->relative_uri)] = 0; - source->priv->absolute_uri = g_strconcat (tmp, (gchar *) relative_uri, NULL); + if (error != NULL) + g_simple_async_result_take_error (simple, error); +} - g_free (tmp); - } +static void +source_remove (ESource *source, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; - g_free (abs_uri); + simple = g_simple_async_result_new ( + G_OBJECT (source), callback, user_data, source_remove); - g_free (source->priv->relative_uri); - source->priv->relative_uri = g_strdup ((gchar *) relative_uri); + g_simple_async_result_set_check_cancellable (simple, cancellable); - changed = TRUE; - } + g_simple_async_result_run_in_thread ( + simple, source_remove_thread, + G_PRIORITY_DEFAULT, cancellable); - if (absolute_uri != NULL) { - g_free (source->priv->absolute_uri); + g_object_unref (simple); +} - if (relative_uri && g_str_equal ((const gchar *) relative_uri, "system") && - (g_str_has_prefix ((const gchar *) absolute_uri, "file:") || g_str_equal ((const gchar *) absolute_uri, "local:/system"))) - source->priv->absolute_uri = g_strdup ("local:system"); - else - source->priv->absolute_uri = g_strdup ((gchar *) absolute_uri); - changed = TRUE; - } +static gboolean +source_remove_finish (ESource *source, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; - if (color == NULL) { - /* It is okay for color_spec to be NULL. */ - changed |= set_color_spec (source, (gchar *) color_spec); - } else { - gchar buffer[8]; - g_snprintf (buffer, sizeof (buffer), "#%s", color); - changed |= set_color_spec (source, buffer); - } + g_return_val_if_fail ( + g_simple_async_result_is_valid ( + result, G_OBJECT (source), source_remove), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + /* Assume success unless a GError is set. */ + return !g_simple_async_result_propagate_error (simple, error); +} + +static gboolean +source_write_sync (ESource *source, + GCancellable *cancellable, + GError **error) +{ + EDBusObject *dbus_object; + EDBusSourceWritable *dbus_source; + gboolean success; + gchar *data; + + dbus_object = E_DBUS_OBJECT (source->priv->dbus_object); + + dbus_source = e_dbus_object_get_source_writable (dbus_object); - if (g_hash_table_size (source->priv->properties) && !node->children) { - g_hash_table_destroy (source->priv->properties); - source->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, g_free); - changed = TRUE; + if (dbus_source == NULL) { + g_set_error ( + error, G_IO_ERROR, + G_IO_ERROR_PERMISSION_DENIED, + _("Data source '%s' is not writable"), + e_source_get_display_name (source)); + return FALSE; } - for (node = node->children; node; node = node->next) { - if (!node->name) - continue; + data = e_source_to_string (source, NULL); + + success = e_dbus_source_writable_call_write_sync ( + dbus_source, data, cancellable, error); + + g_free (data); + + g_object_unref (dbus_source); + + return success; +} + +/* Helper for source_write() */ +static void +source_write_thread (GSimpleAsyncResult *simple, + GObject *object, + GCancellable *cancellable) +{ + GError *error = NULL; + + e_source_write_sync (E_SOURCE (object), cancellable, &error); + + if (error != NULL) + g_simple_async_result_take_error (simple, error); +} + +static void +source_write (ESource *source, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + + simple = g_simple_async_result_new ( + G_OBJECT (source), callback, user_data, source_write); + + g_simple_async_result_set_check_cancellable (simple, cancellable); + + g_simple_async_result_run_in_thread ( + simple, source_write_thread, + G_PRIORITY_DEFAULT, cancellable); - if (!strcmp ((gchar *)node->name, "properties")) { - GHashTable *temp = source->priv->properties; - source->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, g_free); - import_properties (source, node); - if (!compare_str_hashes (temp, source->priv->properties)) - changed = TRUE; - g_hash_table_destroy (temp); - break; + g_object_unref (simple); +} + +static gboolean +source_write_finish (ESource *source, + GAsyncResult *result, + GError **error) +{ + GSimpleAsyncResult *simple; + + g_return_val_if_fail ( + g_simple_async_result_is_valid ( + result, G_OBJECT (source), source_write), FALSE); + + simple = G_SIMPLE_ASYNC_RESULT (result); + + /* Assume success unless a GError is set. */ + return !g_simple_async_result_propagate_error (simple, error); +} + +static gboolean +source_initable_init (GInitable *initable, + GCancellable *cancellable, + GError **error) +{ + ESource *source; + gboolean success = TRUE; + + source = E_SOURCE (initable); + + /* The D-Bus object has the unique identifier (UID). */ + if (source->priv->dbus_object != NULL) { + EDBusObject *dbus_object; + EDBusSource *dbus_source; + + dbus_object = E_DBUS_OBJECT (source->priv->dbus_object); + + /* An EDBusObject lacking an EDBusSource + * interface indicates a programmer error. */ + dbus_source = e_dbus_object_get_source (dbus_object); + g_return_val_if_fail (E_DBUS_IS_SOURCE (dbus_source), FALSE); + + /* Allow authentication prompts for a data source + * when a new client-side proxy object is created. + * The thought being if you cancel an authentication + * prompt you won't be bothered again until you start + * (or restart) a new E-D-S client app. + * + * Failure here is non-fatal, ignore errors. + * + * XXX Only GDBusProxy objects may call this. Sources + * created server-side can't invoke remote methods. + */ + if (G_IS_DBUS_PROXY (dbus_source)) + e_dbus_source_call_allow_auth_prompt_sync ( + dbus_source, cancellable, NULL); + + /* The UID never changes, so we can cache a copy. */ + source->priv->uid = e_dbus_source_dup_uid (dbus_source); + + g_signal_connect ( + dbus_source, "notify::data", + G_CALLBACK (source_notify_dbus_data_cb), source); + + success = source_parse_dbus_data (source, error); + + /* Avoid a spurious "changed" emission. */ + g_mutex_lock (source->priv->changed_lock); + if (source->priv->changed != NULL) { + g_source_destroy (source->priv->changed); + g_source_unref (source->priv->changed); + source->priv->changed = NULL; } + g_mutex_unlock (source->priv->changed_lock); + + g_object_unref (dbus_source); + + /* No D-Bus object implies we're configuring a new source, + * so generate a new unique identifier (UID) for it. */ + } else { + source->priv->uid = e_uid_new (); } - retval = TRUE; + return success; +} -done: - if (changed) - g_signal_emit (source, signals[CHANGED], 0); +static void +e_source_class_init (ESourceClass *class) +{ + GObjectClass *object_class; - if (changed_return != NULL) - *changed_return = changed; + g_type_class_add_private (class, sizeof (ESourcePrivate)); - if (name != NULL) - xmlFree (name); - if (relative_uri != NULL) - xmlFree (relative_uri); - if (absolute_uri != NULL) - xmlFree (absolute_uri); - if (color_spec != NULL) - xmlFree (color_spec); - if (color != NULL) - xmlFree (color); + object_class = G_OBJECT_CLASS (class); + object_class->set_property = source_set_property; + object_class->get_property = source_get_property; + object_class->dispose = source_dispose; + object_class->finalize = source_finalize; + object_class->notify = source_notify; + + class->remove_sync = source_remove_sync; + class->remove = source_remove; + class->remove_finish = source_remove_finish; + class->write_sync = source_write_sync; + class->write = source_write; + class->write_finish = source_write_finish; + + g_object_class_install_property ( + object_class, + PROP_DBUS_OBJECT, + g_param_spec_object ( + "dbus-object", + "D-Bus Object", + "The D-Bus object for the data source", + E_DBUS_TYPE_OBJECT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_DISPLAY_NAME, + g_param_spec_string ( + "display-name", + "Display Name", + "The human-readable name of the data source", + _("Unnamed"), + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_ENABLED, + g_param_spec_boolean ( + "enabled", + "Enabled", + "Whether the data source is enabled", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_MAIN_CONTEXT, + g_param_spec_boxed ( + "main-context", + "Main Context", + "The GMainContext used for signal emissions", + G_TYPE_MAIN_CONTEXT, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_PARENT, + g_param_spec_string ( + "parent", + "Parent", + "The unique identity of the parent data source", + NULL, + G_PARAM_READWRITE | + G_PARAM_STATIC_STRINGS | + E_SOURCE_PARAM_SETTING)); + + g_object_class_install_property ( + object_class, + PROP_REMOVABLE, + g_param_spec_boolean ( + "removable", + "Removable", + "Whether the data source is removable", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_UID, + g_param_spec_string ( + "uid", + "UID", + "The unique identity of the data source", + NULL, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + g_object_class_install_property ( + object_class, + PROP_WRITABLE, + g_param_spec_boolean ( + "writable", + "Writable", + "Whether the data source is writable", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * ESource::changed: + * @source: the #ESource that received the signal + * + * The ::changed signal is emitted when a property in @source or + * one of its extension objects changes. A common use for this + * signal is to notify a #GtkTreeModel containing data collected + * from #ESources that it needs to update a row. + **/ + signals[CHANGED] = g_signal_new ( + "changed", + G_TYPE_FROM_CLASS (class), + G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE, + G_STRUCT_OFFSET (ESourceClass, changed), + NULL, NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); - return retval; + /* Register built-in ESourceExtension types. */ + REGISTER_TYPE (E_TYPE_SOURCE_ADDRESS_BOOK); + REGISTER_TYPE (E_TYPE_SOURCE_ALARMS); + REGISTER_TYPE (E_TYPE_SOURCE_AUTHENTICATION); + REGISTER_TYPE (E_TYPE_SOURCE_AUTOCOMPLETE); + REGISTER_TYPE (E_TYPE_SOURCE_CALENDAR); + REGISTER_TYPE (E_TYPE_SOURCE_COLLECTION); + REGISTER_TYPE (E_TYPE_SOURCE_GOA); + REGISTER_TYPE (E_TYPE_SOURCE_MAIL_ACCOUNT); + REGISTER_TYPE (E_TYPE_SOURCE_MAIL_COMPOSITION); + REGISTER_TYPE (E_TYPE_SOURCE_MAIL_IDENTITY); + REGISTER_TYPE (E_TYPE_SOURCE_MAIL_SIGNATURE); + REGISTER_TYPE (E_TYPE_SOURCE_MAIL_SUBMISSION); + REGISTER_TYPE (E_TYPE_SOURCE_MAIL_TRANSPORT); + REGISTER_TYPE (E_TYPE_SOURCE_MDN); + REGISTER_TYPE (E_TYPE_SOURCE_MEMO_LIST); + REGISTER_TYPE (E_TYPE_SOURCE_OFFLINE); + REGISTER_TYPE (E_TYPE_SOURCE_OPENPGP); + REGISTER_TYPE (E_TYPE_SOURCE_REFRESH); + REGISTER_TYPE (E_TYPE_SOURCE_SECURITY); + REGISTER_TYPE (E_TYPE_SOURCE_SELECTABLE); + REGISTER_TYPE (E_TYPE_SOURCE_SMIME); + REGISTER_TYPE (E_TYPE_SOURCE_TASK_LIST); + REGISTER_TYPE (E_TYPE_SOURCE_WEBDAV); + + e_source_camel_register_types (); +} + +static void +e_source_initable_init (GInitableIface *interface) +{ + interface->init = source_initable_init; +} + +static void +e_source_init (ESource *source) +{ + GHashTable *extensions; + + extensions = g_hash_table_new_full ( + (GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_object_unref); + + source->priv = E_SOURCE_GET_PRIVATE (source); + source->priv->changed_lock = g_mutex_new (); + source->priv->property_lock = g_mutex_new (); + source->priv->key_file = g_key_file_new (); + source->priv->extensions = extensions; + + g_static_rec_mutex_init (&source->priv->lock); } /** - * e_source_uid_from_xml_node: - * @node: a pointer to an XML node + * e_source_new: + * @dbus_object: (allow-none): a #GDBusObject or %NULL + * @main_context: (allow-none): a #GMainContext or %NULL + * @error: return location for a #GError, or %NULL * - * Assuming that @node is a valid #ESource specification, retrieve the - * source's unique identifier string from it. Free the returned string - * with g_free(). + * Creates a new #ESource instance. * - * Returns: the unique ID of the source specified by @node, - * or %NULL if @node is not a valid specification + * The #ESource::changed signal will be emitted from @main_context if given, + * or else from the thread-default #GMainContext at the time this function is + * called. + * + * The only time the function should be called outside of #ESourceRegistry + * is to create a so-called "scratch" #ESource for editing in a Properties + * window or an account setup assistant. + * + * FIXME: Elaborate on scratch sources. + * + * Returns: a new #ESource, or %NULL on error + * + * Since: 3.6 **/ -gchar * -e_source_uid_from_xml_node (xmlNodePtr node) +ESource * +e_source_new (GDBusObject *dbus_object, + GMainContext *main_context, + GError **error) { - xmlChar *prop; - gchar *uid = NULL; - - prop = xmlGetProp (node, (xmlChar *) "uid"); - - if (prop != NULL) { - uid = g_strdup ((gchar *) prop); - xmlFree (prop); - } - - return uid; + if (dbus_object != NULL) + g_return_val_if_fail (E_DBUS_IS_OBJECT (dbus_object), NULL); + + return g_initable_new ( + E_TYPE_SOURCE, NULL, error, + "dbus-object", dbus_object, + "main-context", main_context, + NULL); } /** - * e_source_build_absolute_uri: + * e_source_hash: * @source: an #ESource * - * Builds an absolute URI string using the base URI of the #ESourceGroup - * to which @source belongs, and its own relative URI. This function - * ignores any custom absolute URIs set with e_source_set_absolute_uri(). - * Free the returned string with g_free(). + * Generates a hash value for @source. This function is intended for + * easily hashing an #ESource to add to a #GHashTable or similar data + * structure. + * + * Returns: a hash value for @source. * - * Returns: a newly-allocated absolute URI string + * Since: 3.6 **/ -gchar * -e_source_build_absolute_uri (ESource *source) +guint +e_source_hash (ESource *source) { - const gchar *base_uri_str; - gchar *uri_str; - - g_return_val_if_fail (source->priv->group != NULL, NULL); - - base_uri_str = e_source_group_peek_base_uri (source->priv->group); - - /* If last character in base URI is a slash, just concat the - * strings. We don't want to compress e.g. the trailing :// - * in a protocol specification Note: Do not use - * G_DIR_SEPARATOR or g_build_filename() when manipulating - * URIs. URIs use normal ("forward") slashes also on Windows. - */ - if (*base_uri_str && *(base_uri_str + strlen (base_uri_str) - 1) == '/') - uri_str = g_strconcat (base_uri_str, source->priv->relative_uri, NULL); - else { - if (source->priv->relative_uri != NULL) - uri_str = g_strconcat (base_uri_str, g_str_equal (base_uri_str, "local:") ? "" : "/", source->priv->relative_uri, - NULL); - else - uri_str = g_strdup (base_uri_str); - } + const gchar *uid; + + g_return_val_if_fail (E_IS_SOURCE (source), 0); - return uri_str; + uid = e_source_get_uid (source); + + return g_str_hash (uid); } /** - * e_source_set_group: - * @source: an #ESource - * @group: an #ESourceGroup - * - * If the read-only flag for @source is set, the function does nothing. + * e_source_equal: + * @source1: the first #ESource + * @source2: the second #ESource * - * Otherwise, sets the group membership for @source. + * Checks two #ESource instances for equality. #ESource instances are + * equal if their unique identifier strings are equal. * - * - * - * If you want to add an #ESource to an #ESourceGroup, use - * e_source_group_add_source(). This function only notifies - * @source of its group membership, but makes no effort to - * verify that membership with @group. - * - * + * Returns: %TRUE if @source1 and @source2 are equal * - * This will emit the #ESource::changed signal if the group membership - * actually changed. + * Since: 3.6 **/ -void -e_source_set_group (ESource *source, - ESourceGroup *group) +gboolean +e_source_equal (ESource *source1, + ESource *source2) { - g_return_if_fail (E_IS_SOURCE (source)); - g_return_if_fail (group == NULL || E_IS_SOURCE_GROUP (group)); - - if (source->priv->readonly) - return; + const gchar *uid1, *uid2; - if (source->priv->group == group) - return; + g_return_val_if_fail (E_IS_SOURCE (source1), FALSE); + g_return_val_if_fail (E_IS_SOURCE (source2), FALSE); - if (source->priv->group != NULL) - g_object_weak_unref ( - G_OBJECT (source->priv->group), - (GWeakNotify) group_weak_notify, source); + if (source1 == source2) + return TRUE; - source->priv->group = group; - if (group != NULL) - g_object_weak_ref ( - G_OBJECT (group), (GWeakNotify) - group_weak_notify, source); + uid1 = e_source_get_uid (source1); + uid2 = e_source_get_uid (source2); - g_signal_emit (source, signals[CHANGED], 0); + return g_str_equal (uid1, uid2); } /** - * e_source_set_name: + * e_source_changed: * @source: an #ESource - * @name: a display name * - * If the read-only flag for @source is set, the function does nothing. + * Emits the #ESource::changed signal from an idle callback in + * @source's #ESource:main-context. * - * Otherwise, sets the display name for @source. + * This function is primarily intended for use by #ESourceExtension + * when emitting a #GObject::notify signal on one of its properties. * - * This will emit the #ESource::changed signal if the display name - * actually changed. + * Since: 3.6 **/ void -e_source_set_name (ESource *source, - const gchar *name) +e_source_changed (ESource *source) { g_return_if_fail (E_IS_SOURCE (source)); - g_return_if_fail (name != NULL); - - if (source->priv->readonly) - return; - if (source->priv->name != NULL && - strcmp (source->priv->name, name) == 0) - return; + g_mutex_lock (source->priv->changed_lock); + if (source->priv->changed == NULL) { + source->priv->changed = g_idle_source_new (); + g_source_set_callback ( + source->priv->changed, + source_idle_changed_cb, + source, (GDestroyNotify) NULL); + g_source_attach ( + source->priv->changed, + source->priv->main_context); + } + g_mutex_unlock (source->priv->changed_lock); +} - g_free (source->priv->name); - source->priv->name = g_strdup (name); +/** + * e_source_get_uid: + * @source: an #ESource + * + * Returns the unique identifier string for @source. + * + * Returns: the UID for @source + * + * Since: 3.6 + **/ +const gchar * +e_source_get_uid (ESource *source) +{ + g_return_val_if_fail (E_IS_SOURCE (source), NULL); - g_signal_emit (source, signals[CHANGED], 0); + return source->priv->uid; } /** - * e_source_set_relative_uri: + * e_source_dup_uid: * @source: an #ESource - * @relative_uri: a relative URI string * - * If the read-only flag for @source is set, the function does nothing. + * Thread-safe variation of e_source_get_uid(). + * Use this function when accessing @source from multiple threads. * - * Otherwise, sets the relative URI for @source. If @source is a member - * of an #ESourceGroup and has not been given a custom absolute URI, the - * function also generates a new absolute URI for @source. + * The returned string should be freed with g_free() when no longer needed. * - * This will emit the #ESource::changed signal if the relative URI - * actually changed. + * Returns: a newly-allocated copy of #ESource:uid + * + * Since: 3.6 **/ -void -e_source_set_relative_uri (ESource *source, - const gchar *relative_uri) +gchar * +e_source_dup_uid (ESource *source) { - gchar *absolute_uri, *old_abs_uri = NULL; + const gchar *protected; + gchar *duplicate; - g_return_if_fail (E_IS_SOURCE (source)); + g_return_val_if_fail (E_IS_SOURCE (source), NULL); - if (source->priv->readonly) - return; + /* Perhaps we don't need to lock the mutex since + * this is a read-only property but it can't hurt. */ - if (source->priv->relative_uri == relative_uri || - (source->priv->relative_uri && relative_uri && g_str_equal (source->priv->relative_uri, relative_uri))) - return; + g_mutex_lock (source->priv->property_lock); - if (source->priv->group) - old_abs_uri = e_source_build_absolute_uri (source); + protected = e_source_get_uid (source); + duplicate = g_strdup (protected); - g_free (source->priv->relative_uri); - source->priv->relative_uri = g_strdup (relative_uri); + g_mutex_unlock (source->priv->property_lock); - /* reset the absolute uri, if it's a generated one */ - if (source->priv->absolute_uri && - (!old_abs_uri || g_str_equal (source->priv->absolute_uri, old_abs_uri)) && - (absolute_uri = e_source_build_absolute_uri (source))) { - g_free (source->priv->absolute_uri); - source->priv->absolute_uri = absolute_uri; - } + return duplicate; +} - g_free (old_abs_uri); +/** + * e_source_get_parent: + * @source: an #ESource + * + * Returns the unique identifier string of the parent #ESource. + * + * Returns: the UID of the parent #ESource + * + * Since: 3.6 + **/ +const gchar * +e_source_get_parent (ESource *source) +{ + g_return_val_if_fail (E_IS_SOURCE (source), NULL); - g_signal_emit (source, signals[CHANGED], 0); + return source->priv->parent; } /** - * e_source_set_absolute_uri: + * e_source_dup_parent: * @source: an #ESource - * @absolute_uri: an absolute URI string, or %NULL * - * Sets a custom absolute URI for @source. If @absolute_uri is %NULL, the - * custom absolute URI is cleared and @source will fall back to its relative - * URI plus the base URI of its containing #ESourceGroup. + * Thread-safe variation of e_source_get_parent(). + * Use this function when accessing @source from multiple threads. * - * This will emit the #ESource::changed signal if the custom absolute URI - * actually changed. + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESource:parent + * + * Since: 3.6 **/ -void -e_source_set_absolute_uri (ESource *source, - const gchar *absolute_uri) +gchar * +e_source_dup_parent (ESource *source) { - g_return_if_fail (E_IS_SOURCE (source)); + const gchar *protected; + gchar *duplicate; - if ((absolute_uri == source->priv->absolute_uri && absolute_uri == NULL) - || (absolute_uri && source->priv->absolute_uri && !strcmp (source->priv->absolute_uri, absolute_uri))) - return; + g_return_val_if_fail (E_IS_SOURCE (source), NULL); - g_free (source->priv->absolute_uri); - source->priv->absolute_uri = g_strdup (absolute_uri); + g_mutex_lock (source->priv->property_lock); - g_signal_emit (source, signals[CHANGED], 0); + protected = e_source_get_parent (source); + duplicate = g_strdup (protected); + + g_mutex_unlock (source->priv->property_lock); + + return duplicate; } /** - * e_source_set_readonly: + * e_source_set_parent: * @source: an #ESource - * @readonly: a read-only flag + * @parent: (allow-none): the UID of the parent #ESource, or %NULL * - * Sets @source as being read-only (%TRUE) or writable (%FALSE). - * A read-only #ESource ignores attempts to change its display name, - * #ESourceGroup, relative URI or color. + * Identifies the parent of @source by its unique identifier string. + * This can only be set prior to adding @source to an #ESourceRegistry. * - * This will emit the #ESource::changed signal if the read-only state - * actually changed. + * The internal copy of #ESource:parent is automatically stripped of leading + * and trailing whitespace. If the resulting string is empty, %NULL is set + * instead. + * + * Since: 3.6 **/ void -e_source_set_readonly (ESource *source, - gboolean readonly) +e_source_set_parent (ESource *source, + const gchar *parent) { g_return_if_fail (E_IS_SOURCE (source)); - if (source->priv->readonly == readonly) - return; + g_mutex_lock (source->priv->property_lock); - source->priv->readonly = readonly; + g_free (source->priv->parent); + source->priv->parent = e_util_strdup_strip (parent); - g_signal_emit (source, signals[CHANGED], 0); + g_mutex_unlock (source->priv->property_lock); + + g_object_notify (G_OBJECT (source), "parent"); +} +/** + * e_source_get_enabled: + * @source: an #ESource + * + * Returns %TRUE if @source is enabled. + * + * An application should try to honor this setting if at all possible, + * even if it does not provide a way to change the setting through its + * user interface. Disabled data sources should generally be hidden. + * + * Returns: whether @source is enabled + * + * Since: 3.6 + **/ +gboolean +e_source_get_enabled (ESource *source) +{ + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + + return source->priv->enabled; } /** - * e_source_set_color_spec: + * e_source_set_enabled: * @source: an #ESource - * @color_spec: a string specifying the color + * @enabled: whether to enable @source * - * Store a textual representation of a color in @source. The @color_spec - * string should be parsable by #gdk_color_parse(), or %NULL to unset the - * color in @source. + * Enables or disables @source. * - * This will emit the #ESource::changed signal if the color representation - * actually changed. + * An application should try to honor this setting if at all possible, + * even if it does not provide a way to change the setting through its + * user interface. Disabled data sources should generally be hidden. * - * Since: 1.10 + * Since: 3.6 **/ void -e_source_set_color_spec (ESource *source, - const gchar *color_spec) +e_source_set_enabled (ESource *source, + gboolean enabled) { g_return_if_fail (E_IS_SOURCE (source)); - if (!source->priv->readonly && set_color_spec (source, color_spec)) - g_signal_emit (source, signals[CHANGED], 0); + if (enabled == source->priv->enabled) + return; + + source->priv->enabled = enabled; + + g_object_notify (G_OBJECT (source), "enabled"); } /** - * e_source_peek_group: + * e_source_get_writable: * @source: an #ESource * - * Returns the #ESourceGroup to which @source belongs, or %NULL if it - * does not belong to a group. + * Returns whether the D-Bus service will accept changes to @source. + * If @source is not writable, calls to e_source_write() will fail. * - * Returns: (transfer none): the #ESourceGroup to which the source belongs + * Returns: whether @source is writable + * + * Since: 3.6 **/ -ESourceGroup * -e_source_peek_group (ESource *source) +gboolean +e_source_get_writable (ESource *source) { - g_return_val_if_fail (E_IS_SOURCE (source), NULL); + EDBusObject *dbus_object; + EDBusSourceWritable *dbus_source; + + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + + dbus_object = E_DBUS_OBJECT (source->priv->dbus_object); + dbus_source = e_dbus_object_peek_source_writable (dbus_object); - return source->priv->group; + return (dbus_source != NULL); } /** - * e_source_peek_uid: + * e_source_get_removable: * @source: an #ESource * - * Returns the unique identifier string for @source. + * Returns whether the D-Bus service will allow @source to be removed. + * If @source is not writable, calls to e_source_remove() will fail. + * + * Returns: whether @source is removable * - * Returns: the source's unique ID + * Since: 3.6 **/ -const gchar * -e_source_peek_uid (ESource *source) +gboolean +e_source_get_removable (ESource *source) { - g_return_val_if_fail (E_IS_SOURCE (source), NULL); + EDBusObject *dbus_object; + EDBusSourceRemovable *dbus_source; - return source->priv->uid; + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + + dbus_object = E_DBUS_OBJECT (source->priv->dbus_object); + dbus_source = e_dbus_object_peek_source_removable (dbus_object); + + return (dbus_source != NULL); } /** - * e_source_peek_name: + * e_source_get_extension: * @source: an #ESource + * @extension_name: an extension name + * + * Returns an instance of some #ESourceExtension subclass which registered + * itself under @extension_name. If no such instance exists within @source, + * one will be created. It is the caller's responsibility to know which + * subclass is being returned. + * + * If you just want to test for the existence of an extension within @source + * without creating it, use e_source_has_extension(). * - * Returns the display name for @source. + * Extension instances are owned by their #ESource and should not be + * referenced directly. Instead, reference the #ESource instance and + * use this function to fetch the extension instance as needed. * - * Returns: the source's display name + * Returns: an instance of some #ESourceExtension subclass + * + * Since: 3.6 **/ -const gchar * -e_source_peek_name (ESource *source) +gpointer +e_source_get_extension (ESource *source, + const gchar *extension_name) { + ESourceExtension *extension; + GHashTable *hash_table; + GTypeClass *class; + g_return_val_if_fail (E_IS_SOURCE (source), NULL); + g_return_val_if_fail (extension_name != NULL, NULL); + + g_static_rec_mutex_lock (&source->priv->lock); + + /* Check if we already have the extension. */ + extension = g_hash_table_lookup ( + source->priv->extensions, extension_name); + if (extension != NULL) + goto exit; + + /* Find all subclasses of ESourceExtensionClass. */ + hash_table = source_find_extension_classes (); + class = g_hash_table_lookup (hash_table, extension_name); + + /* Create a new instance of the appropriate GType. */ + if (class != NULL) { + extension = g_object_new ( + G_TYPE_FROM_CLASS (class), + "source", source, NULL); + source_load_from_key_file ( + G_OBJECT (extension), + source->priv->key_file, + extension_name); + g_hash_table_insert ( + source->priv->extensions, + g_strdup (extension_name), extension); + } else { + /* XXX Tie this into a debug setting for ESources. */ +#ifdef DEBUG + g_critical ( + "No registered GType for ESource " + "extension '%s'", extension_name); +#endif + } + + g_hash_table_destroy (hash_table); - return source->priv->name; +exit: + g_static_rec_mutex_unlock (&source->priv->lock); + + return extension; } /** - * e_source_peek_relative_uri: + * e_source_has_extension: * @source: an #ESource + * @extension_name: an extension name + * + * Checks whether @source has an #ESourceExtension with the given name. * - * Returns the relative URI for @source. + * Returns: %TRUE if @source has such an extension, %FALSE if not * - * Returns: the source's relative URI + * Since: 3.6 **/ -const gchar * -e_source_peek_relative_uri (ESource *source) +gboolean +e_source_has_extension (ESource *source, + const gchar *extension_name) { - g_return_val_if_fail (E_IS_SOURCE (source), NULL); + ESourceExtension *extension; + + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + g_return_val_if_fail (extension_name != NULL, FALSE); + + g_static_rec_mutex_lock (&source->priv->lock); + + /* Two cases to check for, either one is good enough: + * 1) Our internal GKeyFile has a group named 'extension_name'. + * 2) Our 'extensions' table has an entry for 'extension_name'. + * + * We have to check both data structures in case a new extension + * not present in the GKeyFile was instantiated, but we have not + * yet updated our internal GKeyFile. A common occurrence when + * editing a brand new data source. + * + * When checking the GKeyFile we want to actually fetch the + * extension with e_source_get_extension() to make sure it's + * a registered extension name and not just an arbitrary key + * file group name. */ + + if (g_key_file_has_group (source->priv->key_file, extension_name)) { + extension = e_source_get_extension (source, extension_name); + } else { + GHashTable *hash_table = source->priv->extensions; + extension = g_hash_table_lookup (hash_table, extension_name); + } + + g_static_rec_mutex_unlock (&source->priv->lock); - return source->priv->relative_uri; + return (extension != NULL); } /** - * e_source_peek_absolute_uri: + * e_source_ref_dbus_object: * @source: an #ESource * - * Returns the absolute URI for @source if it has one, or else %NULL if - * it has only a relative URI. e_source_get_uri() may be more convenient. + * Returns the #GDBusObject that was passed to e_source_new(). * - * Returns: the source's own absolute URI, or %NULL + * The returned #GDBusObject is referenced for thread-safety and must be + * unreferenced with g_object_unref() when finished with it. + * + * Returns: (transfer full): the #GDBusObject for @source, or %NULL + * + * Since: 3.6 **/ -const gchar * -e_source_peek_absolute_uri (ESource *source) +GDBusObject * +e_source_ref_dbus_object (ESource *source) { g_return_val_if_fail (E_IS_SOURCE (source), NULL); - return source->priv->absolute_uri; + if (source->priv->dbus_object == NULL) + return NULL; + + return g_object_ref (source->priv->dbus_object); } /** - * e_source_peek_color_spec: + * e_source_ref_main_context: * @source: an #ESource * - * Return the textual representation of the color for @source, or %NULL if it - * has none. The returned string should be parsable by #gdk_color_parse(). + * Returns the #GMainContext from which #ESource::changed signals are + * emitted. + * + * The returned #GMainContext is referenced for thread-safety and must be + * unreferenced with g_main_context_unref() when finished with it. * - * Returns: a string specifying the color + * Returns: (transfer full): the #GMainContext for signal emissions * - * Since: 1.10 + * Since: 3.6 **/ -const gchar * -e_source_peek_color_spec (ESource *source) +GMainContext * +e_source_ref_main_context (ESource *source) { g_return_val_if_fail (E_IS_SOURCE (source), NULL); - return source->priv->color_spec; + return g_main_context_ref (source->priv->main_context); } /** - * e_source_get_readonly: + * e_source_get_display_name: * @source: an #ESource * - * Returns the read-only flag for @source. + * Returns the display name for @source. Use the display name to + * represent the #ESource in a user interface. + * + * Returns: the display name for @source * - * Returns: %TRUE if the source is read-only, %FALSE if it's writable + * Since: 3.6 **/ -gboolean -e_source_get_readonly (ESource *source) +const gchar * +e_source_get_display_name (ESource *source) { - g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + g_return_val_if_fail (E_IS_SOURCE (source), NULL); - return source->priv->readonly; + return source->priv->display_name; } /** - * e_source_get_uri: + * e_source_dup_display_name: * @source: an #ESource * - * Returns a newly-allocated copy of an absolute URI for @source. If - * @source has no absolute URI of its own, the URI is constructed from - * the base URI of its #ESourceGroup and its relative URI. Free the - * returned string with g_free(). + * Thread-safe variation of e_source_get_display_name(). + * Use this function when accessing @source from multiple threads. * - * Returns: a newly-allocated absolute URI string + * The returned string should be freed with g_free() when no longer needed. + * + * Returns: a newly-allocated copy of #ESource:display-name + * + * Since: 3.6 **/ gchar * -e_source_get_uri (ESource *source) +e_source_dup_display_name (ESource *source) { - g_return_val_if_fail (E_IS_SOURCE (source), NULL); + const gchar *protected; + gchar *duplicate; - if (source->priv->group == NULL) { - if (source->priv->absolute_uri != NULL) - return g_strdup (source->priv->absolute_uri); - - g_warning ("e_source_get_uri () called on source with no absolute URI!"); - return NULL; - } - else if (source->priv->absolute_uri != NULL) /* source->priv->group != NULL */ - return g_strdup (source->priv->absolute_uri); - else - return e_source_build_absolute_uri (source); -} + g_return_val_if_fail (E_IS_SOURCE (source), NULL); -static void -property_dump_cb (const xmlChar *key, - const xmlChar *value, - xmlNodePtr root) -{ - xmlNodePtr node; + g_mutex_lock (source->priv->property_lock); - node = xmlNewChild (root, NULL, (xmlChar *)"property", NULL); - xmlSetProp (node, (xmlChar *)"name", key); - xmlSetProp (node, (xmlChar *)"value", value); -} + protected = e_source_get_display_name (source); + duplicate = g_strdup (protected); -static xmlNodePtr -dump_common_to_xml_node (ESource *source, - xmlNodePtr parent_node) -{ - ESourcePrivate *priv; - xmlNodePtr node; - const gchar *abs_uri = NULL, *relative_uri = NULL; - - priv = source->priv; - - if (parent_node) - node = xmlNewChild (parent_node, NULL, (xmlChar *)"source", NULL); - else - node = xmlNewNode (NULL, (xmlChar *)"source"); - - xmlSetProp (node, (xmlChar *)"uid", (xmlChar *)e_source_get_uid (source)); - xmlSetProp (node, (xmlChar *)"name", (xmlChar *)e_source_get_display_name (source)); - abs_uri = e_source_peek_absolute_uri (source); - /* do not store absolute uris for local:system sources */ - relative_uri = e_source_peek_relative_uri (source); - if (abs_uri && !(relative_uri && g_str_equal (relative_uri, "system") && - (g_str_has_prefix (abs_uri, "file:") || g_str_has_prefix (abs_uri, "local:")))) - xmlSetProp (node, (xmlChar *)"uri", (xmlChar *)abs_uri); - if (relative_uri) - xmlSetProp (node, (xmlChar *)"relative_uri", (xmlChar *)relative_uri); - - if (priv->color_spec != NULL) - xmlSetProp (node, (xmlChar *)"color_spec", (xmlChar *)priv->color_spec); - - if (g_hash_table_size (priv->properties) != 0) { - xmlNodePtr properties_node; - - properties_node = xmlNewChild (node, NULL, (xmlChar *)"properties", NULL); - g_hash_table_foreach (priv->properties, (GHFunc) property_dump_cb, properties_node); - } + g_mutex_unlock (source->priv->property_lock); - return node; + return duplicate; } /** - * e_source_dump_to_xml_node: + * e_source_set_display_name: * @source: an #ESource - * @parent_node: location to add XML data + * @display_name: a display name + * + * Sets the display name for @source. The @display_name argument must be a + * valid UTF-8 string. Use the display name to represent the #ESource in a + * user interface. * - * Converts @source to an xmlNode structure - * and adds it as a child of @parent_node. + * The internal copy of @display_name is automatically stripped of leading + * and trailing whitespace. + * + * Since: 3.6 **/ void -e_source_dump_to_xml_node (ESource *source, - xmlNodePtr parent_node) +e_source_set_display_name (ESource *source, + const gchar *display_name) { g_return_if_fail (E_IS_SOURCE (source)); + g_return_if_fail (display_name != NULL); + g_return_if_fail (g_utf8_validate (display_name, -1, NULL)); + + g_mutex_lock (source->priv->property_lock); + + g_free (source->priv->display_name); + source->priv->display_name = g_strdup (display_name); + + /* Strip leading and trailing whitespace. */ + g_strstrip (source->priv->display_name); - dump_common_to_xml_node (source, parent_node); + g_mutex_unlock (source->priv->property_lock); + + g_object_notify (G_OBJECT (source), "display-name"); } /** - * e_source_to_standalone_xml: - * @source: an #ESource + * e_source_compare_by_display_name: + * @source1: the first #ESource + * @source2: the second #ESource * - * Converts @source to an XML string for permanent storage. - * Free the returned string with g_free(). + * Compares two #ESource instances by their display names. Useful for + * ordering sources in a user interface. + * + * Returns: a negative value if @source1 compares before @source2, zero if + * they compare equal, or a positive value if @source1 compares + * after @source2 * - * Returns: a newly-allocated XML string + * Since: 3.6 **/ -gchar * -e_source_to_standalone_xml (ESource *source) +gint +e_source_compare_by_display_name (ESource *source1, + ESource *source2) { - xmlDocPtr doc; - xmlNodePtr node; - xmlChar *xml_buffer; - gchar *returned_buffer; - gint xml_buffer_size; - gchar *uri; - - g_return_val_if_fail (E_IS_SOURCE (source), NULL); - - doc = xmlNewDoc ((xmlChar *)"1.0"); - node = dump_common_to_xml_node (source, NULL); + const gchar *display_name1; + const gchar *display_name2; - xmlDocSetRootElement (doc, node); + display_name1 = e_source_get_display_name (source1); + display_name2 = e_source_get_display_name (source2); - uri = e_source_get_uri (source); - xmlSetProp (node, (xmlChar *)"uri", (xmlChar *)uri); - g_free (uri); - - xmlDocDumpMemory (doc, &xml_buffer, &xml_buffer_size); - xmlFreeDoc (doc); - - returned_buffer = g_malloc (xml_buffer_size + 1); - memcpy (returned_buffer, xml_buffer, xml_buffer_size); - returned_buffer[xml_buffer_size] = '\0'; - xmlFree (xml_buffer); - - return returned_buffer; + return g_utf8_collate (display_name1, display_name2); } /** - * e_source_equal: - * @a: an #ESource - * @b: another #ESource + * e_source_to_string: + * @source: an #ESource + * @length: (allow-none): return location for the length of the returned + * string, or %NULL * - * Compares if @a is equivalent to @b. + * Outputs the current contents of @source as a key file string. + * Free the returned string with g_free(). * - * Returns: %TRUE if @a is equivalent to @b, %FALSE otherwise + * Returns: a newly-allocated string * - * Since: 2.24 + * Since: 3.6 **/ -gboolean -e_source_equal (ESource *a, - ESource *b) +gchar * +e_source_to_string (ESource *source, + gsize *length) { - g_return_val_if_fail (E_IS_SOURCE (a), FALSE); - g_return_val_if_fail (E_IS_SOURCE (b), FALSE); - - #define ONLY_ONE_NULL(aa, bb) (((aa) == NULL && (bb) != NULL) || ((aa) != NULL && (bb) == NULL)) - - /* Compare source stuff */ - if (a->priv->uid - && b->priv->uid - && g_ascii_strcasecmp (a->priv->uid, b->priv->uid)) - return FALSE; + GHashTableIter iter; + GKeyFile *key_file; + gpointer group_name; + gpointer extension; + gchar *data; - if (a->priv->name - && b->priv->name - && g_ascii_strcasecmp (a->priv->name, b->priv->name)) - return FALSE; + g_return_val_if_fail (E_IS_SOURCE (source), NULL); - if (a->priv->relative_uri - && b->priv->relative_uri - && g_ascii_strcasecmp (a->priv->relative_uri, b->priv->relative_uri)) - return FALSE; + g_static_rec_mutex_lock (&source->priv->lock); - if (a->priv->absolute_uri - && b->priv->absolute_uri - && g_ascii_strcasecmp (a->priv->absolute_uri, b->priv->absolute_uri)) - return FALSE; + key_file = source->priv->key_file; - if ((a->priv->color_spec - && b->priv->color_spec - && g_ascii_strcasecmp (a->priv->color_spec, b->priv->color_spec)) || - (ONLY_ONE_NULL (a->priv->color_spec, b->priv->color_spec))) - return FALSE; + source_save_to_key_file ( + G_OBJECT (source), key_file, PRIMARY_GROUP_NAME); - if (a->priv->readonly != b->priv->readonly) - return FALSE; + g_hash_table_iter_init (&iter, source->priv->extensions); + while (g_hash_table_iter_next (&iter, &group_name, &extension)) + source_save_to_key_file (extension, key_file, group_name); - if (!compare_str_hashes (a->priv->properties, b->priv->properties)) - return FALSE; + data = g_key_file_to_data (key_file, length, NULL); - #undef ONLY_ONE_NULL + g_static_rec_mutex_unlock (&source->priv->lock); - return TRUE; + return data; } /** - * e_source_xmlstr_equal: - * @a: an XML representation of an #ESource - * @b: an XML representation of another #ESource + * e_source_parameter_to_key: + * @param_name: a #GParamSpec name * - * Compares if @a is equivalent to @b. + * Converts a #GParamSpec name (e.g. "foo-bar" or "foo_bar") + * to "CamelCase" for use as a #GKeyFile key (e.g. "FooBar"). * - * Returns: %TRUE if @a is equivalent to @b, %FALSE otherwise + * This function is made public only to aid in account migration. + * Applications should not need to use this. * - * Since: 2.24 + * Since: 3.6 **/ -gboolean -e_source_xmlstr_equal (const gchar *a, - const gchar *b) +gchar * +e_source_parameter_to_key (const gchar *param_name) { - ESource *srca, *srcb; - gboolean retval; + gboolean uppercase = TRUE; + gchar *key, *cp; + gint ii; - srca = e_source_new_from_standalone_xml (a); - srcb = e_source_new_from_standalone_xml (b); + g_return_val_if_fail (param_name != NULL, NULL); - retval = e_source_equal (srca, srcb); + key = cp = g_malloc0 (strlen (param_name) + 1); - g_object_unref (srca); - g_object_unref (srcb); + for (ii = 0; param_name[ii] != '\0'; ii++) { + if (g_ascii_isalnum (param_name[ii]) && uppercase) { + *cp++ = g_ascii_toupper (param_name[ii]); + uppercase = FALSE; + } else if (param_name[ii] == '-' || param_name[ii] == '_') + uppercase = TRUE; + else + *cp++ = param_name[ii]; + } - return retval; + return key; } /** - * e_source_new_from_standalone_xml: - * @xml: an XML representation of an #ESource + * e_source_remove_sync: + * @source: the #ESource to be removed + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @error: return location for a #GError, or %NULL * - * Constructs an #ESource instance from an XML string representation, - * probably generated by e_source_to_standalone_xml(). + * Requests the D-Bus service to delete the key files for @source and all of + * its descendants and broadcast their removal to all clients. If an error + * occurs, the functon will set @error and return %FALSE. * - * Returns: a new #ESource + * Returns: %TRUE on success, %FALSE on failure + * + * Since: 3.6 **/ -ESource * -e_source_new_from_standalone_xml (const gchar *xml) +gboolean +e_source_remove_sync (ESource *source, + GCancellable *cancellable, + GError **error) { - xmlDocPtr doc; - xmlNodePtr root; - ESource *source; + ESourceClass *class; - doc = xmlParseDoc ((xmlChar *) xml); - if (doc == NULL) - return NULL; - - root = doc->children; - if (strcmp ((gchar *)root->name, "source") != 0) - return NULL; + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); - source = e_source_new_from_xml_node (root); - xmlFreeDoc (doc); + class = E_SOURCE_GET_CLASS (source); + g_return_val_if_fail (class->remove_sync != NULL, FALSE); - return source; + return class->remove_sync (source, cancellable, error); } /** - * e_source_get_property: - * @source: an #ESource - * @property_name: a custom property name + * e_source_remove: + * @source: the #ESource to be removed + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @callback: (scope async): a #GAsyncReadyCallback to call when the request + * is satisfied + * @user_data: (closure): data to pass to the callback function + * + * Asynchronously requests the D-Bus service to delete the key files for + * @source all of its descendants and broadcast their removal to all clients. * - * Looks up the value of a custom #ESource property. If no such - * property name exists in @source, the function returns %NULL. + * When the operation is finished, @callback will be called. You can then + * call e_source_remove_finish() to get the result of the operation. * - * Returns: the property value, or %NULL + * Since: 3.6 **/ -const gchar * -e_source_get_property (ESource *source, - const gchar *property_name) +void +e_source_remove (ESource *source, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { - const gchar *property_value; + ESourceClass *class; - g_return_val_if_fail (E_IS_SOURCE (source), NULL); - g_return_val_if_fail (property_name != NULL, NULL); + g_return_if_fail (E_IS_SOURCE (source)); - property_value = g_hash_table_lookup ( - source->priv->properties, property_name); + class = E_SOURCE_GET_CLASS (source); + g_return_if_fail (class->remove != NULL); - return property_value; + class->remove (source, cancellable, callback, user_data); } /** - * e_source_get_duped_property: - * @source: an #ESource - * @property_name: a custom property name + * e_source_remove_finish: + * @source: the #ESource to be removed + * @result: a #GAsyncResult + * @error: return location for a #GError, or %NULL * - * Looks up the value of a custom #ESource property and returns a - * newly-allocated copy of the value. If no such property name exists - * in @source, the function returns %NULL. Free the returned value - * with g_free(). + * Finishes the operation started with e_source_remove(). If an + * error occured, the function will set @error and return %FALSE. * - * Returns: a newly-allocated copy of the property value, or %NULL + * Returns: %TRUE on success, %FALSE of failure * - * Since: 1.12 + * Since: 3.6 **/ -gchar * -e_source_get_duped_property (ESource *source, - const gchar *property_name) +gboolean +e_source_remove_finish (ESource *source, + GAsyncResult *result, + GError **error) { - const gchar *property_value; + ESourceClass *class; - g_return_val_if_fail (E_IS_SOURCE (source), NULL); - g_return_val_if_fail (property_name != NULL, NULL); + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); - property_value = e_source_get_property (source, property_name); + class = E_SOURCE_GET_CLASS (source); + g_return_val_if_fail (class->remove_finish != NULL, FALSE); - return g_strdup (property_value); + return class->remove_finish (source, result, error); } /** - * e_source_set_property: - * @source: an #ESource - * @property_name: a custom property name - * @property_value: (allow-none): a new value for the property, or %NULL + * e_source_write_sync: + * @source: a writable #ESource + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @error: return location for a #GError, or %NULL + * + * Submits the current contents of @source to the D-Bus service to be + * written to disk and broadcast to other clients. This can only be + * called on #ESource:writable data sources. * - * Create a new custom #ESource property or replaces an existing one. If - * @property_value is %NULL, the property is removed from @source. This - * will also emit a #ESource::changed signal. + * Returns: %TRUE on success, %FALSE on failure + * + * Since: 3.6 **/ -void -e_source_set_property (ESource *source, - const gchar *property_name, - const gchar *property_value) +gboolean +e_source_write_sync (ESource *source, + GCancellable *cancellable, + GError **error) { - const gchar *old_value; - - g_return_if_fail (E_IS_SOURCE (source)); - g_return_if_fail (property_name != NULL); - - old_value = g_hash_table_lookup (source->priv->properties, property_name); + ESourceClass *class; - if (g_strcmp0 (old_value, property_value) == 0) - return; + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); - if (property_value != NULL) - g_hash_table_replace ( - source->priv->properties, - g_strdup (property_name), - g_strdup (property_value)); - else - g_hash_table_remove ( - source->priv->properties, property_name); + class = E_SOURCE_GET_CLASS (source); + g_return_val_if_fail (class->write_sync != NULL, FALSE); - g_signal_emit (source, signals[CHANGED], 0); + return class->write_sync (source, cancellable, error); } /** - * e_source_foreach_property: - * @source: an #ESource - * @func: (scope call): the function to call for each property - * @user_data: user data to pass to the function + * e_source_write: + * @source: a writable #ESource + * @cancellable: (allow-none): optional #GCancellable object, or %NULL + * @callback: (scope async): a #GAsyncReadyCallback to call when the request + * is satisfied + * @user_data: (closure): data to pass to the callback function * - * Calls the given function for each property in @source. The function - * is passed the name and value of each property, and the given @user_data - * argument. The properties may not be modified while iterating over them. + * Asynchronously submits the current contents of @source to the D-Bus + * service to be written to disk and broadcast to other clients. This + * can only be called on #ESource:writable data sources. + * + * When the operation is finished, @callback will be called. You can then + * call e_source_write_finish() to get the result of the operation. + * + * Since: 3.6 **/ void -e_source_foreach_property (ESource *source, - GHFunc func, - gpointer user_data) +e_source_write (ESource *source, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) { + ESourceClass *class; + g_return_if_fail (E_IS_SOURCE (source)); - g_return_if_fail (func != NULL); - g_hash_table_foreach (source->priv->properties, func, user_data); -} + class = E_SOURCE_GET_CLASS (source); + g_return_if_fail (class->write != NULL); -static void -copy_property (const gchar *key, - const gchar *value, - ESource *new_source) -{ - e_source_set_property (new_source, key, value); + class->write (source, cancellable, callback, user_data); } /** - * e_source_copy: - * @source: an #ESource + * e_source_write_finish: + * @source: a writable #ESource + * @result: a #GAsyncResult + * @error: return location for a #GError, or %NULL + * + * Finishes the operation started with e_source_write(). If an + * error occurred, the function will set @error and return %FALSE. * - * Creates a new #ESource instance from @source, such that passing @source - * and the newly created instance to e_source_equal() would return %TRUE. + * Returns: %TRUE on success, %FALSE on failure * - * Returns: (transfer full): a newly-created #ESource + * Since: 3.6 **/ -ESource * -e_source_copy (ESource *source) +gboolean +e_source_write_finish (ESource *source, + GAsyncResult *result, + GError **error) { - ESource *new_source; - - g_return_val_if_fail (E_IS_SOURCE (source), NULL); + ESourceClass *class; - new_source = g_object_new (E_TYPE_SOURCE, NULL); - new_source->priv->uid = g_strdup (e_source_get_uid (source)); - - e_source_set_name (new_source, e_source_get_display_name (source)); - - new_source->priv->color_spec = - g_strdup (source->priv->color_spec); - new_source->priv->absolute_uri = - g_strdup (e_source_peek_absolute_uri (source)); - new_source->priv->relative_uri = - g_strdup (e_source_peek_relative_uri (source)); + g_return_val_if_fail (E_IS_SOURCE (source), FALSE); + g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE); - e_source_foreach_property (source, (GHFunc) copy_property, new_source); + class = E_SOURCE_GET_CLASS (source); + g_return_val_if_fail (class->write_finish != NULL, FALSE); - return new_source; + return class->write_finish (source, result, error); } diff --git a/libedataserver/e-source.h b/libedataserver/e-source.h index 8e0db20..df2b520 100644 --- a/libedataserver/e-source.h +++ b/libedataserver/e-source.h @@ -1,30 +1,25 @@ -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ -/* e-source.h - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) +/* + * e-source.h * * This program is free software; you can redistribute it and/or - * modify it under the terms of version 2 of the GNU Lesser General Public - * License as published by the Free Software Foundation. + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. * * 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. + * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this program; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. + * License along with the program; if not, see * - * Author: Ettore Perazzoli */ #ifndef E_SOURCE_H #define E_SOURCE_H -#include -#include +#include /* Standard GObject macros */ #define E_TYPE_SOURCE \ @@ -45,20 +40,30 @@ (G_TYPE_INSTANCE_GET_CLASS \ ((obj), E_TYPE_SOURCE, ESourceClass)) +/** + * E_SOURCE_PARAM_SETTING: + * + * Extends #GParamFlags to indicate the #GObject property is associated + * with a key file value. Use this flag when installing #GObject properties + * in #ESourceExtension subclasses. + * + * Since: 3.6 + **/ +#define E_SOURCE_PARAM_SETTING (1 << G_PARAM_USER_SHIFT) + G_BEGIN_DECLS typedef struct _ESource ESource; typedef struct _ESourceClass ESourceClass; typedef struct _ESourcePrivate ESourcePrivate; -/* Avoids a cyclic dependency. */ -struct _ESourceGroup; - /** * ESource: * * Contains only private data that should be read and manipulated using the * functions below. + * + * Since: 3.6 **/ struct _ESource { GObject parent; @@ -70,64 +75,88 @@ struct _ESourceClass { /* Signals */ void (*changed) (ESource *source); + + /* Methods */ + gboolean (*remove_sync) (ESource *source, + GCancellable *cancellable, + GError **error); + void (*remove) (ESource *source, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (*remove_finish) (ESource *source, + GAsyncResult *result, + GError **error); + gboolean (*write_sync) (ESource *source, + GCancellable *cancellable, + GError **error); + void (*write) (ESource *source, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (*write_finish) (ESource *source, + GAsyncResult *result, + GError **error); + + /* Reserved slots. */ + gpointer reserved[16]; }; GType e_source_get_type (void) G_GNUC_CONST; -ESource * e_source_new (const gchar *name, - const gchar *relative_uri); -ESource * e_source_new_with_absolute_uri (const gchar *name, - const gchar *absolute_uri); -ESource * e_source_new_from_xml_node (xmlNodePtr node); -ESource * e_source_new_from_standalone_xml - (const gchar *xml); -ESource * e_source_copy (ESource *source); -gboolean e_source_update_from_xml_node (ESource *source, - xmlNodePtr node, - gboolean *changed_return); -gchar * e_source_uid_from_xml_node (xmlNodePtr node); -void e_source_set_group (ESource *source, - struct _ESourceGroup *group); -void e_source_set_name (ESource *source, - const gchar *name); -void e_source_set_relative_uri (ESource *source, - const gchar *relative_uri); -void e_source_set_absolute_uri (ESource *source, - const gchar *absolute_uri); -void e_source_set_color_spec (ESource *source, - const gchar *color_spec); -void e_source_set_readonly (ESource *source, - gboolean readonly); -struct _ESourceGroup * - e_source_peek_group (ESource *source); -const gchar * e_source_peek_uid (ESource *source); -const gchar * e_source_peek_name (ESource *source); -const gchar * e_source_peek_relative_uri (ESource *source); -const gchar * e_source_peek_absolute_uri (ESource *source); -const gchar * e_source_peek_color_spec (ESource *source); -gboolean e_source_get_readonly (ESource *source); -gchar * e_source_get_uri (ESource *source); -void e_source_dump_to_xml_node (ESource *source, - xmlNodePtr parent_node); -gchar * e_source_to_standalone_xml (ESource *source); -const gchar * e_source_get_property (ESource *source, - const gchar *property_name); -void e_source_set_property (ESource *source, - const gchar *property_name, - const gchar *property_value); -void e_source_foreach_property (ESource *source, - GHFunc func, +ESource * e_source_new (GDBusObject *dbus_object, + GMainContext *main_context, + GError **error); +guint e_source_hash (ESource *source); +gboolean e_source_equal (ESource *source1, + ESource *source2); +void e_source_changed (ESource *source); +const gchar * e_source_get_uid (ESource *source); +gchar * e_source_dup_uid (ESource *source); +const gchar * e_source_get_parent (ESource *source); +gchar * e_source_dup_parent (ESource *source); +void e_source_set_parent (ESource *source, + const gchar *parent); +gboolean e_source_get_enabled (ESource *source); +void e_source_set_enabled (ESource *source, + gboolean enabled); +gboolean e_source_get_writable (ESource *source); +gboolean e_source_get_removable (ESource *source); +gpointer e_source_get_extension (ESource *source, + const gchar *extension_name); +gboolean e_source_has_extension (ESource *source, + const gchar *extension_name); +GDBusObject * e_source_ref_dbus_object (ESource *source); +GMainContext * e_source_ref_main_context (ESource *source); +const gchar * e_source_get_display_name (ESource *source); +gchar * e_source_dup_display_name (ESource *source); +void e_source_set_display_name (ESource *source, + const gchar *display_name); +gint e_source_compare_by_display_name + (ESource *source1, + ESource *source2); +gchar * e_source_to_string (ESource *source, + gsize *length); +gchar * e_source_parameter_to_key (const gchar *param_name); +gboolean e_source_remove_sync (ESource *source, + GCancellable *cancellable, + GError **error); +void e_source_remove (ESource *source, + GCancellable *cancellable, + GAsyncReadyCallback callback, gpointer user_data); -gchar * e_source_get_duped_property (ESource *source, - const gchar *property_name); -gchar * e_source_build_absolute_uri (ESource *source); -gboolean e_source_equal (ESource *a, - ESource *b); -gboolean e_source_xmlstr_equal (const gchar *a, - const gchar *b); - -/* New names to be used in the upcoming ESource rewrite. */ -#define e_source_get_uid e_source_peek_uid -#define e_source_get_display_name e_source_peek_name +gboolean e_source_remove_finish (ESource *source, + GAsyncResult *result, + GError **error); +gboolean e_source_write_sync (ESource *source, + GCancellable *cancellable, + GError **error); +void e_source_write (ESource *source, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean e_source_write_finish (ESource *source, + GAsyncResult *result, + GError **error); G_END_DECLS diff --git a/libedataserver/e-system-source.c b/libedataserver/e-system-source.c new file mode 100644 index 0000000..12a7d1a --- /dev/null +++ b/libedataserver/e-system-source.c @@ -0,0 +1,117 @@ +/* + * e-system-source.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#include "e-system-source.h" + +#include +#include + +#include "e-data-server-util.h" + +G_DEFINE_TYPE ( + ESystemSource, + e_system_source, + E_TYPE_SOURCE) + +static void +system_source_notify (GObject *object, + GParamSpec *pspec) +{ + ESource *source = E_SOURCE (object); + + /* GObject does not allow subclasses to override property flags, + * so we'll keep the "backend-name" and "display-name" properties + * fixed by intercepting attempts to change them and setting them + * back to their proper values. Hokey but works. */ + + if (g_strcmp0 (pspec->name, "backend-name") == 0) { + if (e_source_get_backend_name (source) != NULL) { + e_source_set_backend_name (source, NULL); + return; + } + } + + if (g_strcmp0 (pspec->name, "display-name") == 0) { + const gchar *display_name; + const gchar *proper_value; + + display_name = e_source_get_display_name (source); + proper_value = _("Personal"); + + if (g_strcmp0 (display_name, proper_value) != 0) { + e_source_set_display_name (source, proper_value); + return; + } + } + + /* Chain up to parent's notify() method. */ + G_OBJECT_CLASS (e_system_source_parent_class)->notify (object, pspec); +} + +static void +e_system_source_class_init (ESystemSourceClass *class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (class); + object_class->notify = system_source_notify; +} + +static void +e_system_source_init (ESystemSource *source) +{ +} + +ESource * +e_system_source_new (void) +{ + GSettings *settings; + ESource *source; + GFile *file; + const gchar *data_dir; + gchar *path; + + data_dir = e_get_user_data_dir (); + path = g_build_filename (data_dir, "sources", "system", NULL); + file = g_file_new_for_path (path); + g_free (path); + + /* This function must not fail, so if a "system" key file + * exists and fails to load, delete it and try again. */ + source = g_initable_new ( + E_TYPE_SYSTEM_SOURCE, NULL, NULL, "file", file, NULL); + if (source == NULL) { + g_file_delete (file, NULL, NULL); + source = g_initable_new ( + E_TYPE_SYSTEM_SOURCE, NULL, NULL, "file", file, NULL); + } + + g_object_unref (file); + + g_return_val_if_fail (E_IS_SYSTEM_SOURCE (source), NULL); + + /* Set the "parent" key directly through its GSettings. + * + * XXX To set this during object construction we would have + * to override the GInitable interface. Too much hassle + * for now. Maybe revisit this in the future. */ + settings = e_source_get_settings (source); + g_settings_set_string (settings, "parent", "local"); + + return source; +} diff --git a/libedataserver/e-system-source.h b/libedataserver/e-system-source.h new file mode 100644 index 0000000..959a8f2 --- /dev/null +++ b/libedataserver/e-system-source.h @@ -0,0 +1,63 @@ +/* + * e-system-source.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef E_SYSTEM_SOURCE_H +#define E_SYSTEM_SOURCE_H + +#include + +/* Standard GObject macros */ +#define E_TYPE_SYSTEM_SOURCE \ + (e_system_source_get_type ()) +#define E_SYSTEM_SOURCE(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_SYSTEM_SOURCE, ESystemSource)) +#define E_SYSTEM_SOURCE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), E_TYPE_SYSTEM_SOURCE, ESystemSourceClass)) +#define E_IS_SYSTEM_SOURCE(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), E_TYPE_SYSTEM_SOURCE)) +#define E_IS_SYSTEM_SOURCE_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), E_TYPE_SYSTEM_SOURCE)) +#define E_SYSTEM_SOURCE_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), E_TYPE_SOURCE, ESystemSourceClass)) + +G_BEGIN_DECLS + +typedef struct _ESystemSource ESystemSource; +typedef struct _ESystemSourceClass ESystemSourceClass; +typedef struct _ESystemSourcePrivate ESystemSourcePrivate; + +struct _ESystemSource { + ESource parent; + ESystemSourcePrivate *priv; +}; + +struct _ESystemSourceClass { + ESourceClass parent_class; +}; + +GType e_system_source_get_type (void) G_GNUC_CONST; +ESource * e_system_source_new (void); + +G_END_DECLS + +#endif /* E_SYSTEM_SOURCE_H */ diff --git a/libedataserver/libedataserver.pc.in b/libedataserver/libedataserver.pc.in index 460a9cb..4cd47bc 100644 --- a/libedataserver/libedataserver.pc.in +++ b/libedataserver/libedataserver.pc.in @@ -10,6 +10,6 @@ privincludedir=@privincludedir@ Name: libedataserver Description: Utility library for Evolution Data Server Version: @VERSION@ -Requires: gio-2.0 gmodule-2.0 libxml-2.0 gconf-2.0 libsoup-2.4 +Requires: gio-2.0 gmodule-2.0 camel-1.2 gnome-keyring-1 libxml-2.0 gconf-2.0 libsoup-2.4 Libs: -L${libdir} -ledataserver-@API_VERSION@ Cflags: -I${privincludedir}