From 3de3407cfbe33d916005783d1591c21dd8797e04 Mon Sep 17 00:00:00 2001 From: Packit Service Date: Dec 09 2020 13:10:48 +0000 Subject: Prepare for a new update Reverting patches so we can apply the latest update and changes can be seen in the spec file and sources. --- diff --git a/src/addressbook/CMakeLists.txt b/src/addressbook/CMakeLists.txt index 1663af4..9852fb3 100644 --- a/src/addressbook/CMakeLists.txt +++ b/src/addressbook/CMakeLists.txt @@ -45,7 +45,6 @@ target_compile_options(ebookbackendews PUBLIC ${LIBEBOOK_CFLAGS} ${LIBEDATABOOK_CFLAGS} ${LIBEDATASERVER_CFLAGS} - ${LIBICAL_CFLAGS} ${MSPACK_CFLAGS} ${SOUP_CFLAGS} ) @@ -61,7 +60,6 @@ target_include_directories(ebookbackendews PUBLIC ${LIBEBOOK_INCLUDE_DIRS} ${LIBEDATABOOK_INCLUDE_DIRS} ${LIBEDATASERVER_INCLUDE_DIRS} - ${LIBICAL_INCLUDE_DIRS} ${MSPACK_INCLUDE_DIRS} ${SOUP_INCLUDE_DIRS} ) @@ -72,7 +70,6 @@ target_link_libraries(ebookbackendews ${LIBEBOOK_LDFLAGS} ${LIBEDATABOOK_LDFLAGS} ${LIBEDATASERVER_LDFLAGS} - ${LIBICAL_LDFLAGS} ${MATH_LDFLAGS} ${MSPACK_LDFLAGS} ${SOUP_LDFLAGS} diff --git a/src/addressbook/CMakeLists.txt.birthday-date b/src/addressbook/CMakeLists.txt.birthday-date deleted file mode 100644 index 9852fb3..0000000 --- a/src/addressbook/CMakeLists.txt.birthday-date +++ /dev/null @@ -1,151 +0,0 @@ -set(DEPENDENCIES - evolution-ews -) - -set(SOURCES - ews-oab-props.h - ews-oab-decoder.c - ews-oab-decoder.h - ews-oab-decompress.h - e-book-backend-ews.c - e-book-backend-ews.h - e-book-backend-ews-factory.c -) - -if(WITH_MSPACK) - list(APPEND SOURCES - ews-oab-decompress.c - ) -else(WITH_MSPACK) - list(APPEND SOURCES - mspack/lzx.h - mspack/lzxd.c - mspack/readbits.h - mspack/readhuff.h - mspack/oab-decompress.c - ) -endif(WITH_MSPACK) - -add_library(ebookbackendews MODULE - ${SOURCES} -) - -add_dependencies(ebookbackendews - ${DEPENDENCIES} -) - -target_compile_definitions(ebookbackendews PRIVATE - -DG_LOG_DOMAIN=\"ebookbackendews\" - -DBACKENDDIR=\"${ebook_backenddir}\" - -DEXCHANGE_EWS_LOCALEDIR=\"${LOCALE_INSTALL_DIR}\" -) - -target_compile_options(ebookbackendews PUBLIC - ${LIBEBACKEND_CFLAGS} - ${LIBEBOOK_CFLAGS} - ${LIBEDATABOOK_CFLAGS} - ${LIBEDATASERVER_CFLAGS} - ${MSPACK_CFLAGS} - ${SOUP_CFLAGS} -) - -target_include_directories(ebookbackendews PUBLIC - ${CMAKE_BINARY_DIR} - ${CMAKE_SOURCE_DIR} - ${CMAKE_BINARY_DIR}/src - ${CMAKE_SOURCE_DIR}/src - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} - ${LIBEBACKEND_INCLUDE_DIRS} - ${LIBEBOOK_INCLUDE_DIRS} - ${LIBEDATABOOK_INCLUDE_DIRS} - ${LIBEDATASERVER_INCLUDE_DIRS} - ${MSPACK_INCLUDE_DIRS} - ${SOUP_INCLUDE_DIRS} -) - -target_link_libraries(ebookbackendews - ${DEPENDENCIES} - ${LIBEBACKEND_LDFLAGS} - ${LIBEBOOK_LDFLAGS} - ${LIBEDATABOOK_LDFLAGS} - ${LIBEDATASERVER_LDFLAGS} - ${MATH_LDFLAGS} - ${MSPACK_LDFLAGS} - ${SOUP_LDFLAGS} -) - -install(TARGETS ebookbackendews - DESTINATION ${ebook_backenddir} -) - -# ****************************** -# Internal test programs -# ****************************** - -if(WITH_MSPACK) - add_executable(gal-lzx-decompress-test - ews-oab-decompress.c - ews-oab-decompress.h - gal-lzx-decompress-test.c - ) - - target_compile_definitions(gal-lzx-decompress-test PRIVATE - -DG_LOG_DOMAIN=\"gal-lzx-decompress-test\" - ) - - target_compile_options(gal-lzx-decompress-test PUBLIC - ${GNOME_PLATFORM_CFLAGS} - ${MSPACK_CFLAGS} - ) - - target_include_directories(gal-lzx-decompress-test PUBLIC - ${CMAKE_BINARY_DIR} - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} - ${GNOME_PLATFORM_INCLUDE_DIRS} - ${MSPACK_INCLUDE_DIRS} - ) - - target_link_libraries(gal-lzx-decompress-test - ${GNOME_PLATFORM_LDFLAGS} - ${MSPACK_LDFLAGS} - ) - - # ************************************************************** - - add_executable(oab-decode-test - ews-oab-decoder.c - ews-oab-decoder.h - oab-decode-test.c - ) - - target_compile_definitions(oab-decode-test PRIVATE - -DG_LOG_DOMAIN=\"oab-decode-test\" - ) - - target_compile_options(oab-decode-test PUBLIC - ${GNOME_PLATFORM_CFLAGS} - ${LIBEBOOK_CFLAGS} - ${LIBEDATABOOK_CFLAGS} - ${MSPACK_CFLAGS} - ) - - target_include_directories(oab-decode-test PUBLIC - ${CMAKE_BINARY_DIR} - ${CMAKE_CURRENT_BINARY_DIR} - ${CMAKE_CURRENT_SOURCE_DIR} - ${GNOME_PLATFORM_INCLUDE_DIRS} - ${LIBEBOOK_INCLUDE_DIRS} - ${LIBEDATABOOK_INCLUDE_DIRS} - ${MSPACK_INCLUDE_DIRS} - ) - - target_link_libraries(oab-decode-test - ${GNOME_PLATFORM_LDFLAGS} - ${LIBEBOOK_LDFLAGS} - ${LIBEDATABOOK_LDFLAGS} - ${MSPACK_LDFLAGS} - ${MATH_LDFLAGS} - ) -endif(WITH_MSPACK) diff --git a/src/addressbook/e-book-backend-ews.c b/src/addressbook/e-book-backend-ews.c index e412bc4..9fc0554 100644 --- a/src/addressbook/e-book-backend-ews.c +++ b/src/addressbook/e-book-backend-ews.c @@ -37,8 +37,6 @@ #include #include -#include - #include #include "server/e-ews-item-change.h" @@ -270,46 +268,55 @@ ebews_populate_nick_name (EBookBackendEws *bbews, } static void -ebews_populate_date_value (EBookBackendEws *bbews, +ebews_populate_birth_date (EBookBackendEws *bbews, EContact *contact, - EContactField field, - time_t value) + EEwsItem *item, + GCancellable *cancellable, + GError **error) { - if (value > (time_t) 0) { - struct icaltimetype itt; + time_t bdate; + GDate date; + EContactDate edate; - itt = icaltime_from_timet_with_zone (value, TRUE, icaltimezone_get_utc_timezone ()); + bdate = e_ews_item_get_birthday (item); - if (icaltime_is_valid_time (itt) && !icaltime_is_null_time (itt)) { - EContactDate edate = { 0 }; + if (bdate) { + g_date_clear (&date, 1); + g_date_set_time_t (&date, bdate); - edate.year = itt.year; - edate.month = itt.month; - edate.day = itt.day; + edate.year = date.year; + edate.month = date.month; + edate.day = date.day; - e_contact_set (contact, field, &edate); - } + if (g_date_valid (&date)) + e_contact_set (contact, E_CONTACT_BIRTH_DATE, &edate); } } static void -ebews_populate_birth_date (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - ebews_populate_date_value (bbews, contact, E_CONTACT_BIRTH_DATE, e_ews_item_get_birthday (item)); -} - -static void ebews_populate_anniversary (EBookBackendEws *bbews, EContact *contact, EEwsItem *item, GCancellable *cancellable, GError **error) { - ebews_populate_date_value (bbews, contact, E_CONTACT_ANNIVERSARY, e_ews_item_get_wedding_anniversary (item)); + time_t bdate; + GDate date; + EContactDate edate; + + bdate = e_ews_item_get_wedding_anniversary (item); + + if (bdate) { + g_date_clear (&date, 1); + g_date_set_time_t (&date, bdate); + + edate.year = date.year; + edate.month = date.month; + edate.day = date.day; + + if (g_date_valid (&date)) + e_contact_set (contact, E_CONTACT_ANNIVERSARY, &edate); + } } static EContactPhoto * @@ -593,41 +600,34 @@ ebews_set_full_name (ESoapMessage *msg, e_contact_name_free (name); } +/* TODO Set birth and anniversary dates */ static void -ebews_set_date_value (ESoapMessage *message, - EContact *contact, - EContactField field, - const gchar *element_name) +ebews_set_birth_date (ESoapMessage *message, + EContact *contact) { EContactDate *date; - gchar *value; + gchar *birthday; - date = e_contact_get (contact, field); + date = e_contact_get (contact, E_CONTACT_BIRTH_DATE); if (!date) return; - value = g_strdup_printf ("%04d-%02d-%02dT00:00:00Z", + birthday = g_strdup_printf ( + "%04d-%02d-%02dT00:00:00", date->year, date->month, date->day); - e_ews_message_write_string_parameter (message, element_name, NULL, value); + e_ews_message_write_string_parameter (message, "Birthday", NULL, birthday); - e_contact_date_free (date); - g_free (value); -} + g_free (birthday); -static void -ebews_set_birth_date (ESoapMessage *message, - EContact *contact) -{ - ebews_set_date_value (message, contact, E_CONTACT_BIRTH_DATE, "Birthday"); } static void ebews_set_anniversary (ESoapMessage *message, EContact *contact) { - ebews_set_date_value (message, contact, E_CONTACT_ANNIVERSARY, "WeddingAnniversary"); + } static void @@ -700,7 +700,6 @@ add_physical_address (ESoapMessage *msg, e_ews_message_write_string_parameter (msg, "Street", NULL, contact_addr->street); e_ews_message_write_string_parameter (msg, "City", NULL, contact_addr->locality); e_ews_message_write_string_parameter (msg, "State", NULL, contact_addr->region); - e_ews_message_write_string_parameter (msg, "CountryOrRegion", NULL, contact_addr->country); e_ews_message_write_string_parameter (msg, "PostalCode", NULL, contact_addr->code); e_soap_message_end_element (msg); @@ -838,33 +837,30 @@ ebews_set_full_name_changes (EBookBackendEws *bbews, } static void -ebews_set_date_value_changes (ESoapMessage *message, +ebews_set_birth_date_changes (EBookBackendEws *bbews, + ESoapMessage *message, EContact *new, EContact *old, - EContactField field, - const gchar *element_name) + gchar **out_new_change_key, + GCancellable *cancellable, + GError **error) { EContactDate *new_date, *old_date; + gchar *birthday; if (!message) return; - new_date = e_contact_get (new, field); - old_date = e_contact_get (old, field); + new_date = e_contact_get (new, E_CONTACT_BIRTH_DATE); + old_date = e_contact_get (old, E_CONTACT_BIRTH_DATE); if (!e_contact_date_equal (new_date, old_date)) { - if (new_date) { - gchar *value; - - value = g_strdup_printf ("%04d-%02d-%02dT00:00:00Z", - new_date->year, new_date->month, new_date->day); + birthday = g_strdup_printf ( + "%04d-%02d-%02dT00:00:00", + new_date->year, new_date->month, new_date->day); - convert_contact_property_to_updatexml (message, element_name, value, "contacts", NULL, NULL); - - g_free (value); - } else { - e_ews_message_add_delete_item_field (message, element_name, "contacts"); - } + convert_contact_property_to_updatexml (message, "Birthday", birthday, "contacts", NULL, NULL); + g_free (birthday); } e_contact_date_free (new_date); @@ -872,18 +868,6 @@ ebews_set_date_value_changes (ESoapMessage *message, } static void -ebews_set_birth_date_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - ebews_set_date_value_changes (message, new, old, E_CONTACT_BIRTH_DATE, "Birthday"); -} - -static void ebews_set_anniversary_changes (EBookBackendEws *bbews, ESoapMessage *message, EContact *new, @@ -892,7 +876,7 @@ ebews_set_anniversary_changes (EBookBackendEws *bbews, GCancellable *cancellable, GError **error) { - ebews_set_date_value_changes (message, new, old, E_CONTACT_ANNIVERSARY, "WeddingAnniversary"); + } static void @@ -1207,8 +1191,6 @@ compare_address (ESoapMessage *message, convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "City", new_address->locality, "contacts", "PhysicalAddresses", key); if (set || g_strcmp0 (new_address->region, old_address->region) != 0) convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "State", new_address->region, "contacts", "PhysicalAddresses", key); - if (set || g_strcmp0 (new_address->country, old_address->country) != 0) - convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "CountryOrRegion", new_address->country, "contacts", "PhysicalAddresses", key); if (set || g_strcmp0 (new_address->code, old_address->code) != 0) convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "PostalCode", new_address->code, "contacts", "PhysicalAddresses", key); @@ -1388,7 +1370,7 @@ static const struct field_element_mapping { { E_CONTACT_SPOUSE, ELEMENT_TYPE_SIMPLE, "SpouseName", e_ews_item_get_spouse_name}, { E_CONTACT_FAMILY_NAME, ELEMENT_TYPE_SIMPLE, "Surname", e_ews_item_get_surname}, { E_CONTACT_GIVEN_NAME, ELEMENT_TYPE_COMPLEX, "GivenName", NULL, ebews_populate_givenname, ebews_set_givenname, ebews_set_givenname_changes}, - { E_CONTACT_ANNIVERSARY, ELEMENT_TYPE_COMPLEX, "WeddingAnniversary", NULL, ebews_populate_anniversary, ebews_set_anniversary, ebews_set_anniversary_changes }, + { E_CONTACT_BIRTH_DATE, ELEMENT_TYPE_COMPLEX, "WeddingAnniversary", NULL, ebews_populate_anniversary, ebews_set_anniversary, ebews_set_anniversary_changes }, { E_CONTACT_PHOTO, ELEMENT_TYPE_COMPLEX, "Photo", NULL, ebews_populate_photo, ebews_set_photo, ebews_set_photo_changes }, /* Should take of uid and changekey (REV) */ @@ -2916,8 +2898,7 @@ ebb_ews_connect_sync (EBookMetaBackend *meta_backend, bbews->priv->cnc, "proxy-resolver", G_BINDING_SYNC_CREATE); - *out_auth_result = e_ews_connection_try_credentials_sync (bbews->priv->cnc, credentials, NULL, - out_certificate_pem, out_certificate_errors, cancellable, error); + *out_auth_result = e_ews_connection_try_credentials_sync (bbews->priv->cnc, credentials, cancellable, error); if (*out_auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) { ESource *source = e_backend_get_source (E_BACKEND (bbews)); @@ -3530,7 +3511,6 @@ ebb_ews_get_backend_property (EBookBackend *book_backend, e_contact_field_name (E_CONTACT_ADDRESS_WORK), e_contact_field_name (E_CONTACT_ADDRESS_HOME), e_contact_field_name (E_CONTACT_ADDRESS_OTHER), - e_contact_field_name (E_CONTACT_ANNIVERSARY), e_contact_field_name (E_CONTACT_BIRTH_DATE), e_contact_field_name (E_CONTACT_NOTE), e_contact_field_name (E_CONTACT_PHOTO), diff --git a/src/addressbook/e-book-backend-ews.c.birthday-date b/src/addressbook/e-book-backend-ews.c.birthday-date deleted file mode 100644 index cdf5b13..0000000 --- a/src/addressbook/e-book-backend-ews.c.birthday-date +++ /dev/null @@ -1,3666 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ - -/* e-book-backend-ews.c - Ews contact backend. - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com) - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU 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. - * - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "server/e-ews-item-change.h" -#include "server/e-ews-message.h" -#include "server/e-ews-connection.h" -#include "server/e-ews-connection-utils.h" -#include "server/e-ews-item.h" -#include "server/e-ews-query-to-restriction.h" -#include "server/e-source-ews-folder.h" - -#include "e-book-backend-ews.h" -#include "ews-oab-decoder.h" -#include "ews-oab-decompress.h" - -#ifdef G_OS_WIN32 -#ifdef gmtime_r -#undef gmtime_r -#endif - -/* The gmtime() in Microsoft's C library is MT-safe */ -#define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0) -#endif - -#define d(x) - -#define EDB_ERROR(_code) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, NULL) -#define EDB_ERROR_EX(_code,_msg) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, _msg) - -#define X_EWS_ORIGINAL_VCARD "X-EWS-ORIGINAL-VCARD" -#define X_EWS_CHANGEKEY "X-EWS-CHANGEKEY" -#define X_EWS_GAL_SHA1 "X-EWS-GAL-SHA1" - -#define EWS_MAX_FETCH_COUNT 500 - -#define ELEMENT_TYPE_SIMPLE 0x01 /* simple string fields */ -#define ELEMENT_TYPE_COMPLEX 0x02 /* complex fields while require different get/set functions */ - -/* passing field uris for PhysicalAddress, PhoneNumbers causes error, so we - * use Default view to fetch them. Thus the summary props just have attachments - * and some additional properties that are not return with Default view */ -#define CONTACT_ITEM_PROPS "item:Attachments item:HasAttachments item:Body item:LastModifiedTime contacts:Manager contacts:Department contacts:SpouseName contacts:AssistantName contacts:BusinessHomePage contacts:Birthday" - -struct _EBookBackendEwsPrivate { - GRecMutex cnc_lock; - EEwsConnection *cnc; - - gchar *folder_id; - gboolean is_gal; - - guint subscription_key; - - /* used for storing attachments */ - gchar *attachments_dir; -}; - -G_DEFINE_TYPE (EBookBackendEws, e_book_backend_ews, E_TYPE_BOOK_META_BACKEND) - -static CamelEwsSettings * -ebb_ews_get_collection_settings (EBookBackendEws *bbews) -{ - ESource *source; - ESource *collection; - ESourceCamel *extension; - ESourceRegistry *registry; - CamelSettings *settings; - const gchar *extension_name; - - source = e_backend_get_source (E_BACKEND (bbews)); - registry = e_book_backend_get_registry (E_BOOK_BACKEND (bbews)); - - extension_name = e_source_camel_get_extension_name ("ews"); - e_source_camel_generate_subtype ("ews", CAMEL_TYPE_EWS_SETTINGS); - - /* The collection settings live in our parent data source. */ - collection = e_source_registry_find_extension (registry, source, extension_name); - g_return_val_if_fail (collection != NULL, NULL); - - extension = e_source_get_extension (collection, extension_name); - settings = e_source_camel_get_settings (extension); - - g_object_unref (collection); - - return CAMEL_EWS_SETTINGS (settings); -} - -static void -ebb_ews_convert_error_to_edb_error (GError **perror) -{ - GError *error = NULL; - - if (!perror || !*perror || (*perror)->domain == E_DATA_BOOK_ERROR) - return; - - if ((*perror)->domain == EWS_CONNECTION_ERROR) { - switch ((*perror)->code) { - case EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED: - error = EDB_ERROR_EX (AUTHENTICATION_FAILED, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_FOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_MANAGEDFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PARENTFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PUBLICFOLDERSERVERNOTFOUND: - error = EDB_ERROR_EX (NO_SUCH_BOOK, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_EVENTNOTFOUND: - case EWS_CONNECTION_ERROR_ITEMNOTFOUND: - error = EDB_ERROR_EX (CONTACT_NOT_FOUND, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_UNAVAILABLE: - g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND, (*perror)->message); - break; - } - - if (!error) - error = EDB_ERROR_EX (OTHER_ERROR, (*perror)->message); - } - - if (error) { - g_error_free (*perror); - *perror = error; - } -} - -static void -ebb_ews_maybe_disconnect_sync (EBookBackendEws *bbews, - GError **in_perror, - GCancellable *cancellable) -{ - g_return_if_fail (E_IS_BOOK_BACKEND_EWS (bbews)); - - if (in_perror && g_error_matches (*in_perror, E_DATA_BOOK_ERROR, E_DATA_BOOK_STATUS_AUTHENTICATION_FAILED)) { - e_book_meta_backend_disconnect_sync (E_BOOK_META_BACKEND (bbews), cancellable, NULL); - e_backend_schedule_credentials_required (E_BACKEND (bbews), E_SOURCE_CREDENTIALS_REASON_REJECTED, NULL, 0, NULL, NULL, G_STRFUNC); - } -} - -static const struct phone_field_mapping { - EContactField field; - const gchar *element; -} phone_field_map[] = { - {E_CONTACT_PHONE_ASSISTANT, "AssistantPhone"}, - {E_CONTACT_PHONE_BUSINESS_FAX, "BusinessFax"}, - {E_CONTACT_PHONE_BUSINESS, "BusinessPhone"}, - {E_CONTACT_PHONE_BUSINESS_2, "BusinessPhone2"}, - {E_CONTACT_PHONE_CAR, "CarPhone"}, - {E_CONTACT_PHONE_COMPANY, "CompanyMainPhone"}, - {E_CONTACT_PHONE_HOME_FAX, "HomeFax"}, - {E_CONTACT_PHONE_HOME, "HomePhone"}, - {E_CONTACT_PHONE_HOME_2, "HomePhone2"}, - {E_CONTACT_PHONE_ISDN, "Isdn"}, - {E_CONTACT_PHONE_MOBILE, "MobilePhone"}, - {E_CONTACT_PHONE_OTHER_FAX, "OtherFax"}, - {E_CONTACT_PHONE_OTHER, "OtherTelephone"}, - {E_CONTACT_PHONE_PAGER, "Pager"}, - {E_CONTACT_PHONE_PRIMARY, "PrimaryPhone"}, - {E_CONTACT_PHONE_RADIO, "RadioPhone"}, - {E_CONTACT_PHONE_TELEX, "Telex"}, - {E_CONTACT_PHONE_TTYTDD, "TtyTddPhone"} -}; - -static void -ebews_populate_rev (EContact *contact, - EEwsItem *item) -{ - struct tm stm; - time_t tt = 0; - gchar time_string[100] = { 0 }; - - g_return_if_fail (E_IS_CONTACT (contact)); - - if (item) { - g_return_if_fail (E_IS_EWS_ITEM (item)); - - tt = e_ews_item_get_last_modified_time (item); - } - - if (tt <= 0) - tt = time (NULL); - - gmtime_r (&tt, &stm); - strftime (time_string, 100, "%Y-%m-%dT%H:%M:%SZ", &stm); - - e_contact_set (contact, E_CONTACT_REV, time_string); -} - -static void -ebews_populate_uid (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - const EwsId *id; - - id = e_ews_item_get_id (item); - if (id) { - e_contact_set (contact, E_CONTACT_UID, id->id); - ebews_populate_rev (contact, item); - e_vcard_util_set_x_attribute (E_VCARD (contact), X_EWS_CHANGEKEY, id->change_key); - } -} - -static void -ebews_populate_full_name (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - const EwsCompleteName *cn; - - cn = e_ews_item_get_complete_name (item); - if (cn) - e_contact_set (contact, E_CONTACT_FULL_NAME, cn->full_name); -} - -static void -ebews_populate_nick_name (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - const EwsCompleteName *cn; - - cn = e_ews_item_get_complete_name (item); - if (cn) - e_contact_set (contact, E_CONTACT_NICKNAME, cn->nick_name); -} - -static void -ebews_populate_birth_date (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - time_t bdate; - GDate date; - EContactDate edate; - - bdate = e_ews_item_get_birthday (item); - - if (bdate) { - g_date_clear (&date, 1); - g_date_set_time_t (&date, bdate); - - edate.year = date.year; - edate.month = date.month; - edate.day = date.day; - - if (g_date_valid (&date)) - e_contact_set (contact, E_CONTACT_BIRTH_DATE, &edate); - } -} - -static void -ebews_populate_anniversary (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - time_t bdate; - GDate date; - EContactDate edate; - - bdate = e_ews_item_get_wedding_anniversary (item); - - if (bdate) { - g_date_clear (&date, 1); - g_date_set_time_t (&date, bdate); - - edate.year = date.year; - edate.month = date.month; - edate.day = date.day; - - if (g_date_valid (&date)) - e_contact_set (contact, E_CONTACT_ANNIVERSARY, &edate); - } -} - -static EContactPhoto * -get_photo (EBookBackendEws *bbews, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - EContactPhoto *photo = NULL; - EEwsAdditionalProps *add_props = NULL; - EEwsAttachmentInfo *info; - GSList *contact_item_ids = NULL, *new_items = NULL; - GSList *attachments = NULL, *attachments_ids = NULL; - const guchar *content; - const gchar *contact_photo_id; - const EwsId *id; - gsize len; - - id = e_ews_item_get_id (item); - if (!id) - return NULL; - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("item:Attachments"); - - contact_item_ids = g_slist_prepend (contact_item_ids, g_strdup (id->id)); - if (!e_ews_connection_get_items_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - contact_item_ids, - "IdOnly", - add_props, - FALSE, - NULL, - E_EWS_BODY_TYPE_TEXT, - &new_items, - NULL, - NULL, - cancellable, - error)) - goto exit; - - contact_photo_id = e_ews_item_get_contact_photo_id (new_items->data); - if (!contact_photo_id) - goto exit; - - attachments_ids = g_slist_prepend (attachments_ids, g_strdup (contact_photo_id)); - if (!e_ews_connection_get_attachments_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - NULL, - attachments_ids, - NULL, - FALSE, - &attachments, - NULL, - NULL, - cancellable, - error)) - goto exit; - - info = attachments->data; - content = (guchar *) e_ews_attachment_info_get_inlined_data (info, &len); - - photo = e_contact_photo_new (); - photo->type = E_CONTACT_PHOTO_TYPE_INLINED; - e_contact_photo_set_inlined (photo, content, len); - -exit: - e_ews_additional_props_free (add_props); - g_slist_free_full (contact_item_ids, g_free); - g_slist_free_full (new_items, g_object_unref); - g_slist_free_full (attachments_ids, g_free); - g_slist_free_full (attachments, (GDestroyNotify) e_ews_attachment_info_free); - - return photo; -} - -static void -ebews_populate_photo (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - EContactPhoto *photo; - - /* - * Support for ContactPhoto was added in Exchange 2010 SP2. - * We don't want to try to set/get this property if we are running in older version of the server. - */ - if (!e_ews_connection_satisfies_server_version (bbews->priv->cnc, E_EWS_EXCHANGE_2010_SP2)) - return; - - photo = get_photo (bbews, item, cancellable, error); - if (!photo) { - return; - } - - e_contact_set (contact, E_CONTACT_PHOTO, photo); - e_contact_photo_free (photo); -} - -static void -set_phone_number (EContact *contact, - EContactField field, - EEwsItem *item, - const gchar *item_field) -{ - const gchar *pn; - - pn = e_ews_item_get_phone_number (item, item_field); - if (pn && *pn) - e_contact_set (contact, field, pn); -} - -static void -ebews_populate_phone_numbers (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - gint i; - - for (i = 0; i < G_N_ELEMENTS (phone_field_map); i++) - set_phone_number (contact, phone_field_map[i].field, item, phone_field_map[i].element); -} - -static void -copy_ews_address_to_contact_address (EContactAddress *contact_addr, - const EwsAddress *address) -{ - contact_addr->address_format = NULL; - contact_addr->po = NULL; - contact_addr->street = g_strdup (address->street); - contact_addr->ext = NULL; - contact_addr->locality = g_strdup (address->city); - contact_addr->region = g_strdup (address->state); - contact_addr->code = g_strdup (address->postal_code); - contact_addr->country = g_strdup (address->country); -} - -static gboolean -ews_address_is_empty (const EwsAddress *address) -{ - if (!address) - return TRUE; - -#define is_empty_str(x) (!(x) || (!*(x))) - - return is_empty_str (address->street) && - is_empty_str (address->city) && - is_empty_str (address->state) && - is_empty_str (address->postal_code) && - is_empty_str (address->country); - -#undef is_empty_str -} - -static void -set_address (EContact *contact, - EContactField field, - EEwsItem *item, - const gchar *item_field) -{ - const EwsAddress *address; - - address = e_ews_item_get_physical_address (item, item_field); - if (address && !ews_address_is_empty (address)) { - EContactAddress *addr; - - addr = g_new0 (EContactAddress, 1); - copy_ews_address_to_contact_address (addr, address); - e_contact_set (contact, field, addr); - e_contact_address_free (addr); - } -} - -static void -ebews_populate_address (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - - set_address (contact, E_CONTACT_ADDRESS_WORK, item, "Business"); - set_address (contact, E_CONTACT_ADDRESS_HOME, item, "Home"); - set_address (contact, E_CONTACT_ADDRESS_OTHER, item, "Other"); -} - -static void -ebews_populate_ims (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - /* TODO : The fields returned by server does not match with the EContact fields - * for the IMS, handle it later */ -} - -static void -ebews_populate_notes (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - const gchar *notes = e_ews_item_get_notes (item); - if (!notes) - return; - - e_contact_set (contact, E_CONTACT_NOTE, notes); -} - -static void -set_email_address (EContact *contact, - EContactField field, - EEwsItem *item, - const gchar *item_field, - gboolean require_smtp_prefix) -{ - const gchar *ea; - - ea = e_ews_item_get_email_address (item, item_field); - if (ea && g_ascii_strncasecmp (ea, "SMTP:", 5) == 0) - ea = ea + 5; - else if (require_smtp_prefix) - ea = NULL; - - if (ea && *ea) - e_contact_set (contact, field, ea); -} - -static void -ebews_populate_emails_ex (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - gboolean require_smtp_prefix) -{ - set_email_address (contact, E_CONTACT_EMAIL_1, item, "EmailAddress1", require_smtp_prefix); - set_email_address (contact, E_CONTACT_EMAIL_2, item, "EmailAddress2", require_smtp_prefix); - set_email_address (contact, E_CONTACT_EMAIL_3, item, "EmailAddress3", require_smtp_prefix); -} - -static void -ebews_populate_emails (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **errror) -{ - ebews_populate_emails_ex (bbews, contact, item, FALSE); -} - -static void -ebews_set_item_id (ESoapMessage *message, - EContact *contact) -{ - -} - -static void -ebews_set_full_name (ESoapMessage *msg, - EContact *contact) -{ - EContactName *name; - - name = e_contact_get (contact, E_CONTACT_NAME); - if (!name) - return; - - if (name->given) - e_ews_message_write_string_parameter (msg, "GivenName", NULL, name->given); - - if (name->additional && *name->additional) - e_ews_message_write_string_parameter (msg, "MiddleName", NULL, name->additional); - - e_contact_name_free (name); -} - -/* TODO Set birth and anniversary dates */ -static void -ebews_set_birth_date (ESoapMessage *message, - EContact *contact) -{ - EContactDate *date; - gchar *birthday; - - date = e_contact_get (contact, E_CONTACT_BIRTH_DATE); - - if (!date) - return; - - birthday = g_strdup_printf ( - "%04d-%02d-%02dT00:00:00", - date->year, date->month, date->day); - - e_ews_message_write_string_parameter (message, "Birthday", NULL, birthday); - - g_free (birthday); - -} - -static void -ebews_set_anniversary (ESoapMessage *message, - EContact *contact) -{ - -} - -static void -ebews_set_photo (ESoapMessage *message, - EContact *contact) -{ - -} - -static gboolean -add_entry (ESoapMessage *msg, - EContact *contact, - EContactField field, - const gchar *entry_name, - const gchar *include_hdr) -{ - gchar *entry_val; - - entry_val = e_contact_get (contact, field); - - if (entry_val && *entry_val) { - if (include_hdr) - e_soap_message_start_element (msg, include_hdr, NULL, NULL); - - e_ews_message_write_string_parameter_with_attribute (msg, "Entry", NULL, entry_val, "Key", entry_name); - - g_free (entry_val); - return TRUE; - } - - g_free (entry_val); - return FALSE; -} - -static void -ebews_set_phone_numbers (ESoapMessage *msg, - EContact *contact) -{ - gint i; - const gchar *include_hdr = "PhoneNumbers"; - - for (i = 0; i < G_N_ELEMENTS (phone_field_map); i++) { - if (add_entry (msg, contact, phone_field_map[i].field, phone_field_map[i].element, include_hdr)) - include_hdr = NULL; - } - - if (!include_hdr) - e_soap_message_end_element (msg); -} - -static gboolean -add_physical_address (ESoapMessage *msg, - EContact *contact, - EContactField field, - const gchar *entry_name, - gboolean include_start_hdr) -{ - EContactAddress *contact_addr; - - contact_addr = e_contact_get (contact, field); - if (!contact_addr) - return FALSE; - - if (include_start_hdr) - e_soap_message_start_element (msg, "PhysicalAddresses", NULL, NULL); - - e_soap_message_start_element (msg, "Entry", NULL, NULL); - - e_soap_message_add_attribute (msg, "Key", entry_name, NULL, NULL); - e_ews_message_write_string_parameter (msg, "Street", NULL, contact_addr->street); - e_ews_message_write_string_parameter (msg, "City", NULL, contact_addr->locality); - e_ews_message_write_string_parameter (msg, "State", NULL, contact_addr->region); - e_ews_message_write_string_parameter (msg, "CountryOrRegion", NULL, contact_addr->country); - e_ews_message_write_string_parameter (msg, "PostalCode", NULL, contact_addr->code); - - e_soap_message_end_element (msg); - e_contact_address_free (contact_addr); - - return TRUE; -} - -static void -ebews_set_address (ESoapMessage *msg, - EContact *contact) -{ - gboolean include_hdr = TRUE; - - if (add_physical_address (msg, contact, E_CONTACT_ADDRESS_WORK, "Business", include_hdr)) - include_hdr = FALSE; - if (add_physical_address (msg, contact, E_CONTACT_ADDRESS_HOME, "Home", include_hdr)) - include_hdr = FALSE; - if (add_physical_address (msg, contact, E_CONTACT_ADDRESS_OTHER, "Other", include_hdr)) - include_hdr = FALSE; - - if (!include_hdr) - e_soap_message_end_element (msg); -} - -static void -ebews_set_ims (ESoapMessage *message, - EContact *contact) -{ - -} - -static void -ebews_set_notes (ESoapMessage *msg, - EContact *contact) -{ - gchar *notes = e_contact_get (contact, E_CONTACT_NOTE); - if (!notes) - return; - - e_ews_message_write_string_parameter_with_attribute (msg, "Body", NULL, notes, "BodyType", "Text"); - - g_free (notes); -} - -static void -ebews_set_emails (ESoapMessage *msg, - EContact *contact) -{ - const gchar *include_hdr = "EmailAddresses"; - - if (add_entry (msg, contact, E_CONTACT_EMAIL_1, "EmailAddress1", include_hdr)) - include_hdr = NULL; - if (add_entry (msg, contact, E_CONTACT_EMAIL_2, "EmailAddress2", include_hdr)) - include_hdr = NULL; - if (add_entry (msg, contact, E_CONTACT_EMAIL_3, "EmailAddress3", include_hdr)) - include_hdr = NULL; - - if (!include_hdr) - e_soap_message_end_element (msg); -} - -static void -convert_contact_property_to_updatexml (ESoapMessage *msg, - const gchar *name, - const gchar *value, - const gchar *prefix, - const gchar *attr_name, - const gchar *attr_value) -{ - e_ews_message_start_set_item_field (msg, name, prefix, "Contact"); - e_ews_message_write_string_parameter_with_attribute (msg, name, NULL, value, attr_name, attr_value); - e_ews_message_end_set_item_field (msg); -} - -static void -convert_indexed_contact_property_to_updatexml (ESoapMessage *message, - const gchar *name, - const gchar *value, - const gchar *prefix, - const gchar *element_name, - const gchar *key) -{ - gboolean delete_field = FALSE; - - if (!value || g_strcmp0 (value, "") == 0) - delete_field = TRUE; - e_ews_message_start_set_indexed_item_field (message, name , prefix, "Contact", key, delete_field); - - if (!delete_field) - { - e_soap_message_start_element (message, element_name, NULL, NULL); - e_ews_message_write_string_parameter_with_attribute (message, "Entry", NULL, value, "Key", key); - e_soap_message_end_element (message); - } - e_ews_message_end_set_indexed_item_field (message, delete_field); -} - -static void -ebews_set_full_name_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - EContactName *name, *old_name; - - if (!message) - return; - - name = e_contact_get (new, E_CONTACT_NAME); - old_name = e_contact_get (old, E_CONTACT_NAME); - if (!old_name && !name) - return; - - if (!old_name) { - convert_contact_property_to_updatexml (message, "GivenName", name->given, "contacts", NULL, NULL); - convert_contact_property_to_updatexml (message, "MiddleName", name->additional, "contacts", NULL, NULL); - } else if (!name) { - convert_contact_property_to_updatexml (message, "GivenName", "", "contacts", NULL, NULL); - - convert_contact_property_to_updatexml (message, "MiddleName", "", "contacts", NULL, NULL); - } else { - if (g_strcmp0 (name->given, old_name->given) != 0) - convert_contact_property_to_updatexml (message, "GivenName", name->given, "contacts", NULL, NULL); - if (g_strcmp0 (name->additional, old_name->additional) != 0) - convert_contact_property_to_updatexml (message, "MiddleName", name->additional, "contacts", NULL, NULL); - } - - e_contact_name_free (name); - e_contact_name_free (old_name); - -} - -static void -ebews_set_birth_date_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - EContactDate *new_date, *old_date; - gchar *birthday; - - if (!message) - return; - - new_date = e_contact_get (new, E_CONTACT_BIRTH_DATE); - old_date = e_contact_get (old, E_CONTACT_BIRTH_DATE); - - if (!e_contact_date_equal (new_date, old_date)) { - birthday = g_strdup_printf ( - "%04d-%02d-%02dT00:00:00", - new_date->year, new_date->month, new_date->day); - - convert_contact_property_to_updatexml (message, "Birthday", birthday, "contacts", NULL, NULL); - g_free (birthday); - } - - e_contact_date_free (new_date); - e_contact_date_free (old_date); -} - -static void -ebews_set_anniversary_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - -} - -static void -set_photo (EBookBackendEws *bbews, - const EwsId *item_id, - EContact *contact, - EContactPhoto *photo, - gchar **new_change_key, - GCancellable *cancellable, - GError **error) -{ - EEwsAttachmentInfo *info; - EwsId *id = NULL; - GSList *files = NULL; - const guchar *data; - gsize len; - - if (!item_id) { - id = g_new0 (EwsId, 1); - id->id = e_contact_get (contact, E_CONTACT_UID); - id->change_key = e_vcard_util_dup_x_attribute (E_VCARD (contact), X_EWS_CHANGEKEY); - if (!id->change_key) - id->change_key = e_contact_get (contact, E_CONTACT_REV); - - item_id = id; - } - - data = e_contact_photo_get_inlined (photo, &len); - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_INLINED); - e_ews_attachment_info_set_inlined_data (info, data, len); - e_ews_attachment_info_set_mime_type (info, "image/jpeg"); - e_ews_attachment_info_set_filename (info, "ContactPicture.jpg"); - - files = g_slist_append (files, info); - - e_ews_connection_create_attachments_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - item_id, - files, - TRUE, - new_change_key, - NULL, - cancellable, - error); - - if (id) { - g_free (id->change_key); - g_free (id->id); - g_free (id); - } - - g_slist_free_full (files, (GDestroyNotify) e_ews_attachment_info_free); -} - -static gboolean -ebb_ews_photo_changed (EBookMetaBackend *meta_backend, - EContact *old_contact, - EContact *new_contact, - GCancellable *cancellable) -{ - EContact *old_contact_copy = NULL; - EContactPhoto *old_photo; - EContactPhoto *new_photo; - gboolean changed = FALSE; - - old_photo = e_contact_get (old_contact, E_CONTACT_PHOTO); - new_photo = e_contact_get (new_contact, E_CONTACT_PHOTO); - - if (!old_photo && new_photo) - changed = TRUE; - - if (old_photo && !new_photo) - changed = TRUE; - - /* old_photo comes from cache, thus it's always URI (to local file or elsewhere), - while the new_photo is to be saved, which is always inlined. */ - if (!changed && old_photo && new_photo && - old_photo->type == E_CONTACT_PHOTO_TYPE_URI && - new_photo->type == E_CONTACT_PHOTO_TYPE_INLINED) { - e_contact_photo_free (old_photo); - old_photo = NULL; - - old_contact_copy = e_contact_duplicate (old_contact); - - if (e_book_meta_backend_inline_local_photos_sync (meta_backend, old_contact_copy, cancellable, NULL)) - old_photo = e_contact_get (old_contact_copy, E_CONTACT_PHOTO); - } - - if (old_photo && new_photo && - old_photo->type == E_CONTACT_PHOTO_TYPE_INLINED && - new_photo->type == E_CONTACT_PHOTO_TYPE_INLINED) { - guchar *old_data; - guchar *new_data; - gsize old_length; - gsize new_length; - - old_data = old_photo->data.inlined.data; - new_data = new_photo->data.inlined.data; - - old_length = old_photo->data.inlined.length; - new_length = new_photo->data.inlined.length; - - changed = - (old_length != new_length) || - (memcmp (old_data, new_data, old_length) != 0); - } - - e_contact_photo_free (old_photo); - e_contact_photo_free (new_photo); - g_clear_object (&old_contact_copy); - - return changed; -} - -static void -ebews_set_photo_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - EContactPhoto *new_photo = NULL; - EEwsAdditionalProps *add_props = NULL; - GSList *contact_item_ids = NULL, *new_items = NULL, *attachments_ids = NULL; - gchar *id = NULL; - const gchar *contact_photo_id; - gchar *new_change_key = NULL; - - /* - * Support for ContactPhoto was added in Exchange 2010 SP2. - * We don't want to try to set/get this property if we are running in older version of the server. - */ - if (!e_ews_connection_satisfies_server_version (bbews->priv->cnc, E_EWS_EXCHANGE_2010_SP2)) { - return; - } - - if (message) { - /* Photo changes can be done only in pre-flight stage, - because it modifies ChangeKey */ - return; - } - - if (!ebb_ews_photo_changed (E_BOOK_META_BACKEND (bbews), old, new, cancellable)) - return; - - new_photo = e_contact_get (new, E_CONTACT_PHOTO); - id = e_contact_get (old, E_CONTACT_UID); - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("item:Attachments"); - - contact_item_ids = g_slist_append (contact_item_ids, id); - if (!e_ews_connection_get_items_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - contact_item_ids, - "IdOnly", - add_props, - FALSE, - NULL, - E_EWS_BODY_TYPE_TEXT, - &new_items, - NULL, - NULL, - cancellable, - error)) - goto exit; - - contact_photo_id = e_ews_item_get_contact_photo_id (new_items->data); - if (contact_photo_id) { - attachments_ids = g_slist_prepend (attachments_ids, g_strdup (contact_photo_id)); - if (!e_ews_connection_delete_attachments_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - attachments_ids, - &new_change_key, - cancellable, - error)) - goto exit; - } - - if (new_photo) { - EwsId *item_id = NULL; - - if (new_change_key) { - item_id = g_new0 (EwsId, 1); - item_id->id = e_contact_get (new, E_CONTACT_UID); - item_id->change_key = new_change_key; - - new_change_key = NULL; - } - - set_photo (bbews, item_id, new, new_photo, &new_change_key, cancellable, error); - - if (item_id) { - if (!new_change_key) { - new_change_key = item_id->change_key; - item_id->change_key = NULL; - } - - g_free (item_id->id); - g_free (item_id->change_key); - g_free (item_id); - } - } - - exit: - e_ews_additional_props_free (add_props); - e_contact_photo_free (new_photo); - g_slist_free_full (contact_item_ids, g_free); - g_slist_free_full (new_items, g_object_unref); - g_slist_free_full (attachments_ids, g_free); - - if (new_change_key && out_new_change_key) - *out_new_change_key = new_change_key; - else - g_free (new_change_key); -} - -static void -ebews_set_phone_number_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - gint i; - gchar *new_value, *old_value; - - if (!message) - return; - - for (i = 0; i < G_N_ELEMENTS (phone_field_map); i++) { - new_value = e_contact_get (new, phone_field_map[i].field); - old_value = e_contact_get (old, phone_field_map[i].field); - - if (g_strcmp0 (new_value, old_value) != 0) - convert_indexed_contact_property_to_updatexml (message, "PhoneNumber", new_value, "contacts", "PhoneNumbers", phone_field_map[i].element); - - g_free (new_value); - g_free (old_value); - } -} - -static void -convert_indexed_contact_property_to_updatexml_physical_address (ESoapMessage *message, - const gchar *name, - const gchar *uri_element, - const gchar *value, - const gchar *prefix, - const gchar *element_name, - const gchar *key) -{ - gchar * fielduri = NULL; - gboolean delete_field = FALSE; - - if (!value || g_strcmp0 (value, "") == 0) - delete_field = TRUE; - - fielduri = g_strconcat (name, ":", uri_element, NULL); - - e_ews_message_start_set_indexed_item_field (message, fielduri , prefix, "Contact", key, delete_field); - - if (!delete_field) - { - e_soap_message_start_element (message, element_name, NULL, NULL); - - e_soap_message_start_element (message, "Entry", NULL, NULL); - e_soap_message_add_attribute (message, "Key", key, NULL, NULL); - e_ews_message_write_string_parameter (message, uri_element, NULL, value); - e_soap_message_end_element (message); - - e_soap_message_end_element (message); - } - e_ews_message_end_set_indexed_item_field (message, delete_field); -} - -static void -compare_address (ESoapMessage *message, - EContact *new, - EContact *old, - EContactField field, - const gchar *key) -{ - EContactAddress *new_address, *old_address; - gboolean set = FALSE; - - new_address = e_contact_get (new, field); - old_address = e_contact_get (old, field); - - if (!new_address && !old_address) - return; - - if (!old_address && new_address) - set = TRUE; - - if (!new_address && old_address) - { - set = TRUE; - new_address = g_new0 (EContactAddress, 1); - } - - if (set || g_strcmp0 (new_address->street, old_address->street) != 0) - convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "Street", new_address->street, "contacts", "PhysicalAddresses", key); - if (set || g_strcmp0 (new_address->locality, old_address->locality) != 0) - convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "City", new_address->locality, "contacts", "PhysicalAddresses", key); - if (set || g_strcmp0 (new_address->region, old_address->region) != 0) - convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "State", new_address->region, "contacts", "PhysicalAddresses", key); - if (set || g_strcmp0 (new_address->country, old_address->country) != 0) - convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "CountryOrRegion", new_address->country, "contacts", "PhysicalAddresses", key); - if (set || g_strcmp0 (new_address->code, old_address->code) != 0) - convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "PostalCode", new_address->code, "contacts", "PhysicalAddresses", key); - - e_contact_address_free (old_address); - e_contact_address_free (new_address); -} - -static void -ebews_set_address_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - if (!message) - return; - - compare_address (message, new, old, E_CONTACT_ADDRESS_WORK, "Business"); - compare_address (message, new, old, E_CONTACT_ADDRESS_HOME, "Home"); - compare_address (message, new, old, E_CONTACT_ADDRESS_OTHER, "Other"); -} - -static void -ebews_set_im_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - -} - -static void -ebews_set_notes_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - gchar *old_notes, *new_notes; - - if (!message) - return; - - old_notes = e_contact_get (old, E_CONTACT_NOTE); - new_notes = e_contact_get (new, E_CONTACT_NOTE); - - if (g_strcmp0 (old_notes, new_notes) != 0) { - convert_contact_property_to_updatexml ( - message, "Body", new_notes ? new_notes : "", "item", "BodyType", "Text"); - } - - g_free (old_notes); - g_free (new_notes); -} - -static void -ebews_set_email_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - gchar *new_value, *old_value; - - if (!message) - return; - - new_value = e_contact_get (new, E_CONTACT_EMAIL_1); - old_value = e_contact_get (old, E_CONTACT_EMAIL_1); - if (g_strcmp0 (new_value, old_value) != 0) - convert_indexed_contact_property_to_updatexml (message, "EmailAddress", new_value, "contacts", "EmailAddresses", "EmailAddress1"); - g_free (new_value); - g_free (old_value); - - new_value = e_contact_get (new, E_CONTACT_EMAIL_2); - old_value = e_contact_get (old, E_CONTACT_EMAIL_2); - if (g_strcmp0 (new_value, old_value) != 0) - convert_indexed_contact_property_to_updatexml (message, "EmailAddress", new_value, "contacts", "EmailAddresses", "EmailAddress2"); - g_free (new_value); - g_free (old_value); - - new_value = e_contact_get (new, E_CONTACT_EMAIL_3); - old_value = e_contact_get (old, E_CONTACT_EMAIL_3); - if (g_strcmp0 (new_value, old_value) != 0) - convert_indexed_contact_property_to_updatexml (message, "EmailAddress", new_value, "contacts", "EmailAddresses", "EmailAddress3"); - g_free (new_value); - g_free (old_value); -} - -static void -ebews_populate_givenname (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - const gchar *givenname; - - givenname = e_ews_item_get_givenname (item); - if (givenname && *givenname) - e_contact_set (contact, E_CONTACT_GIVEN_NAME, givenname); -} - -static void -ebews_set_givenname (ESoapMessage *message, - EContact *contact) -{ - /* Does nothing, the "GivenName" is filled by the "FullName" code */ -} - -static void -ebews_set_givenname_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - /* Does nothing, the "GivenName" is filled by the "FullName" code */ -} - -static const gchar * -ebews_get_fileas_or_display_name (EEwsItem *item) -{ - const gchar *value; - - value = e_ews_item_get_fileas (item); - if (!value || !*value) - value = e_ews_item_get_display_name (item); - - return value; -} - -static const struct field_element_mapping { - EContactField field_id; - gint element_type; - const gchar *element_name; - /* set function for simple string type values */ - const gchar * (*get_simple_prop_func) (EEwsItem *item); - void (*populate_contact_func)(EBookBackendEws *bbews, EContact *contact, EEwsItem *item, GCancellable *cancellable, GError **error); - void (*set_value_in_soap_message) (ESoapMessage *message, EContact *contact); - void (*set_changes) (EBookBackendEws *bbews, ESoapMessage *message, EContact *new, EContact *old, gchar **out_new_change_key, GCancellable *cancellable, GError **error); - -} mappings[] = { - /* The order should be maintained for create contacts to work */ - { E_CONTACT_NOTE, ELEMENT_TYPE_COMPLEX, "Notes", NULL, ebews_populate_notes, ebews_set_notes, ebews_set_notes_changes }, - { E_CONTACT_FILE_AS, ELEMENT_TYPE_SIMPLE, "FileAs", ebews_get_fileas_or_display_name }, - { E_CONTACT_FULL_NAME, ELEMENT_TYPE_COMPLEX, "CompleteName", NULL, ebews_populate_full_name, ebews_set_full_name, ebews_set_full_name_changes}, - { E_CONTACT_NICKNAME, ELEMENT_TYPE_SIMPLE, "Nickname", NULL, ebews_populate_nick_name}, - { E_CONTACT_ORG, ELEMENT_TYPE_SIMPLE, "CompanyName", e_ews_item_get_company_name}, - /* should take care of all email adresss fields */ - { E_CONTACT_EMAIL_1, ELEMENT_TYPE_COMPLEX, "EmailAddresses", NULL, ebews_populate_emails, ebews_set_emails, ebews_set_email_changes }, - /* should take care of home, work and other adresss fields */ - { E_CONTACT_ADDRESS_HOME, ELEMENT_TYPE_COMPLEX, "PhysicalAddresses", NULL, ebews_populate_address, ebews_set_address, ebews_set_address_changes }, - /* should take care of all phone number fields */ - { E_CONTACT_PHONE_PRIMARY, ELEMENT_TYPE_COMPLEX , "PhoneNumbers", NULL, ebews_populate_phone_numbers, ebews_set_phone_numbers, ebews_set_phone_number_changes}, - { E_CONTACT_ASSISTANT, ELEMENT_TYPE_SIMPLE, "AssistantName", e_ews_item_get_assistant_name}, - { E_CONTACT_BIRTH_DATE, ELEMENT_TYPE_COMPLEX, "Birthday", NULL, ebews_populate_birth_date, ebews_set_birth_date, ebews_set_birth_date_changes }, - { E_CONTACT_HOMEPAGE_URL, ELEMENT_TYPE_SIMPLE, "BusinessHomePage", e_ews_item_get_business_homepage}, - { E_CONTACT_ORG_UNIT, ELEMENT_TYPE_SIMPLE, "Department", e_ews_item_get_department}, - /* should take care of all im fields */ - { E_CONTACT_IM_AIM, ELEMENT_TYPE_COMPLEX, "ImAddresses", NULL, ebews_populate_ims, ebews_set_ims, ebews_set_im_changes }, - { E_CONTACT_TITLE, ELEMENT_TYPE_SIMPLE, "JobTitle", e_ews_item_get_job_title}, - { E_CONTACT_MANAGER, ELEMENT_TYPE_SIMPLE, "Manager", e_ews_item_get_manager}, - { E_CONTACT_OFFICE, ELEMENT_TYPE_SIMPLE, "OfficeLocation", e_ews_item_get_office_location}, - { E_CONTACT_SPOUSE, ELEMENT_TYPE_SIMPLE, "Profession", e_ews_item_get_profession}, - { E_CONTACT_SPOUSE, ELEMENT_TYPE_SIMPLE, "SpouseName", e_ews_item_get_spouse_name}, - { E_CONTACT_FAMILY_NAME, ELEMENT_TYPE_SIMPLE, "Surname", e_ews_item_get_surname}, - { E_CONTACT_GIVEN_NAME, ELEMENT_TYPE_COMPLEX, "GivenName", NULL, ebews_populate_givenname, ebews_set_givenname, ebews_set_givenname_changes}, - { E_CONTACT_BIRTH_DATE, ELEMENT_TYPE_COMPLEX, "WeddingAnniversary", NULL, ebews_populate_anniversary, ebews_set_anniversary, ebews_set_anniversary_changes }, - { E_CONTACT_PHOTO, ELEMENT_TYPE_COMPLEX, "Photo", NULL, ebews_populate_photo, ebews_set_photo, ebews_set_photo_changes }, - - /* Should take of uid and changekey (REV) */ - { E_CONTACT_UID, ELEMENT_TYPE_COMPLEX, "ItemId", NULL, ebews_populate_uid, ebews_set_item_id}, -}; - -static void -ebb_ews_write_dl_members (ESoapMessage *msg, - EContact *contact) -{ - GSList *emails, *l; - - e_soap_message_start_element (msg, "Members", NULL, NULL); - - emails = e_contact_get (contact, E_CONTACT_EMAIL); - for (l = emails; l; l = l->next) { - CamelInternetAddress *addr; - - if (l->data == NULL) - continue; - - addr = camel_internet_address_new (); - if (camel_address_decode (CAMEL_ADDRESS (addr), l->data) > 0) { - const gchar *name = NULL, *email = NULL; - - if (camel_internet_address_get (addr, 0, &name, &email) && email) { - e_soap_message_start_element (msg, "Member", NULL, NULL); - e_soap_message_start_element (msg, "Mailbox", NULL, NULL); - e_ews_message_write_string_parameter (msg, "Name", NULL, name ? name : email); - e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, email); - e_soap_message_end_element (msg); /* Mailbox */ - e_soap_message_end_element (msg); /* Member */ - } - } - g_object_unref (addr); - } - - g_slist_free_full (emails, g_free); - e_soap_message_end_element (msg); /* Members */ -} - -static void -ebb_ews_convert_dl_to_xml_cb (ESoapMessage *msg, - gpointer user_data) -{ - EContact *contact = user_data; - EVCardAttribute *attribute; - GList *values; - - /* Prepare DistributionList node in the SOAP message */ - e_soap_message_start_element (msg, "DistributionList", NULL, NULL); - - attribute = e_vcard_get_attribute (E_VCARD (contact), EVC_FN); - values = e_vcard_attribute_get_values (attribute); - e_ews_message_write_string_parameter (msg, "DisplayName", NULL, values->data); - - ebb_ews_write_dl_members (msg, contact); - - e_soap_message_end_element (msg); /* DistributionList */ -} - -static void -ebb_ews_convert_contact_to_xml_cb (ESoapMessage *msg, - gpointer user_data) -{ - EContact *contact = user_data; - gint i, element_type; - - /* Prepare Contact node in the SOAP message */ - e_soap_message_start_element (msg, "Contact", NULL, NULL); - - for (i = 0; i < G_N_ELEMENTS (mappings); i++) { - element_type = mappings[i].element_type; - - if (element_type == ELEMENT_TYPE_SIMPLE) { - gchar *val; - - /* skip uid while creating contacts */ - if (mappings[i].field_id == E_CONTACT_UID) - continue; - - val = e_contact_get (contact, mappings[i].field_id); - if (val && *val) - e_ews_message_write_string_parameter (msg, mappings[i].element_name, NULL, val); - g_free (val); - } else - mappings[i].set_value_in_soap_message (msg, contact); - } - - /* end of "Contact" */ - e_soap_message_end_element (msg); -} - -typedef struct _ConvertData { - EBookBackendEws *bbews; - GCancellable *cancellable; - GError **error; - - EContact *old_contact; - EContact *new_contact; - gchar *change_key; -} ConvertData; - -static void -ebb_ews_convert_dl_to_updatexml_cb (ESoapMessage *msg, - gpointer user_data) -{ - ConvertData *cd = user_data; - EContact *old_contact = cd->old_contact; - EContact *new_contact = cd->new_contact; - gchar *change_key = NULL; - - if (!cd->change_key) { - change_key = e_vcard_util_dup_x_attribute (E_VCARD (old_contact), X_EWS_CHANGEKEY); - if (!change_key) - change_key = e_contact_get (old_contact, E_CONTACT_REV); - } - - e_ews_message_start_item_change (msg, E_EWS_ITEMCHANGE_TYPE_ITEM, - e_contact_get_const (old_contact, E_CONTACT_UID), - cd->change_key ? cd->change_key : change_key, - 0); - e_ews_message_start_set_item_field (msg, "Members", "distributionlist", "DistributionList"); - ebb_ews_write_dl_members (msg, new_contact); - e_ews_message_end_set_item_field (msg); - e_ews_message_end_item_change (msg); - - g_free (change_key); -} - -static void -ebb_ews_convert_contact_to_updatexml_cb (ESoapMessage *msg, - gpointer user_data) -{ - ConvertData *cd = user_data; - EContact *old_contact = cd->old_contact; - EContact *new_contact = cd->new_contact; - gchar *value = NULL, *old_value = NULL; - gchar *change_key = NULL; - gint i, element_type; - - /* Pre-flight, to update the ChangeKey if needed */ - for (i = 0; i < G_N_ELEMENTS (mappings); i++) { - element_type = mappings[i].element_type; - - if (element_type == ELEMENT_TYPE_COMPLEX) { - gchar *new_change_key = NULL; - - if (mappings[i].field_id == E_CONTACT_UID) - continue; - - mappings[i].set_changes (cd->bbews, NULL, new_contact, old_contact, &new_change_key, cd->cancellable, cd->error); - - if (new_change_key) { - g_free (cd->change_key); - cd->change_key = new_change_key; - } - } - } - - if (!cd->change_key) { - change_key = e_vcard_util_dup_x_attribute (E_VCARD (old_contact), X_EWS_CHANGEKEY); - if (!change_key) - change_key = e_contact_get (old_contact, E_CONTACT_REV); - } - - e_ews_message_start_item_change (msg, E_EWS_ITEMCHANGE_TYPE_ITEM, - e_contact_get_const (old_contact, E_CONTACT_UID), - cd->change_key ? cd->change_key : change_key, - 0); - - /* Iterate for each field in contact */ - - for (i = 0; i < G_N_ELEMENTS (mappings); i++) { - element_type = mappings[i].element_type; - if (element_type == ELEMENT_TYPE_SIMPLE) { - value = e_contact_get (new_contact, mappings[i].field_id); - old_value = e_contact_get (old_contact, mappings[i].field_id); - if (g_strcmp0 (value, old_value) != 0) - convert_contact_property_to_updatexml (msg, mappings[i].element_name, value, "contacts", NULL, NULL); - if (value) - g_free (value); - if (old_value) - g_free (old_value); - } else if (element_type == ELEMENT_TYPE_COMPLEX) { - gchar *new_change_key = NULL; - - if (mappings[i].field_id == E_CONTACT_UID) - continue; - - mappings[i].set_changes (cd->bbews, msg, new_contact, old_contact, &new_change_key, cd->cancellable, cd->error); - - if (new_change_key) { - g_free (cd->change_key); - cd->change_key = new_change_key; - } - } - } - - e_ews_message_end_item_change (msg); - - g_free (change_key); -} - -static EContact * -ebb_ews_item_to_contact (EBookBackendEws *bbews, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - EContact *contact; - gint ii, element_type; - - contact = e_contact_new (); - - for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) { - element_type = mappings[ii].element_type; - - if (element_type == ELEMENT_TYPE_SIMPLE && !mappings[ii].populate_contact_func) { - const gchar *val = mappings[ii].get_simple_prop_func (item); - - if (val != NULL) - e_contact_set (contact, mappings[ii].field_id, val); - } else { - mappings[ii].populate_contact_func (bbews, contact, item, cancellable, error); - } - } - - return contact; -} - -static void -ebb_ews_items_to_contacts (EBookBackendEws *bbews, - const GSList *new_items, - GSList **contacts, - GCancellable *cancellable, - GError **error) -{ - GSList *link; - - for (link = (GSList *) new_items; link; link = g_slist_next (link)) { - EContact *contact; - EEwsItem *item = link->data; - EVCardAttribute *attr; - - if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - contact = ebb_ews_item_to_contact (bbews, item, cancellable, error); - - attr = e_vcard_attribute_new (NULL, "X-EWS-KIND"); - e_vcard_add_attribute_with_value (E_VCARD (contact), attr, "DT_MAILUSER"); - - *contacts = g_slist_prepend (*contacts, contact); - } -} - -static void -ebb_ews_mailbox_to_contact (EBookBackendEws *bbews, - EContact **contact, - GHashTable *values, - const EwsMailbox *mb) -{ - CamelInternetAddress *addr; - gchar *value; - - if (!mb->name && !mb->email) - return; - - addr = camel_internet_address_new (); - - camel_internet_address_add (addr, mb->name, mb->email ? mb->email : ""); - value = camel_address_encode (CAMEL_ADDRESS (addr)); - - if (value && (!values || g_hash_table_lookup (values, value) == NULL)) { - EVCardAttribute *attr; - - attr = e_vcard_attribute_new (NULL, EVC_EMAIL); - e_vcard_attribute_add_value (attr, value); - e_vcard_append_attribute (E_VCARD (*contact), attr); - - if (values) - g_hash_table_insert (values, g_strdup (value), GINT_TO_POINTER (1)); - } else { - g_free (value); - } - - g_object_unref (addr); -} - -static gboolean -ebb_ews_traverse_dl (EBookBackendEws *bbews, - EContact **contact, - GHashTable *items, - GHashTable *values, - EwsMailbox *mb, - GCancellable *cancellable, - GError **error) -{ - if (g_strcmp0 (mb->mailbox_type, "PrivateDL") == 0 || - g_strcmp0 (mb->mailbox_type, "PublicDL") == 0) { - GSList *members = NULL, *l; - gboolean includes_last; - gboolean ret = FALSE; - const gchar *ident; - GError *local_error = NULL; - - if (mb->item_id && mb->item_id->id) - ident = mb->item_id->id; - else if (mb->email) - ident = mb->email; - else - return FALSE; - - if (g_hash_table_lookup (items, ident) != NULL) - return TRUE; - - g_hash_table_insert (items, g_strdup (ident), GINT_TO_POINTER (1)); - - ret = e_ews_connection_expand_dl_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - mb, - &members, - &includes_last, - cancellable, - &local_error); - - if (!ret) { - if (g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NAMERESOLUTIONNORESULTS)) { - g_clear_error (&local_error); - if (mb->email && *mb->email) - ebb_ews_mailbox_to_contact (bbews, contact, values, mb); - - ret = TRUE; - members = NULL; - } else { - if (local_error) - g_propagate_error (error, local_error); - - return FALSE; - } - } - - for (l = members; l; l = l->next) { - ret = ebb_ews_traverse_dl (bbews, contact, items, values, l->data, cancellable, error); - if (!ret) - break; - } - - g_slist_free_full (members, (GDestroyNotify) e_ews_mailbox_free); - return ret; - } else { - ebb_ews_mailbox_to_contact (bbews, contact, values, mb); - - return TRUE; - } -} - -static EContact * -ebb_ews_get_dl_info (EBookBackendEws *bbews, - EEwsItem *item, - const EwsId *id, - const gchar *d_name, - GSList *members, - GCancellable *cancellable, - GError **error) -{ - GHashTable *items, *values; - GSList *l; - EContact *contact; - - contact = e_contact_new (); - e_contact_set (contact, E_CONTACT_UID, id->id); - e_vcard_util_set_x_attribute (E_VCARD (contact), X_EWS_CHANGEKEY, id->change_key); - ebews_populate_rev (contact, item); - - e_contact_set (contact, E_CONTACT_IS_LIST, GINT_TO_POINTER (TRUE)); - e_contact_set (contact, E_CONTACT_LIST_SHOW_ADDRESSES, GINT_TO_POINTER (TRUE)); - e_contact_set (contact, E_CONTACT_FULL_NAME, d_name); - - items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - for (l = members; l != NULL; l = l->next) { - if (!ebb_ews_traverse_dl (bbews, &contact, items, values, l->data, cancellable, error)) { - g_object_unref (contact); - contact = NULL; - goto exit; - } - } - - exit: - g_hash_table_destroy (items); - g_hash_table_destroy (values); - - return contact; -} - -static gboolean -ebb_ews_get_dl_info_gal (EBookBackendEws *bbews, - EContact *contact, - EwsMailbox *mb, - GCancellable *cancellable, - GError **error) -{ - GHashTable *items, *values; - gboolean success; - - items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - success = ebb_ews_traverse_dl (bbews, &contact, items, values, mb, cancellable, error); - - if (success) { - e_contact_set (contact, E_CONTACT_IS_LIST, GINT_TO_POINTER (TRUE)); - e_contact_set (contact, E_CONTACT_LIST_SHOW_ADDRESSES, GINT_TO_POINTER (TRUE)); - } - - g_hash_table_destroy (items); - g_hash_table_destroy (values); - - return success; -} - -static gboolean -ebb_ews_contacts_append_dl (EBookBackendEws *bbews, - EEwsItem *item, - const EwsId *id, - const gchar *d_name, - GSList *members, - GSList **contacts, - GCancellable *cancellable, - GError **error) -{ - EContact *contact; - EVCardAttribute *attr; - - contact = ebb_ews_get_dl_info (bbews, item, id, d_name, members, cancellable, error); - if (contact == NULL) - return FALSE; - - attr = e_vcard_attribute_new (NULL, "X-EWS-KIND"); - e_vcard_add_attribute_with_value (E_VCARD (contact), attr, "DT_DISTLIST"); - - *contacts = g_slist_prepend (*contacts, contact); - - return TRUE; -} - -static gboolean -ebb_ews_fetch_items_sync (EBookBackendEws *bbews, - const GSList *items, /* EEwsItem * */ - GSList **contacts, - GCancellable *cancellable, - GError **error) -{ - GSList *contact_item_ids = NULL, *dl_ids = NULL, *link; - GSList *new_items = NULL; - gboolean ret = FALSE; - - for (link = (GSList *) items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (type == E_EWS_ITEM_TYPE_CONTACT) - contact_item_ids = g_slist_prepend (contact_item_ids, g_strdup (id->id)); - else if (type == E_EWS_ITEM_TYPE_GROUP) { - /* store a list of EwsMailBox's in case of distribution lists */ - dl_ids = g_slist_prepend (dl_ids, g_strdup (id->id)); - } - } - - /* TODO fetch attachments */ - if (contact_item_ids) { - EEwsAdditionalProps *add_props; - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup (CONTACT_ITEM_PROPS); - - ret = e_ews_connection_get_items_sync ( - bbews->priv->cnc, EWS_PRIORITY_MEDIUM, - contact_item_ids, "Default", add_props, - FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &new_items, NULL, NULL, - cancellable, error); - - e_ews_additional_props_free (add_props); - - if (!ret) - goto cleanup; - } - - if (new_items) { - ebb_ews_items_to_contacts (bbews, new_items, contacts, cancellable, error); - - g_slist_free_full (new_items, g_object_unref); - new_items = NULL; - } - - /* Get the display names of the distribution lists */ - if (dl_ids) { - if (!e_ews_connection_get_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, dl_ids, "Default", NULL, - FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &new_items, NULL, NULL, cancellable, error)) - goto cleanup; - } - - for (link = new_items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const gchar *d_name; - const EwsId *id; - EwsMailbox *mb; - GSList *members = NULL; - gboolean includes_last; - GError *local_error = NULL; - - if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - id = e_ews_item_get_id (item); - mb = g_new0 (EwsMailbox, 1); - mb->item_id = (EwsId *) id; - - d_name = e_ews_item_get_subject (item); - if (e_ews_connection_expand_dl_sync ( - bbews->priv->cnc, EWS_PRIORITY_MEDIUM, mb, &members, - &includes_last, cancellable, &local_error)) { - ret = ebb_ews_contacts_append_dl (bbews, item, id, d_name, members, contacts, cancellable, error); - g_slist_free_full (members, (GDestroyNotify) e_ews_mailbox_free); - } else { - ret = g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NAMERESOLUTIONNORESULTS); - if (ret) { - g_clear_error (&local_error); - } else if (local_error) { - g_propagate_error (error, local_error); - } - } - - g_free (mb); - - if (!ret) - goto cleanup; - } - - cleanup: - g_slist_free_full (new_items, g_object_unref); - g_slist_free_full (contact_item_ids, g_free); - g_slist_free_full (dl_ids, g_free); - - return ret; -} - -static void -ebb_ews_server_notification_cb (EBookBackendEws *bbews, - const GSList *events, - EEwsConnection *cnc) -{ - GSList *link; - gboolean update_folder = FALSE; - - g_return_if_fail (E_IS_BOOK_BACKEND_EWS (bbews)); - - for (link = (GSList *) events; link && !update_folder; link = g_slist_next (link)) { - EEwsNotificationEvent *event = link->data; - - switch (event->type) { - case E_EWS_NOTIFICATION_EVENT_CREATED: - case E_EWS_NOTIFICATION_EVENT_DELETED: - case E_EWS_NOTIFICATION_EVENT_MODIFIED: - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (g_strcmp0 (event->folder_id, bbews->priv->folder_id) == 0) - update_folder = TRUE; - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - break; - case E_EWS_NOTIFICATION_EVENT_MOVED: - case E_EWS_NOTIFICATION_EVENT_COPIED: - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (g_strcmp0 (event->folder_id, bbews->priv->folder_id) == 0 || - g_strcmp0 (event->old_folder_id, bbews->priv->folder_id) == 0) - update_folder = TRUE; - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - break; - default: - return; - } - } - - if (update_folder) - e_book_meta_backend_schedule_refresh (E_BOOK_META_BACKEND (bbews)); -} - -static void -ebb_ews_unset_connection (EBookBackendEws *bbews) -{ - g_return_if_fail (E_IS_BOOK_BACKEND_EWS (bbews)); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (bbews->priv->cnc) { - e_ews_connection_set_disconnected_flag (bbews->priv->cnc, TRUE); - - g_signal_handlers_disconnect_by_func (bbews->priv->cnc, ebb_ews_server_notification_cb, bbews); - - if (bbews->priv->subscription_key != 0) { - e_ews_connection_disable_notifications_sync ( - bbews->priv->cnc, - bbews->priv->subscription_key); - bbews->priv->subscription_key = 0; - } - } - - g_clear_object (&bbews->priv->cnc); - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); -} - -static gint -det_sort_func (gconstpointer _a, - gconstpointer _b) -{ - const EwsOALDetails *a = _a, *b = _b; - - return a->seq - b->seq; -} - -static gchar * -ebb_ews_download_gal_file (EBookBackendEws *bbews, - EwsOALDetails *full, - GCancellable *cancellable, - GError **error) -{ - EEwsConnection *oab_cnc; - gchar *full_url, *oab_url; - gchar *download_path = NULL; - gchar *password; - CamelEwsSettings *ews_settings; - const gchar *cache_dir; - - ews_settings = ebb_ews_get_collection_settings (bbews); - - /* oab url with oab.xml removed from the suffix */ - oab_url = camel_ews_settings_dup_oaburl (ews_settings); - if (!oab_url || !*oab_url) { - g_free (oab_url); - return NULL; - } - - if (g_str_has_suffix (oab_url, "oab.xml")) - oab_url [strlen (oab_url) - 7] = '\0'; - - full_url = g_strconcat (oab_url, full->filename, NULL); - cache_dir = e_book_backend_get_cache_dir (E_BOOK_BACKEND (bbews)); - download_path = g_build_filename (cache_dir, full->filename, NULL); - - oab_cnc = e_ews_connection_new_for_backend (E_BACKEND (bbews), e_book_backend_get_registry (E_BOOK_BACKEND (bbews)), full_url, ews_settings); - - e_binding_bind_property ( - bbews, "proxy-resolver", - oab_cnc, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - password = e_ews_connection_dup_password (bbews->priv->cnc); - e_ews_connection_set_password (oab_cnc, password); - g_free (password); - - if (!e_ews_connection_download_oal_file_sync (oab_cnc, download_path, NULL, NULL, cancellable, error)) { - g_free (download_path); - download_path = NULL; - } else { - d (printf ("OAL file downloaded %s\n", download_path)); - } - - g_object_unref (oab_cnc); - g_free (oab_url); - g_free (full_url); - - return download_path; -} - -static gchar * -ebb_ews_download_full_gal (EBookBackendEws *bbews, - EwsOALDetails *full, - GCancellable *cancellable, - GError **error) -{ - ESource *source; - const gchar *cache_dir; - gchar *lzx_path, *oab_file, *oab_path; - - lzx_path = ebb_ews_download_gal_file (bbews, full, cancellable, error); - if (!lzx_path) - return NULL; - - source = e_backend_get_source (E_BACKEND (bbews)); - oab_file = g_strdup_printf ("%s-%d.oab", e_source_get_display_name (source), full->seq); - cache_dir = e_book_backend_get_cache_dir (E_BOOK_BACKEND (bbews)); - oab_path = g_build_filename (cache_dir, oab_file, NULL); - - if (!ews_oab_decompress_full (lzx_path, oab_path, error)) { - g_free (oab_path); - oab_path = NULL; - } else { - d (printf ("OAL file decompressed %s\n", oab_path)); - } - - if (lzx_path) { - g_unlink (lzx_path); - g_free (lzx_path); - } - - g_free (oab_file); - - return oab_path; -} - -static gchar * -ebb_ews_download_gal (EBookBackendEws *bbews, - EBookCache *book_cache, - EwsOALDetails *full, - GSList *deltas, - guint32 seq, - GCancellable *cancellable, - GError **error) -{ -#ifdef WITH_MSPACK - GSList *link; - gchar *thisoab; - - thisoab = e_cache_dup_key (E_CACHE (book_cache), "oab-filename", NULL); - if (!thisoab) - goto full; - - for (link = deltas; link; link = g_slist_next (link)) { - EwsOALDetails *det = link->data; - ESource *source; - gchar *oab_file, *lzx_path, *nextoab; - const gchar *cache_dir; - GError *local_error = NULL; - - seq++; - if (det->seq != seq) - break; - - lzx_path = ebb_ews_download_gal_file (bbews, det, cancellable, NULL); - if (!lzx_path) - break; - - source = e_backend_get_source (E_BACKEND (bbews)); - oab_file = g_strdup_printf ("%s-%d.oab", e_source_get_display_name (source), seq); - cache_dir = e_book_backend_get_cache_dir (E_BOOK_BACKEND (bbews)); - nextoab = g_build_filename (cache_dir, oab_file, NULL); - g_free (oab_file); - - ews_oab_decompress_patch (lzx_path, thisoab, nextoab, &local_error); - - /* Free the LZX file */ - g_unlink (lzx_path); - g_free (lzx_path); - - /* Free the previous OAB file */ - g_unlink (thisoab); - g_free (thisoab); - - thisoab = nextoab; - - /* For once we are *allowed* to use the error instead of having to - * check the return value of the function. It's our *own* error. */ - if (local_error) { - d (printf ("Failed to apply incremental patch: %s\n", local_error->message)); - g_error_free (local_error); - break; - } - - d (printf ("Created %s from delta\n", thisoab)); - - if (seq == full->seq) - return thisoab; - } - - if (thisoab) { - g_unlink (thisoab); - g_free (thisoab); - } - full: -#endif /* WITH_MSPACK */ - d (printf ("Ewsgal: Downloading full gal \n")); - return ebb_ews_download_full_gal (bbews, full, cancellable, error); -} - -static void -ebb_ews_remove_old_gal_file (EBookCache *book_cache) -{ - gchar *filename; - - g_return_if_fail (E_IS_BOOK_CACHE (book_cache)); - - filename = e_cache_dup_key (E_CACHE (book_cache), "oab-filename", NULL); - - if (filename) - g_unlink (filename); - g_free (filename); -} - -struct _db_data { - GHashTable *uids; - GHashTable *sha1s; - gint unchanged; - gint changed; - gint added; - gint percent; - GSList *created_objects; - GSList *modified_objects; -}; - -static gboolean -ebb_ews_gal_filter_contact (goffset offset, - const gchar *sha1, - gpointer user_data, - GError **error) -{ - struct _db_data *data = (struct _db_data *) user_data; - gchar *uid; - - /* Is there an existing identical record, with the same SHA1? */ - uid = g_hash_table_lookup (data->sha1s, sha1); - if (!uid) - return TRUE; - - /* Remove it from the hash tables so it doesn't get deleted at the end. */ - g_hash_table_remove (data->sha1s, sha1); - g_hash_table_remove (data->uids, uid); - data->unchanged++; - - /* Don't bother to parse and process this record. */ - return FALSE; -} - -static void -ebb_ews_gal_store_contact (EContact *contact, - goffset offset, - const gchar *sha1, - guint percent, - gpointer user_data, - GError **error) -{ - struct _db_data *data = (struct _db_data *) user_data; - - if (contact) { - const gchar *uid = e_contact_get_const (contact, E_CONTACT_UID); - EBookMetaBackendInfo *nfo; - - ebews_populate_rev (contact, NULL); - e_vcard_util_set_x_attribute (E_VCARD (contact), X_EWS_GAL_SHA1, sha1); - - nfo = e_book_meta_backend_info_new (uid, e_contact_get_const (contact, E_CONTACT_REV), NULL, NULL); - nfo->object = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30); - - if (g_hash_table_remove (data->uids, uid)) { - data->changed++; - data->modified_objects = g_slist_prepend (data->modified_objects, nfo); - } else { - data->added++; - data->created_objects = g_slist_prepend (data->created_objects, nfo); - } - } - - if (data->percent != percent) { - data->percent = percent; - - d (printf ("GAL processing contacts, %d%% complete (%d added, %d changed, %d unchanged\n", - percent, data->added, data->changed, data->unchanged)); - } -} - -static gboolean -ebb_ews_gather_existing_uids_cb (EBookCache *book_cache, - const gchar *uid, - const gchar *revision, - const gchar *object, - const gchar *extra, - EOfflineState offline_state, - gpointer user_data) -{ - struct _db_data *data = user_data; - EVCard *vcard; - gchar *dup_uid, *dup_sha1 = NULL; - - g_return_val_if_fail (data != NULL, FALSE); - g_return_val_if_fail (data->uids != NULL, FALSE); - g_return_val_if_fail (data->sha1s != NULL, FALSE); - g_return_val_if_fail (object != NULL, FALSE); - - vcard = e_vcard_new_from_string (object); - if (vcard) { - dup_sha1 = e_vcard_util_dup_x_attribute (vcard, X_EWS_GAL_SHA1); - g_object_unref (vcard); - } - - dup_uid = g_strdup (uid); - if (!dup_sha1) - dup_sha1 = g_strdup (revision); - - g_hash_table_insert (data->uids, dup_uid, dup_sha1); - if (dup_sha1) - g_hash_table_insert (data->sha1s, dup_sha1, dup_uid); - - return TRUE; -} - -static gboolean -ebb_ews_check_gal_changes (EBookBackendEws *bbews, - EBookCache *book_cache, - const gchar *filename, - GSList **out_created_objects, /*EBookMetaBackendInfo * */ - GSList **out_modified_objects, /*EBookMetaBackendInfo * */ - GSList **out_removed_objects, /*EBookMetaBackendInfo * */ - GCancellable *cancellable, - GError **error) -{ - EwsOabDecoder *eod; - gboolean success = TRUE; - struct _db_data data; -#if d(1) + 0 - gint64 t1, t2; -#endif - GError *local_error = NULL; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (bbews), FALSE); - g_return_val_if_fail (E_IS_BOOK_CACHE (book_cache), FALSE); - g_return_val_if_fail (filename != NULL, FALSE); - g_return_val_if_fail (out_created_objects != NULL, FALSE); - g_return_val_if_fail (out_modified_objects != NULL, FALSE); - g_return_val_if_fail (out_removed_objects != NULL, FALSE); - - data.created_objects = NULL; - data.modified_objects = NULL; - data.unchanged = data.changed = data.added = 0; - data.percent = 0; - data.uids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - data.sha1s = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - d (t1 = g_get_monotonic_time ()); - - e_book_cache_search_with_callback (book_cache, NULL, ebb_ews_gather_existing_uids_cb, &data, cancellable, NULL); - - eod = ews_oab_decoder_new (filename, bbews->priv->attachments_dir, &local_error); - if (!local_error) { - GHashTableIter iter; - gpointer key; - - success = ews_oab_decoder_decode (eod, ebb_ews_gal_filter_contact, ebb_ews_gal_store_contact, &data, cancellable, &local_error); - - if (success) { - *out_created_objects = data.created_objects; - *out_modified_objects = data.modified_objects; - *out_removed_objects = NULL; - - g_hash_table_iter_init (&iter, data.uids); - while (g_hash_table_iter_next (&iter, &key, NULL)) { - const gchar *uid = key; - - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_book_meta_backend_info_new (uid, NULL, NULL, NULL)); - } - } else { - g_slist_free_full (data.created_objects, e_book_meta_backend_info_free); - g_slist_free_full (data.modified_objects, e_book_meta_backend_info_free); - } - } else { - success = FALSE; - } - - d (t2 = g_get_monotonic_time ()); - d (printf ("GAL update completed %ssuccessfully in %" G_GINT64_FORMAT " µs. Added: %d, Changed: %d, Unchanged %d, Removed: %d (%s)\n", - success ? "" : "un", (gint64) (t2 - t1), data.added, data.changed, data.unchanged, g_hash_table_size (data.uids), - local_error ? local_error->message : "no error")); - - g_hash_table_destroy (data.sha1s); - g_hash_table_destroy (data.uids); - - if (local_error) - g_propagate_error (error, local_error); - - return success; -} - -static void -ebb_ews_remove_original_vcard (EContact *contact) -{ - g_return_if_fail (E_IS_CONTACT (contact)); - - e_vcard_remove_attributes (E_VCARD (contact), NULL, X_EWS_ORIGINAL_VCARD); -} - -static void -ebb_ews_store_original_vcard (EContact *contact) -{ - EVCard *vcard; - EVCardAttribute *attr; - gchar *vcard_str; - - g_return_if_fail (E_IS_CONTACT (contact)); - - ebb_ews_remove_original_vcard (contact); - - vcard = E_VCARD (contact); - - vcard_str = e_vcard_to_string (vcard, EVC_FORMAT_VCARD_30); - - attr = e_vcard_attribute_new ("", X_EWS_ORIGINAL_VCARD); - e_vcard_attribute_add_value (attr, vcard_str); - e_vcard_add_attribute (vcard, attr); - - g_free (vcard_str); -} - -static const gchar * -ebb_ews_get_original_vcard (EContact *contact) -{ - EVCardAttribute *attr; - GList *values = NULL; - const gchar *vcard; - - g_return_val_if_fail (E_IS_CONTACT (contact), NULL); - - attr = e_vcard_get_attribute (E_VCARD (contact), X_EWS_ORIGINAL_VCARD); - if (!attr) - return NULL; - - values = e_vcard_attribute_get_values (attr); - if (!values) - return NULL; - - vcard = values->data; - - if (vcard && *vcard) - return vcard; - - return NULL; -} - -typedef struct { - /* For future use */ - gpointer restriction; - - gboolean is_autocompletion; - gchar *auto_comp_str; -} EBookBackendEwsSExpData; - -static ESExpResult * -ebb_ews_func_not (ESExp *f, - gint argc, - ESExpResult **argv, - gpointer data) -{ - ESExpResult *r; - - if (argc != 1 || argv[0]->type != ESEXP_RES_UNDEFINED) { - e_sexp_fatal_error (f, "parse error"); - return NULL; - } - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - - return r; -} - -static ESExpResult * -ebb_ews_func_and_or (ESExp *f, - gint argc, - ESExpResult **argv, - gpointer and) -{ - ESExpResult *r; - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - - return r; -} - -/* TODO implement */ -static ESExpResult * -ebb_ews_func_is (struct _ESExp *f, - gint argc, - struct _ESExpResult **argv, - gpointer data) -{ - ESExpResult *r; - - if (argc != 2 - && argv[0]->type != ESEXP_RES_STRING - && argv[1]->type != ESEXP_RES_STRING) { - e_sexp_fatal_error (f, "parse error"); - return NULL; - } - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - - return r; -} - -/* TODO implement */ -static ESExpResult * -ebb_ews_func_endswith (struct _ESExp *f, - gint argc, - struct _ESExpResult **argv, - gpointer data) -{ - ESExpResult *r; - - if (argc != 2 - && argv[0]->type != ESEXP_RES_STRING - && argv[1]->type != ESEXP_RES_STRING) { - e_sexp_fatal_error (f, "parse error"); - return NULL; - } - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - - return r; - -} - -/* TODO implement */ -static ESExpResult * -ebb_ews_func_contains (struct _ESExp *f, - gint argc, - struct _ESExpResult **argv, - gpointer data) -{ - ESExpResult *r; - EBookBackendEwsSExpData *sdata = data; - const gchar *propname, *str; - - if (argc != 2 - && argv[0]->type != ESEXP_RES_STRING - && argv[1]->type != ESEXP_RES_STRING) { - e_sexp_fatal_error (f, "parse error"); - return NULL; - } - - propname = argv[0]->value.string; - str = argv[1]->value.string; - - if (!strcmp (propname, "full_name") || !strcmp (propname, "email")) { - if (!sdata->auto_comp_str) { - sdata->auto_comp_str = g_strdup (str); - sdata->is_autocompletion = TRUE; - } - } - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - - return r; - -} - -/* We are just handling for autocompletion now. We need to support other fields after implementing - * Restrictions and find_items request */ -static ESExpResult * -ebb_ews_func_beginswith (struct _ESExp *f, - gint argc, - struct _ESExpResult **argv, - gpointer data) -{ - ESExpResult *r; - const gchar *propname, *str; - EBookBackendEwsSExpData *sdata = data; - - if (argc != 2 || - argv[0]->type != ESEXP_RES_STRING || - argv[1]->type != ESEXP_RES_STRING) { - e_sexp_fatal_error (f, "parse error"); - return NULL; - } - - propname = argv[0]->value.string; - str = argv[1]->value.string; - - if (!strcmp (propname, "full_name") || !strcmp (propname, "email")) { - if (!sdata->auto_comp_str) { - sdata->auto_comp_str = g_strdup (str); - sdata->is_autocompletion = TRUE; - } - } - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - return r; -} - -static struct { - const gchar *name; - ESExpFunc *func; - guint flags; -} symbols[] = { - { "and", ebb_ews_func_and_or, 0 }, - { "or", ebb_ews_func_and_or, 0}, - { "not", ebb_ews_func_not, 0 }, - { "contains", ebb_ews_func_contains, 0}, - { "is", ebb_ews_func_is, 0}, - { "beginswith", ebb_ews_func_beginswith, 0}, - { "endswith", ebb_ews_func_endswith, 0}, -}; - -/* FIXME build a complete filter from the query that can be used by find_items */ -static gboolean -ebb_ews_build_restriction (const gchar *query, - gchar **auto_comp_str) -{ - ESExpResult *r; - ESExp *sexp; - EBookBackendEwsSExpData *sdata; - gboolean autocompletion = FALSE; - gint i; - - *auto_comp_str = NULL; - - sexp = e_sexp_new (); - sdata = g_new0 (EBookBackendEwsSExpData, 1); - sdata->is_autocompletion = FALSE; - - for (i = 0; i < G_N_ELEMENTS (symbols); i++) { - e_sexp_add_function ( - sexp, 0, (gchar *) symbols[i].name, - symbols[i].func, - sdata); - } - - e_sexp_input_text (sexp, query, strlen (query)); - e_sexp_parse (sexp); - - r = e_sexp_eval (sexp); - if (r) { - autocompletion = sdata->is_autocompletion; - if (autocompletion) - *auto_comp_str = sdata->auto_comp_str; - else - g_free (sdata->auto_comp_str); - } - - e_sexp_result_free (sexp, r); - g_object_unref (sexp); - g_free (sdata); - - return autocompletion && *auto_comp_str; -} - -static gboolean -ebb_ews_update_cache_for_expression (EBookBackendEws *bbews, - const gchar *expr, - GCancellable *cancellable, - GError **error) -{ - EBookMetaBackend *meta_backend; - CamelEwsSettings *ews_settings; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (bbews), FALSE); - - /* Resolve names in GAL only for GAL */ - if (!bbews->priv->is_gal) - return TRUE; - - ews_settings = ebb_ews_get_collection_settings (bbews); - - if (camel_ews_settings_get_oab_offline (ews_settings)) - return TRUE; - - meta_backend = E_BOOK_META_BACKEND (bbews); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - /* Search only if not searching for everything */ - if (expr && *expr && g_ascii_strcasecmp (expr, "(contains \"x-evolution-any-field\" \"\")") != 0) { - gchar *restriction_expr = NULL; - GSList *mailboxes = NULL, *contacts = NULL, *found_infos = NULL; - gboolean includes_last_item = TRUE; - - success = ebb_ews_build_restriction (expr, &restriction_expr) && - e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, error) && - e_ews_connection_resolve_names_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, restriction_expr, - EWS_SEARCH_AD, NULL, TRUE, &mailboxes, &contacts, &includes_last_item, cancellable, error); - - if (success) { - GSList *mlink, *clink; - - for (mlink = mailboxes, clink = contacts; mlink; mlink = g_slist_next (mlink), clink = g_slist_next (clink)) { - EwsMailbox *mb = mlink->data; - EEwsItem *contact_item = clink ? clink->data : NULL; - EBookMetaBackendInfo *nfo; - EContact *contact = NULL; - gboolean is_public_dl = FALSE; - const gchar *str; - - if (g_strcmp0 (mb->mailbox_type, "PublicDL") == 0) { - contact = e_contact_new (); - - if (!ebb_ews_get_dl_info_gal (bbews, contact, mb, cancellable, NULL)) { - g_clear_object (&contact); - } else { - is_public_dl = TRUE; - } - } - - if (!contact && contact_item && e_ews_item_get_item_type (contact_item) == E_EWS_ITEM_TYPE_CONTACT) - contact = ebb_ews_item_to_contact (bbews, contact_item, cancellable, NULL); - - if (!contact) - contact = e_contact_new (); - - /* We do not get an id from the server, so just using email_id as uid for now */ - e_contact_set (contact, E_CONTACT_UID, mb->email); - - /* There is no ChangeKey provided either, thus make up some revision, - to have the contact always updated in the local cache. */ - ebews_populate_rev (contact, NULL); - - str = e_contact_get_const (contact, E_CONTACT_FULL_NAME); - if (!str || !*str) - e_contact_set (contact, E_CONTACT_FULL_NAME, mb->name); - - str = e_contact_get_const (contact, E_CONTACT_EMAIL_1); - if (!str || !*str || (!is_public_dl && contact_item && e_ews_item_get_item_type (contact_item) == E_EWS_ITEM_TYPE_CONTACT)) { - /* Cleanup first, then re-add only SMTP addresses */ - e_contact_set (contact, E_CONTACT_EMAIL_1, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_2, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_3, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_4, NULL); - e_contact_set (contact, E_CONTACT_EMAIL, NULL); - - ebews_populate_emails_ex (bbews, contact, contact_item, TRUE); - } - - str = e_contact_get_const (contact, E_CONTACT_EMAIL_1); - if (!str || !*str) { - e_contact_set (contact, E_CONTACT_EMAIL_1, mb->email); - } else if (!is_public_dl && mb->email && (!mb->routing_type || g_ascii_strcasecmp (mb->routing_type, "SMTP") == 0)) { - EContactField fields[3] = { E_CONTACT_EMAIL_2, E_CONTACT_EMAIL_3, E_CONTACT_EMAIL_4 }; - gchar *emails[3]; - gint ii, ff = 0; - - emails[0] = e_contact_get (contact, E_CONTACT_EMAIL_1); - emails[1] = e_contact_get (contact, E_CONTACT_EMAIL_2); - emails[2] = e_contact_get (contact, E_CONTACT_EMAIL_3); - - /* Make the mailbox email the primary email and skip duplicates */ - e_contact_set (contact, E_CONTACT_EMAIL_1, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_2, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_3, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_4, NULL); - e_contact_set (contact, E_CONTACT_EMAIL, NULL); - - e_contact_set (contact, E_CONTACT_EMAIL_1, mb->email); - - for (ii = 0; ii < 3; ii++) { - if (emails[ii] && g_ascii_strcasecmp (emails[ii], mb->email) != 0) { - e_contact_set (contact, fields[ff], emails[ii]); - ff++; - } - - g_free (emails[ii]); - } - } - - ebb_ews_store_original_vcard (contact); - - nfo = e_book_meta_backend_info_new (e_contact_get_const (contact, E_CONTACT_UID), - e_contact_get_const (contact, E_CONTACT_REV), NULL, NULL); - nfo->object = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30); - - found_infos = g_slist_prepend (found_infos, nfo); - - g_object_unref (contact); - } - } - - g_slist_free_full (mailboxes, (GDestroyNotify) e_ews_mailbox_free); - e_util_free_nullable_object_slist (contacts); - - if (success) { - GSList *created_objects = NULL, *modified_objects = NULL; - - success = e_book_meta_backend_split_changes_sync (meta_backend, found_infos, &created_objects, - &modified_objects, NULL, cancellable, error); - if (success) - success = e_book_meta_backend_process_changes_sync (meta_backend, created_objects, - modified_objects, NULL, cancellable, error); - - g_slist_free_full (created_objects, e_book_meta_backend_info_free); - g_slist_free_full (modified_objects, e_book_meta_backend_info_free); - } - - g_slist_free_full (found_infos, e_book_meta_backend_info_free); - g_free (restriction_expr); - } - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - ebb_ews_convert_error_to_edb_error (error); - ebb_ews_maybe_disconnect_sync (bbews, error, cancellable); - - return success; -} - -static GSList * /* the possibly modified 'in_items' */ -ebb_ews_verify_changes (EBookCache *book_cache, - GSList *in_items, /* EEwsItem * */ - GCancellable *cancellable) -{ - GSList *items = NULL, *link; - - g_return_val_if_fail (E_IS_BOOK_CACHE (book_cache), in_items); - - for (link = in_items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (!g_cancellable_is_cancelled (cancellable) && ( - type == E_EWS_ITEM_TYPE_CONTACT || - type == E_EWS_ITEM_TYPE_GROUP)) { - EContact *existing = NULL; - - if (e_book_cache_get_contact (book_cache, id->id, TRUE, &existing, cancellable, NULL) && - existing) { - gchar *change_key; - - change_key = e_vcard_util_dup_x_attribute (E_VCARD (existing), X_EWS_CHANGEKEY); - if (!change_key) - change_key = e_contact_get (existing, E_CONTACT_REV); - - if (g_strcmp0 (change_key, id->change_key) == 0) { - g_object_unref (item); - } else { - items = g_slist_prepend (items, item); - } - - g_free (change_key); - } else { - items = g_slist_prepend (items, item); - } - - g_clear_object (&existing); - } else { - items = g_slist_prepend (items, item); - } - } - - g_slist_free (in_items); - - return items; -} - -static GSList * /* EBookMetaBackendInfo */ -ebb_ews_contacts_to_infos (const GSList *contacts) /* EContact * */ -{ - GSList *nfos = NULL, *link; - - for (link = (GSList *) contacts; link; link = g_slist_next (link)) { - EContact *contact = link->data; - EBookMetaBackendInfo *nfo; - - if (!E_IS_CONTACT (contact)) - continue; - - ebb_ews_store_original_vcard (contact); - - nfo = e_book_meta_backend_info_new ( - e_contact_get_const (contact, E_CONTACT_UID), - e_contact_get_const (contact, E_CONTACT_REV), - NULL, NULL); - nfo->object = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30); - - nfos = g_slist_prepend (nfos, nfo); - } - - return nfos; -} - -static gboolean -ebb_ews_connect_sync (EBookMetaBackend *meta_backend, - const ENamedParameters *credentials, - ESourceAuthenticationResult *out_auth_result, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - CamelEwsSettings *ews_settings; - gchar *hosturl; - gboolean success = FALSE; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_auth_result != NULL, FALSE); - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (bbews->priv->cnc) { - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - *out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED; - - return TRUE; - } - - ews_settings = ebb_ews_get_collection_settings (bbews); - hosturl = camel_ews_settings_dup_hosturl (ews_settings); - - bbews->priv->cnc = e_ews_connection_new_for_backend (E_BACKEND (bbews), e_book_backend_get_registry (E_BOOK_BACKEND (bbews)), hosturl, ews_settings); - - e_binding_bind_property ( - bbews, "proxy-resolver", - bbews->priv->cnc, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - *out_auth_result = e_ews_connection_try_credentials_sync (bbews->priv->cnc, credentials, NULL, - out_certificate_pem, out_certificate_errors, cancellable, error); - - if (*out_auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) { - ESource *source = e_backend_get_source (E_BACKEND (bbews)); - ESourceEwsFolder *ews_folder; - gchar *gal_uid; - - ews_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER); - - g_free (bbews->priv->folder_id); - bbews->priv->folder_id = e_source_ews_folder_dup_id (ews_folder); - - gal_uid = camel_ews_settings_dup_gal_uid (ews_settings); - bbews->priv->is_gal = g_strcmp0 (e_source_get_uid (source), gal_uid) == 0; - - g_free (gal_uid); - - g_signal_connect_swapped (bbews->priv->cnc, "server-notification", - G_CALLBACK (ebb_ews_server_notification_cb), bbews); - - if (!bbews->priv->is_gal && - camel_ews_settings_get_listen_notifications (ews_settings) && - e_ews_connection_satisfies_server_version (bbews->priv->cnc, E_EWS_EXCHANGE_2010_SP1)) { - GSList *folders = NULL; - - folders = g_slist_prepend (folders, bbews->priv->folder_id); - - e_ews_connection_enable_notifications_sync (bbews->priv->cnc, - folders, &bbews->priv->subscription_key); - - g_slist_free (folders); - } - - e_book_backend_set_writable (E_BOOK_BACKEND (bbews), !bbews->priv->is_gal); - success = TRUE; - } else { - ebb_ews_convert_error_to_edb_error (error); - g_clear_object (&bbews->priv->cnc); - } - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - g_free (hosturl); - - return success; -} - -static gboolean -ebb_ews_disconnect_sync (EBookMetaBackend *meta_backend, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - ebb_ews_unset_connection (bbews); - - return TRUE; -} - -static gboolean -ebb_ews_get_changes_sync (EBookMetaBackend *meta_backend, - const gchar *last_sync_tag, - gboolean is_repeat, - gchar **out_new_sync_tag, - gboolean *out_repeat, - GSList **out_created_objects, - GSList **out_modified_objects, - GSList **out_removed_objects, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - EBookCache *book_cache; - gboolean success = TRUE; - GError *local_error = NULL; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_new_sync_tag != NULL, FALSE); - g_return_val_if_fail (out_repeat != NULL, FALSE); - g_return_val_if_fail (out_created_objects != NULL, FALSE); - g_return_val_if_fail (out_modified_objects != NULL, FALSE); - g_return_val_if_fail (out_removed_objects != NULL, FALSE); - - *out_created_objects = NULL; - *out_modified_objects = NULL; - *out_removed_objects = NULL; - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - book_cache = e_book_meta_backend_ref_cache (meta_backend); - g_return_val_if_fail (E_IS_BOOK_CACHE (book_cache), FALSE); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (bbews->priv->is_gal) { - CamelEwsSettings *ews_settings; - gchar *oab_url; - - ews_settings = ebb_ews_get_collection_settings (bbews); - oab_url = camel_ews_settings_dup_oaburl (ews_settings); - - if (oab_url && *oab_url && - camel_ews_settings_get_oab_offline (ews_settings)) { - EEwsConnection *oab_cnc; - GSList *full_l = NULL, *deltas = NULL, *link; - EwsOALDetails *full = NULL; - gchar *password, *etag = NULL; - gint sequence; - - sequence = e_cache_get_key_int (E_CACHE (book_cache), "gal-sequence", NULL); - if (sequence == -1) - sequence = 0; - - oab_cnc = e_ews_connection_new_for_backend (E_BACKEND (bbews), e_book_backend_get_registry (E_BOOK_BACKEND (bbews)), oab_url, ews_settings); - - e_binding_bind_property ( - bbews, "proxy-resolver", - oab_cnc, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - password = e_ews_connection_dup_password (bbews->priv->cnc); - e_ews_connection_set_password (oab_cnc, password); - e_util_safe_free_string (password); - - d (printf ("Ewsgal: Fetching oal full details file\n")); - if (!e_ews_connection_get_oal_detail_sync (oab_cnc, bbews->priv->folder_id, NULL, last_sync_tag, &full_l, &etag, cancellable, &local_error)) { - if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_NOT_MODIFIED)) { - g_clear_error (&local_error); - } else { - success = FALSE; - } - } - - if (success && full_l) { - guint32 delta_size = 0; - - for (link = full_l; link; link = g_slist_next (link)) { - EwsOALDetails *det = link->data; - - /* Throw away anything older than we already have */ - if (det->seq <= sequence) { - ews_oal_details_free (det); - } else if (!g_strcmp0 (det->type, "Full")) { - if (full) - ews_oal_details_free (full); - full = det; - } else if (sequence > 0 && !g_strcmp0 (det->type, "Diff")) { - delta_size += det->size; - deltas = g_slist_insert_sorted (deltas, det, det_sort_func); - } else { - ews_oal_details_free (det); - } - } - - g_slist_free (full_l); - full_l = NULL; - - /* If the deltas would be bigger, just download the new full file */ - if (full && delta_size > full->size) { - g_slist_free_full (deltas, (GDestroyNotify) ews_oal_details_free); - deltas = NULL; - } - } - - if (full) { - gchar *uncompressed_filename; - - uncompressed_filename = ebb_ews_download_gal (bbews, book_cache, full, deltas, sequence, cancellable, &local_error); - if (!uncompressed_filename) { - success = FALSE; - } else { - d (printf ("Ewsgal: Removing old gal\n")); - /* remove old_gal_file */ - ebb_ews_remove_old_gal_file (book_cache); - - d (printf ("Ewsgal: Check for changes in GAL\n")); - success = ebb_ews_check_gal_changes (bbews, book_cache, uncompressed_filename, - out_created_objects, out_modified_objects, out_removed_objects, cancellable, &local_error); - - if (success) { - if (e_cache_set_key (E_CACHE (book_cache), "oab-filename", uncompressed_filename, NULL)) { - /* Don't let it get deleted */ - g_free (uncompressed_filename); - uncompressed_filename = NULL; - } - - e_cache_set_key_int (E_CACHE (book_cache), "gal-sequence", full->seq, NULL); - - d (printf ("Ewsgal: sync successfully completed\n")); - } - - ews_oal_details_free (full); - } - - if (uncompressed_filename) { - /* preserve the oab file once we are able to decode the differential updates */ - g_unlink (uncompressed_filename); - g_free (uncompressed_filename); - } - } - - g_slist_free_full (full_l, (GDestroyNotify) ews_oal_details_free); - g_slist_free_full (deltas, (GDestroyNotify) ews_oal_details_free); - g_clear_object (&oab_cnc); - - if (success) - *out_new_sync_tag = etag; - else - g_free (etag); - - if (local_error) { - g_prefix_error (&local_error, "%s", _("Failed to update GAL:")); - g_propagate_error (error, local_error); - } - } - - g_free (oab_url); - } else { - GSList *items_created = NULL, *items_modified = NULL, *items_deleted = NULL, *link; - gboolean includes_last_item = TRUE; - - success = e_ews_connection_sync_folder_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, - last_sync_tag, bbews->priv->folder_id, "IdOnly", NULL, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - - if (!success && - g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) { - g_clear_error (&local_error); - - e_book_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL); - - success = e_ews_connection_sync_folder_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, - NULL, bbews->priv->folder_id, "IdOnly", NULL, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - } - - if (success) { - GSList *contacts_created = NULL, *contacts_modified = NULL; - - /* The sync state doesn't cover changes made by save_contact_sync(), - thus verify the changes, instead of re-donwloading the contacts again */ - items_created = ebb_ews_verify_changes (book_cache, items_created, cancellable); - items_modified = ebb_ews_verify_changes (book_cache, items_modified, cancellable); - - if (items_created) { - success = ebb_ews_fetch_items_sync (bbews, items_created, &contacts_created, cancellable, error); - if (success) - *out_created_objects = ebb_ews_contacts_to_infos (contacts_created); - } - - if (items_modified) { - success = ebb_ews_fetch_items_sync (bbews, items_modified, &contacts_modified, cancellable, error); - if (success) - *out_modified_objects = ebb_ews_contacts_to_infos (contacts_modified); - } - - for (link = items_deleted; link; link = g_slist_next (link)) { - const gchar *uid = link->data; - - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_book_meta_backend_info_new (uid, NULL, NULL, NULL)); - } - - g_slist_free_full (contacts_created, g_object_unref); - g_slist_free_full (contacts_modified, g_object_unref); - - *out_repeat = !includes_last_item; - } else if (local_error) { - g_propagate_error (error, local_error); - } - - g_slist_free_full (items_created, g_object_unref); - g_slist_free_full (items_modified, g_object_unref); - g_slist_free_full (items_deleted, g_free); - } - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - ebb_ews_convert_error_to_edb_error (error); - ebb_ews_maybe_disconnect_sync (bbews, error, cancellable); - - g_clear_object (&book_cache); - - return success; -} - -static gboolean -ebb_ews_load_contact_sync (EBookMetaBackend *meta_backend, - const gchar *uid, - const gchar *extra, - EContact **out_contact, - gchar **out_extra, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - GSList *ids, *items = NULL; - gboolean success; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (uid != NULL, FALSE); - g_return_val_if_fail (out_contact, FALSE); - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - ids = g_slist_prepend (NULL, (gpointer) uid); - - success = e_ews_connection_get_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, ids, "IdOnly", - NULL, FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &items, NULL, NULL, cancellable, error); - - g_slist_free (ids); - - if (!items) - success = FALSE; - - if (success) { - GSList *contacts = NULL; - - success = ebb_ews_fetch_items_sync (bbews, items, &contacts, cancellable, error); - if (success && contacts) { - *out_contact = g_object_ref (contacts->data); - - ebb_ews_store_original_vcard (*out_contact); - } - - g_slist_free_full (contacts, g_object_unref); - } - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - g_slist_free_full (items, g_object_unref); - - ebb_ews_convert_error_to_edb_error (error); - ebb_ews_maybe_disconnect_sync (bbews, error, cancellable); - - return success; -} - -static gboolean -ebb_ews_save_contact_sync (EBookMetaBackend *meta_backend, - gboolean overwrite_existing, - EConflictResolution conflict_resolution, - /* const */ EContact *contact, - const gchar *extra, - gchar **out_new_uid, - gchar **out_new_extra, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - EwsFolderId *fid; - GSList *items = NULL; - gboolean is_dl = FALSE; - gboolean success; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (E_IS_CONTACT (contact), FALSE); - g_return_val_if_fail (out_new_uid != NULL, FALSE); - g_return_val_if_fail (out_new_extra != NULL, FALSE); - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (e_contact_get (contact, E_CONTACT_IS_LIST)) { - if (!e_ews_connection_satisfies_server_version (bbews->priv->cnc, E_EWS_EXCHANGE_2010)) { - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - g_propagate_error (error, EDB_ERROR_EX (NOT_SUPPORTED, - _("Cannot save contact list, it’s only supported on EWS Server 2010 or later"))); - return FALSE; - } - - is_dl = TRUE; - } - - fid = e_ews_folder_id_new (bbews->priv->folder_id, NULL, FALSE); - if (overwrite_existing) { - EBookCache *book_cache; - EContact *old_contact = NULL; - - book_cache = e_book_meta_backend_ref_cache (meta_backend); - - success = e_book_cache_get_contact (book_cache, e_contact_get_const (contact, E_CONTACT_UID), FALSE, &old_contact, cancellable, error); - if (success) { - const gchar *original_vcard; - - /* This is for offline changes, where the EContact in the cache - is already modified, while the original, the one on the server, - is different. Using the cached EContact in this case generates - empty UpdateItem request and nothing is saved. */ - original_vcard = ebb_ews_get_original_vcard (old_contact); - if (original_vcard) { - EContact *tmp; - - tmp = e_contact_new_from_vcard (original_vcard); - if (tmp) { - g_object_unref (old_contact); - old_contact = tmp; - } - } - } - - if (success) { - ConvertData cd; - const gchar *conflict_res = "AlwaysOverwrite"; - - cd.bbews = bbews; - cd.cancellable = cancellable; - cd.error = error; - cd.old_contact = old_contact; - cd.new_contact = contact; - cd.change_key = NULL; - - if (conflict_resolution == E_CONFLICT_RESOLUTION_FAIL) - conflict_res = "NeverOverwrite"; - - success = e_ews_connection_update_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, - conflict_res, "SendAndSaveCopy", "SendToAllAndSaveCopy", - bbews->priv->folder_id, is_dl ? ebb_ews_convert_dl_to_updatexml_cb : ebb_ews_convert_contact_to_updatexml_cb, - &cd, &items, cancellable, error); - - g_free (cd.change_key); - } - - g_clear_object (&old_contact); - g_clear_object (&book_cache); - } else { - success = e_ews_connection_create_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, NULL, NULL, - fid, is_dl ? ebb_ews_convert_dl_to_xml_cb : ebb_ews_convert_contact_to_xml_cb, contact, - &items, cancellable, error); - } - - if (success && items) { - EEwsItem *item = items->data; - const EwsId *item_id; - - item_id = e_ews_item_get_id (item); - *out_new_uid = g_strdup (item_id->id); - - /* - * Support for ContactPhoto was added in Exchange 2010 SP2. - * We don't want to try to set/get this property if we are running in older version of the server. - */ - if (!overwrite_existing && - e_ews_connection_satisfies_server_version (bbews->priv->cnc, E_EWS_EXCHANGE_2010_SP2)) { - EContactPhoto *photo; - - /* - * The contact photo is basically an attachment with a special name. - * Considering this, we only can set the contact photo after create the contact itself. - * Then we are able to attach the picture to the "Contact Item". - */ - photo = e_contact_get (contact, E_CONTACT_PHOTO); - if (photo) { - GError *local_error = NULL; - - set_photo (bbews, item_id, contact, photo, NULL, cancellable, &local_error); - e_contact_photo_free (photo); - - if (local_error) { - g_propagate_error (error, local_error); - g_prefix_error (error, "%s", _("Failed to set contact photo:")); - success = FALSE; - } - } - } - } - - g_slist_free_full (items, g_object_unref); - e_ews_folder_id_free (fid); - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - ebb_ews_convert_error_to_edb_error (error); - ebb_ews_maybe_disconnect_sync (bbews, error, cancellable); - - return success; -} - -static gboolean -ebb_ews_remove_contact_sync (EBookMetaBackend *meta_backend, - EConflictResolution conflict_resolution, - const gchar *uid, - const gchar *extra, - const gchar *object, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - GSList *ids; - gboolean success; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - ids = g_slist_prepend (NULL, (gpointer) uid); - - success = e_ews_connection_delete_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, ids, EWS_HARD_DELETE, 0, FALSE, cancellable, error); - - g_slist_free (ids); - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - ebb_ews_convert_error_to_edb_error (error); - ebb_ews_maybe_disconnect_sync (bbews, error, cancellable); - - return success; -} - -static gboolean -ebb_ews_search_sync (EBookMetaBackend *meta_backend, - const gchar *expr, - gboolean meta_contact, - GSList **out_contacts, - GCancellable *cancellable, - GError **error) -{ - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - - /* Ignore errors, just try its best */ - ebb_ews_update_cache_for_expression (E_BOOK_BACKEND_EWS (meta_backend), expr, cancellable, NULL); - - /* Chain up to parent's method */ - return E_BOOK_META_BACKEND_CLASS (e_book_backend_ews_parent_class)->search_sync (meta_backend, expr, meta_contact, - out_contacts, cancellable, error); -} - -static gboolean -ebb_ews_search_uids_sync (EBookMetaBackend *meta_backend, - const gchar *expr, - GSList **out_uids, - GCancellable *cancellable, - GError **error) -{ - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - - /* Ignore errors, just try its best */ - ebb_ews_update_cache_for_expression (E_BOOK_BACKEND_EWS (meta_backend), expr, cancellable, NULL); - - /* Chain up to parent's method */ - return E_BOOK_META_BACKEND_CLASS (e_book_backend_ews_parent_class)->search_uids_sync (meta_backend, expr, - out_uids, cancellable, error); -} - -static gchar * -ebb_ews_get_backend_property (EBookBackend *book_backend, - const gchar *prop_name) -{ - EBookBackendEws *bbews; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (book_backend), NULL); - g_return_val_if_fail (prop_name != NULL, NULL); - - bbews = E_BOOK_BACKEND_EWS (book_backend); - - if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) { - CamelEwsSettings *ews_settings; - - ews_settings = ebb_ews_get_collection_settings (bbews); - - return g_strjoin (",", - "net", - "contact-lists", - e_book_meta_backend_get_capabilities (E_BOOK_META_BACKEND (book_backend)), - (!bbews->priv->is_gal || camel_ews_settings_get_oab_offline (ews_settings)) ? "do-initial-query" : NULL, - NULL); - } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) { - return g_strdup (e_contact_field_name (E_CONTACT_FILE_AS)); - } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) { - GString *buffer; - gchar *fields; - gint ii; - - buffer = g_string_sized_new (1024); - - for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) { - if (mappings[ii].element_type != ELEMENT_TYPE_SIMPLE) - continue; - - if (buffer->len > 0) - g_string_append_c (buffer, ','); - g_string_append (buffer, e_contact_field_name (mappings[ii].field_id)); - } - - for (ii = 0; ii < G_N_ELEMENTS (phone_field_map); ii++) { - if (buffer->len > 0) - g_string_append_c (buffer, ','); - g_string_append (buffer, e_contact_field_name (phone_field_map[ii].field)); - } - - fields = g_strjoin ( - ",", - buffer->str, - e_contact_field_name (E_CONTACT_FULL_NAME), - e_contact_field_name (E_CONTACT_NICKNAME), - e_contact_field_name (E_CONTACT_FAMILY_NAME), - e_contact_field_name (E_CONTACT_EMAIL_1), - e_contact_field_name (E_CONTACT_EMAIL_2), - e_contact_field_name (E_CONTACT_EMAIL_3), - e_contact_field_name (E_CONTACT_ADDRESS_WORK), - e_contact_field_name (E_CONTACT_ADDRESS_HOME), - e_contact_field_name (E_CONTACT_ADDRESS_OTHER), - e_contact_field_name (E_CONTACT_BIRTH_DATE), - e_contact_field_name (E_CONTACT_NOTE), - e_contact_field_name (E_CONTACT_PHOTO), - NULL); - - g_string_free (buffer, TRUE); - - return fields; - } - - /* Chain up to parent's method. */ - return E_BOOK_BACKEND_CLASS (e_book_backend_ews_parent_class)->get_backend_property (book_backend, prop_name); -} - -static gboolean -ebb_ews_get_destination_address (EBackend *backend, - gchar **host, - guint16 *port) -{ - CamelEwsSettings *ews_settings; - SoupURI *soup_uri; - gchar *host_url; - gboolean result = FALSE; - - g_return_val_if_fail (port != NULL, FALSE); - g_return_val_if_fail (host != NULL, FALSE); - - /* Sanity checking */ - if (!e_book_backend_get_registry (E_BOOK_BACKEND (backend)) || - !e_backend_get_source (backend)) - return FALSE; - - ews_settings = ebb_ews_get_collection_settings (E_BOOK_BACKEND_EWS (backend)); - g_return_val_if_fail (ews_settings != NULL, FALSE); - - host_url = camel_ews_settings_dup_hosturl (ews_settings); - g_return_val_if_fail (host_url != NULL, FALSE); - - soup_uri = soup_uri_new (host_url); - if (soup_uri) { - *host = g_strdup (soup_uri_get_host (soup_uri)); - *port = soup_uri_get_port (soup_uri); - - result = *host && **host; - if (!result) { - g_free (*host); - *host = NULL; - } - - soup_uri_free (soup_uri); - } - - g_free (host_url); - - return result; -} - -static void -e_book_backend_ews_constructed (GObject *object) -{ - EBookBackendEws *bbews = E_BOOK_BACKEND_EWS (object); - EBookCache *book_cache; - gchar *cache_dirname; - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_book_backend_ews_parent_class)->constructed (object); - - book_cache = e_book_meta_backend_ref_cache (E_BOOK_META_BACKEND (bbews)); - - cache_dirname = g_path_get_dirname (e_cache_get_filename (E_CACHE (book_cache))); - - g_clear_object (&book_cache); - - bbews->priv->attachments_dir = g_build_filename (cache_dirname, "attachments", NULL); - g_mkdir_with_parents (bbews->priv->attachments_dir, 0777); - - g_free (cache_dirname); -} - -static void -e_book_backend_ews_dispose (GObject *object) -{ - EBookBackendEws *bbews = E_BOOK_BACKEND_EWS (object); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - g_clear_object (&bbews->priv->cnc); - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_book_backend_ews_parent_class)->dispose (object); -} - -static void -e_book_backend_ews_finalize (GObject *object) -{ - EBookBackendEws *bbews = E_BOOK_BACKEND_EWS (object); - - g_free (bbews->priv->folder_id); - g_free (bbews->priv->attachments_dir); - - g_rec_mutex_clear (&bbews->priv->cnc_lock); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_book_backend_ews_parent_class)->finalize (object); -} - -static void -e_book_backend_ews_init (EBookBackendEws *bbews) -{ - bbews->priv = G_TYPE_INSTANCE_GET_PRIVATE (bbews, E_TYPE_BOOK_BACKEND_EWS, EBookBackendEwsPrivate); - - g_rec_mutex_init (&bbews->priv->cnc_lock); -} - -static void -e_book_backend_ews_class_init (EBookBackendEwsClass *klass) -{ - GObjectClass *object_class; - EBackendClass *backend_class; - EBookBackendClass *book_backend_class; - EBookMetaBackendClass *book_meta_backend_class; - - g_type_class_add_private (klass, sizeof (EBookBackendEwsPrivate)); - - book_meta_backend_class = E_BOOK_META_BACKEND_CLASS (klass); - book_meta_backend_class->backend_module_filename = "libebookbackendews.so"; - book_meta_backend_class->backend_factory_type_name = "EBookBackendEwsFactory"; - book_meta_backend_class->connect_sync = ebb_ews_connect_sync; - book_meta_backend_class->disconnect_sync = ebb_ews_disconnect_sync; - book_meta_backend_class->get_changes_sync = ebb_ews_get_changes_sync; - book_meta_backend_class->load_contact_sync = ebb_ews_load_contact_sync; - book_meta_backend_class->save_contact_sync = ebb_ews_save_contact_sync; - book_meta_backend_class->remove_contact_sync = ebb_ews_remove_contact_sync; - book_meta_backend_class->search_sync = ebb_ews_search_sync; - book_meta_backend_class->search_uids_sync = ebb_ews_search_uids_sync; - - book_backend_class = E_BOOK_BACKEND_CLASS (klass); - book_backend_class->get_backend_property = ebb_ews_get_backend_property; - - backend_class = E_BACKEND_CLASS (klass); - backend_class->get_destination_address = ebb_ews_get_destination_address; - - object_class = G_OBJECT_CLASS (klass); - object_class->constructed = e_book_backend_ews_constructed; - object_class->dispose = e_book_backend_ews_dispose; - object_class->finalize = e_book_backend_ews_finalize; -} diff --git a/src/addressbook/e-book-backend-ews.c.contact-country-forgotten b/src/addressbook/e-book-backend-ews.c.contact-country-forgotten deleted file mode 100644 index 9fc0554..0000000 --- a/src/addressbook/e-book-backend-ews.c.contact-country-forgotten +++ /dev/null @@ -1,3662 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ - -/* e-book-backend-ews.c - Ews contact backend. - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com) - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU 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. - * - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "server/e-ews-item-change.h" -#include "server/e-ews-message.h" -#include "server/e-ews-connection.h" -#include "server/e-ews-connection-utils.h" -#include "server/e-ews-item.h" -#include "server/e-ews-query-to-restriction.h" -#include "server/e-source-ews-folder.h" - -#include "e-book-backend-ews.h" -#include "ews-oab-decoder.h" -#include "ews-oab-decompress.h" - -#ifdef G_OS_WIN32 -#ifdef gmtime_r -#undef gmtime_r -#endif - -/* The gmtime() in Microsoft's C library is MT-safe */ -#define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0) -#endif - -#define d(x) - -#define EDB_ERROR(_code) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, NULL) -#define EDB_ERROR_EX(_code,_msg) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, _msg) - -#define X_EWS_ORIGINAL_VCARD "X-EWS-ORIGINAL-VCARD" -#define X_EWS_CHANGEKEY "X-EWS-CHANGEKEY" -#define X_EWS_GAL_SHA1 "X-EWS-GAL-SHA1" - -#define EWS_MAX_FETCH_COUNT 500 - -#define ELEMENT_TYPE_SIMPLE 0x01 /* simple string fields */ -#define ELEMENT_TYPE_COMPLEX 0x02 /* complex fields while require different get/set functions */ - -/* passing field uris for PhysicalAddress, PhoneNumbers causes error, so we - * use Default view to fetch them. Thus the summary props just have attachments - * and some additional properties that are not return with Default view */ -#define CONTACT_ITEM_PROPS "item:Attachments item:HasAttachments item:Body item:LastModifiedTime contacts:Manager contacts:Department contacts:SpouseName contacts:AssistantName contacts:BusinessHomePage contacts:Birthday" - -struct _EBookBackendEwsPrivate { - GRecMutex cnc_lock; - EEwsConnection *cnc; - - gchar *folder_id; - gboolean is_gal; - - guint subscription_key; - - /* used for storing attachments */ - gchar *attachments_dir; -}; - -G_DEFINE_TYPE (EBookBackendEws, e_book_backend_ews, E_TYPE_BOOK_META_BACKEND) - -static CamelEwsSettings * -ebb_ews_get_collection_settings (EBookBackendEws *bbews) -{ - ESource *source; - ESource *collection; - ESourceCamel *extension; - ESourceRegistry *registry; - CamelSettings *settings; - const gchar *extension_name; - - source = e_backend_get_source (E_BACKEND (bbews)); - registry = e_book_backend_get_registry (E_BOOK_BACKEND (bbews)); - - extension_name = e_source_camel_get_extension_name ("ews"); - e_source_camel_generate_subtype ("ews", CAMEL_TYPE_EWS_SETTINGS); - - /* The collection settings live in our parent data source. */ - collection = e_source_registry_find_extension (registry, source, extension_name); - g_return_val_if_fail (collection != NULL, NULL); - - extension = e_source_get_extension (collection, extension_name); - settings = e_source_camel_get_settings (extension); - - g_object_unref (collection); - - return CAMEL_EWS_SETTINGS (settings); -} - -static void -ebb_ews_convert_error_to_edb_error (GError **perror) -{ - GError *error = NULL; - - if (!perror || !*perror || (*perror)->domain == E_DATA_BOOK_ERROR) - return; - - if ((*perror)->domain == EWS_CONNECTION_ERROR) { - switch ((*perror)->code) { - case EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED: - error = EDB_ERROR_EX (AUTHENTICATION_FAILED, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_FOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_MANAGEDFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PARENTFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PUBLICFOLDERSERVERNOTFOUND: - error = EDB_ERROR_EX (NO_SUCH_BOOK, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_EVENTNOTFOUND: - case EWS_CONNECTION_ERROR_ITEMNOTFOUND: - error = EDB_ERROR_EX (CONTACT_NOT_FOUND, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_UNAVAILABLE: - g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND, (*perror)->message); - break; - } - - if (!error) - error = EDB_ERROR_EX (OTHER_ERROR, (*perror)->message); - } - - if (error) { - g_error_free (*perror); - *perror = error; - } -} - -static void -ebb_ews_maybe_disconnect_sync (EBookBackendEws *bbews, - GError **in_perror, - GCancellable *cancellable) -{ - g_return_if_fail (E_IS_BOOK_BACKEND_EWS (bbews)); - - if (in_perror && g_error_matches (*in_perror, E_DATA_BOOK_ERROR, E_DATA_BOOK_STATUS_AUTHENTICATION_FAILED)) { - e_book_meta_backend_disconnect_sync (E_BOOK_META_BACKEND (bbews), cancellable, NULL); - e_backend_schedule_credentials_required (E_BACKEND (bbews), E_SOURCE_CREDENTIALS_REASON_REJECTED, NULL, 0, NULL, NULL, G_STRFUNC); - } -} - -static const struct phone_field_mapping { - EContactField field; - const gchar *element; -} phone_field_map[] = { - {E_CONTACT_PHONE_ASSISTANT, "AssistantPhone"}, - {E_CONTACT_PHONE_BUSINESS_FAX, "BusinessFax"}, - {E_CONTACT_PHONE_BUSINESS, "BusinessPhone"}, - {E_CONTACT_PHONE_BUSINESS_2, "BusinessPhone2"}, - {E_CONTACT_PHONE_CAR, "CarPhone"}, - {E_CONTACT_PHONE_COMPANY, "CompanyMainPhone"}, - {E_CONTACT_PHONE_HOME_FAX, "HomeFax"}, - {E_CONTACT_PHONE_HOME, "HomePhone"}, - {E_CONTACT_PHONE_HOME_2, "HomePhone2"}, - {E_CONTACT_PHONE_ISDN, "Isdn"}, - {E_CONTACT_PHONE_MOBILE, "MobilePhone"}, - {E_CONTACT_PHONE_OTHER_FAX, "OtherFax"}, - {E_CONTACT_PHONE_OTHER, "OtherTelephone"}, - {E_CONTACT_PHONE_PAGER, "Pager"}, - {E_CONTACT_PHONE_PRIMARY, "PrimaryPhone"}, - {E_CONTACT_PHONE_RADIO, "RadioPhone"}, - {E_CONTACT_PHONE_TELEX, "Telex"}, - {E_CONTACT_PHONE_TTYTDD, "TtyTddPhone"} -}; - -static void -ebews_populate_rev (EContact *contact, - EEwsItem *item) -{ - struct tm stm; - time_t tt = 0; - gchar time_string[100] = { 0 }; - - g_return_if_fail (E_IS_CONTACT (contact)); - - if (item) { - g_return_if_fail (E_IS_EWS_ITEM (item)); - - tt = e_ews_item_get_last_modified_time (item); - } - - if (tt <= 0) - tt = time (NULL); - - gmtime_r (&tt, &stm); - strftime (time_string, 100, "%Y-%m-%dT%H:%M:%SZ", &stm); - - e_contact_set (contact, E_CONTACT_REV, time_string); -} - -static void -ebews_populate_uid (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - const EwsId *id; - - id = e_ews_item_get_id (item); - if (id) { - e_contact_set (contact, E_CONTACT_UID, id->id); - ebews_populate_rev (contact, item); - e_vcard_util_set_x_attribute (E_VCARD (contact), X_EWS_CHANGEKEY, id->change_key); - } -} - -static void -ebews_populate_full_name (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - const EwsCompleteName *cn; - - cn = e_ews_item_get_complete_name (item); - if (cn) - e_contact_set (contact, E_CONTACT_FULL_NAME, cn->full_name); -} - -static void -ebews_populate_nick_name (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - const EwsCompleteName *cn; - - cn = e_ews_item_get_complete_name (item); - if (cn) - e_contact_set (contact, E_CONTACT_NICKNAME, cn->nick_name); -} - -static void -ebews_populate_birth_date (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - time_t bdate; - GDate date; - EContactDate edate; - - bdate = e_ews_item_get_birthday (item); - - if (bdate) { - g_date_clear (&date, 1); - g_date_set_time_t (&date, bdate); - - edate.year = date.year; - edate.month = date.month; - edate.day = date.day; - - if (g_date_valid (&date)) - e_contact_set (contact, E_CONTACT_BIRTH_DATE, &edate); - } -} - -static void -ebews_populate_anniversary (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - time_t bdate; - GDate date; - EContactDate edate; - - bdate = e_ews_item_get_wedding_anniversary (item); - - if (bdate) { - g_date_clear (&date, 1); - g_date_set_time_t (&date, bdate); - - edate.year = date.year; - edate.month = date.month; - edate.day = date.day; - - if (g_date_valid (&date)) - e_contact_set (contact, E_CONTACT_ANNIVERSARY, &edate); - } -} - -static EContactPhoto * -get_photo (EBookBackendEws *bbews, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - EContactPhoto *photo = NULL; - EEwsAdditionalProps *add_props = NULL; - EEwsAttachmentInfo *info; - GSList *contact_item_ids = NULL, *new_items = NULL; - GSList *attachments = NULL, *attachments_ids = NULL; - const guchar *content; - const gchar *contact_photo_id; - const EwsId *id; - gsize len; - - id = e_ews_item_get_id (item); - if (!id) - return NULL; - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("item:Attachments"); - - contact_item_ids = g_slist_prepend (contact_item_ids, g_strdup (id->id)); - if (!e_ews_connection_get_items_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - contact_item_ids, - "IdOnly", - add_props, - FALSE, - NULL, - E_EWS_BODY_TYPE_TEXT, - &new_items, - NULL, - NULL, - cancellable, - error)) - goto exit; - - contact_photo_id = e_ews_item_get_contact_photo_id (new_items->data); - if (!contact_photo_id) - goto exit; - - attachments_ids = g_slist_prepend (attachments_ids, g_strdup (contact_photo_id)); - if (!e_ews_connection_get_attachments_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - NULL, - attachments_ids, - NULL, - FALSE, - &attachments, - NULL, - NULL, - cancellable, - error)) - goto exit; - - info = attachments->data; - content = (guchar *) e_ews_attachment_info_get_inlined_data (info, &len); - - photo = e_contact_photo_new (); - photo->type = E_CONTACT_PHOTO_TYPE_INLINED; - e_contact_photo_set_inlined (photo, content, len); - -exit: - e_ews_additional_props_free (add_props); - g_slist_free_full (contact_item_ids, g_free); - g_slist_free_full (new_items, g_object_unref); - g_slist_free_full (attachments_ids, g_free); - g_slist_free_full (attachments, (GDestroyNotify) e_ews_attachment_info_free); - - return photo; -} - -static void -ebews_populate_photo (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - EContactPhoto *photo; - - /* - * Support for ContactPhoto was added in Exchange 2010 SP2. - * We don't want to try to set/get this property if we are running in older version of the server. - */ - if (!e_ews_connection_satisfies_server_version (bbews->priv->cnc, E_EWS_EXCHANGE_2010_SP2)) - return; - - photo = get_photo (bbews, item, cancellable, error); - if (!photo) { - return; - } - - e_contact_set (contact, E_CONTACT_PHOTO, photo); - e_contact_photo_free (photo); -} - -static void -set_phone_number (EContact *contact, - EContactField field, - EEwsItem *item, - const gchar *item_field) -{ - const gchar *pn; - - pn = e_ews_item_get_phone_number (item, item_field); - if (pn && *pn) - e_contact_set (contact, field, pn); -} - -static void -ebews_populate_phone_numbers (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - gint i; - - for (i = 0; i < G_N_ELEMENTS (phone_field_map); i++) - set_phone_number (contact, phone_field_map[i].field, item, phone_field_map[i].element); -} - -static void -copy_ews_address_to_contact_address (EContactAddress *contact_addr, - const EwsAddress *address) -{ - contact_addr->address_format = NULL; - contact_addr->po = NULL; - contact_addr->street = g_strdup (address->street); - contact_addr->ext = NULL; - contact_addr->locality = g_strdup (address->city); - contact_addr->region = g_strdup (address->state); - contact_addr->code = g_strdup (address->postal_code); - contact_addr->country = g_strdup (address->country); -} - -static gboolean -ews_address_is_empty (const EwsAddress *address) -{ - if (!address) - return TRUE; - -#define is_empty_str(x) (!(x) || (!*(x))) - - return is_empty_str (address->street) && - is_empty_str (address->city) && - is_empty_str (address->state) && - is_empty_str (address->postal_code) && - is_empty_str (address->country); - -#undef is_empty_str -} - -static void -set_address (EContact *contact, - EContactField field, - EEwsItem *item, - const gchar *item_field) -{ - const EwsAddress *address; - - address = e_ews_item_get_physical_address (item, item_field); - if (address && !ews_address_is_empty (address)) { - EContactAddress *addr; - - addr = g_new0 (EContactAddress, 1); - copy_ews_address_to_contact_address (addr, address); - e_contact_set (contact, field, addr); - e_contact_address_free (addr); - } -} - -static void -ebews_populate_address (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - - set_address (contact, E_CONTACT_ADDRESS_WORK, item, "Business"); - set_address (contact, E_CONTACT_ADDRESS_HOME, item, "Home"); - set_address (contact, E_CONTACT_ADDRESS_OTHER, item, "Other"); -} - -static void -ebews_populate_ims (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - /* TODO : The fields returned by server does not match with the EContact fields - * for the IMS, handle it later */ -} - -static void -ebews_populate_notes (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - const gchar *notes = e_ews_item_get_notes (item); - if (!notes) - return; - - e_contact_set (contact, E_CONTACT_NOTE, notes); -} - -static void -set_email_address (EContact *contact, - EContactField field, - EEwsItem *item, - const gchar *item_field, - gboolean require_smtp_prefix) -{ - const gchar *ea; - - ea = e_ews_item_get_email_address (item, item_field); - if (ea && g_ascii_strncasecmp (ea, "SMTP:", 5) == 0) - ea = ea + 5; - else if (require_smtp_prefix) - ea = NULL; - - if (ea && *ea) - e_contact_set (contact, field, ea); -} - -static void -ebews_populate_emails_ex (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - gboolean require_smtp_prefix) -{ - set_email_address (contact, E_CONTACT_EMAIL_1, item, "EmailAddress1", require_smtp_prefix); - set_email_address (contact, E_CONTACT_EMAIL_2, item, "EmailAddress2", require_smtp_prefix); - set_email_address (contact, E_CONTACT_EMAIL_3, item, "EmailAddress3", require_smtp_prefix); -} - -static void -ebews_populate_emails (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **errror) -{ - ebews_populate_emails_ex (bbews, contact, item, FALSE); -} - -static void -ebews_set_item_id (ESoapMessage *message, - EContact *contact) -{ - -} - -static void -ebews_set_full_name (ESoapMessage *msg, - EContact *contact) -{ - EContactName *name; - - name = e_contact_get (contact, E_CONTACT_NAME); - if (!name) - return; - - if (name->given) - e_ews_message_write_string_parameter (msg, "GivenName", NULL, name->given); - - if (name->additional && *name->additional) - e_ews_message_write_string_parameter (msg, "MiddleName", NULL, name->additional); - - e_contact_name_free (name); -} - -/* TODO Set birth and anniversary dates */ -static void -ebews_set_birth_date (ESoapMessage *message, - EContact *contact) -{ - EContactDate *date; - gchar *birthday; - - date = e_contact_get (contact, E_CONTACT_BIRTH_DATE); - - if (!date) - return; - - birthday = g_strdup_printf ( - "%04d-%02d-%02dT00:00:00", - date->year, date->month, date->day); - - e_ews_message_write_string_parameter (message, "Birthday", NULL, birthday); - - g_free (birthday); - -} - -static void -ebews_set_anniversary (ESoapMessage *message, - EContact *contact) -{ - -} - -static void -ebews_set_photo (ESoapMessage *message, - EContact *contact) -{ - -} - -static gboolean -add_entry (ESoapMessage *msg, - EContact *contact, - EContactField field, - const gchar *entry_name, - const gchar *include_hdr) -{ - gchar *entry_val; - - entry_val = e_contact_get (contact, field); - - if (entry_val && *entry_val) { - if (include_hdr) - e_soap_message_start_element (msg, include_hdr, NULL, NULL); - - e_ews_message_write_string_parameter_with_attribute (msg, "Entry", NULL, entry_val, "Key", entry_name); - - g_free (entry_val); - return TRUE; - } - - g_free (entry_val); - return FALSE; -} - -static void -ebews_set_phone_numbers (ESoapMessage *msg, - EContact *contact) -{ - gint i; - const gchar *include_hdr = "PhoneNumbers"; - - for (i = 0; i < G_N_ELEMENTS (phone_field_map); i++) { - if (add_entry (msg, contact, phone_field_map[i].field, phone_field_map[i].element, include_hdr)) - include_hdr = NULL; - } - - if (!include_hdr) - e_soap_message_end_element (msg); -} - -static gboolean -add_physical_address (ESoapMessage *msg, - EContact *contact, - EContactField field, - const gchar *entry_name, - gboolean include_start_hdr) -{ - EContactAddress *contact_addr; - - contact_addr = e_contact_get (contact, field); - if (!contact_addr) - return FALSE; - - if (include_start_hdr) - e_soap_message_start_element (msg, "PhysicalAddresses", NULL, NULL); - - e_soap_message_start_element (msg, "Entry", NULL, NULL); - - e_soap_message_add_attribute (msg, "Key", entry_name, NULL, NULL); - e_ews_message_write_string_parameter (msg, "Street", NULL, contact_addr->street); - e_ews_message_write_string_parameter (msg, "City", NULL, contact_addr->locality); - e_ews_message_write_string_parameter (msg, "State", NULL, contact_addr->region); - e_ews_message_write_string_parameter (msg, "PostalCode", NULL, contact_addr->code); - - e_soap_message_end_element (msg); - e_contact_address_free (contact_addr); - - return TRUE; -} - -static void -ebews_set_address (ESoapMessage *msg, - EContact *contact) -{ - gboolean include_hdr = TRUE; - - if (add_physical_address (msg, contact, E_CONTACT_ADDRESS_WORK, "Business", include_hdr)) - include_hdr = FALSE; - if (add_physical_address (msg, contact, E_CONTACT_ADDRESS_HOME, "Home", include_hdr)) - include_hdr = FALSE; - if (add_physical_address (msg, contact, E_CONTACT_ADDRESS_OTHER, "Other", include_hdr)) - include_hdr = FALSE; - - if (!include_hdr) - e_soap_message_end_element (msg); -} - -static void -ebews_set_ims (ESoapMessage *message, - EContact *contact) -{ - -} - -static void -ebews_set_notes (ESoapMessage *msg, - EContact *contact) -{ - gchar *notes = e_contact_get (contact, E_CONTACT_NOTE); - if (!notes) - return; - - e_ews_message_write_string_parameter_with_attribute (msg, "Body", NULL, notes, "BodyType", "Text"); - - g_free (notes); -} - -static void -ebews_set_emails (ESoapMessage *msg, - EContact *contact) -{ - const gchar *include_hdr = "EmailAddresses"; - - if (add_entry (msg, contact, E_CONTACT_EMAIL_1, "EmailAddress1", include_hdr)) - include_hdr = NULL; - if (add_entry (msg, contact, E_CONTACT_EMAIL_2, "EmailAddress2", include_hdr)) - include_hdr = NULL; - if (add_entry (msg, contact, E_CONTACT_EMAIL_3, "EmailAddress3", include_hdr)) - include_hdr = NULL; - - if (!include_hdr) - e_soap_message_end_element (msg); -} - -static void -convert_contact_property_to_updatexml (ESoapMessage *msg, - const gchar *name, - const gchar *value, - const gchar *prefix, - const gchar *attr_name, - const gchar *attr_value) -{ - e_ews_message_start_set_item_field (msg, name, prefix, "Contact"); - e_ews_message_write_string_parameter_with_attribute (msg, name, NULL, value, attr_name, attr_value); - e_ews_message_end_set_item_field (msg); -} - -static void -convert_indexed_contact_property_to_updatexml (ESoapMessage *message, - const gchar *name, - const gchar *value, - const gchar *prefix, - const gchar *element_name, - const gchar *key) -{ - gboolean delete_field = FALSE; - - if (!value || g_strcmp0 (value, "") == 0) - delete_field = TRUE; - e_ews_message_start_set_indexed_item_field (message, name , prefix, "Contact", key, delete_field); - - if (!delete_field) - { - e_soap_message_start_element (message, element_name, NULL, NULL); - e_ews_message_write_string_parameter_with_attribute (message, "Entry", NULL, value, "Key", key); - e_soap_message_end_element (message); - } - e_ews_message_end_set_indexed_item_field (message, delete_field); -} - -static void -ebews_set_full_name_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - EContactName *name, *old_name; - - if (!message) - return; - - name = e_contact_get (new, E_CONTACT_NAME); - old_name = e_contact_get (old, E_CONTACT_NAME); - if (!old_name && !name) - return; - - if (!old_name) { - convert_contact_property_to_updatexml (message, "GivenName", name->given, "contacts", NULL, NULL); - convert_contact_property_to_updatexml (message, "MiddleName", name->additional, "contacts", NULL, NULL); - } else if (!name) { - convert_contact_property_to_updatexml (message, "GivenName", "", "contacts", NULL, NULL); - - convert_contact_property_to_updatexml (message, "MiddleName", "", "contacts", NULL, NULL); - } else { - if (g_strcmp0 (name->given, old_name->given) != 0) - convert_contact_property_to_updatexml (message, "GivenName", name->given, "contacts", NULL, NULL); - if (g_strcmp0 (name->additional, old_name->additional) != 0) - convert_contact_property_to_updatexml (message, "MiddleName", name->additional, "contacts", NULL, NULL); - } - - e_contact_name_free (name); - e_contact_name_free (old_name); - -} - -static void -ebews_set_birth_date_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - EContactDate *new_date, *old_date; - gchar *birthday; - - if (!message) - return; - - new_date = e_contact_get (new, E_CONTACT_BIRTH_DATE); - old_date = e_contact_get (old, E_CONTACT_BIRTH_DATE); - - if (!e_contact_date_equal (new_date, old_date)) { - birthday = g_strdup_printf ( - "%04d-%02d-%02dT00:00:00", - new_date->year, new_date->month, new_date->day); - - convert_contact_property_to_updatexml (message, "Birthday", birthday, "contacts", NULL, NULL); - g_free (birthday); - } - - e_contact_date_free (new_date); - e_contact_date_free (old_date); -} - -static void -ebews_set_anniversary_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - -} - -static void -set_photo (EBookBackendEws *bbews, - const EwsId *item_id, - EContact *contact, - EContactPhoto *photo, - gchar **new_change_key, - GCancellable *cancellable, - GError **error) -{ - EEwsAttachmentInfo *info; - EwsId *id = NULL; - GSList *files = NULL; - const guchar *data; - gsize len; - - if (!item_id) { - id = g_new0 (EwsId, 1); - id->id = e_contact_get (contact, E_CONTACT_UID); - id->change_key = e_vcard_util_dup_x_attribute (E_VCARD (contact), X_EWS_CHANGEKEY); - if (!id->change_key) - id->change_key = e_contact_get (contact, E_CONTACT_REV); - - item_id = id; - } - - data = e_contact_photo_get_inlined (photo, &len); - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_INLINED); - e_ews_attachment_info_set_inlined_data (info, data, len); - e_ews_attachment_info_set_mime_type (info, "image/jpeg"); - e_ews_attachment_info_set_filename (info, "ContactPicture.jpg"); - - files = g_slist_append (files, info); - - e_ews_connection_create_attachments_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - item_id, - files, - TRUE, - new_change_key, - NULL, - cancellable, - error); - - if (id) { - g_free (id->change_key); - g_free (id->id); - g_free (id); - } - - g_slist_free_full (files, (GDestroyNotify) e_ews_attachment_info_free); -} - -static gboolean -ebb_ews_photo_changed (EBookMetaBackend *meta_backend, - EContact *old_contact, - EContact *new_contact, - GCancellable *cancellable) -{ - EContact *old_contact_copy = NULL; - EContactPhoto *old_photo; - EContactPhoto *new_photo; - gboolean changed = FALSE; - - old_photo = e_contact_get (old_contact, E_CONTACT_PHOTO); - new_photo = e_contact_get (new_contact, E_CONTACT_PHOTO); - - if (!old_photo && new_photo) - changed = TRUE; - - if (old_photo && !new_photo) - changed = TRUE; - - /* old_photo comes from cache, thus it's always URI (to local file or elsewhere), - while the new_photo is to be saved, which is always inlined. */ - if (!changed && old_photo && new_photo && - old_photo->type == E_CONTACT_PHOTO_TYPE_URI && - new_photo->type == E_CONTACT_PHOTO_TYPE_INLINED) { - e_contact_photo_free (old_photo); - old_photo = NULL; - - old_contact_copy = e_contact_duplicate (old_contact); - - if (e_book_meta_backend_inline_local_photos_sync (meta_backend, old_contact_copy, cancellable, NULL)) - old_photo = e_contact_get (old_contact_copy, E_CONTACT_PHOTO); - } - - if (old_photo && new_photo && - old_photo->type == E_CONTACT_PHOTO_TYPE_INLINED && - new_photo->type == E_CONTACT_PHOTO_TYPE_INLINED) { - guchar *old_data; - guchar *new_data; - gsize old_length; - gsize new_length; - - old_data = old_photo->data.inlined.data; - new_data = new_photo->data.inlined.data; - - old_length = old_photo->data.inlined.length; - new_length = new_photo->data.inlined.length; - - changed = - (old_length != new_length) || - (memcmp (old_data, new_data, old_length) != 0); - } - - e_contact_photo_free (old_photo); - e_contact_photo_free (new_photo); - g_clear_object (&old_contact_copy); - - return changed; -} - -static void -ebews_set_photo_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - EContactPhoto *new_photo = NULL; - EEwsAdditionalProps *add_props = NULL; - GSList *contact_item_ids = NULL, *new_items = NULL, *attachments_ids = NULL; - gchar *id = NULL; - const gchar *contact_photo_id; - gchar *new_change_key = NULL; - - /* - * Support for ContactPhoto was added in Exchange 2010 SP2. - * We don't want to try to set/get this property if we are running in older version of the server. - */ - if (!e_ews_connection_satisfies_server_version (bbews->priv->cnc, E_EWS_EXCHANGE_2010_SP2)) { - return; - } - - if (message) { - /* Photo changes can be done only in pre-flight stage, - because it modifies ChangeKey */ - return; - } - - if (!ebb_ews_photo_changed (E_BOOK_META_BACKEND (bbews), old, new, cancellable)) - return; - - new_photo = e_contact_get (new, E_CONTACT_PHOTO); - id = e_contact_get (old, E_CONTACT_UID); - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("item:Attachments"); - - contact_item_ids = g_slist_append (contact_item_ids, id); - if (!e_ews_connection_get_items_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - contact_item_ids, - "IdOnly", - add_props, - FALSE, - NULL, - E_EWS_BODY_TYPE_TEXT, - &new_items, - NULL, - NULL, - cancellable, - error)) - goto exit; - - contact_photo_id = e_ews_item_get_contact_photo_id (new_items->data); - if (contact_photo_id) { - attachments_ids = g_slist_prepend (attachments_ids, g_strdup (contact_photo_id)); - if (!e_ews_connection_delete_attachments_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - attachments_ids, - &new_change_key, - cancellable, - error)) - goto exit; - } - - if (new_photo) { - EwsId *item_id = NULL; - - if (new_change_key) { - item_id = g_new0 (EwsId, 1); - item_id->id = e_contact_get (new, E_CONTACT_UID); - item_id->change_key = new_change_key; - - new_change_key = NULL; - } - - set_photo (bbews, item_id, new, new_photo, &new_change_key, cancellable, error); - - if (item_id) { - if (!new_change_key) { - new_change_key = item_id->change_key; - item_id->change_key = NULL; - } - - g_free (item_id->id); - g_free (item_id->change_key); - g_free (item_id); - } - } - - exit: - e_ews_additional_props_free (add_props); - e_contact_photo_free (new_photo); - g_slist_free_full (contact_item_ids, g_free); - g_slist_free_full (new_items, g_object_unref); - g_slist_free_full (attachments_ids, g_free); - - if (new_change_key && out_new_change_key) - *out_new_change_key = new_change_key; - else - g_free (new_change_key); -} - -static void -ebews_set_phone_number_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - gint i; - gchar *new_value, *old_value; - - if (!message) - return; - - for (i = 0; i < G_N_ELEMENTS (phone_field_map); i++) { - new_value = e_contact_get (new, phone_field_map[i].field); - old_value = e_contact_get (old, phone_field_map[i].field); - - if (g_strcmp0 (new_value, old_value) != 0) - convert_indexed_contact_property_to_updatexml (message, "PhoneNumber", new_value, "contacts", "PhoneNumbers", phone_field_map[i].element); - - g_free (new_value); - g_free (old_value); - } -} - -static void -convert_indexed_contact_property_to_updatexml_physical_address (ESoapMessage *message, - const gchar *name, - const gchar *uri_element, - const gchar *value, - const gchar *prefix, - const gchar *element_name, - const gchar *key) -{ - gchar * fielduri = NULL; - gboolean delete_field = FALSE; - - if (!value || g_strcmp0 (value, "") == 0) - delete_field = TRUE; - - fielduri = g_strconcat (name, ":", uri_element, NULL); - - e_ews_message_start_set_indexed_item_field (message, fielduri , prefix, "Contact", key, delete_field); - - if (!delete_field) - { - e_soap_message_start_element (message, element_name, NULL, NULL); - - e_soap_message_start_element (message, "Entry", NULL, NULL); - e_soap_message_add_attribute (message, "Key", key, NULL, NULL); - e_ews_message_write_string_parameter (message, uri_element, NULL, value); - e_soap_message_end_element (message); - - e_soap_message_end_element (message); - } - e_ews_message_end_set_indexed_item_field (message, delete_field); -} - -static void -compare_address (ESoapMessage *message, - EContact *new, - EContact *old, - EContactField field, - const gchar *key) -{ - EContactAddress *new_address, *old_address; - gboolean set = FALSE; - - new_address = e_contact_get (new, field); - old_address = e_contact_get (old, field); - - if (!new_address && !old_address) - return; - - if (!old_address && new_address) - set = TRUE; - - if (!new_address && old_address) - { - set = TRUE; - new_address = g_new0 (EContactAddress, 1); - } - - if (set || g_strcmp0 (new_address->street, old_address->street) != 0) - convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "Street", new_address->street, "contacts", "PhysicalAddresses", key); - if (set || g_strcmp0 (new_address->locality, old_address->locality) != 0) - convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "City", new_address->locality, "contacts", "PhysicalAddresses", key); - if (set || g_strcmp0 (new_address->region, old_address->region) != 0) - convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "State", new_address->region, "contacts", "PhysicalAddresses", key); - if (set || g_strcmp0 (new_address->code, old_address->code) != 0) - convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "PostalCode", new_address->code, "contacts", "PhysicalAddresses", key); - - e_contact_address_free (old_address); - e_contact_address_free (new_address); -} - -static void -ebews_set_address_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - if (!message) - return; - - compare_address (message, new, old, E_CONTACT_ADDRESS_WORK, "Business"); - compare_address (message, new, old, E_CONTACT_ADDRESS_HOME, "Home"); - compare_address (message, new, old, E_CONTACT_ADDRESS_OTHER, "Other"); -} - -static void -ebews_set_im_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - -} - -static void -ebews_set_notes_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - gchar *old_notes, *new_notes; - - if (!message) - return; - - old_notes = e_contact_get (old, E_CONTACT_NOTE); - new_notes = e_contact_get (new, E_CONTACT_NOTE); - - if (g_strcmp0 (old_notes, new_notes) != 0) { - convert_contact_property_to_updatexml ( - message, "Body", new_notes ? new_notes : "", "item", "BodyType", "Text"); - } - - g_free (old_notes); - g_free (new_notes); -} - -static void -ebews_set_email_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - gchar *new_value, *old_value; - - if (!message) - return; - - new_value = e_contact_get (new, E_CONTACT_EMAIL_1); - old_value = e_contact_get (old, E_CONTACT_EMAIL_1); - if (g_strcmp0 (new_value, old_value) != 0) - convert_indexed_contact_property_to_updatexml (message, "EmailAddress", new_value, "contacts", "EmailAddresses", "EmailAddress1"); - g_free (new_value); - g_free (old_value); - - new_value = e_contact_get (new, E_CONTACT_EMAIL_2); - old_value = e_contact_get (old, E_CONTACT_EMAIL_2); - if (g_strcmp0 (new_value, old_value) != 0) - convert_indexed_contact_property_to_updatexml (message, "EmailAddress", new_value, "contacts", "EmailAddresses", "EmailAddress2"); - g_free (new_value); - g_free (old_value); - - new_value = e_contact_get (new, E_CONTACT_EMAIL_3); - old_value = e_contact_get (old, E_CONTACT_EMAIL_3); - if (g_strcmp0 (new_value, old_value) != 0) - convert_indexed_contact_property_to_updatexml (message, "EmailAddress", new_value, "contacts", "EmailAddresses", "EmailAddress3"); - g_free (new_value); - g_free (old_value); -} - -static void -ebews_populate_givenname (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - const gchar *givenname; - - givenname = e_ews_item_get_givenname (item); - if (givenname && *givenname) - e_contact_set (contact, E_CONTACT_GIVEN_NAME, givenname); -} - -static void -ebews_set_givenname (ESoapMessage *message, - EContact *contact) -{ - /* Does nothing, the "GivenName" is filled by the "FullName" code */ -} - -static void -ebews_set_givenname_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - /* Does nothing, the "GivenName" is filled by the "FullName" code */ -} - -static const gchar * -ebews_get_fileas_or_display_name (EEwsItem *item) -{ - const gchar *value; - - value = e_ews_item_get_fileas (item); - if (!value || !*value) - value = e_ews_item_get_display_name (item); - - return value; -} - -static const struct field_element_mapping { - EContactField field_id; - gint element_type; - const gchar *element_name; - /* set function for simple string type values */ - const gchar * (*get_simple_prop_func) (EEwsItem *item); - void (*populate_contact_func)(EBookBackendEws *bbews, EContact *contact, EEwsItem *item, GCancellable *cancellable, GError **error); - void (*set_value_in_soap_message) (ESoapMessage *message, EContact *contact); - void (*set_changes) (EBookBackendEws *bbews, ESoapMessage *message, EContact *new, EContact *old, gchar **out_new_change_key, GCancellable *cancellable, GError **error); - -} mappings[] = { - /* The order should be maintained for create contacts to work */ - { E_CONTACT_NOTE, ELEMENT_TYPE_COMPLEX, "Notes", NULL, ebews_populate_notes, ebews_set_notes, ebews_set_notes_changes }, - { E_CONTACT_FILE_AS, ELEMENT_TYPE_SIMPLE, "FileAs", ebews_get_fileas_or_display_name }, - { E_CONTACT_FULL_NAME, ELEMENT_TYPE_COMPLEX, "CompleteName", NULL, ebews_populate_full_name, ebews_set_full_name, ebews_set_full_name_changes}, - { E_CONTACT_NICKNAME, ELEMENT_TYPE_SIMPLE, "Nickname", NULL, ebews_populate_nick_name}, - { E_CONTACT_ORG, ELEMENT_TYPE_SIMPLE, "CompanyName", e_ews_item_get_company_name}, - /* should take care of all email adresss fields */ - { E_CONTACT_EMAIL_1, ELEMENT_TYPE_COMPLEX, "EmailAddresses", NULL, ebews_populate_emails, ebews_set_emails, ebews_set_email_changes }, - /* should take care of home, work and other adresss fields */ - { E_CONTACT_ADDRESS_HOME, ELEMENT_TYPE_COMPLEX, "PhysicalAddresses", NULL, ebews_populate_address, ebews_set_address, ebews_set_address_changes }, - /* should take care of all phone number fields */ - { E_CONTACT_PHONE_PRIMARY, ELEMENT_TYPE_COMPLEX , "PhoneNumbers", NULL, ebews_populate_phone_numbers, ebews_set_phone_numbers, ebews_set_phone_number_changes}, - { E_CONTACT_ASSISTANT, ELEMENT_TYPE_SIMPLE, "AssistantName", e_ews_item_get_assistant_name}, - { E_CONTACT_BIRTH_DATE, ELEMENT_TYPE_COMPLEX, "Birthday", NULL, ebews_populate_birth_date, ebews_set_birth_date, ebews_set_birth_date_changes }, - { E_CONTACT_HOMEPAGE_URL, ELEMENT_TYPE_SIMPLE, "BusinessHomePage", e_ews_item_get_business_homepage}, - { E_CONTACT_ORG_UNIT, ELEMENT_TYPE_SIMPLE, "Department", e_ews_item_get_department}, - /* should take care of all im fields */ - { E_CONTACT_IM_AIM, ELEMENT_TYPE_COMPLEX, "ImAddresses", NULL, ebews_populate_ims, ebews_set_ims, ebews_set_im_changes }, - { E_CONTACT_TITLE, ELEMENT_TYPE_SIMPLE, "JobTitle", e_ews_item_get_job_title}, - { E_CONTACT_MANAGER, ELEMENT_TYPE_SIMPLE, "Manager", e_ews_item_get_manager}, - { E_CONTACT_OFFICE, ELEMENT_TYPE_SIMPLE, "OfficeLocation", e_ews_item_get_office_location}, - { E_CONTACT_SPOUSE, ELEMENT_TYPE_SIMPLE, "Profession", e_ews_item_get_profession}, - { E_CONTACT_SPOUSE, ELEMENT_TYPE_SIMPLE, "SpouseName", e_ews_item_get_spouse_name}, - { E_CONTACT_FAMILY_NAME, ELEMENT_TYPE_SIMPLE, "Surname", e_ews_item_get_surname}, - { E_CONTACT_GIVEN_NAME, ELEMENT_TYPE_COMPLEX, "GivenName", NULL, ebews_populate_givenname, ebews_set_givenname, ebews_set_givenname_changes}, - { E_CONTACT_BIRTH_DATE, ELEMENT_TYPE_COMPLEX, "WeddingAnniversary", NULL, ebews_populate_anniversary, ebews_set_anniversary, ebews_set_anniversary_changes }, - { E_CONTACT_PHOTO, ELEMENT_TYPE_COMPLEX, "Photo", NULL, ebews_populate_photo, ebews_set_photo, ebews_set_photo_changes }, - - /* Should take of uid and changekey (REV) */ - { E_CONTACT_UID, ELEMENT_TYPE_COMPLEX, "ItemId", NULL, ebews_populate_uid, ebews_set_item_id}, -}; - -static void -ebb_ews_write_dl_members (ESoapMessage *msg, - EContact *contact) -{ - GSList *emails, *l; - - e_soap_message_start_element (msg, "Members", NULL, NULL); - - emails = e_contact_get (contact, E_CONTACT_EMAIL); - for (l = emails; l; l = l->next) { - CamelInternetAddress *addr; - - if (l->data == NULL) - continue; - - addr = camel_internet_address_new (); - if (camel_address_decode (CAMEL_ADDRESS (addr), l->data) > 0) { - const gchar *name = NULL, *email = NULL; - - if (camel_internet_address_get (addr, 0, &name, &email) && email) { - e_soap_message_start_element (msg, "Member", NULL, NULL); - e_soap_message_start_element (msg, "Mailbox", NULL, NULL); - e_ews_message_write_string_parameter (msg, "Name", NULL, name ? name : email); - e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, email); - e_soap_message_end_element (msg); /* Mailbox */ - e_soap_message_end_element (msg); /* Member */ - } - } - g_object_unref (addr); - } - - g_slist_free_full (emails, g_free); - e_soap_message_end_element (msg); /* Members */ -} - -static void -ebb_ews_convert_dl_to_xml_cb (ESoapMessage *msg, - gpointer user_data) -{ - EContact *contact = user_data; - EVCardAttribute *attribute; - GList *values; - - /* Prepare DistributionList node in the SOAP message */ - e_soap_message_start_element (msg, "DistributionList", NULL, NULL); - - attribute = e_vcard_get_attribute (E_VCARD (contact), EVC_FN); - values = e_vcard_attribute_get_values (attribute); - e_ews_message_write_string_parameter (msg, "DisplayName", NULL, values->data); - - ebb_ews_write_dl_members (msg, contact); - - e_soap_message_end_element (msg); /* DistributionList */ -} - -static void -ebb_ews_convert_contact_to_xml_cb (ESoapMessage *msg, - gpointer user_data) -{ - EContact *contact = user_data; - gint i, element_type; - - /* Prepare Contact node in the SOAP message */ - e_soap_message_start_element (msg, "Contact", NULL, NULL); - - for (i = 0; i < G_N_ELEMENTS (mappings); i++) { - element_type = mappings[i].element_type; - - if (element_type == ELEMENT_TYPE_SIMPLE) { - gchar *val; - - /* skip uid while creating contacts */ - if (mappings[i].field_id == E_CONTACT_UID) - continue; - - val = e_contact_get (contact, mappings[i].field_id); - if (val && *val) - e_ews_message_write_string_parameter (msg, mappings[i].element_name, NULL, val); - g_free (val); - } else - mappings[i].set_value_in_soap_message (msg, contact); - } - - /* end of "Contact" */ - e_soap_message_end_element (msg); -} - -typedef struct _ConvertData { - EBookBackendEws *bbews; - GCancellable *cancellable; - GError **error; - - EContact *old_contact; - EContact *new_contact; - gchar *change_key; -} ConvertData; - -static void -ebb_ews_convert_dl_to_updatexml_cb (ESoapMessage *msg, - gpointer user_data) -{ - ConvertData *cd = user_data; - EContact *old_contact = cd->old_contact; - EContact *new_contact = cd->new_contact; - gchar *change_key = NULL; - - if (!cd->change_key) { - change_key = e_vcard_util_dup_x_attribute (E_VCARD (old_contact), X_EWS_CHANGEKEY); - if (!change_key) - change_key = e_contact_get (old_contact, E_CONTACT_REV); - } - - e_ews_message_start_item_change (msg, E_EWS_ITEMCHANGE_TYPE_ITEM, - e_contact_get_const (old_contact, E_CONTACT_UID), - cd->change_key ? cd->change_key : change_key, - 0); - e_ews_message_start_set_item_field (msg, "Members", "distributionlist", "DistributionList"); - ebb_ews_write_dl_members (msg, new_contact); - e_ews_message_end_set_item_field (msg); - e_ews_message_end_item_change (msg); - - g_free (change_key); -} - -static void -ebb_ews_convert_contact_to_updatexml_cb (ESoapMessage *msg, - gpointer user_data) -{ - ConvertData *cd = user_data; - EContact *old_contact = cd->old_contact; - EContact *new_contact = cd->new_contact; - gchar *value = NULL, *old_value = NULL; - gchar *change_key = NULL; - gint i, element_type; - - /* Pre-flight, to update the ChangeKey if needed */ - for (i = 0; i < G_N_ELEMENTS (mappings); i++) { - element_type = mappings[i].element_type; - - if (element_type == ELEMENT_TYPE_COMPLEX) { - gchar *new_change_key = NULL; - - if (mappings[i].field_id == E_CONTACT_UID) - continue; - - mappings[i].set_changes (cd->bbews, NULL, new_contact, old_contact, &new_change_key, cd->cancellable, cd->error); - - if (new_change_key) { - g_free (cd->change_key); - cd->change_key = new_change_key; - } - } - } - - if (!cd->change_key) { - change_key = e_vcard_util_dup_x_attribute (E_VCARD (old_contact), X_EWS_CHANGEKEY); - if (!change_key) - change_key = e_contact_get (old_contact, E_CONTACT_REV); - } - - e_ews_message_start_item_change (msg, E_EWS_ITEMCHANGE_TYPE_ITEM, - e_contact_get_const (old_contact, E_CONTACT_UID), - cd->change_key ? cd->change_key : change_key, - 0); - - /* Iterate for each field in contact */ - - for (i = 0; i < G_N_ELEMENTS (mappings); i++) { - element_type = mappings[i].element_type; - if (element_type == ELEMENT_TYPE_SIMPLE) { - value = e_contact_get (new_contact, mappings[i].field_id); - old_value = e_contact_get (old_contact, mappings[i].field_id); - if (g_strcmp0 (value, old_value) != 0) - convert_contact_property_to_updatexml (msg, mappings[i].element_name, value, "contacts", NULL, NULL); - if (value) - g_free (value); - if (old_value) - g_free (old_value); - } else if (element_type == ELEMENT_TYPE_COMPLEX) { - gchar *new_change_key = NULL; - - if (mappings[i].field_id == E_CONTACT_UID) - continue; - - mappings[i].set_changes (cd->bbews, msg, new_contact, old_contact, &new_change_key, cd->cancellable, cd->error); - - if (new_change_key) { - g_free (cd->change_key); - cd->change_key = new_change_key; - } - } - } - - e_ews_message_end_item_change (msg); - - g_free (change_key); -} - -static EContact * -ebb_ews_item_to_contact (EBookBackendEws *bbews, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - EContact *contact; - gint ii, element_type; - - contact = e_contact_new (); - - for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) { - element_type = mappings[ii].element_type; - - if (element_type == ELEMENT_TYPE_SIMPLE && !mappings[ii].populate_contact_func) { - const gchar *val = mappings[ii].get_simple_prop_func (item); - - if (val != NULL) - e_contact_set (contact, mappings[ii].field_id, val); - } else { - mappings[ii].populate_contact_func (bbews, contact, item, cancellable, error); - } - } - - return contact; -} - -static void -ebb_ews_items_to_contacts (EBookBackendEws *bbews, - const GSList *new_items, - GSList **contacts, - GCancellable *cancellable, - GError **error) -{ - GSList *link; - - for (link = (GSList *) new_items; link; link = g_slist_next (link)) { - EContact *contact; - EEwsItem *item = link->data; - EVCardAttribute *attr; - - if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - contact = ebb_ews_item_to_contact (bbews, item, cancellable, error); - - attr = e_vcard_attribute_new (NULL, "X-EWS-KIND"); - e_vcard_add_attribute_with_value (E_VCARD (contact), attr, "DT_MAILUSER"); - - *contacts = g_slist_prepend (*contacts, contact); - } -} - -static void -ebb_ews_mailbox_to_contact (EBookBackendEws *bbews, - EContact **contact, - GHashTable *values, - const EwsMailbox *mb) -{ - CamelInternetAddress *addr; - gchar *value; - - if (!mb->name && !mb->email) - return; - - addr = camel_internet_address_new (); - - camel_internet_address_add (addr, mb->name, mb->email ? mb->email : ""); - value = camel_address_encode (CAMEL_ADDRESS (addr)); - - if (value && (!values || g_hash_table_lookup (values, value) == NULL)) { - EVCardAttribute *attr; - - attr = e_vcard_attribute_new (NULL, EVC_EMAIL); - e_vcard_attribute_add_value (attr, value); - e_vcard_append_attribute (E_VCARD (*contact), attr); - - if (values) - g_hash_table_insert (values, g_strdup (value), GINT_TO_POINTER (1)); - } else { - g_free (value); - } - - g_object_unref (addr); -} - -static gboolean -ebb_ews_traverse_dl (EBookBackendEws *bbews, - EContact **contact, - GHashTable *items, - GHashTable *values, - EwsMailbox *mb, - GCancellable *cancellable, - GError **error) -{ - if (g_strcmp0 (mb->mailbox_type, "PrivateDL") == 0 || - g_strcmp0 (mb->mailbox_type, "PublicDL") == 0) { - GSList *members = NULL, *l; - gboolean includes_last; - gboolean ret = FALSE; - const gchar *ident; - GError *local_error = NULL; - - if (mb->item_id && mb->item_id->id) - ident = mb->item_id->id; - else if (mb->email) - ident = mb->email; - else - return FALSE; - - if (g_hash_table_lookup (items, ident) != NULL) - return TRUE; - - g_hash_table_insert (items, g_strdup (ident), GINT_TO_POINTER (1)); - - ret = e_ews_connection_expand_dl_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - mb, - &members, - &includes_last, - cancellable, - &local_error); - - if (!ret) { - if (g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NAMERESOLUTIONNORESULTS)) { - g_clear_error (&local_error); - if (mb->email && *mb->email) - ebb_ews_mailbox_to_contact (bbews, contact, values, mb); - - ret = TRUE; - members = NULL; - } else { - if (local_error) - g_propagate_error (error, local_error); - - return FALSE; - } - } - - for (l = members; l; l = l->next) { - ret = ebb_ews_traverse_dl (bbews, contact, items, values, l->data, cancellable, error); - if (!ret) - break; - } - - g_slist_free_full (members, (GDestroyNotify) e_ews_mailbox_free); - return ret; - } else { - ebb_ews_mailbox_to_contact (bbews, contact, values, mb); - - return TRUE; - } -} - -static EContact * -ebb_ews_get_dl_info (EBookBackendEws *bbews, - EEwsItem *item, - const EwsId *id, - const gchar *d_name, - GSList *members, - GCancellable *cancellable, - GError **error) -{ - GHashTable *items, *values; - GSList *l; - EContact *contact; - - contact = e_contact_new (); - e_contact_set (contact, E_CONTACT_UID, id->id); - e_vcard_util_set_x_attribute (E_VCARD (contact), X_EWS_CHANGEKEY, id->change_key); - ebews_populate_rev (contact, item); - - e_contact_set (contact, E_CONTACT_IS_LIST, GINT_TO_POINTER (TRUE)); - e_contact_set (contact, E_CONTACT_LIST_SHOW_ADDRESSES, GINT_TO_POINTER (TRUE)); - e_contact_set (contact, E_CONTACT_FULL_NAME, d_name); - - items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - for (l = members; l != NULL; l = l->next) { - if (!ebb_ews_traverse_dl (bbews, &contact, items, values, l->data, cancellable, error)) { - g_object_unref (contact); - contact = NULL; - goto exit; - } - } - - exit: - g_hash_table_destroy (items); - g_hash_table_destroy (values); - - return contact; -} - -static gboolean -ebb_ews_get_dl_info_gal (EBookBackendEws *bbews, - EContact *contact, - EwsMailbox *mb, - GCancellable *cancellable, - GError **error) -{ - GHashTable *items, *values; - gboolean success; - - items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - success = ebb_ews_traverse_dl (bbews, &contact, items, values, mb, cancellable, error); - - if (success) { - e_contact_set (contact, E_CONTACT_IS_LIST, GINT_TO_POINTER (TRUE)); - e_contact_set (contact, E_CONTACT_LIST_SHOW_ADDRESSES, GINT_TO_POINTER (TRUE)); - } - - g_hash_table_destroy (items); - g_hash_table_destroy (values); - - return success; -} - -static gboolean -ebb_ews_contacts_append_dl (EBookBackendEws *bbews, - EEwsItem *item, - const EwsId *id, - const gchar *d_name, - GSList *members, - GSList **contacts, - GCancellable *cancellable, - GError **error) -{ - EContact *contact; - EVCardAttribute *attr; - - contact = ebb_ews_get_dl_info (bbews, item, id, d_name, members, cancellable, error); - if (contact == NULL) - return FALSE; - - attr = e_vcard_attribute_new (NULL, "X-EWS-KIND"); - e_vcard_add_attribute_with_value (E_VCARD (contact), attr, "DT_DISTLIST"); - - *contacts = g_slist_prepend (*contacts, contact); - - return TRUE; -} - -static gboolean -ebb_ews_fetch_items_sync (EBookBackendEws *bbews, - const GSList *items, /* EEwsItem * */ - GSList **contacts, - GCancellable *cancellable, - GError **error) -{ - GSList *contact_item_ids = NULL, *dl_ids = NULL, *link; - GSList *new_items = NULL; - gboolean ret = FALSE; - - for (link = (GSList *) items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (type == E_EWS_ITEM_TYPE_CONTACT) - contact_item_ids = g_slist_prepend (contact_item_ids, g_strdup (id->id)); - else if (type == E_EWS_ITEM_TYPE_GROUP) { - /* store a list of EwsMailBox's in case of distribution lists */ - dl_ids = g_slist_prepend (dl_ids, g_strdup (id->id)); - } - } - - /* TODO fetch attachments */ - if (contact_item_ids) { - EEwsAdditionalProps *add_props; - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup (CONTACT_ITEM_PROPS); - - ret = e_ews_connection_get_items_sync ( - bbews->priv->cnc, EWS_PRIORITY_MEDIUM, - contact_item_ids, "Default", add_props, - FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &new_items, NULL, NULL, - cancellable, error); - - e_ews_additional_props_free (add_props); - - if (!ret) - goto cleanup; - } - - if (new_items) { - ebb_ews_items_to_contacts (bbews, new_items, contacts, cancellable, error); - - g_slist_free_full (new_items, g_object_unref); - new_items = NULL; - } - - /* Get the display names of the distribution lists */ - if (dl_ids) { - if (!e_ews_connection_get_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, dl_ids, "Default", NULL, - FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &new_items, NULL, NULL, cancellable, error)) - goto cleanup; - } - - for (link = new_items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const gchar *d_name; - const EwsId *id; - EwsMailbox *mb; - GSList *members = NULL; - gboolean includes_last; - GError *local_error = NULL; - - if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - id = e_ews_item_get_id (item); - mb = g_new0 (EwsMailbox, 1); - mb->item_id = (EwsId *) id; - - d_name = e_ews_item_get_subject (item); - if (e_ews_connection_expand_dl_sync ( - bbews->priv->cnc, EWS_PRIORITY_MEDIUM, mb, &members, - &includes_last, cancellable, &local_error)) { - ret = ebb_ews_contacts_append_dl (bbews, item, id, d_name, members, contacts, cancellable, error); - g_slist_free_full (members, (GDestroyNotify) e_ews_mailbox_free); - } else { - ret = g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NAMERESOLUTIONNORESULTS); - if (ret) { - g_clear_error (&local_error); - } else if (local_error) { - g_propagate_error (error, local_error); - } - } - - g_free (mb); - - if (!ret) - goto cleanup; - } - - cleanup: - g_slist_free_full (new_items, g_object_unref); - g_slist_free_full (contact_item_ids, g_free); - g_slist_free_full (dl_ids, g_free); - - return ret; -} - -static void -ebb_ews_server_notification_cb (EBookBackendEws *bbews, - const GSList *events, - EEwsConnection *cnc) -{ - GSList *link; - gboolean update_folder = FALSE; - - g_return_if_fail (E_IS_BOOK_BACKEND_EWS (bbews)); - - for (link = (GSList *) events; link && !update_folder; link = g_slist_next (link)) { - EEwsNotificationEvent *event = link->data; - - switch (event->type) { - case E_EWS_NOTIFICATION_EVENT_CREATED: - case E_EWS_NOTIFICATION_EVENT_DELETED: - case E_EWS_NOTIFICATION_EVENT_MODIFIED: - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (g_strcmp0 (event->folder_id, bbews->priv->folder_id) == 0) - update_folder = TRUE; - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - break; - case E_EWS_NOTIFICATION_EVENT_MOVED: - case E_EWS_NOTIFICATION_EVENT_COPIED: - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (g_strcmp0 (event->folder_id, bbews->priv->folder_id) == 0 || - g_strcmp0 (event->old_folder_id, bbews->priv->folder_id) == 0) - update_folder = TRUE; - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - break; - default: - return; - } - } - - if (update_folder) - e_book_meta_backend_schedule_refresh (E_BOOK_META_BACKEND (bbews)); -} - -static void -ebb_ews_unset_connection (EBookBackendEws *bbews) -{ - g_return_if_fail (E_IS_BOOK_BACKEND_EWS (bbews)); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (bbews->priv->cnc) { - e_ews_connection_set_disconnected_flag (bbews->priv->cnc, TRUE); - - g_signal_handlers_disconnect_by_func (bbews->priv->cnc, ebb_ews_server_notification_cb, bbews); - - if (bbews->priv->subscription_key != 0) { - e_ews_connection_disable_notifications_sync ( - bbews->priv->cnc, - bbews->priv->subscription_key); - bbews->priv->subscription_key = 0; - } - } - - g_clear_object (&bbews->priv->cnc); - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); -} - -static gint -det_sort_func (gconstpointer _a, - gconstpointer _b) -{ - const EwsOALDetails *a = _a, *b = _b; - - return a->seq - b->seq; -} - -static gchar * -ebb_ews_download_gal_file (EBookBackendEws *bbews, - EwsOALDetails *full, - GCancellable *cancellable, - GError **error) -{ - EEwsConnection *oab_cnc; - gchar *full_url, *oab_url; - gchar *download_path = NULL; - gchar *password; - CamelEwsSettings *ews_settings; - const gchar *cache_dir; - - ews_settings = ebb_ews_get_collection_settings (bbews); - - /* oab url with oab.xml removed from the suffix */ - oab_url = camel_ews_settings_dup_oaburl (ews_settings); - if (!oab_url || !*oab_url) { - g_free (oab_url); - return NULL; - } - - if (g_str_has_suffix (oab_url, "oab.xml")) - oab_url [strlen (oab_url) - 7] = '\0'; - - full_url = g_strconcat (oab_url, full->filename, NULL); - cache_dir = e_book_backend_get_cache_dir (E_BOOK_BACKEND (bbews)); - download_path = g_build_filename (cache_dir, full->filename, NULL); - - oab_cnc = e_ews_connection_new_for_backend (E_BACKEND (bbews), e_book_backend_get_registry (E_BOOK_BACKEND (bbews)), full_url, ews_settings); - - e_binding_bind_property ( - bbews, "proxy-resolver", - oab_cnc, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - password = e_ews_connection_dup_password (bbews->priv->cnc); - e_ews_connection_set_password (oab_cnc, password); - g_free (password); - - if (!e_ews_connection_download_oal_file_sync (oab_cnc, download_path, NULL, NULL, cancellable, error)) { - g_free (download_path); - download_path = NULL; - } else { - d (printf ("OAL file downloaded %s\n", download_path)); - } - - g_object_unref (oab_cnc); - g_free (oab_url); - g_free (full_url); - - return download_path; -} - -static gchar * -ebb_ews_download_full_gal (EBookBackendEws *bbews, - EwsOALDetails *full, - GCancellable *cancellable, - GError **error) -{ - ESource *source; - const gchar *cache_dir; - gchar *lzx_path, *oab_file, *oab_path; - - lzx_path = ebb_ews_download_gal_file (bbews, full, cancellable, error); - if (!lzx_path) - return NULL; - - source = e_backend_get_source (E_BACKEND (bbews)); - oab_file = g_strdup_printf ("%s-%d.oab", e_source_get_display_name (source), full->seq); - cache_dir = e_book_backend_get_cache_dir (E_BOOK_BACKEND (bbews)); - oab_path = g_build_filename (cache_dir, oab_file, NULL); - - if (!ews_oab_decompress_full (lzx_path, oab_path, error)) { - g_free (oab_path); - oab_path = NULL; - } else { - d (printf ("OAL file decompressed %s\n", oab_path)); - } - - if (lzx_path) { - g_unlink (lzx_path); - g_free (lzx_path); - } - - g_free (oab_file); - - return oab_path; -} - -static gchar * -ebb_ews_download_gal (EBookBackendEws *bbews, - EBookCache *book_cache, - EwsOALDetails *full, - GSList *deltas, - guint32 seq, - GCancellable *cancellable, - GError **error) -{ -#ifdef WITH_MSPACK - GSList *link; - gchar *thisoab; - - thisoab = e_cache_dup_key (E_CACHE (book_cache), "oab-filename", NULL); - if (!thisoab) - goto full; - - for (link = deltas; link; link = g_slist_next (link)) { - EwsOALDetails *det = link->data; - ESource *source; - gchar *oab_file, *lzx_path, *nextoab; - const gchar *cache_dir; - GError *local_error = NULL; - - seq++; - if (det->seq != seq) - break; - - lzx_path = ebb_ews_download_gal_file (bbews, det, cancellable, NULL); - if (!lzx_path) - break; - - source = e_backend_get_source (E_BACKEND (bbews)); - oab_file = g_strdup_printf ("%s-%d.oab", e_source_get_display_name (source), seq); - cache_dir = e_book_backend_get_cache_dir (E_BOOK_BACKEND (bbews)); - nextoab = g_build_filename (cache_dir, oab_file, NULL); - g_free (oab_file); - - ews_oab_decompress_patch (lzx_path, thisoab, nextoab, &local_error); - - /* Free the LZX file */ - g_unlink (lzx_path); - g_free (lzx_path); - - /* Free the previous OAB file */ - g_unlink (thisoab); - g_free (thisoab); - - thisoab = nextoab; - - /* For once we are *allowed* to use the error instead of having to - * check the return value of the function. It's our *own* error. */ - if (local_error) { - d (printf ("Failed to apply incremental patch: %s\n", local_error->message)); - g_error_free (local_error); - break; - } - - d (printf ("Created %s from delta\n", thisoab)); - - if (seq == full->seq) - return thisoab; - } - - if (thisoab) { - g_unlink (thisoab); - g_free (thisoab); - } - full: -#endif /* WITH_MSPACK */ - d (printf ("Ewsgal: Downloading full gal \n")); - return ebb_ews_download_full_gal (bbews, full, cancellable, error); -} - -static void -ebb_ews_remove_old_gal_file (EBookCache *book_cache) -{ - gchar *filename; - - g_return_if_fail (E_IS_BOOK_CACHE (book_cache)); - - filename = e_cache_dup_key (E_CACHE (book_cache), "oab-filename", NULL); - - if (filename) - g_unlink (filename); - g_free (filename); -} - -struct _db_data { - GHashTable *uids; - GHashTable *sha1s; - gint unchanged; - gint changed; - gint added; - gint percent; - GSList *created_objects; - GSList *modified_objects; -}; - -static gboolean -ebb_ews_gal_filter_contact (goffset offset, - const gchar *sha1, - gpointer user_data, - GError **error) -{ - struct _db_data *data = (struct _db_data *) user_data; - gchar *uid; - - /* Is there an existing identical record, with the same SHA1? */ - uid = g_hash_table_lookup (data->sha1s, sha1); - if (!uid) - return TRUE; - - /* Remove it from the hash tables so it doesn't get deleted at the end. */ - g_hash_table_remove (data->sha1s, sha1); - g_hash_table_remove (data->uids, uid); - data->unchanged++; - - /* Don't bother to parse and process this record. */ - return FALSE; -} - -static void -ebb_ews_gal_store_contact (EContact *contact, - goffset offset, - const gchar *sha1, - guint percent, - gpointer user_data, - GError **error) -{ - struct _db_data *data = (struct _db_data *) user_data; - - if (contact) { - const gchar *uid = e_contact_get_const (contact, E_CONTACT_UID); - EBookMetaBackendInfo *nfo; - - ebews_populate_rev (contact, NULL); - e_vcard_util_set_x_attribute (E_VCARD (contact), X_EWS_GAL_SHA1, sha1); - - nfo = e_book_meta_backend_info_new (uid, e_contact_get_const (contact, E_CONTACT_REV), NULL, NULL); - nfo->object = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30); - - if (g_hash_table_remove (data->uids, uid)) { - data->changed++; - data->modified_objects = g_slist_prepend (data->modified_objects, nfo); - } else { - data->added++; - data->created_objects = g_slist_prepend (data->created_objects, nfo); - } - } - - if (data->percent != percent) { - data->percent = percent; - - d (printf ("GAL processing contacts, %d%% complete (%d added, %d changed, %d unchanged\n", - percent, data->added, data->changed, data->unchanged)); - } -} - -static gboolean -ebb_ews_gather_existing_uids_cb (EBookCache *book_cache, - const gchar *uid, - const gchar *revision, - const gchar *object, - const gchar *extra, - EOfflineState offline_state, - gpointer user_data) -{ - struct _db_data *data = user_data; - EVCard *vcard; - gchar *dup_uid, *dup_sha1 = NULL; - - g_return_val_if_fail (data != NULL, FALSE); - g_return_val_if_fail (data->uids != NULL, FALSE); - g_return_val_if_fail (data->sha1s != NULL, FALSE); - g_return_val_if_fail (object != NULL, FALSE); - - vcard = e_vcard_new_from_string (object); - if (vcard) { - dup_sha1 = e_vcard_util_dup_x_attribute (vcard, X_EWS_GAL_SHA1); - g_object_unref (vcard); - } - - dup_uid = g_strdup (uid); - if (!dup_sha1) - dup_sha1 = g_strdup (revision); - - g_hash_table_insert (data->uids, dup_uid, dup_sha1); - if (dup_sha1) - g_hash_table_insert (data->sha1s, dup_sha1, dup_uid); - - return TRUE; -} - -static gboolean -ebb_ews_check_gal_changes (EBookBackendEws *bbews, - EBookCache *book_cache, - const gchar *filename, - GSList **out_created_objects, /*EBookMetaBackendInfo * */ - GSList **out_modified_objects, /*EBookMetaBackendInfo * */ - GSList **out_removed_objects, /*EBookMetaBackendInfo * */ - GCancellable *cancellable, - GError **error) -{ - EwsOabDecoder *eod; - gboolean success = TRUE; - struct _db_data data; -#if d(1) + 0 - gint64 t1, t2; -#endif - GError *local_error = NULL; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (bbews), FALSE); - g_return_val_if_fail (E_IS_BOOK_CACHE (book_cache), FALSE); - g_return_val_if_fail (filename != NULL, FALSE); - g_return_val_if_fail (out_created_objects != NULL, FALSE); - g_return_val_if_fail (out_modified_objects != NULL, FALSE); - g_return_val_if_fail (out_removed_objects != NULL, FALSE); - - data.created_objects = NULL; - data.modified_objects = NULL; - data.unchanged = data.changed = data.added = 0; - data.percent = 0; - data.uids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - data.sha1s = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - d (t1 = g_get_monotonic_time ()); - - e_book_cache_search_with_callback (book_cache, NULL, ebb_ews_gather_existing_uids_cb, &data, cancellable, NULL); - - eod = ews_oab_decoder_new (filename, bbews->priv->attachments_dir, &local_error); - if (!local_error) { - GHashTableIter iter; - gpointer key; - - success = ews_oab_decoder_decode (eod, ebb_ews_gal_filter_contact, ebb_ews_gal_store_contact, &data, cancellable, &local_error); - - if (success) { - *out_created_objects = data.created_objects; - *out_modified_objects = data.modified_objects; - *out_removed_objects = NULL; - - g_hash_table_iter_init (&iter, data.uids); - while (g_hash_table_iter_next (&iter, &key, NULL)) { - const gchar *uid = key; - - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_book_meta_backend_info_new (uid, NULL, NULL, NULL)); - } - } else { - g_slist_free_full (data.created_objects, e_book_meta_backend_info_free); - g_slist_free_full (data.modified_objects, e_book_meta_backend_info_free); - } - } else { - success = FALSE; - } - - d (t2 = g_get_monotonic_time ()); - d (printf ("GAL update completed %ssuccessfully in %" G_GINT64_FORMAT " µs. Added: %d, Changed: %d, Unchanged %d, Removed: %d (%s)\n", - success ? "" : "un", (gint64) (t2 - t1), data.added, data.changed, data.unchanged, g_hash_table_size (data.uids), - local_error ? local_error->message : "no error")); - - g_hash_table_destroy (data.sha1s); - g_hash_table_destroy (data.uids); - - if (local_error) - g_propagate_error (error, local_error); - - return success; -} - -static void -ebb_ews_remove_original_vcard (EContact *contact) -{ - g_return_if_fail (E_IS_CONTACT (contact)); - - e_vcard_remove_attributes (E_VCARD (contact), NULL, X_EWS_ORIGINAL_VCARD); -} - -static void -ebb_ews_store_original_vcard (EContact *contact) -{ - EVCard *vcard; - EVCardAttribute *attr; - gchar *vcard_str; - - g_return_if_fail (E_IS_CONTACT (contact)); - - ebb_ews_remove_original_vcard (contact); - - vcard = E_VCARD (contact); - - vcard_str = e_vcard_to_string (vcard, EVC_FORMAT_VCARD_30); - - attr = e_vcard_attribute_new ("", X_EWS_ORIGINAL_VCARD); - e_vcard_attribute_add_value (attr, vcard_str); - e_vcard_add_attribute (vcard, attr); - - g_free (vcard_str); -} - -static const gchar * -ebb_ews_get_original_vcard (EContact *contact) -{ - EVCardAttribute *attr; - GList *values = NULL; - const gchar *vcard; - - g_return_val_if_fail (E_IS_CONTACT (contact), NULL); - - attr = e_vcard_get_attribute (E_VCARD (contact), X_EWS_ORIGINAL_VCARD); - if (!attr) - return NULL; - - values = e_vcard_attribute_get_values (attr); - if (!values) - return NULL; - - vcard = values->data; - - if (vcard && *vcard) - return vcard; - - return NULL; -} - -typedef struct { - /* For future use */ - gpointer restriction; - - gboolean is_autocompletion; - gchar *auto_comp_str; -} EBookBackendEwsSExpData; - -static ESExpResult * -ebb_ews_func_not (ESExp *f, - gint argc, - ESExpResult **argv, - gpointer data) -{ - ESExpResult *r; - - if (argc != 1 || argv[0]->type != ESEXP_RES_UNDEFINED) { - e_sexp_fatal_error (f, "parse error"); - return NULL; - } - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - - return r; -} - -static ESExpResult * -ebb_ews_func_and_or (ESExp *f, - gint argc, - ESExpResult **argv, - gpointer and) -{ - ESExpResult *r; - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - - return r; -} - -/* TODO implement */ -static ESExpResult * -ebb_ews_func_is (struct _ESExp *f, - gint argc, - struct _ESExpResult **argv, - gpointer data) -{ - ESExpResult *r; - - if (argc != 2 - && argv[0]->type != ESEXP_RES_STRING - && argv[1]->type != ESEXP_RES_STRING) { - e_sexp_fatal_error (f, "parse error"); - return NULL; - } - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - - return r; -} - -/* TODO implement */ -static ESExpResult * -ebb_ews_func_endswith (struct _ESExp *f, - gint argc, - struct _ESExpResult **argv, - gpointer data) -{ - ESExpResult *r; - - if (argc != 2 - && argv[0]->type != ESEXP_RES_STRING - && argv[1]->type != ESEXP_RES_STRING) { - e_sexp_fatal_error (f, "parse error"); - return NULL; - } - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - - return r; - -} - -/* TODO implement */ -static ESExpResult * -ebb_ews_func_contains (struct _ESExp *f, - gint argc, - struct _ESExpResult **argv, - gpointer data) -{ - ESExpResult *r; - EBookBackendEwsSExpData *sdata = data; - const gchar *propname, *str; - - if (argc != 2 - && argv[0]->type != ESEXP_RES_STRING - && argv[1]->type != ESEXP_RES_STRING) { - e_sexp_fatal_error (f, "parse error"); - return NULL; - } - - propname = argv[0]->value.string; - str = argv[1]->value.string; - - if (!strcmp (propname, "full_name") || !strcmp (propname, "email")) { - if (!sdata->auto_comp_str) { - sdata->auto_comp_str = g_strdup (str); - sdata->is_autocompletion = TRUE; - } - } - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - - return r; - -} - -/* We are just handling for autocompletion now. We need to support other fields after implementing - * Restrictions and find_items request */ -static ESExpResult * -ebb_ews_func_beginswith (struct _ESExp *f, - gint argc, - struct _ESExpResult **argv, - gpointer data) -{ - ESExpResult *r; - const gchar *propname, *str; - EBookBackendEwsSExpData *sdata = data; - - if (argc != 2 || - argv[0]->type != ESEXP_RES_STRING || - argv[1]->type != ESEXP_RES_STRING) { - e_sexp_fatal_error (f, "parse error"); - return NULL; - } - - propname = argv[0]->value.string; - str = argv[1]->value.string; - - if (!strcmp (propname, "full_name") || !strcmp (propname, "email")) { - if (!sdata->auto_comp_str) { - sdata->auto_comp_str = g_strdup (str); - sdata->is_autocompletion = TRUE; - } - } - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - return r; -} - -static struct { - const gchar *name; - ESExpFunc *func; - guint flags; -} symbols[] = { - { "and", ebb_ews_func_and_or, 0 }, - { "or", ebb_ews_func_and_or, 0}, - { "not", ebb_ews_func_not, 0 }, - { "contains", ebb_ews_func_contains, 0}, - { "is", ebb_ews_func_is, 0}, - { "beginswith", ebb_ews_func_beginswith, 0}, - { "endswith", ebb_ews_func_endswith, 0}, -}; - -/* FIXME build a complete filter from the query that can be used by find_items */ -static gboolean -ebb_ews_build_restriction (const gchar *query, - gchar **auto_comp_str) -{ - ESExpResult *r; - ESExp *sexp; - EBookBackendEwsSExpData *sdata; - gboolean autocompletion = FALSE; - gint i; - - *auto_comp_str = NULL; - - sexp = e_sexp_new (); - sdata = g_new0 (EBookBackendEwsSExpData, 1); - sdata->is_autocompletion = FALSE; - - for (i = 0; i < G_N_ELEMENTS (symbols); i++) { - e_sexp_add_function ( - sexp, 0, (gchar *) symbols[i].name, - symbols[i].func, - sdata); - } - - e_sexp_input_text (sexp, query, strlen (query)); - e_sexp_parse (sexp); - - r = e_sexp_eval (sexp); - if (r) { - autocompletion = sdata->is_autocompletion; - if (autocompletion) - *auto_comp_str = sdata->auto_comp_str; - else - g_free (sdata->auto_comp_str); - } - - e_sexp_result_free (sexp, r); - g_object_unref (sexp); - g_free (sdata); - - return autocompletion && *auto_comp_str; -} - -static gboolean -ebb_ews_update_cache_for_expression (EBookBackendEws *bbews, - const gchar *expr, - GCancellable *cancellable, - GError **error) -{ - EBookMetaBackend *meta_backend; - CamelEwsSettings *ews_settings; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (bbews), FALSE); - - /* Resolve names in GAL only for GAL */ - if (!bbews->priv->is_gal) - return TRUE; - - ews_settings = ebb_ews_get_collection_settings (bbews); - - if (camel_ews_settings_get_oab_offline (ews_settings)) - return TRUE; - - meta_backend = E_BOOK_META_BACKEND (bbews); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - /* Search only if not searching for everything */ - if (expr && *expr && g_ascii_strcasecmp (expr, "(contains \"x-evolution-any-field\" \"\")") != 0) { - gchar *restriction_expr = NULL; - GSList *mailboxes = NULL, *contacts = NULL, *found_infos = NULL; - gboolean includes_last_item = TRUE; - - success = ebb_ews_build_restriction (expr, &restriction_expr) && - e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, error) && - e_ews_connection_resolve_names_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, restriction_expr, - EWS_SEARCH_AD, NULL, TRUE, &mailboxes, &contacts, &includes_last_item, cancellable, error); - - if (success) { - GSList *mlink, *clink; - - for (mlink = mailboxes, clink = contacts; mlink; mlink = g_slist_next (mlink), clink = g_slist_next (clink)) { - EwsMailbox *mb = mlink->data; - EEwsItem *contact_item = clink ? clink->data : NULL; - EBookMetaBackendInfo *nfo; - EContact *contact = NULL; - gboolean is_public_dl = FALSE; - const gchar *str; - - if (g_strcmp0 (mb->mailbox_type, "PublicDL") == 0) { - contact = e_contact_new (); - - if (!ebb_ews_get_dl_info_gal (bbews, contact, mb, cancellable, NULL)) { - g_clear_object (&contact); - } else { - is_public_dl = TRUE; - } - } - - if (!contact && contact_item && e_ews_item_get_item_type (contact_item) == E_EWS_ITEM_TYPE_CONTACT) - contact = ebb_ews_item_to_contact (bbews, contact_item, cancellable, NULL); - - if (!contact) - contact = e_contact_new (); - - /* We do not get an id from the server, so just using email_id as uid for now */ - e_contact_set (contact, E_CONTACT_UID, mb->email); - - /* There is no ChangeKey provided either, thus make up some revision, - to have the contact always updated in the local cache. */ - ebews_populate_rev (contact, NULL); - - str = e_contact_get_const (contact, E_CONTACT_FULL_NAME); - if (!str || !*str) - e_contact_set (contact, E_CONTACT_FULL_NAME, mb->name); - - str = e_contact_get_const (contact, E_CONTACT_EMAIL_1); - if (!str || !*str || (!is_public_dl && contact_item && e_ews_item_get_item_type (contact_item) == E_EWS_ITEM_TYPE_CONTACT)) { - /* Cleanup first, then re-add only SMTP addresses */ - e_contact_set (contact, E_CONTACT_EMAIL_1, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_2, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_3, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_4, NULL); - e_contact_set (contact, E_CONTACT_EMAIL, NULL); - - ebews_populate_emails_ex (bbews, contact, contact_item, TRUE); - } - - str = e_contact_get_const (contact, E_CONTACT_EMAIL_1); - if (!str || !*str) { - e_contact_set (contact, E_CONTACT_EMAIL_1, mb->email); - } else if (!is_public_dl && mb->email && (!mb->routing_type || g_ascii_strcasecmp (mb->routing_type, "SMTP") == 0)) { - EContactField fields[3] = { E_CONTACT_EMAIL_2, E_CONTACT_EMAIL_3, E_CONTACT_EMAIL_4 }; - gchar *emails[3]; - gint ii, ff = 0; - - emails[0] = e_contact_get (contact, E_CONTACT_EMAIL_1); - emails[1] = e_contact_get (contact, E_CONTACT_EMAIL_2); - emails[2] = e_contact_get (contact, E_CONTACT_EMAIL_3); - - /* Make the mailbox email the primary email and skip duplicates */ - e_contact_set (contact, E_CONTACT_EMAIL_1, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_2, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_3, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_4, NULL); - e_contact_set (contact, E_CONTACT_EMAIL, NULL); - - e_contact_set (contact, E_CONTACT_EMAIL_1, mb->email); - - for (ii = 0; ii < 3; ii++) { - if (emails[ii] && g_ascii_strcasecmp (emails[ii], mb->email) != 0) { - e_contact_set (contact, fields[ff], emails[ii]); - ff++; - } - - g_free (emails[ii]); - } - } - - ebb_ews_store_original_vcard (contact); - - nfo = e_book_meta_backend_info_new (e_contact_get_const (contact, E_CONTACT_UID), - e_contact_get_const (contact, E_CONTACT_REV), NULL, NULL); - nfo->object = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30); - - found_infos = g_slist_prepend (found_infos, nfo); - - g_object_unref (contact); - } - } - - g_slist_free_full (mailboxes, (GDestroyNotify) e_ews_mailbox_free); - e_util_free_nullable_object_slist (contacts); - - if (success) { - GSList *created_objects = NULL, *modified_objects = NULL; - - success = e_book_meta_backend_split_changes_sync (meta_backend, found_infos, &created_objects, - &modified_objects, NULL, cancellable, error); - if (success) - success = e_book_meta_backend_process_changes_sync (meta_backend, created_objects, - modified_objects, NULL, cancellable, error); - - g_slist_free_full (created_objects, e_book_meta_backend_info_free); - g_slist_free_full (modified_objects, e_book_meta_backend_info_free); - } - - g_slist_free_full (found_infos, e_book_meta_backend_info_free); - g_free (restriction_expr); - } - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - ebb_ews_convert_error_to_edb_error (error); - ebb_ews_maybe_disconnect_sync (bbews, error, cancellable); - - return success; -} - -static GSList * /* the possibly modified 'in_items' */ -ebb_ews_verify_changes (EBookCache *book_cache, - GSList *in_items, /* EEwsItem * */ - GCancellable *cancellable) -{ - GSList *items = NULL, *link; - - g_return_val_if_fail (E_IS_BOOK_CACHE (book_cache), in_items); - - for (link = in_items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (!g_cancellable_is_cancelled (cancellable) && ( - type == E_EWS_ITEM_TYPE_CONTACT || - type == E_EWS_ITEM_TYPE_GROUP)) { - EContact *existing = NULL; - - if (e_book_cache_get_contact (book_cache, id->id, TRUE, &existing, cancellable, NULL) && - existing) { - gchar *change_key; - - change_key = e_vcard_util_dup_x_attribute (E_VCARD (existing), X_EWS_CHANGEKEY); - if (!change_key) - change_key = e_contact_get (existing, E_CONTACT_REV); - - if (g_strcmp0 (change_key, id->change_key) == 0) { - g_object_unref (item); - } else { - items = g_slist_prepend (items, item); - } - - g_free (change_key); - } else { - items = g_slist_prepend (items, item); - } - - g_clear_object (&existing); - } else { - items = g_slist_prepend (items, item); - } - } - - g_slist_free (in_items); - - return items; -} - -static GSList * /* EBookMetaBackendInfo */ -ebb_ews_contacts_to_infos (const GSList *contacts) /* EContact * */ -{ - GSList *nfos = NULL, *link; - - for (link = (GSList *) contacts; link; link = g_slist_next (link)) { - EContact *contact = link->data; - EBookMetaBackendInfo *nfo; - - if (!E_IS_CONTACT (contact)) - continue; - - ebb_ews_store_original_vcard (contact); - - nfo = e_book_meta_backend_info_new ( - e_contact_get_const (contact, E_CONTACT_UID), - e_contact_get_const (contact, E_CONTACT_REV), - NULL, NULL); - nfo->object = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30); - - nfos = g_slist_prepend (nfos, nfo); - } - - return nfos; -} - -static gboolean -ebb_ews_connect_sync (EBookMetaBackend *meta_backend, - const ENamedParameters *credentials, - ESourceAuthenticationResult *out_auth_result, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - CamelEwsSettings *ews_settings; - gchar *hosturl; - gboolean success = FALSE; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_auth_result != NULL, FALSE); - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (bbews->priv->cnc) { - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - *out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED; - - return TRUE; - } - - ews_settings = ebb_ews_get_collection_settings (bbews); - hosturl = camel_ews_settings_dup_hosturl (ews_settings); - - bbews->priv->cnc = e_ews_connection_new_for_backend (E_BACKEND (bbews), e_book_backend_get_registry (E_BOOK_BACKEND (bbews)), hosturl, ews_settings); - - e_binding_bind_property ( - bbews, "proxy-resolver", - bbews->priv->cnc, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - *out_auth_result = e_ews_connection_try_credentials_sync (bbews->priv->cnc, credentials, cancellable, error); - - if (*out_auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) { - ESource *source = e_backend_get_source (E_BACKEND (bbews)); - ESourceEwsFolder *ews_folder; - gchar *gal_uid; - - ews_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER); - - g_free (bbews->priv->folder_id); - bbews->priv->folder_id = e_source_ews_folder_dup_id (ews_folder); - - gal_uid = camel_ews_settings_dup_gal_uid (ews_settings); - bbews->priv->is_gal = g_strcmp0 (e_source_get_uid (source), gal_uid) == 0; - - g_free (gal_uid); - - g_signal_connect_swapped (bbews->priv->cnc, "server-notification", - G_CALLBACK (ebb_ews_server_notification_cb), bbews); - - if (!bbews->priv->is_gal && - camel_ews_settings_get_listen_notifications (ews_settings) && - e_ews_connection_satisfies_server_version (bbews->priv->cnc, E_EWS_EXCHANGE_2010_SP1)) { - GSList *folders = NULL; - - folders = g_slist_prepend (folders, bbews->priv->folder_id); - - e_ews_connection_enable_notifications_sync (bbews->priv->cnc, - folders, &bbews->priv->subscription_key); - - g_slist_free (folders); - } - - e_book_backend_set_writable (E_BOOK_BACKEND (bbews), !bbews->priv->is_gal); - success = TRUE; - } else { - ebb_ews_convert_error_to_edb_error (error); - g_clear_object (&bbews->priv->cnc); - } - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - g_free (hosturl); - - return success; -} - -static gboolean -ebb_ews_disconnect_sync (EBookMetaBackend *meta_backend, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - ebb_ews_unset_connection (bbews); - - return TRUE; -} - -static gboolean -ebb_ews_get_changes_sync (EBookMetaBackend *meta_backend, - const gchar *last_sync_tag, - gboolean is_repeat, - gchar **out_new_sync_tag, - gboolean *out_repeat, - GSList **out_created_objects, - GSList **out_modified_objects, - GSList **out_removed_objects, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - EBookCache *book_cache; - gboolean success = TRUE; - GError *local_error = NULL; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_new_sync_tag != NULL, FALSE); - g_return_val_if_fail (out_repeat != NULL, FALSE); - g_return_val_if_fail (out_created_objects != NULL, FALSE); - g_return_val_if_fail (out_modified_objects != NULL, FALSE); - g_return_val_if_fail (out_removed_objects != NULL, FALSE); - - *out_created_objects = NULL; - *out_modified_objects = NULL; - *out_removed_objects = NULL; - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - book_cache = e_book_meta_backend_ref_cache (meta_backend); - g_return_val_if_fail (E_IS_BOOK_CACHE (book_cache), FALSE); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (bbews->priv->is_gal) { - CamelEwsSettings *ews_settings; - gchar *oab_url; - - ews_settings = ebb_ews_get_collection_settings (bbews); - oab_url = camel_ews_settings_dup_oaburl (ews_settings); - - if (oab_url && *oab_url && - camel_ews_settings_get_oab_offline (ews_settings)) { - EEwsConnection *oab_cnc; - GSList *full_l = NULL, *deltas = NULL, *link; - EwsOALDetails *full = NULL; - gchar *password, *etag = NULL; - gint sequence; - - sequence = e_cache_get_key_int (E_CACHE (book_cache), "gal-sequence", NULL); - if (sequence == -1) - sequence = 0; - - oab_cnc = e_ews_connection_new_for_backend (E_BACKEND (bbews), e_book_backend_get_registry (E_BOOK_BACKEND (bbews)), oab_url, ews_settings); - - e_binding_bind_property ( - bbews, "proxy-resolver", - oab_cnc, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - password = e_ews_connection_dup_password (bbews->priv->cnc); - e_ews_connection_set_password (oab_cnc, password); - e_util_safe_free_string (password); - - d (printf ("Ewsgal: Fetching oal full details file\n")); - if (!e_ews_connection_get_oal_detail_sync (oab_cnc, bbews->priv->folder_id, NULL, last_sync_tag, &full_l, &etag, cancellable, &local_error)) { - if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_NOT_MODIFIED)) { - g_clear_error (&local_error); - } else { - success = FALSE; - } - } - - if (success && full_l) { - guint32 delta_size = 0; - - for (link = full_l; link; link = g_slist_next (link)) { - EwsOALDetails *det = link->data; - - /* Throw away anything older than we already have */ - if (det->seq <= sequence) { - ews_oal_details_free (det); - } else if (!g_strcmp0 (det->type, "Full")) { - if (full) - ews_oal_details_free (full); - full = det; - } else if (sequence > 0 && !g_strcmp0 (det->type, "Diff")) { - delta_size += det->size; - deltas = g_slist_insert_sorted (deltas, det, det_sort_func); - } else { - ews_oal_details_free (det); - } - } - - g_slist_free (full_l); - full_l = NULL; - - /* If the deltas would be bigger, just download the new full file */ - if (full && delta_size > full->size) { - g_slist_free_full (deltas, (GDestroyNotify) ews_oal_details_free); - deltas = NULL; - } - } - - if (full) { - gchar *uncompressed_filename; - - uncompressed_filename = ebb_ews_download_gal (bbews, book_cache, full, deltas, sequence, cancellable, &local_error); - if (!uncompressed_filename) { - success = FALSE; - } else { - d (printf ("Ewsgal: Removing old gal\n")); - /* remove old_gal_file */ - ebb_ews_remove_old_gal_file (book_cache); - - d (printf ("Ewsgal: Check for changes in GAL\n")); - success = ebb_ews_check_gal_changes (bbews, book_cache, uncompressed_filename, - out_created_objects, out_modified_objects, out_removed_objects, cancellable, &local_error); - - if (success) { - if (e_cache_set_key (E_CACHE (book_cache), "oab-filename", uncompressed_filename, NULL)) { - /* Don't let it get deleted */ - g_free (uncompressed_filename); - uncompressed_filename = NULL; - } - - e_cache_set_key_int (E_CACHE (book_cache), "gal-sequence", full->seq, NULL); - - d (printf ("Ewsgal: sync successfully completed\n")); - } - - ews_oal_details_free (full); - } - - if (uncompressed_filename) { - /* preserve the oab file once we are able to decode the differential updates */ - g_unlink (uncompressed_filename); - g_free (uncompressed_filename); - } - } - - g_slist_free_full (full_l, (GDestroyNotify) ews_oal_details_free); - g_slist_free_full (deltas, (GDestroyNotify) ews_oal_details_free); - g_clear_object (&oab_cnc); - - if (success) - *out_new_sync_tag = etag; - else - g_free (etag); - - if (local_error) { - g_prefix_error (&local_error, "%s", _("Failed to update GAL:")); - g_propagate_error (error, local_error); - } - } - - g_free (oab_url); - } else { - GSList *items_created = NULL, *items_modified = NULL, *items_deleted = NULL, *link; - gboolean includes_last_item = TRUE; - - success = e_ews_connection_sync_folder_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, - last_sync_tag, bbews->priv->folder_id, "IdOnly", NULL, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - - if (!success && - g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) { - g_clear_error (&local_error); - - e_book_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL); - - success = e_ews_connection_sync_folder_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, - NULL, bbews->priv->folder_id, "IdOnly", NULL, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - } - - if (success) { - GSList *contacts_created = NULL, *contacts_modified = NULL; - - /* The sync state doesn't cover changes made by save_contact_sync(), - thus verify the changes, instead of re-donwloading the contacts again */ - items_created = ebb_ews_verify_changes (book_cache, items_created, cancellable); - items_modified = ebb_ews_verify_changes (book_cache, items_modified, cancellable); - - if (items_created) { - success = ebb_ews_fetch_items_sync (bbews, items_created, &contacts_created, cancellable, error); - if (success) - *out_created_objects = ebb_ews_contacts_to_infos (contacts_created); - } - - if (items_modified) { - success = ebb_ews_fetch_items_sync (bbews, items_modified, &contacts_modified, cancellable, error); - if (success) - *out_modified_objects = ebb_ews_contacts_to_infos (contacts_modified); - } - - for (link = items_deleted; link; link = g_slist_next (link)) { - const gchar *uid = link->data; - - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_book_meta_backend_info_new (uid, NULL, NULL, NULL)); - } - - g_slist_free_full (contacts_created, g_object_unref); - g_slist_free_full (contacts_modified, g_object_unref); - - *out_repeat = !includes_last_item; - } else if (local_error) { - g_propagate_error (error, local_error); - } - - g_slist_free_full (items_created, g_object_unref); - g_slist_free_full (items_modified, g_object_unref); - g_slist_free_full (items_deleted, g_free); - } - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - ebb_ews_convert_error_to_edb_error (error); - ebb_ews_maybe_disconnect_sync (bbews, error, cancellable); - - g_clear_object (&book_cache); - - return success; -} - -static gboolean -ebb_ews_load_contact_sync (EBookMetaBackend *meta_backend, - const gchar *uid, - const gchar *extra, - EContact **out_contact, - gchar **out_extra, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - GSList *ids, *items = NULL; - gboolean success; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (uid != NULL, FALSE); - g_return_val_if_fail (out_contact, FALSE); - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - ids = g_slist_prepend (NULL, (gpointer) uid); - - success = e_ews_connection_get_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, ids, "IdOnly", - NULL, FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &items, NULL, NULL, cancellable, error); - - g_slist_free (ids); - - if (!items) - success = FALSE; - - if (success) { - GSList *contacts = NULL; - - success = ebb_ews_fetch_items_sync (bbews, items, &contacts, cancellable, error); - if (success && contacts) { - *out_contact = g_object_ref (contacts->data); - - ebb_ews_store_original_vcard (*out_contact); - } - - g_slist_free_full (contacts, g_object_unref); - } - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - g_slist_free_full (items, g_object_unref); - - ebb_ews_convert_error_to_edb_error (error); - ebb_ews_maybe_disconnect_sync (bbews, error, cancellable); - - return success; -} - -static gboolean -ebb_ews_save_contact_sync (EBookMetaBackend *meta_backend, - gboolean overwrite_existing, - EConflictResolution conflict_resolution, - /* const */ EContact *contact, - const gchar *extra, - gchar **out_new_uid, - gchar **out_new_extra, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - EwsFolderId *fid; - GSList *items = NULL; - gboolean is_dl = FALSE; - gboolean success; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (E_IS_CONTACT (contact), FALSE); - g_return_val_if_fail (out_new_uid != NULL, FALSE); - g_return_val_if_fail (out_new_extra != NULL, FALSE); - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (e_contact_get (contact, E_CONTACT_IS_LIST)) { - if (!e_ews_connection_satisfies_server_version (bbews->priv->cnc, E_EWS_EXCHANGE_2010)) { - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - g_propagate_error (error, EDB_ERROR_EX (NOT_SUPPORTED, - _("Cannot save contact list, it’s only supported on EWS Server 2010 or later"))); - return FALSE; - } - - is_dl = TRUE; - } - - fid = e_ews_folder_id_new (bbews->priv->folder_id, NULL, FALSE); - if (overwrite_existing) { - EBookCache *book_cache; - EContact *old_contact = NULL; - - book_cache = e_book_meta_backend_ref_cache (meta_backend); - - success = e_book_cache_get_contact (book_cache, e_contact_get_const (contact, E_CONTACT_UID), FALSE, &old_contact, cancellable, error); - if (success) { - const gchar *original_vcard; - - /* This is for offline changes, where the EContact in the cache - is already modified, while the original, the one on the server, - is different. Using the cached EContact in this case generates - empty UpdateItem request and nothing is saved. */ - original_vcard = ebb_ews_get_original_vcard (old_contact); - if (original_vcard) { - EContact *tmp; - - tmp = e_contact_new_from_vcard (original_vcard); - if (tmp) { - g_object_unref (old_contact); - old_contact = tmp; - } - } - } - - if (success) { - ConvertData cd; - const gchar *conflict_res = "AlwaysOverwrite"; - - cd.bbews = bbews; - cd.cancellable = cancellable; - cd.error = error; - cd.old_contact = old_contact; - cd.new_contact = contact; - cd.change_key = NULL; - - if (conflict_resolution == E_CONFLICT_RESOLUTION_FAIL) - conflict_res = "NeverOverwrite"; - - success = e_ews_connection_update_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, - conflict_res, "SendAndSaveCopy", "SendToAllAndSaveCopy", - bbews->priv->folder_id, is_dl ? ebb_ews_convert_dl_to_updatexml_cb : ebb_ews_convert_contact_to_updatexml_cb, - &cd, &items, cancellable, error); - - g_free (cd.change_key); - } - - g_clear_object (&old_contact); - g_clear_object (&book_cache); - } else { - success = e_ews_connection_create_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, NULL, NULL, - fid, is_dl ? ebb_ews_convert_dl_to_xml_cb : ebb_ews_convert_contact_to_xml_cb, contact, - &items, cancellable, error); - } - - if (success && items) { - EEwsItem *item = items->data; - const EwsId *item_id; - - item_id = e_ews_item_get_id (item); - *out_new_uid = g_strdup (item_id->id); - - /* - * Support for ContactPhoto was added in Exchange 2010 SP2. - * We don't want to try to set/get this property if we are running in older version of the server. - */ - if (!overwrite_existing && - e_ews_connection_satisfies_server_version (bbews->priv->cnc, E_EWS_EXCHANGE_2010_SP2)) { - EContactPhoto *photo; - - /* - * The contact photo is basically an attachment with a special name. - * Considering this, we only can set the contact photo after create the contact itself. - * Then we are able to attach the picture to the "Contact Item". - */ - photo = e_contact_get (contact, E_CONTACT_PHOTO); - if (photo) { - GError *local_error = NULL; - - set_photo (bbews, item_id, contact, photo, NULL, cancellable, &local_error); - e_contact_photo_free (photo); - - if (local_error) { - g_propagate_error (error, local_error); - g_prefix_error (error, "%s", _("Failed to set contact photo:")); - success = FALSE; - } - } - } - } - - g_slist_free_full (items, g_object_unref); - e_ews_folder_id_free (fid); - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - ebb_ews_convert_error_to_edb_error (error); - ebb_ews_maybe_disconnect_sync (bbews, error, cancellable); - - return success; -} - -static gboolean -ebb_ews_remove_contact_sync (EBookMetaBackend *meta_backend, - EConflictResolution conflict_resolution, - const gchar *uid, - const gchar *extra, - const gchar *object, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - GSList *ids; - gboolean success; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - ids = g_slist_prepend (NULL, (gpointer) uid); - - success = e_ews_connection_delete_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, ids, EWS_HARD_DELETE, 0, FALSE, cancellable, error); - - g_slist_free (ids); - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - ebb_ews_convert_error_to_edb_error (error); - ebb_ews_maybe_disconnect_sync (bbews, error, cancellable); - - return success; -} - -static gboolean -ebb_ews_search_sync (EBookMetaBackend *meta_backend, - const gchar *expr, - gboolean meta_contact, - GSList **out_contacts, - GCancellable *cancellable, - GError **error) -{ - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - - /* Ignore errors, just try its best */ - ebb_ews_update_cache_for_expression (E_BOOK_BACKEND_EWS (meta_backend), expr, cancellable, NULL); - - /* Chain up to parent's method */ - return E_BOOK_META_BACKEND_CLASS (e_book_backend_ews_parent_class)->search_sync (meta_backend, expr, meta_contact, - out_contacts, cancellable, error); -} - -static gboolean -ebb_ews_search_uids_sync (EBookMetaBackend *meta_backend, - const gchar *expr, - GSList **out_uids, - GCancellable *cancellable, - GError **error) -{ - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - - /* Ignore errors, just try its best */ - ebb_ews_update_cache_for_expression (E_BOOK_BACKEND_EWS (meta_backend), expr, cancellable, NULL); - - /* Chain up to parent's method */ - return E_BOOK_META_BACKEND_CLASS (e_book_backend_ews_parent_class)->search_uids_sync (meta_backend, expr, - out_uids, cancellable, error); -} - -static gchar * -ebb_ews_get_backend_property (EBookBackend *book_backend, - const gchar *prop_name) -{ - EBookBackendEws *bbews; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (book_backend), NULL); - g_return_val_if_fail (prop_name != NULL, NULL); - - bbews = E_BOOK_BACKEND_EWS (book_backend); - - if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) { - CamelEwsSettings *ews_settings; - - ews_settings = ebb_ews_get_collection_settings (bbews); - - return g_strjoin (",", - "net", - "contact-lists", - e_book_meta_backend_get_capabilities (E_BOOK_META_BACKEND (book_backend)), - (!bbews->priv->is_gal || camel_ews_settings_get_oab_offline (ews_settings)) ? "do-initial-query" : NULL, - NULL); - } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) { - return g_strdup (e_contact_field_name (E_CONTACT_FILE_AS)); - } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) { - GString *buffer; - gchar *fields; - gint ii; - - buffer = g_string_sized_new (1024); - - for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) { - if (mappings[ii].element_type != ELEMENT_TYPE_SIMPLE) - continue; - - if (buffer->len > 0) - g_string_append_c (buffer, ','); - g_string_append (buffer, e_contact_field_name (mappings[ii].field_id)); - } - - for (ii = 0; ii < G_N_ELEMENTS (phone_field_map); ii++) { - if (buffer->len > 0) - g_string_append_c (buffer, ','); - g_string_append (buffer, e_contact_field_name (phone_field_map[ii].field)); - } - - fields = g_strjoin ( - ",", - buffer->str, - e_contact_field_name (E_CONTACT_FULL_NAME), - e_contact_field_name (E_CONTACT_NICKNAME), - e_contact_field_name (E_CONTACT_FAMILY_NAME), - e_contact_field_name (E_CONTACT_EMAIL_1), - e_contact_field_name (E_CONTACT_EMAIL_2), - e_contact_field_name (E_CONTACT_EMAIL_3), - e_contact_field_name (E_CONTACT_ADDRESS_WORK), - e_contact_field_name (E_CONTACT_ADDRESS_HOME), - e_contact_field_name (E_CONTACT_ADDRESS_OTHER), - e_contact_field_name (E_CONTACT_BIRTH_DATE), - e_contact_field_name (E_CONTACT_NOTE), - e_contact_field_name (E_CONTACT_PHOTO), - NULL); - - g_string_free (buffer, TRUE); - - return fields; - } - - /* Chain up to parent's method. */ - return E_BOOK_BACKEND_CLASS (e_book_backend_ews_parent_class)->get_backend_property (book_backend, prop_name); -} - -static gboolean -ebb_ews_get_destination_address (EBackend *backend, - gchar **host, - guint16 *port) -{ - CamelEwsSettings *ews_settings; - SoupURI *soup_uri; - gchar *host_url; - gboolean result = FALSE; - - g_return_val_if_fail (port != NULL, FALSE); - g_return_val_if_fail (host != NULL, FALSE); - - /* Sanity checking */ - if (!e_book_backend_get_registry (E_BOOK_BACKEND (backend)) || - !e_backend_get_source (backend)) - return FALSE; - - ews_settings = ebb_ews_get_collection_settings (E_BOOK_BACKEND_EWS (backend)); - g_return_val_if_fail (ews_settings != NULL, FALSE); - - host_url = camel_ews_settings_dup_hosturl (ews_settings); - g_return_val_if_fail (host_url != NULL, FALSE); - - soup_uri = soup_uri_new (host_url); - if (soup_uri) { - *host = g_strdup (soup_uri_get_host (soup_uri)); - *port = soup_uri_get_port (soup_uri); - - result = *host && **host; - if (!result) { - g_free (*host); - *host = NULL; - } - - soup_uri_free (soup_uri); - } - - g_free (host_url); - - return result; -} - -static void -e_book_backend_ews_constructed (GObject *object) -{ - EBookBackendEws *bbews = E_BOOK_BACKEND_EWS (object); - EBookCache *book_cache; - gchar *cache_dirname; - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_book_backend_ews_parent_class)->constructed (object); - - book_cache = e_book_meta_backend_ref_cache (E_BOOK_META_BACKEND (bbews)); - - cache_dirname = g_path_get_dirname (e_cache_get_filename (E_CACHE (book_cache))); - - g_clear_object (&book_cache); - - bbews->priv->attachments_dir = g_build_filename (cache_dirname, "attachments", NULL); - g_mkdir_with_parents (bbews->priv->attachments_dir, 0777); - - g_free (cache_dirname); -} - -static void -e_book_backend_ews_dispose (GObject *object) -{ - EBookBackendEws *bbews = E_BOOK_BACKEND_EWS (object); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - g_clear_object (&bbews->priv->cnc); - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_book_backend_ews_parent_class)->dispose (object); -} - -static void -e_book_backend_ews_finalize (GObject *object) -{ - EBookBackendEws *bbews = E_BOOK_BACKEND_EWS (object); - - g_free (bbews->priv->folder_id); - g_free (bbews->priv->attachments_dir); - - g_rec_mutex_clear (&bbews->priv->cnc_lock); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_book_backend_ews_parent_class)->finalize (object); -} - -static void -e_book_backend_ews_init (EBookBackendEws *bbews) -{ - bbews->priv = G_TYPE_INSTANCE_GET_PRIVATE (bbews, E_TYPE_BOOK_BACKEND_EWS, EBookBackendEwsPrivate); - - g_rec_mutex_init (&bbews->priv->cnc_lock); -} - -static void -e_book_backend_ews_class_init (EBookBackendEwsClass *klass) -{ - GObjectClass *object_class; - EBackendClass *backend_class; - EBookBackendClass *book_backend_class; - EBookMetaBackendClass *book_meta_backend_class; - - g_type_class_add_private (klass, sizeof (EBookBackendEwsPrivate)); - - book_meta_backend_class = E_BOOK_META_BACKEND_CLASS (klass); - book_meta_backend_class->backend_module_filename = "libebookbackendews.so"; - book_meta_backend_class->backend_factory_type_name = "EBookBackendEwsFactory"; - book_meta_backend_class->connect_sync = ebb_ews_connect_sync; - book_meta_backend_class->disconnect_sync = ebb_ews_disconnect_sync; - book_meta_backend_class->get_changes_sync = ebb_ews_get_changes_sync; - book_meta_backend_class->load_contact_sync = ebb_ews_load_contact_sync; - book_meta_backend_class->save_contact_sync = ebb_ews_save_contact_sync; - book_meta_backend_class->remove_contact_sync = ebb_ews_remove_contact_sync; - book_meta_backend_class->search_sync = ebb_ews_search_sync; - book_meta_backend_class->search_uids_sync = ebb_ews_search_uids_sync; - - book_backend_class = E_BOOK_BACKEND_CLASS (klass); - book_backend_class->get_backend_property = ebb_ews_get_backend_property; - - backend_class = E_BACKEND_CLASS (klass); - backend_class->get_destination_address = ebb_ews_get_destination_address; - - object_class = G_OBJECT_CLASS (klass); - object_class->constructed = e_book_backend_ews_constructed; - object_class->dispose = e_book_backend_ews_dispose; - object_class->finalize = e_book_backend_ews_finalize; -} diff --git a/src/addressbook/e-book-backend-ews.c.cve-2019-3890 b/src/addressbook/e-book-backend-ews.c.cve-2019-3890 deleted file mode 100644 index fca6c9e..0000000 --- a/src/addressbook/e-book-backend-ews.c.cve-2019-3890 +++ /dev/null @@ -1,3665 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ - -/* e-book-backend-ews.c - Ews contact backend. - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com) - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU 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. - * - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include "server/e-ews-item-change.h" -#include "server/e-ews-message.h" -#include "server/e-ews-connection.h" -#include "server/e-ews-connection-utils.h" -#include "server/e-ews-item.h" -#include "server/e-ews-query-to-restriction.h" -#include "server/e-source-ews-folder.h" - -#include "e-book-backend-ews.h" -#include "ews-oab-decoder.h" -#include "ews-oab-decompress.h" - -#ifdef G_OS_WIN32 -#ifdef gmtime_r -#undef gmtime_r -#endif - -/* The gmtime() in Microsoft's C library is MT-safe */ -#define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0) -#endif - -#define d(x) - -#define EDB_ERROR(_code) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, NULL) -#define EDB_ERROR_EX(_code,_msg) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, _msg) - -#define X_EWS_ORIGINAL_VCARD "X-EWS-ORIGINAL-VCARD" -#define X_EWS_CHANGEKEY "X-EWS-CHANGEKEY" -#define X_EWS_GAL_SHA1 "X-EWS-GAL-SHA1" - -#define EWS_MAX_FETCH_COUNT 500 - -#define ELEMENT_TYPE_SIMPLE 0x01 /* simple string fields */ -#define ELEMENT_TYPE_COMPLEX 0x02 /* complex fields while require different get/set functions */ - -/* passing field uris for PhysicalAddress, PhoneNumbers causes error, so we - * use Default view to fetch them. Thus the summary props just have attachments - * and some additional properties that are not return with Default view */ -#define CONTACT_ITEM_PROPS "item:Attachments item:HasAttachments item:Body item:LastModifiedTime contacts:Manager contacts:Department contacts:SpouseName contacts:AssistantName contacts:BusinessHomePage contacts:Birthday" - -struct _EBookBackendEwsPrivate { - GRecMutex cnc_lock; - EEwsConnection *cnc; - - gchar *folder_id; - gboolean is_gal; - - guint subscription_key; - - /* used for storing attachments */ - gchar *attachments_dir; -}; - -G_DEFINE_TYPE (EBookBackendEws, e_book_backend_ews, E_TYPE_BOOK_META_BACKEND) - -static CamelEwsSettings * -ebb_ews_get_collection_settings (EBookBackendEws *bbews) -{ - ESource *source; - ESource *collection; - ESourceCamel *extension; - ESourceRegistry *registry; - CamelSettings *settings; - const gchar *extension_name; - - source = e_backend_get_source (E_BACKEND (bbews)); - registry = e_book_backend_get_registry (E_BOOK_BACKEND (bbews)); - - extension_name = e_source_camel_get_extension_name ("ews"); - e_source_camel_generate_subtype ("ews", CAMEL_TYPE_EWS_SETTINGS); - - /* The collection settings live in our parent data source. */ - collection = e_source_registry_find_extension (registry, source, extension_name); - g_return_val_if_fail (collection != NULL, NULL); - - extension = e_source_get_extension (collection, extension_name); - settings = e_source_camel_get_settings (extension); - - g_object_unref (collection); - - return CAMEL_EWS_SETTINGS (settings); -} - -static void -ebb_ews_convert_error_to_edb_error (GError **perror) -{ - GError *error = NULL; - - if (!perror || !*perror || (*perror)->domain == E_DATA_BOOK_ERROR) - return; - - if ((*perror)->domain == EWS_CONNECTION_ERROR) { - switch ((*perror)->code) { - case EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED: - error = EDB_ERROR_EX (AUTHENTICATION_FAILED, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_FOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_MANAGEDFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PARENTFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PUBLICFOLDERSERVERNOTFOUND: - error = EDB_ERROR_EX (NO_SUCH_BOOK, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_EVENTNOTFOUND: - case EWS_CONNECTION_ERROR_ITEMNOTFOUND: - error = EDB_ERROR_EX (CONTACT_NOT_FOUND, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_UNAVAILABLE: - g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND, (*perror)->message); - break; - } - - if (!error) - error = EDB_ERROR_EX (OTHER_ERROR, (*perror)->message); - } - - if (error) { - g_error_free (*perror); - *perror = error; - } -} - -static void -ebb_ews_maybe_disconnect_sync (EBookBackendEws *bbews, - GError **in_perror, - GCancellable *cancellable) -{ - g_return_if_fail (E_IS_BOOK_BACKEND_EWS (bbews)); - - if (in_perror && g_error_matches (*in_perror, E_DATA_BOOK_ERROR, E_DATA_BOOK_STATUS_AUTHENTICATION_FAILED)) { - e_book_meta_backend_disconnect_sync (E_BOOK_META_BACKEND (bbews), cancellable, NULL); - e_backend_schedule_credentials_required (E_BACKEND (bbews), E_SOURCE_CREDENTIALS_REASON_REJECTED, NULL, 0, NULL, NULL, G_STRFUNC); - } -} - -static const struct phone_field_mapping { - EContactField field; - const gchar *element; -} phone_field_map[] = { - {E_CONTACT_PHONE_ASSISTANT, "AssistantPhone"}, - {E_CONTACT_PHONE_BUSINESS_FAX, "BusinessFax"}, - {E_CONTACT_PHONE_BUSINESS, "BusinessPhone"}, - {E_CONTACT_PHONE_BUSINESS_2, "BusinessPhone2"}, - {E_CONTACT_PHONE_CAR, "CarPhone"}, - {E_CONTACT_PHONE_COMPANY, "CompanyMainPhone"}, - {E_CONTACT_PHONE_HOME_FAX, "HomeFax"}, - {E_CONTACT_PHONE_HOME, "HomePhone"}, - {E_CONTACT_PHONE_HOME_2, "HomePhone2"}, - {E_CONTACT_PHONE_ISDN, "Isdn"}, - {E_CONTACT_PHONE_MOBILE, "MobilePhone"}, - {E_CONTACT_PHONE_OTHER_FAX, "OtherFax"}, - {E_CONTACT_PHONE_OTHER, "OtherTelephone"}, - {E_CONTACT_PHONE_PAGER, "Pager"}, - {E_CONTACT_PHONE_PRIMARY, "PrimaryPhone"}, - {E_CONTACT_PHONE_RADIO, "RadioPhone"}, - {E_CONTACT_PHONE_TELEX, "Telex"}, - {E_CONTACT_PHONE_TTYTDD, "TtyTddPhone"} -}; - -static void -ebews_populate_rev (EContact *contact, - EEwsItem *item) -{ - struct tm stm; - time_t tt = 0; - gchar time_string[100] = { 0 }; - - g_return_if_fail (E_IS_CONTACT (contact)); - - if (item) { - g_return_if_fail (E_IS_EWS_ITEM (item)); - - tt = e_ews_item_get_last_modified_time (item); - } - - if (tt <= 0) - tt = time (NULL); - - gmtime_r (&tt, &stm); - strftime (time_string, 100, "%Y-%m-%dT%H:%M:%SZ", &stm); - - e_contact_set (contact, E_CONTACT_REV, time_string); -} - -static void -ebews_populate_uid (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - const EwsId *id; - - id = e_ews_item_get_id (item); - if (id) { - e_contact_set (contact, E_CONTACT_UID, id->id); - ebews_populate_rev (contact, item); - e_vcard_util_set_x_attribute (E_VCARD (contact), X_EWS_CHANGEKEY, id->change_key); - } -} - -static void -ebews_populate_full_name (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - const EwsCompleteName *cn; - - cn = e_ews_item_get_complete_name (item); - if (cn) - e_contact_set (contact, E_CONTACT_FULL_NAME, cn->full_name); -} - -static void -ebews_populate_nick_name (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - const EwsCompleteName *cn; - - cn = e_ews_item_get_complete_name (item); - if (cn) - e_contact_set (contact, E_CONTACT_NICKNAME, cn->nick_name); -} - -static void -ebews_populate_birth_date (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - time_t bdate; - GDate date; - EContactDate edate; - - bdate = e_ews_item_get_birthday (item); - - if (bdate) { - g_date_clear (&date, 1); - g_date_set_time_t (&date, bdate); - - edate.year = date.year; - edate.month = date.month; - edate.day = date.day; - - if (g_date_valid (&date)) - e_contact_set (contact, E_CONTACT_BIRTH_DATE, &edate); - } -} - -static void -ebews_populate_anniversary (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - time_t bdate; - GDate date; - EContactDate edate; - - bdate = e_ews_item_get_wedding_anniversary (item); - - if (bdate) { - g_date_clear (&date, 1); - g_date_set_time_t (&date, bdate); - - edate.year = date.year; - edate.month = date.month; - edate.day = date.day; - - if (g_date_valid (&date)) - e_contact_set (contact, E_CONTACT_ANNIVERSARY, &edate); - } -} - -static EContactPhoto * -get_photo (EBookBackendEws *bbews, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - EContactPhoto *photo = NULL; - EEwsAdditionalProps *add_props = NULL; - EEwsAttachmentInfo *info; - GSList *contact_item_ids = NULL, *new_items = NULL; - GSList *attachments = NULL, *attachments_ids = NULL; - const guchar *content; - const gchar *contact_photo_id; - const EwsId *id; - gsize len; - - id = e_ews_item_get_id (item); - if (!id) - return NULL; - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("item:Attachments"); - - contact_item_ids = g_slist_prepend (contact_item_ids, g_strdup (id->id)); - if (!e_ews_connection_get_items_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - contact_item_ids, - "IdOnly", - add_props, - FALSE, - NULL, - E_EWS_BODY_TYPE_TEXT, - &new_items, - NULL, - NULL, - cancellable, - error)) - goto exit; - - contact_photo_id = e_ews_item_get_contact_photo_id (new_items->data); - if (!contact_photo_id) - goto exit; - - attachments_ids = g_slist_prepend (attachments_ids, g_strdup (contact_photo_id)); - if (!e_ews_connection_get_attachments_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - NULL, - attachments_ids, - NULL, - FALSE, - &attachments, - NULL, - NULL, - cancellable, - error)) - goto exit; - - info = attachments->data; - content = (guchar *) e_ews_attachment_info_get_inlined_data (info, &len); - - photo = e_contact_photo_new (); - photo->type = E_CONTACT_PHOTO_TYPE_INLINED; - e_contact_photo_set_inlined (photo, content, len); - -exit: - e_ews_additional_props_free (add_props); - g_slist_free_full (contact_item_ids, g_free); - g_slist_free_full (new_items, g_object_unref); - g_slist_free_full (attachments_ids, g_free); - g_slist_free_full (attachments, (GDestroyNotify) e_ews_attachment_info_free); - - return photo; -} - -static void -ebews_populate_photo (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - EContactPhoto *photo; - - /* - * Support for ContactPhoto was added in Exchange 2010 SP2. - * We don't want to try to set/get this property if we are running in older version of the server. - */ - if (!e_ews_connection_satisfies_server_version (bbews->priv->cnc, E_EWS_EXCHANGE_2010_SP2)) - return; - - photo = get_photo (bbews, item, cancellable, error); - if (!photo) { - return; - } - - e_contact_set (contact, E_CONTACT_PHOTO, photo); - e_contact_photo_free (photo); -} - -static void -set_phone_number (EContact *contact, - EContactField field, - EEwsItem *item, - const gchar *item_field) -{ - const gchar *pn; - - pn = e_ews_item_get_phone_number (item, item_field); - if (pn && *pn) - e_contact_set (contact, field, pn); -} - -static void -ebews_populate_phone_numbers (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - gint i; - - for (i = 0; i < G_N_ELEMENTS (phone_field_map); i++) - set_phone_number (contact, phone_field_map[i].field, item, phone_field_map[i].element); -} - -static void -copy_ews_address_to_contact_address (EContactAddress *contact_addr, - const EwsAddress *address) -{ - contact_addr->address_format = NULL; - contact_addr->po = NULL; - contact_addr->street = g_strdup (address->street); - contact_addr->ext = NULL; - contact_addr->locality = g_strdup (address->city); - contact_addr->region = g_strdup (address->state); - contact_addr->code = g_strdup (address->postal_code); - contact_addr->country = g_strdup (address->country); -} - -static gboolean -ews_address_is_empty (const EwsAddress *address) -{ - if (!address) - return TRUE; - -#define is_empty_str(x) (!(x) || (!*(x))) - - return is_empty_str (address->street) && - is_empty_str (address->city) && - is_empty_str (address->state) && - is_empty_str (address->postal_code) && - is_empty_str (address->country); - -#undef is_empty_str -} - -static void -set_address (EContact *contact, - EContactField field, - EEwsItem *item, - const gchar *item_field) -{ - const EwsAddress *address; - - address = e_ews_item_get_physical_address (item, item_field); - if (address && !ews_address_is_empty (address)) { - EContactAddress *addr; - - addr = g_new0 (EContactAddress, 1); - copy_ews_address_to_contact_address (addr, address); - e_contact_set (contact, field, addr); - e_contact_address_free (addr); - } -} - -static void -ebews_populate_address (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - - set_address (contact, E_CONTACT_ADDRESS_WORK, item, "Business"); - set_address (contact, E_CONTACT_ADDRESS_HOME, item, "Home"); - set_address (contact, E_CONTACT_ADDRESS_OTHER, item, "Other"); -} - -static void -ebews_populate_ims (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - /* TODO : The fields returned by server does not match with the EContact fields - * for the IMS, handle it later */ -} - -static void -ebews_populate_notes (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - const gchar *notes = e_ews_item_get_notes (item); - if (!notes) - return; - - e_contact_set (contact, E_CONTACT_NOTE, notes); -} - -static void -set_email_address (EContact *contact, - EContactField field, - EEwsItem *item, - const gchar *item_field, - gboolean require_smtp_prefix) -{ - const gchar *ea; - - ea = e_ews_item_get_email_address (item, item_field); - if (ea && g_ascii_strncasecmp (ea, "SMTP:", 5) == 0) - ea = ea + 5; - else if (require_smtp_prefix) - ea = NULL; - - if (ea && *ea) - e_contact_set (contact, field, ea); -} - -static void -ebews_populate_emails_ex (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - gboolean require_smtp_prefix) -{ - set_email_address (contact, E_CONTACT_EMAIL_1, item, "EmailAddress1", require_smtp_prefix); - set_email_address (contact, E_CONTACT_EMAIL_2, item, "EmailAddress2", require_smtp_prefix); - set_email_address (contact, E_CONTACT_EMAIL_3, item, "EmailAddress3", require_smtp_prefix); -} - -static void -ebews_populate_emails (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **errror) -{ - ebews_populate_emails_ex (bbews, contact, item, FALSE); -} - -static void -ebews_set_item_id (ESoapMessage *message, - EContact *contact) -{ - -} - -static void -ebews_set_full_name (ESoapMessage *msg, - EContact *contact) -{ - EContactName *name; - - name = e_contact_get (contact, E_CONTACT_NAME); - if (!name) - return; - - if (name->given) - e_ews_message_write_string_parameter (msg, "GivenName", NULL, name->given); - - if (name->additional && *name->additional) - e_ews_message_write_string_parameter (msg, "MiddleName", NULL, name->additional); - - e_contact_name_free (name); -} - -/* TODO Set birth and anniversary dates */ -static void -ebews_set_birth_date (ESoapMessage *message, - EContact *contact) -{ - EContactDate *date; - gchar *birthday; - - date = e_contact_get (contact, E_CONTACT_BIRTH_DATE); - - if (!date) - return; - - birthday = g_strdup_printf ( - "%04d-%02d-%02dT00:00:00", - date->year, date->month, date->day); - - e_ews_message_write_string_parameter (message, "Birthday", NULL, birthday); - - g_free (birthday); - -} - -static void -ebews_set_anniversary (ESoapMessage *message, - EContact *contact) -{ - -} - -static void -ebews_set_photo (ESoapMessage *message, - EContact *contact) -{ - -} - -static gboolean -add_entry (ESoapMessage *msg, - EContact *contact, - EContactField field, - const gchar *entry_name, - const gchar *include_hdr) -{ - gchar *entry_val; - - entry_val = e_contact_get (contact, field); - - if (entry_val && *entry_val) { - if (include_hdr) - e_soap_message_start_element (msg, include_hdr, NULL, NULL); - - e_ews_message_write_string_parameter_with_attribute (msg, "Entry", NULL, entry_val, "Key", entry_name); - - g_free (entry_val); - return TRUE; - } - - g_free (entry_val); - return FALSE; -} - -static void -ebews_set_phone_numbers (ESoapMessage *msg, - EContact *contact) -{ - gint i; - const gchar *include_hdr = "PhoneNumbers"; - - for (i = 0; i < G_N_ELEMENTS (phone_field_map); i++) { - if (add_entry (msg, contact, phone_field_map[i].field, phone_field_map[i].element, include_hdr)) - include_hdr = NULL; - } - - if (!include_hdr) - e_soap_message_end_element (msg); -} - -static gboolean -add_physical_address (ESoapMessage *msg, - EContact *contact, - EContactField field, - const gchar *entry_name, - gboolean include_start_hdr) -{ - EContactAddress *contact_addr; - - contact_addr = e_contact_get (contact, field); - if (!contact_addr) - return FALSE; - - if (include_start_hdr) - e_soap_message_start_element (msg, "PhysicalAddresses", NULL, NULL); - - e_soap_message_start_element (msg, "Entry", NULL, NULL); - - e_soap_message_add_attribute (msg, "Key", entry_name, NULL, NULL); - e_ews_message_write_string_parameter (msg, "Street", NULL, contact_addr->street); - e_ews_message_write_string_parameter (msg, "City", NULL, contact_addr->locality); - e_ews_message_write_string_parameter (msg, "State", NULL, contact_addr->region); - e_ews_message_write_string_parameter (msg, "CountryOrRegion", NULL, contact_addr->country); - e_ews_message_write_string_parameter (msg, "PostalCode", NULL, contact_addr->code); - - e_soap_message_end_element (msg); - e_contact_address_free (contact_addr); - - return TRUE; -} - -static void -ebews_set_address (ESoapMessage *msg, - EContact *contact) -{ - gboolean include_hdr = TRUE; - - if (add_physical_address (msg, contact, E_CONTACT_ADDRESS_WORK, "Business", include_hdr)) - include_hdr = FALSE; - if (add_physical_address (msg, contact, E_CONTACT_ADDRESS_HOME, "Home", include_hdr)) - include_hdr = FALSE; - if (add_physical_address (msg, contact, E_CONTACT_ADDRESS_OTHER, "Other", include_hdr)) - include_hdr = FALSE; - - if (!include_hdr) - e_soap_message_end_element (msg); -} - -static void -ebews_set_ims (ESoapMessage *message, - EContact *contact) -{ - -} - -static void -ebews_set_notes (ESoapMessage *msg, - EContact *contact) -{ - gchar *notes = e_contact_get (contact, E_CONTACT_NOTE); - if (!notes) - return; - - e_ews_message_write_string_parameter_with_attribute (msg, "Body", NULL, notes, "BodyType", "Text"); - - g_free (notes); -} - -static void -ebews_set_emails (ESoapMessage *msg, - EContact *contact) -{ - const gchar *include_hdr = "EmailAddresses"; - - if (add_entry (msg, contact, E_CONTACT_EMAIL_1, "EmailAddress1", include_hdr)) - include_hdr = NULL; - if (add_entry (msg, contact, E_CONTACT_EMAIL_2, "EmailAddress2", include_hdr)) - include_hdr = NULL; - if (add_entry (msg, contact, E_CONTACT_EMAIL_3, "EmailAddress3", include_hdr)) - include_hdr = NULL; - - if (!include_hdr) - e_soap_message_end_element (msg); -} - -static void -convert_contact_property_to_updatexml (ESoapMessage *msg, - const gchar *name, - const gchar *value, - const gchar *prefix, - const gchar *attr_name, - const gchar *attr_value) -{ - e_ews_message_start_set_item_field (msg, name, prefix, "Contact"); - e_ews_message_write_string_parameter_with_attribute (msg, name, NULL, value, attr_name, attr_value); - e_ews_message_end_set_item_field (msg); -} - -static void -convert_indexed_contact_property_to_updatexml (ESoapMessage *message, - const gchar *name, - const gchar *value, - const gchar *prefix, - const gchar *element_name, - const gchar *key) -{ - gboolean delete_field = FALSE; - - if (!value || g_strcmp0 (value, "") == 0) - delete_field = TRUE; - e_ews_message_start_set_indexed_item_field (message, name , prefix, "Contact", key, delete_field); - - if (!delete_field) - { - e_soap_message_start_element (message, element_name, NULL, NULL); - e_ews_message_write_string_parameter_with_attribute (message, "Entry", NULL, value, "Key", key); - e_soap_message_end_element (message); - } - e_ews_message_end_set_indexed_item_field (message, delete_field); -} - -static void -ebews_set_full_name_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - EContactName *name, *old_name; - - if (!message) - return; - - name = e_contact_get (new, E_CONTACT_NAME); - old_name = e_contact_get (old, E_CONTACT_NAME); - if (!old_name && !name) - return; - - if (!old_name) { - convert_contact_property_to_updatexml (message, "GivenName", name->given, "contacts", NULL, NULL); - convert_contact_property_to_updatexml (message, "MiddleName", name->additional, "contacts", NULL, NULL); - } else if (!name) { - convert_contact_property_to_updatexml (message, "GivenName", "", "contacts", NULL, NULL); - - convert_contact_property_to_updatexml (message, "MiddleName", "", "contacts", NULL, NULL); - } else { - if (g_strcmp0 (name->given, old_name->given) != 0) - convert_contact_property_to_updatexml (message, "GivenName", name->given, "contacts", NULL, NULL); - if (g_strcmp0 (name->additional, old_name->additional) != 0) - convert_contact_property_to_updatexml (message, "MiddleName", name->additional, "contacts", NULL, NULL); - } - - e_contact_name_free (name); - e_contact_name_free (old_name); - -} - -static void -ebews_set_birth_date_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - EContactDate *new_date, *old_date; - gchar *birthday; - - if (!message) - return; - - new_date = e_contact_get (new, E_CONTACT_BIRTH_DATE); - old_date = e_contact_get (old, E_CONTACT_BIRTH_DATE); - - if (!e_contact_date_equal (new_date, old_date)) { - birthday = g_strdup_printf ( - "%04d-%02d-%02dT00:00:00", - new_date->year, new_date->month, new_date->day); - - convert_contact_property_to_updatexml (message, "Birthday", birthday, "contacts", NULL, NULL); - g_free (birthday); - } - - e_contact_date_free (new_date); - e_contact_date_free (old_date); -} - -static void -ebews_set_anniversary_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - -} - -static void -set_photo (EBookBackendEws *bbews, - const EwsId *item_id, - EContact *contact, - EContactPhoto *photo, - gchar **new_change_key, - GCancellable *cancellable, - GError **error) -{ - EEwsAttachmentInfo *info; - EwsId *id = NULL; - GSList *files = NULL; - const guchar *data; - gsize len; - - if (!item_id) { - id = g_new0 (EwsId, 1); - id->id = e_contact_get (contact, E_CONTACT_UID); - id->change_key = e_vcard_util_dup_x_attribute (E_VCARD (contact), X_EWS_CHANGEKEY); - if (!id->change_key) - id->change_key = e_contact_get (contact, E_CONTACT_REV); - - item_id = id; - } - - data = e_contact_photo_get_inlined (photo, &len); - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_INLINED); - e_ews_attachment_info_set_inlined_data (info, data, len); - e_ews_attachment_info_set_mime_type (info, "image/jpeg"); - e_ews_attachment_info_set_filename (info, "ContactPicture.jpg"); - - files = g_slist_append (files, info); - - e_ews_connection_create_attachments_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - item_id, - files, - TRUE, - new_change_key, - NULL, - cancellable, - error); - - if (id) { - g_free (id->change_key); - g_free (id->id); - g_free (id); - } - - g_slist_free_full (files, (GDestroyNotify) e_ews_attachment_info_free); -} - -static gboolean -ebb_ews_photo_changed (EBookMetaBackend *meta_backend, - EContact *old_contact, - EContact *new_contact, - GCancellable *cancellable) -{ - EContact *old_contact_copy = NULL; - EContactPhoto *old_photo; - EContactPhoto *new_photo; - gboolean changed = FALSE; - - old_photo = e_contact_get (old_contact, E_CONTACT_PHOTO); - new_photo = e_contact_get (new_contact, E_CONTACT_PHOTO); - - if (!old_photo && new_photo) - changed = TRUE; - - if (old_photo && !new_photo) - changed = TRUE; - - /* old_photo comes from cache, thus it's always URI (to local file or elsewhere), - while the new_photo is to be saved, which is always inlined. */ - if (!changed && old_photo && new_photo && - old_photo->type == E_CONTACT_PHOTO_TYPE_URI && - new_photo->type == E_CONTACT_PHOTO_TYPE_INLINED) { - e_contact_photo_free (old_photo); - old_photo = NULL; - - old_contact_copy = e_contact_duplicate (old_contact); - - if (e_book_meta_backend_inline_local_photos_sync (meta_backend, old_contact_copy, cancellable, NULL)) - old_photo = e_contact_get (old_contact_copy, E_CONTACT_PHOTO); - } - - if (old_photo && new_photo && - old_photo->type == E_CONTACT_PHOTO_TYPE_INLINED && - new_photo->type == E_CONTACT_PHOTO_TYPE_INLINED) { - guchar *old_data; - guchar *new_data; - gsize old_length; - gsize new_length; - - old_data = old_photo->data.inlined.data; - new_data = new_photo->data.inlined.data; - - old_length = old_photo->data.inlined.length; - new_length = new_photo->data.inlined.length; - - changed = - (old_length != new_length) || - (memcmp (old_data, new_data, old_length) != 0); - } - - e_contact_photo_free (old_photo); - e_contact_photo_free (new_photo); - g_clear_object (&old_contact_copy); - - return changed; -} - -static void -ebews_set_photo_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - EContactPhoto *new_photo = NULL; - EEwsAdditionalProps *add_props = NULL; - GSList *contact_item_ids = NULL, *new_items = NULL, *attachments_ids = NULL; - gchar *id = NULL; - const gchar *contact_photo_id; - gchar *new_change_key = NULL; - - /* - * Support for ContactPhoto was added in Exchange 2010 SP2. - * We don't want to try to set/get this property if we are running in older version of the server. - */ - if (!e_ews_connection_satisfies_server_version (bbews->priv->cnc, E_EWS_EXCHANGE_2010_SP2)) { - return; - } - - if (message) { - /* Photo changes can be done only in pre-flight stage, - because it modifies ChangeKey */ - return; - } - - if (!ebb_ews_photo_changed (E_BOOK_META_BACKEND (bbews), old, new, cancellable)) - return; - - new_photo = e_contact_get (new, E_CONTACT_PHOTO); - id = e_contact_get (old, E_CONTACT_UID); - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("item:Attachments"); - - contact_item_ids = g_slist_append (contact_item_ids, id); - if (!e_ews_connection_get_items_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - contact_item_ids, - "IdOnly", - add_props, - FALSE, - NULL, - E_EWS_BODY_TYPE_TEXT, - &new_items, - NULL, - NULL, - cancellable, - error)) - goto exit; - - contact_photo_id = e_ews_item_get_contact_photo_id (new_items->data); - if (contact_photo_id) { - attachments_ids = g_slist_prepend (attachments_ids, g_strdup (contact_photo_id)); - if (!e_ews_connection_delete_attachments_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - attachments_ids, - &new_change_key, - cancellable, - error)) - goto exit; - } - - if (new_photo) { - EwsId *item_id = NULL; - - if (new_change_key) { - item_id = g_new0 (EwsId, 1); - item_id->id = e_contact_get (new, E_CONTACT_UID); - item_id->change_key = new_change_key; - - new_change_key = NULL; - } - - set_photo (bbews, item_id, new, new_photo, &new_change_key, cancellable, error); - - if (item_id) { - if (!new_change_key) { - new_change_key = item_id->change_key; - item_id->change_key = NULL; - } - - g_free (item_id->id); - g_free (item_id->change_key); - g_free (item_id); - } - } - - exit: - e_ews_additional_props_free (add_props); - e_contact_photo_free (new_photo); - g_slist_free_full (contact_item_ids, g_free); - g_slist_free_full (new_items, g_object_unref); - g_slist_free_full (attachments_ids, g_free); - - if (new_change_key && out_new_change_key) - *out_new_change_key = new_change_key; - else - g_free (new_change_key); -} - -static void -ebews_set_phone_number_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - gint i; - gchar *new_value, *old_value; - - if (!message) - return; - - for (i = 0; i < G_N_ELEMENTS (phone_field_map); i++) { - new_value = e_contact_get (new, phone_field_map[i].field); - old_value = e_contact_get (old, phone_field_map[i].field); - - if (g_strcmp0 (new_value, old_value) != 0) - convert_indexed_contact_property_to_updatexml (message, "PhoneNumber", new_value, "contacts", "PhoneNumbers", phone_field_map[i].element); - - g_free (new_value); - g_free (old_value); - } -} - -static void -convert_indexed_contact_property_to_updatexml_physical_address (ESoapMessage *message, - const gchar *name, - const gchar *uri_element, - const gchar *value, - const gchar *prefix, - const gchar *element_name, - const gchar *key) -{ - gchar * fielduri = NULL; - gboolean delete_field = FALSE; - - if (!value || g_strcmp0 (value, "") == 0) - delete_field = TRUE; - - fielduri = g_strconcat (name, ":", uri_element, NULL); - - e_ews_message_start_set_indexed_item_field (message, fielduri , prefix, "Contact", key, delete_field); - - if (!delete_field) - { - e_soap_message_start_element (message, element_name, NULL, NULL); - - e_soap_message_start_element (message, "Entry", NULL, NULL); - e_soap_message_add_attribute (message, "Key", key, NULL, NULL); - e_ews_message_write_string_parameter (message, uri_element, NULL, value); - e_soap_message_end_element (message); - - e_soap_message_end_element (message); - } - e_ews_message_end_set_indexed_item_field (message, delete_field); -} - -static void -compare_address (ESoapMessage *message, - EContact *new, - EContact *old, - EContactField field, - const gchar *key) -{ - EContactAddress *new_address, *old_address; - gboolean set = FALSE; - - new_address = e_contact_get (new, field); - old_address = e_contact_get (old, field); - - if (!new_address && !old_address) - return; - - if (!old_address && new_address) - set = TRUE; - - if (!new_address && old_address) - { - set = TRUE; - new_address = g_new0 (EContactAddress, 1); - } - - if (set || g_strcmp0 (new_address->street, old_address->street) != 0) - convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "Street", new_address->street, "contacts", "PhysicalAddresses", key); - if (set || g_strcmp0 (new_address->locality, old_address->locality) != 0) - convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "City", new_address->locality, "contacts", "PhysicalAddresses", key); - if (set || g_strcmp0 (new_address->region, old_address->region) != 0) - convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "State", new_address->region, "contacts", "PhysicalAddresses", key); - if (set || g_strcmp0 (new_address->country, old_address->country) != 0) - convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "CountryOrRegion", new_address->country, "contacts", "PhysicalAddresses", key); - if (set || g_strcmp0 (new_address->code, old_address->code) != 0) - convert_indexed_contact_property_to_updatexml_physical_address (message, "PhysicalAddress", "PostalCode", new_address->code, "contacts", "PhysicalAddresses", key); - - e_contact_address_free (old_address); - e_contact_address_free (new_address); -} - -static void -ebews_set_address_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - if (!message) - return; - - compare_address (message, new, old, E_CONTACT_ADDRESS_WORK, "Business"); - compare_address (message, new, old, E_CONTACT_ADDRESS_HOME, "Home"); - compare_address (message, new, old, E_CONTACT_ADDRESS_OTHER, "Other"); -} - -static void -ebews_set_im_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - -} - -static void -ebews_set_notes_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - gchar *old_notes, *new_notes; - - if (!message) - return; - - old_notes = e_contact_get (old, E_CONTACT_NOTE); - new_notes = e_contact_get (new, E_CONTACT_NOTE); - - if (g_strcmp0 (old_notes, new_notes) != 0) { - convert_contact_property_to_updatexml ( - message, "Body", new_notes ? new_notes : "", "item", "BodyType", "Text"); - } - - g_free (old_notes); - g_free (new_notes); -} - -static void -ebews_set_email_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - gchar *new_value, *old_value; - - if (!message) - return; - - new_value = e_contact_get (new, E_CONTACT_EMAIL_1); - old_value = e_contact_get (old, E_CONTACT_EMAIL_1); - if (g_strcmp0 (new_value, old_value) != 0) - convert_indexed_contact_property_to_updatexml (message, "EmailAddress", new_value, "contacts", "EmailAddresses", "EmailAddress1"); - g_free (new_value); - g_free (old_value); - - new_value = e_contact_get (new, E_CONTACT_EMAIL_2); - old_value = e_contact_get (old, E_CONTACT_EMAIL_2); - if (g_strcmp0 (new_value, old_value) != 0) - convert_indexed_contact_property_to_updatexml (message, "EmailAddress", new_value, "contacts", "EmailAddresses", "EmailAddress2"); - g_free (new_value); - g_free (old_value); - - new_value = e_contact_get (new, E_CONTACT_EMAIL_3); - old_value = e_contact_get (old, E_CONTACT_EMAIL_3); - if (g_strcmp0 (new_value, old_value) != 0) - convert_indexed_contact_property_to_updatexml (message, "EmailAddress", new_value, "contacts", "EmailAddresses", "EmailAddress3"); - g_free (new_value); - g_free (old_value); -} - -static void -ebews_populate_givenname (EBookBackendEws *bbews, - EContact *contact, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - const gchar *givenname; - - givenname = e_ews_item_get_givenname (item); - if (givenname && *givenname) - e_contact_set (contact, E_CONTACT_GIVEN_NAME, givenname); -} - -static void -ebews_set_givenname (ESoapMessage *message, - EContact *contact) -{ - /* Does nothing, the "GivenName" is filled by the "FullName" code */ -} - -static void -ebews_set_givenname_changes (EBookBackendEws *bbews, - ESoapMessage *message, - EContact *new, - EContact *old, - gchar **out_new_change_key, - GCancellable *cancellable, - GError **error) -{ - /* Does nothing, the "GivenName" is filled by the "FullName" code */ -} - -static const gchar * -ebews_get_fileas_or_display_name (EEwsItem *item) -{ - const gchar *value; - - value = e_ews_item_get_fileas (item); - if (!value || !*value) - value = e_ews_item_get_display_name (item); - - return value; -} - -static const struct field_element_mapping { - EContactField field_id; - gint element_type; - const gchar *element_name; - /* set function for simple string type values */ - const gchar * (*get_simple_prop_func) (EEwsItem *item); - void (*populate_contact_func)(EBookBackendEws *bbews, EContact *contact, EEwsItem *item, GCancellable *cancellable, GError **error); - void (*set_value_in_soap_message) (ESoapMessage *message, EContact *contact); - void (*set_changes) (EBookBackendEws *bbews, ESoapMessage *message, EContact *new, EContact *old, gchar **out_new_change_key, GCancellable *cancellable, GError **error); - -} mappings[] = { - /* The order should be maintained for create contacts to work */ - { E_CONTACT_NOTE, ELEMENT_TYPE_COMPLEX, "Notes", NULL, ebews_populate_notes, ebews_set_notes, ebews_set_notes_changes }, - { E_CONTACT_FILE_AS, ELEMENT_TYPE_SIMPLE, "FileAs", ebews_get_fileas_or_display_name }, - { E_CONTACT_FULL_NAME, ELEMENT_TYPE_COMPLEX, "CompleteName", NULL, ebews_populate_full_name, ebews_set_full_name, ebews_set_full_name_changes}, - { E_CONTACT_NICKNAME, ELEMENT_TYPE_SIMPLE, "Nickname", NULL, ebews_populate_nick_name}, - { E_CONTACT_ORG, ELEMENT_TYPE_SIMPLE, "CompanyName", e_ews_item_get_company_name}, - /* should take care of all email adresss fields */ - { E_CONTACT_EMAIL_1, ELEMENT_TYPE_COMPLEX, "EmailAddresses", NULL, ebews_populate_emails, ebews_set_emails, ebews_set_email_changes }, - /* should take care of home, work and other adresss fields */ - { E_CONTACT_ADDRESS_HOME, ELEMENT_TYPE_COMPLEX, "PhysicalAddresses", NULL, ebews_populate_address, ebews_set_address, ebews_set_address_changes }, - /* should take care of all phone number fields */ - { E_CONTACT_PHONE_PRIMARY, ELEMENT_TYPE_COMPLEX , "PhoneNumbers", NULL, ebews_populate_phone_numbers, ebews_set_phone_numbers, ebews_set_phone_number_changes}, - { E_CONTACT_ASSISTANT, ELEMENT_TYPE_SIMPLE, "AssistantName", e_ews_item_get_assistant_name}, - { E_CONTACT_BIRTH_DATE, ELEMENT_TYPE_COMPLEX, "Birthday", NULL, ebews_populate_birth_date, ebews_set_birth_date, ebews_set_birth_date_changes }, - { E_CONTACT_HOMEPAGE_URL, ELEMENT_TYPE_SIMPLE, "BusinessHomePage", e_ews_item_get_business_homepage}, - { E_CONTACT_ORG_UNIT, ELEMENT_TYPE_SIMPLE, "Department", e_ews_item_get_department}, - /* should take care of all im fields */ - { E_CONTACT_IM_AIM, ELEMENT_TYPE_COMPLEX, "ImAddresses", NULL, ebews_populate_ims, ebews_set_ims, ebews_set_im_changes }, - { E_CONTACT_TITLE, ELEMENT_TYPE_SIMPLE, "JobTitle", e_ews_item_get_job_title}, - { E_CONTACT_MANAGER, ELEMENT_TYPE_SIMPLE, "Manager", e_ews_item_get_manager}, - { E_CONTACT_OFFICE, ELEMENT_TYPE_SIMPLE, "OfficeLocation", e_ews_item_get_office_location}, - { E_CONTACT_SPOUSE, ELEMENT_TYPE_SIMPLE, "Profession", e_ews_item_get_profession}, - { E_CONTACT_SPOUSE, ELEMENT_TYPE_SIMPLE, "SpouseName", e_ews_item_get_spouse_name}, - { E_CONTACT_FAMILY_NAME, ELEMENT_TYPE_SIMPLE, "Surname", e_ews_item_get_surname}, - { E_CONTACT_GIVEN_NAME, ELEMENT_TYPE_COMPLEX, "GivenName", NULL, ebews_populate_givenname, ebews_set_givenname, ebews_set_givenname_changes}, - { E_CONTACT_BIRTH_DATE, ELEMENT_TYPE_COMPLEX, "WeddingAnniversary", NULL, ebews_populate_anniversary, ebews_set_anniversary, ebews_set_anniversary_changes }, - { E_CONTACT_PHOTO, ELEMENT_TYPE_COMPLEX, "Photo", NULL, ebews_populate_photo, ebews_set_photo, ebews_set_photo_changes }, - - /* Should take of uid and changekey (REV) */ - { E_CONTACT_UID, ELEMENT_TYPE_COMPLEX, "ItemId", NULL, ebews_populate_uid, ebews_set_item_id}, -}; - -static void -ebb_ews_write_dl_members (ESoapMessage *msg, - EContact *contact) -{ - GSList *emails, *l; - - e_soap_message_start_element (msg, "Members", NULL, NULL); - - emails = e_contact_get (contact, E_CONTACT_EMAIL); - for (l = emails; l; l = l->next) { - CamelInternetAddress *addr; - - if (l->data == NULL) - continue; - - addr = camel_internet_address_new (); - if (camel_address_decode (CAMEL_ADDRESS (addr), l->data) > 0) { - const gchar *name = NULL, *email = NULL; - - if (camel_internet_address_get (addr, 0, &name, &email) && email) { - e_soap_message_start_element (msg, "Member", NULL, NULL); - e_soap_message_start_element (msg, "Mailbox", NULL, NULL); - e_ews_message_write_string_parameter (msg, "Name", NULL, name ? name : email); - e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, email); - e_soap_message_end_element (msg); /* Mailbox */ - e_soap_message_end_element (msg); /* Member */ - } - } - g_object_unref (addr); - } - - g_slist_free_full (emails, g_free); - e_soap_message_end_element (msg); /* Members */ -} - -static void -ebb_ews_convert_dl_to_xml_cb (ESoapMessage *msg, - gpointer user_data) -{ - EContact *contact = user_data; - EVCardAttribute *attribute; - GList *values; - - /* Prepare DistributionList node in the SOAP message */ - e_soap_message_start_element (msg, "DistributionList", NULL, NULL); - - attribute = e_vcard_get_attribute (E_VCARD (contact), EVC_FN); - values = e_vcard_attribute_get_values (attribute); - e_ews_message_write_string_parameter (msg, "DisplayName", NULL, values->data); - - ebb_ews_write_dl_members (msg, contact); - - e_soap_message_end_element (msg); /* DistributionList */ -} - -static void -ebb_ews_convert_contact_to_xml_cb (ESoapMessage *msg, - gpointer user_data) -{ - EContact *contact = user_data; - gint i, element_type; - - /* Prepare Contact node in the SOAP message */ - e_soap_message_start_element (msg, "Contact", NULL, NULL); - - for (i = 0; i < G_N_ELEMENTS (mappings); i++) { - element_type = mappings[i].element_type; - - if (element_type == ELEMENT_TYPE_SIMPLE) { - gchar *val; - - /* skip uid while creating contacts */ - if (mappings[i].field_id == E_CONTACT_UID) - continue; - - val = e_contact_get (contact, mappings[i].field_id); - if (val && *val) - e_ews_message_write_string_parameter (msg, mappings[i].element_name, NULL, val); - g_free (val); - } else - mappings[i].set_value_in_soap_message (msg, contact); - } - - /* end of "Contact" */ - e_soap_message_end_element (msg); -} - -typedef struct _ConvertData { - EBookBackendEws *bbews; - GCancellable *cancellable; - GError **error; - - EContact *old_contact; - EContact *new_contact; - gchar *change_key; -} ConvertData; - -static void -ebb_ews_convert_dl_to_updatexml_cb (ESoapMessage *msg, - gpointer user_data) -{ - ConvertData *cd = user_data; - EContact *old_contact = cd->old_contact; - EContact *new_contact = cd->new_contact; - gchar *change_key = NULL; - - if (!cd->change_key) { - change_key = e_vcard_util_dup_x_attribute (E_VCARD (old_contact), X_EWS_CHANGEKEY); - if (!change_key) - change_key = e_contact_get (old_contact, E_CONTACT_REV); - } - - e_ews_message_start_item_change (msg, E_EWS_ITEMCHANGE_TYPE_ITEM, - e_contact_get_const (old_contact, E_CONTACT_UID), - cd->change_key ? cd->change_key : change_key, - 0); - e_ews_message_start_set_item_field (msg, "Members", "distributionlist", "DistributionList"); - ebb_ews_write_dl_members (msg, new_contact); - e_ews_message_end_set_item_field (msg); - e_ews_message_end_item_change (msg); - - g_free (change_key); -} - -static void -ebb_ews_convert_contact_to_updatexml_cb (ESoapMessage *msg, - gpointer user_data) -{ - ConvertData *cd = user_data; - EContact *old_contact = cd->old_contact; - EContact *new_contact = cd->new_contact; - gchar *value = NULL, *old_value = NULL; - gchar *change_key = NULL; - gint i, element_type; - - /* Pre-flight, to update the ChangeKey if needed */ - for (i = 0; i < G_N_ELEMENTS (mappings); i++) { - element_type = mappings[i].element_type; - - if (element_type == ELEMENT_TYPE_COMPLEX) { - gchar *new_change_key = NULL; - - if (mappings[i].field_id == E_CONTACT_UID) - continue; - - mappings[i].set_changes (cd->bbews, NULL, new_contact, old_contact, &new_change_key, cd->cancellable, cd->error); - - if (new_change_key) { - g_free (cd->change_key); - cd->change_key = new_change_key; - } - } - } - - if (!cd->change_key) { - change_key = e_vcard_util_dup_x_attribute (E_VCARD (old_contact), X_EWS_CHANGEKEY); - if (!change_key) - change_key = e_contact_get (old_contact, E_CONTACT_REV); - } - - e_ews_message_start_item_change (msg, E_EWS_ITEMCHANGE_TYPE_ITEM, - e_contact_get_const (old_contact, E_CONTACT_UID), - cd->change_key ? cd->change_key : change_key, - 0); - - /* Iterate for each field in contact */ - - for (i = 0; i < G_N_ELEMENTS (mappings); i++) { - element_type = mappings[i].element_type; - if (element_type == ELEMENT_TYPE_SIMPLE) { - value = e_contact_get (new_contact, mappings[i].field_id); - old_value = e_contact_get (old_contact, mappings[i].field_id); - if (g_strcmp0 (value, old_value) != 0) - convert_contact_property_to_updatexml (msg, mappings[i].element_name, value, "contacts", NULL, NULL); - if (value) - g_free (value); - if (old_value) - g_free (old_value); - } else if (element_type == ELEMENT_TYPE_COMPLEX) { - gchar *new_change_key = NULL; - - if (mappings[i].field_id == E_CONTACT_UID) - continue; - - mappings[i].set_changes (cd->bbews, msg, new_contact, old_contact, &new_change_key, cd->cancellable, cd->error); - - if (new_change_key) { - g_free (cd->change_key); - cd->change_key = new_change_key; - } - } - } - - e_ews_message_end_item_change (msg); - - g_free (change_key); -} - -static EContact * -ebb_ews_item_to_contact (EBookBackendEws *bbews, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - EContact *contact; - gint ii, element_type; - - contact = e_contact_new (); - - for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) { - element_type = mappings[ii].element_type; - - if (element_type == ELEMENT_TYPE_SIMPLE && !mappings[ii].populate_contact_func) { - const gchar *val = mappings[ii].get_simple_prop_func (item); - - if (val != NULL) - e_contact_set (contact, mappings[ii].field_id, val); - } else { - mappings[ii].populate_contact_func (bbews, contact, item, cancellable, error); - } - } - - return contact; -} - -static void -ebb_ews_items_to_contacts (EBookBackendEws *bbews, - const GSList *new_items, - GSList **contacts, - GCancellable *cancellable, - GError **error) -{ - GSList *link; - - for (link = (GSList *) new_items; link; link = g_slist_next (link)) { - EContact *contact; - EEwsItem *item = link->data; - EVCardAttribute *attr; - - if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - contact = ebb_ews_item_to_contact (bbews, item, cancellable, error); - - attr = e_vcard_attribute_new (NULL, "X-EWS-KIND"); - e_vcard_add_attribute_with_value (E_VCARD (contact), attr, "DT_MAILUSER"); - - *contacts = g_slist_prepend (*contacts, contact); - } -} - -static void -ebb_ews_mailbox_to_contact (EBookBackendEws *bbews, - EContact **contact, - GHashTable *values, - const EwsMailbox *mb) -{ - CamelInternetAddress *addr; - gchar *value; - - if (!mb->name && !mb->email) - return; - - addr = camel_internet_address_new (); - - camel_internet_address_add (addr, mb->name, mb->email ? mb->email : ""); - value = camel_address_encode (CAMEL_ADDRESS (addr)); - - if (value && (!values || g_hash_table_lookup (values, value) == NULL)) { - EVCardAttribute *attr; - - attr = e_vcard_attribute_new (NULL, EVC_EMAIL); - e_vcard_attribute_add_value (attr, value); - e_vcard_append_attribute (E_VCARD (*contact), attr); - - if (values) - g_hash_table_insert (values, g_strdup (value), GINT_TO_POINTER (1)); - } else { - g_free (value); - } - - g_object_unref (addr); -} - -static gboolean -ebb_ews_traverse_dl (EBookBackendEws *bbews, - EContact **contact, - GHashTable *items, - GHashTable *values, - EwsMailbox *mb, - GCancellable *cancellable, - GError **error) -{ - if (g_strcmp0 (mb->mailbox_type, "PrivateDL") == 0 || - g_strcmp0 (mb->mailbox_type, "PublicDL") == 0) { - GSList *members = NULL, *l; - gboolean includes_last; - gboolean ret = FALSE; - const gchar *ident; - GError *local_error = NULL; - - if (mb->item_id && mb->item_id->id) - ident = mb->item_id->id; - else if (mb->email) - ident = mb->email; - else - return FALSE; - - if (g_hash_table_lookup (items, ident) != NULL) - return TRUE; - - g_hash_table_insert (items, g_strdup (ident), GINT_TO_POINTER (1)); - - ret = e_ews_connection_expand_dl_sync ( - bbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - mb, - &members, - &includes_last, - cancellable, - &local_error); - - if (!ret) { - if (g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NAMERESOLUTIONNORESULTS)) { - g_clear_error (&local_error); - if (mb->email && *mb->email) - ebb_ews_mailbox_to_contact (bbews, contact, values, mb); - - ret = TRUE; - members = NULL; - } else { - if (local_error) - g_propagate_error (error, local_error); - - return FALSE; - } - } - - for (l = members; l; l = l->next) { - ret = ebb_ews_traverse_dl (bbews, contact, items, values, l->data, cancellable, error); - if (!ret) - break; - } - - g_slist_free_full (members, (GDestroyNotify) e_ews_mailbox_free); - return ret; - } else { - ebb_ews_mailbox_to_contact (bbews, contact, values, mb); - - return TRUE; - } -} - -static EContact * -ebb_ews_get_dl_info (EBookBackendEws *bbews, - EEwsItem *item, - const EwsId *id, - const gchar *d_name, - GSList *members, - GCancellable *cancellable, - GError **error) -{ - GHashTable *items, *values; - GSList *l; - EContact *contact; - - contact = e_contact_new (); - e_contact_set (contact, E_CONTACT_UID, id->id); - e_vcard_util_set_x_attribute (E_VCARD (contact), X_EWS_CHANGEKEY, id->change_key); - ebews_populate_rev (contact, item); - - e_contact_set (contact, E_CONTACT_IS_LIST, GINT_TO_POINTER (TRUE)); - e_contact_set (contact, E_CONTACT_LIST_SHOW_ADDRESSES, GINT_TO_POINTER (TRUE)); - e_contact_set (contact, E_CONTACT_FULL_NAME, d_name); - - items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - for (l = members; l != NULL; l = l->next) { - if (!ebb_ews_traverse_dl (bbews, &contact, items, values, l->data, cancellable, error)) { - g_object_unref (contact); - contact = NULL; - goto exit; - } - } - - exit: - g_hash_table_destroy (items); - g_hash_table_destroy (values); - - return contact; -} - -static gboolean -ebb_ews_get_dl_info_gal (EBookBackendEws *bbews, - EContact *contact, - EwsMailbox *mb, - GCancellable *cancellable, - GError **error) -{ - GHashTable *items, *values; - gboolean success; - - items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - values = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - success = ebb_ews_traverse_dl (bbews, &contact, items, values, mb, cancellable, error); - - if (success) { - e_contact_set (contact, E_CONTACT_IS_LIST, GINT_TO_POINTER (TRUE)); - e_contact_set (contact, E_CONTACT_LIST_SHOW_ADDRESSES, GINT_TO_POINTER (TRUE)); - } - - g_hash_table_destroy (items); - g_hash_table_destroy (values); - - return success; -} - -static gboolean -ebb_ews_contacts_append_dl (EBookBackendEws *bbews, - EEwsItem *item, - const EwsId *id, - const gchar *d_name, - GSList *members, - GSList **contacts, - GCancellable *cancellable, - GError **error) -{ - EContact *contact; - EVCardAttribute *attr; - - contact = ebb_ews_get_dl_info (bbews, item, id, d_name, members, cancellable, error); - if (contact == NULL) - return FALSE; - - attr = e_vcard_attribute_new (NULL, "X-EWS-KIND"); - e_vcard_add_attribute_with_value (E_VCARD (contact), attr, "DT_DISTLIST"); - - *contacts = g_slist_prepend (*contacts, contact); - - return TRUE; -} - -static gboolean -ebb_ews_fetch_items_sync (EBookBackendEws *bbews, - const GSList *items, /* EEwsItem * */ - GSList **contacts, - GCancellable *cancellable, - GError **error) -{ - GSList *contact_item_ids = NULL, *dl_ids = NULL, *link; - GSList *new_items = NULL; - gboolean ret = FALSE; - - for (link = (GSList *) items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (type == E_EWS_ITEM_TYPE_CONTACT) - contact_item_ids = g_slist_prepend (contact_item_ids, g_strdup (id->id)); - else if (type == E_EWS_ITEM_TYPE_GROUP) { - /* store a list of EwsMailBox's in case of distribution lists */ - dl_ids = g_slist_prepend (dl_ids, g_strdup (id->id)); - } - } - - /* TODO fetch attachments */ - if (contact_item_ids) { - EEwsAdditionalProps *add_props; - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup (CONTACT_ITEM_PROPS); - - ret = e_ews_connection_get_items_sync ( - bbews->priv->cnc, EWS_PRIORITY_MEDIUM, - contact_item_ids, "Default", add_props, - FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &new_items, NULL, NULL, - cancellable, error); - - e_ews_additional_props_free (add_props); - - if (!ret) - goto cleanup; - } - - if (new_items) { - ebb_ews_items_to_contacts (bbews, new_items, contacts, cancellable, error); - - g_slist_free_full (new_items, g_object_unref); - new_items = NULL; - } - - /* Get the display names of the distribution lists */ - if (dl_ids) { - if (!e_ews_connection_get_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, dl_ids, "Default", NULL, - FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &new_items, NULL, NULL, cancellable, error)) - goto cleanup; - } - - for (link = new_items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const gchar *d_name; - const EwsId *id; - EwsMailbox *mb; - GSList *members = NULL; - gboolean includes_last; - GError *local_error = NULL; - - if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - id = e_ews_item_get_id (item); - mb = g_new0 (EwsMailbox, 1); - mb->item_id = (EwsId *) id; - - d_name = e_ews_item_get_subject (item); - if (e_ews_connection_expand_dl_sync ( - bbews->priv->cnc, EWS_PRIORITY_MEDIUM, mb, &members, - &includes_last, cancellable, &local_error)) { - ret = ebb_ews_contacts_append_dl (bbews, item, id, d_name, members, contacts, cancellable, error); - g_slist_free_full (members, (GDestroyNotify) e_ews_mailbox_free); - } else { - ret = g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NAMERESOLUTIONNORESULTS); - if (ret) { - g_clear_error (&local_error); - } else if (local_error) { - g_propagate_error (error, local_error); - } - } - - g_free (mb); - - if (!ret) - goto cleanup; - } - - cleanup: - g_slist_free_full (new_items, g_object_unref); - g_slist_free_full (contact_item_ids, g_free); - g_slist_free_full (dl_ids, g_free); - - return ret; -} - -static void -ebb_ews_server_notification_cb (EBookBackendEws *bbews, - const GSList *events, - EEwsConnection *cnc) -{ - GSList *link; - gboolean update_folder = FALSE; - - g_return_if_fail (E_IS_BOOK_BACKEND_EWS (bbews)); - - for (link = (GSList *) events; link && !update_folder; link = g_slist_next (link)) { - EEwsNotificationEvent *event = link->data; - - switch (event->type) { - case E_EWS_NOTIFICATION_EVENT_CREATED: - case E_EWS_NOTIFICATION_EVENT_DELETED: - case E_EWS_NOTIFICATION_EVENT_MODIFIED: - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (g_strcmp0 (event->folder_id, bbews->priv->folder_id) == 0) - update_folder = TRUE; - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - break; - case E_EWS_NOTIFICATION_EVENT_MOVED: - case E_EWS_NOTIFICATION_EVENT_COPIED: - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (g_strcmp0 (event->folder_id, bbews->priv->folder_id) == 0 || - g_strcmp0 (event->old_folder_id, bbews->priv->folder_id) == 0) - update_folder = TRUE; - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - break; - default: - return; - } - } - - if (update_folder) - e_book_meta_backend_schedule_refresh (E_BOOK_META_BACKEND (bbews)); -} - -static void -ebb_ews_unset_connection (EBookBackendEws *bbews) -{ - g_return_if_fail (E_IS_BOOK_BACKEND_EWS (bbews)); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (bbews->priv->cnc) { - e_ews_connection_set_disconnected_flag (bbews->priv->cnc, TRUE); - - g_signal_handlers_disconnect_by_func (bbews->priv->cnc, ebb_ews_server_notification_cb, bbews); - - if (bbews->priv->subscription_key != 0) { - e_ews_connection_disable_notifications_sync ( - bbews->priv->cnc, - bbews->priv->subscription_key); - bbews->priv->subscription_key = 0; - } - } - - g_clear_object (&bbews->priv->cnc); - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); -} - -static gint -det_sort_func (gconstpointer _a, - gconstpointer _b) -{ - const EwsOALDetails *a = _a, *b = _b; - - return a->seq - b->seq; -} - -static gchar * -ebb_ews_download_gal_file (EBookBackendEws *bbews, - EwsOALDetails *full, - GCancellable *cancellable, - GError **error) -{ - EEwsConnection *oab_cnc; - gchar *full_url, *oab_url; - gchar *download_path = NULL; - gchar *password; - CamelEwsSettings *ews_settings; - const gchar *cache_dir; - - ews_settings = ebb_ews_get_collection_settings (bbews); - - /* oab url with oab.xml removed from the suffix */ - oab_url = camel_ews_settings_dup_oaburl (ews_settings); - if (!oab_url || !*oab_url) { - g_free (oab_url); - return NULL; - } - - if (g_str_has_suffix (oab_url, "oab.xml")) - oab_url [strlen (oab_url) - 7] = '\0'; - - full_url = g_strconcat (oab_url, full->filename, NULL); - cache_dir = e_book_backend_get_cache_dir (E_BOOK_BACKEND (bbews)); - download_path = g_build_filename (cache_dir, full->filename, NULL); - - oab_cnc = e_ews_connection_new_for_backend (E_BACKEND (bbews), e_book_backend_get_registry (E_BOOK_BACKEND (bbews)), full_url, ews_settings); - - e_binding_bind_property ( - bbews, "proxy-resolver", - oab_cnc, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - password = e_ews_connection_dup_password (bbews->priv->cnc); - e_ews_connection_set_password (oab_cnc, password); - g_free (password); - - if (!e_ews_connection_download_oal_file_sync (oab_cnc, download_path, NULL, NULL, cancellable, error)) { - g_free (download_path); - download_path = NULL; - } else { - d (printf ("OAL file downloaded %s\n", download_path)); - } - - g_object_unref (oab_cnc); - g_free (oab_url); - g_free (full_url); - - return download_path; -} - -static gchar * -ebb_ews_download_full_gal (EBookBackendEws *bbews, - EwsOALDetails *full, - GCancellable *cancellable, - GError **error) -{ - ESource *source; - const gchar *cache_dir; - gchar *lzx_path, *oab_file, *oab_path; - - lzx_path = ebb_ews_download_gal_file (bbews, full, cancellable, error); - if (!lzx_path) - return NULL; - - source = e_backend_get_source (E_BACKEND (bbews)); - oab_file = g_strdup_printf ("%s-%d.oab", e_source_get_display_name (source), full->seq); - cache_dir = e_book_backend_get_cache_dir (E_BOOK_BACKEND (bbews)); - oab_path = g_build_filename (cache_dir, oab_file, NULL); - - if (!ews_oab_decompress_full (lzx_path, oab_path, error)) { - g_free (oab_path); - oab_path = NULL; - } else { - d (printf ("OAL file decompressed %s\n", oab_path)); - } - - if (lzx_path) { - g_unlink (lzx_path); - g_free (lzx_path); - } - - g_free (oab_file); - - return oab_path; -} - -static gchar * -ebb_ews_download_gal (EBookBackendEws *bbews, - EBookCache *book_cache, - EwsOALDetails *full, - GSList *deltas, - guint32 seq, - GCancellable *cancellable, - GError **error) -{ -#ifdef WITH_MSPACK - GSList *link; - gchar *thisoab; - - thisoab = e_cache_dup_key (E_CACHE (book_cache), "oab-filename", NULL); - if (!thisoab) - goto full; - - for (link = deltas; link; link = g_slist_next (link)) { - EwsOALDetails *det = link->data; - ESource *source; - gchar *oab_file, *lzx_path, *nextoab; - const gchar *cache_dir; - GError *local_error = NULL; - - seq++; - if (det->seq != seq) - break; - - lzx_path = ebb_ews_download_gal_file (bbews, det, cancellable, NULL); - if (!lzx_path) - break; - - source = e_backend_get_source (E_BACKEND (bbews)); - oab_file = g_strdup_printf ("%s-%d.oab", e_source_get_display_name (source), seq); - cache_dir = e_book_backend_get_cache_dir (E_BOOK_BACKEND (bbews)); - nextoab = g_build_filename (cache_dir, oab_file, NULL); - g_free (oab_file); - - ews_oab_decompress_patch (lzx_path, thisoab, nextoab, &local_error); - - /* Free the LZX file */ - g_unlink (lzx_path); - g_free (lzx_path); - - /* Free the previous OAB file */ - g_unlink (thisoab); - g_free (thisoab); - - thisoab = nextoab; - - /* For once we are *allowed* to use the error instead of having to - * check the return value of the function. It's our *own* error. */ - if (local_error) { - d (printf ("Failed to apply incremental patch: %s\n", local_error->message)); - g_error_free (local_error); - break; - } - - d (printf ("Created %s from delta\n", thisoab)); - - if (seq == full->seq) - return thisoab; - } - - if (thisoab) { - g_unlink (thisoab); - g_free (thisoab); - } - full: -#endif /* WITH_MSPACK */ - d (printf ("Ewsgal: Downloading full gal \n")); - return ebb_ews_download_full_gal (bbews, full, cancellable, error); -} - -static void -ebb_ews_remove_old_gal_file (EBookCache *book_cache) -{ - gchar *filename; - - g_return_if_fail (E_IS_BOOK_CACHE (book_cache)); - - filename = e_cache_dup_key (E_CACHE (book_cache), "oab-filename", NULL); - - if (filename) - g_unlink (filename); - g_free (filename); -} - -struct _db_data { - GHashTable *uids; - GHashTable *sha1s; - gint unchanged; - gint changed; - gint added; - gint percent; - GSList *created_objects; - GSList *modified_objects; -}; - -static gboolean -ebb_ews_gal_filter_contact (goffset offset, - const gchar *sha1, - gpointer user_data, - GError **error) -{ - struct _db_data *data = (struct _db_data *) user_data; - gchar *uid; - - /* Is there an existing identical record, with the same SHA1? */ - uid = g_hash_table_lookup (data->sha1s, sha1); - if (!uid) - return TRUE; - - /* Remove it from the hash tables so it doesn't get deleted at the end. */ - g_hash_table_remove (data->sha1s, sha1); - g_hash_table_remove (data->uids, uid); - data->unchanged++; - - /* Don't bother to parse and process this record. */ - return FALSE; -} - -static void -ebb_ews_gal_store_contact (EContact *contact, - goffset offset, - const gchar *sha1, - guint percent, - gpointer user_data, - GError **error) -{ - struct _db_data *data = (struct _db_data *) user_data; - - if (contact) { - const gchar *uid = e_contact_get_const (contact, E_CONTACT_UID); - EBookMetaBackendInfo *nfo; - - ebews_populate_rev (contact, NULL); - e_vcard_util_set_x_attribute (E_VCARD (contact), X_EWS_GAL_SHA1, sha1); - - nfo = e_book_meta_backend_info_new (uid, e_contact_get_const (contact, E_CONTACT_REV), NULL, NULL); - nfo->object = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30); - - if (g_hash_table_remove (data->uids, uid)) { - data->changed++; - data->modified_objects = g_slist_prepend (data->modified_objects, nfo); - } else { - data->added++; - data->created_objects = g_slist_prepend (data->created_objects, nfo); - } - } - - if (data->percent != percent) { - data->percent = percent; - - d (printf ("GAL processing contacts, %d%% complete (%d added, %d changed, %d unchanged\n", - percent, data->added, data->changed, data->unchanged)); - } -} - -static gboolean -ebb_ews_gather_existing_uids_cb (EBookCache *book_cache, - const gchar *uid, - const gchar *revision, - const gchar *object, - const gchar *extra, - EOfflineState offline_state, - gpointer user_data) -{ - struct _db_data *data = user_data; - EVCard *vcard; - gchar *dup_uid, *dup_sha1 = NULL; - - g_return_val_if_fail (data != NULL, FALSE); - g_return_val_if_fail (data->uids != NULL, FALSE); - g_return_val_if_fail (data->sha1s != NULL, FALSE); - g_return_val_if_fail (object != NULL, FALSE); - - vcard = e_vcard_new_from_string (object); - if (vcard) { - dup_sha1 = e_vcard_util_dup_x_attribute (vcard, X_EWS_GAL_SHA1); - g_object_unref (vcard); - } - - dup_uid = g_strdup (uid); - if (!dup_sha1) - dup_sha1 = g_strdup (revision); - - g_hash_table_insert (data->uids, dup_uid, dup_sha1); - if (dup_sha1) - g_hash_table_insert (data->sha1s, dup_sha1, dup_uid); - - return TRUE; -} - -static gboolean -ebb_ews_check_gal_changes (EBookBackendEws *bbews, - EBookCache *book_cache, - const gchar *filename, - GSList **out_created_objects, /*EBookMetaBackendInfo * */ - GSList **out_modified_objects, /*EBookMetaBackendInfo * */ - GSList **out_removed_objects, /*EBookMetaBackendInfo * */ - GCancellable *cancellable, - GError **error) -{ - EwsOabDecoder *eod; - gboolean success = TRUE; - struct _db_data data; -#if d(1) + 0 - gint64 t1, t2; -#endif - GError *local_error = NULL; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (bbews), FALSE); - g_return_val_if_fail (E_IS_BOOK_CACHE (book_cache), FALSE); - g_return_val_if_fail (filename != NULL, FALSE); - g_return_val_if_fail (out_created_objects != NULL, FALSE); - g_return_val_if_fail (out_modified_objects != NULL, FALSE); - g_return_val_if_fail (out_removed_objects != NULL, FALSE); - - data.created_objects = NULL; - data.modified_objects = NULL; - data.unchanged = data.changed = data.added = 0; - data.percent = 0; - data.uids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - data.sha1s = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - d (t1 = g_get_monotonic_time ()); - - e_book_cache_search_with_callback (book_cache, NULL, ebb_ews_gather_existing_uids_cb, &data, cancellable, NULL); - - eod = ews_oab_decoder_new (filename, bbews->priv->attachments_dir, &local_error); - if (!local_error) { - GHashTableIter iter; - gpointer key; - - success = ews_oab_decoder_decode (eod, ebb_ews_gal_filter_contact, ebb_ews_gal_store_contact, &data, cancellable, &local_error); - - if (success) { - *out_created_objects = data.created_objects; - *out_modified_objects = data.modified_objects; - *out_removed_objects = NULL; - - g_hash_table_iter_init (&iter, data.uids); - while (g_hash_table_iter_next (&iter, &key, NULL)) { - const gchar *uid = key; - - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_book_meta_backend_info_new (uid, NULL, NULL, NULL)); - } - } else { - g_slist_free_full (data.created_objects, e_book_meta_backend_info_free); - g_slist_free_full (data.modified_objects, e_book_meta_backend_info_free); - } - } else { - success = FALSE; - } - - d (t2 = g_get_monotonic_time ()); - d (printf ("GAL update completed %ssuccessfully in %" G_GINT64_FORMAT " µs. Added: %d, Changed: %d, Unchanged %d, Removed: %d (%s)\n", - success ? "" : "un", (gint64) (t2 - t1), data.added, data.changed, data.unchanged, g_hash_table_size (data.uids), - local_error ? local_error->message : "no error")); - - g_hash_table_destroy (data.sha1s); - g_hash_table_destroy (data.uids); - - if (local_error) - g_propagate_error (error, local_error); - - return success; -} - -static void -ebb_ews_remove_original_vcard (EContact *contact) -{ - g_return_if_fail (E_IS_CONTACT (contact)); - - e_vcard_remove_attributes (E_VCARD (contact), NULL, X_EWS_ORIGINAL_VCARD); -} - -static void -ebb_ews_store_original_vcard (EContact *contact) -{ - EVCard *vcard; - EVCardAttribute *attr; - gchar *vcard_str; - - g_return_if_fail (E_IS_CONTACT (contact)); - - ebb_ews_remove_original_vcard (contact); - - vcard = E_VCARD (contact); - - vcard_str = e_vcard_to_string (vcard, EVC_FORMAT_VCARD_30); - - attr = e_vcard_attribute_new ("", X_EWS_ORIGINAL_VCARD); - e_vcard_attribute_add_value (attr, vcard_str); - e_vcard_add_attribute (vcard, attr); - - g_free (vcard_str); -} - -static const gchar * -ebb_ews_get_original_vcard (EContact *contact) -{ - EVCardAttribute *attr; - GList *values = NULL; - const gchar *vcard; - - g_return_val_if_fail (E_IS_CONTACT (contact), NULL); - - attr = e_vcard_get_attribute (E_VCARD (contact), X_EWS_ORIGINAL_VCARD); - if (!attr) - return NULL; - - values = e_vcard_attribute_get_values (attr); - if (!values) - return NULL; - - vcard = values->data; - - if (vcard && *vcard) - return vcard; - - return NULL; -} - -typedef struct { - /* For future use */ - gpointer restriction; - - gboolean is_autocompletion; - gchar *auto_comp_str; -} EBookBackendEwsSExpData; - -static ESExpResult * -ebb_ews_func_not (ESExp *f, - gint argc, - ESExpResult **argv, - gpointer data) -{ - ESExpResult *r; - - if (argc != 1 || argv[0]->type != ESEXP_RES_UNDEFINED) { - e_sexp_fatal_error (f, "parse error"); - return NULL; - } - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - - return r; -} - -static ESExpResult * -ebb_ews_func_and_or (ESExp *f, - gint argc, - ESExpResult **argv, - gpointer and) -{ - ESExpResult *r; - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - - return r; -} - -/* TODO implement */ -static ESExpResult * -ebb_ews_func_is (struct _ESExp *f, - gint argc, - struct _ESExpResult **argv, - gpointer data) -{ - ESExpResult *r; - - if (argc != 2 - && argv[0]->type != ESEXP_RES_STRING - && argv[1]->type != ESEXP_RES_STRING) { - e_sexp_fatal_error (f, "parse error"); - return NULL; - } - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - - return r; -} - -/* TODO implement */ -static ESExpResult * -ebb_ews_func_endswith (struct _ESExp *f, - gint argc, - struct _ESExpResult **argv, - gpointer data) -{ - ESExpResult *r; - - if (argc != 2 - && argv[0]->type != ESEXP_RES_STRING - && argv[1]->type != ESEXP_RES_STRING) { - e_sexp_fatal_error (f, "parse error"); - return NULL; - } - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - - return r; - -} - -/* TODO implement */ -static ESExpResult * -ebb_ews_func_contains (struct _ESExp *f, - gint argc, - struct _ESExpResult **argv, - gpointer data) -{ - ESExpResult *r; - EBookBackendEwsSExpData *sdata = data; - const gchar *propname, *str; - - if (argc != 2 - && argv[0]->type != ESEXP_RES_STRING - && argv[1]->type != ESEXP_RES_STRING) { - e_sexp_fatal_error (f, "parse error"); - return NULL; - } - - propname = argv[0]->value.string; - str = argv[1]->value.string; - - if (!strcmp (propname, "full_name") || !strcmp (propname, "email")) { - if (!sdata->auto_comp_str) { - sdata->auto_comp_str = g_strdup (str); - sdata->is_autocompletion = TRUE; - } - } - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - - return r; - -} - -/* We are just handling for autocompletion now. We need to support other fields after implementing - * Restrictions and find_items request */ -static ESExpResult * -ebb_ews_func_beginswith (struct _ESExp *f, - gint argc, - struct _ESExpResult **argv, - gpointer data) -{ - ESExpResult *r; - const gchar *propname, *str; - EBookBackendEwsSExpData *sdata = data; - - if (argc != 2 || - argv[0]->type != ESEXP_RES_STRING || - argv[1]->type != ESEXP_RES_STRING) { - e_sexp_fatal_error (f, "parse error"); - return NULL; - } - - propname = argv[0]->value.string; - str = argv[1]->value.string; - - if (!strcmp (propname, "full_name") || !strcmp (propname, "email")) { - if (!sdata->auto_comp_str) { - sdata->auto_comp_str = g_strdup (str); - sdata->is_autocompletion = TRUE; - } - } - - r = e_sexp_result_new (f, ESEXP_RES_BOOL); - r->value.boolean = FALSE; - return r; -} - -static struct { - const gchar *name; - ESExpFunc *func; - guint flags; -} symbols[] = { - { "and", ebb_ews_func_and_or, 0 }, - { "or", ebb_ews_func_and_or, 0}, - { "not", ebb_ews_func_not, 0 }, - { "contains", ebb_ews_func_contains, 0}, - { "is", ebb_ews_func_is, 0}, - { "beginswith", ebb_ews_func_beginswith, 0}, - { "endswith", ebb_ews_func_endswith, 0}, -}; - -/* FIXME build a complete filter from the query that can be used by find_items */ -static gboolean -ebb_ews_build_restriction (const gchar *query, - gchar **auto_comp_str) -{ - ESExpResult *r; - ESExp *sexp; - EBookBackendEwsSExpData *sdata; - gboolean autocompletion = FALSE; - gint i; - - *auto_comp_str = NULL; - - sexp = e_sexp_new (); - sdata = g_new0 (EBookBackendEwsSExpData, 1); - sdata->is_autocompletion = FALSE; - - for (i = 0; i < G_N_ELEMENTS (symbols); i++) { - e_sexp_add_function ( - sexp, 0, (gchar *) symbols[i].name, - symbols[i].func, - sdata); - } - - e_sexp_input_text (sexp, query, strlen (query)); - e_sexp_parse (sexp); - - r = e_sexp_eval (sexp); - if (r) { - autocompletion = sdata->is_autocompletion; - if (autocompletion) - *auto_comp_str = sdata->auto_comp_str; - else - g_free (sdata->auto_comp_str); - } - - e_sexp_result_free (sexp, r); - g_object_unref (sexp); - g_free (sdata); - - return autocompletion && *auto_comp_str; -} - -static gboolean -ebb_ews_update_cache_for_expression (EBookBackendEws *bbews, - const gchar *expr, - GCancellable *cancellable, - GError **error) -{ - EBookMetaBackend *meta_backend; - CamelEwsSettings *ews_settings; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (bbews), FALSE); - - /* Resolve names in GAL only for GAL */ - if (!bbews->priv->is_gal) - return TRUE; - - ews_settings = ebb_ews_get_collection_settings (bbews); - - if (camel_ews_settings_get_oab_offline (ews_settings)) - return TRUE; - - meta_backend = E_BOOK_META_BACKEND (bbews); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - /* Search only if not searching for everything */ - if (expr && *expr && g_ascii_strcasecmp (expr, "(contains \"x-evolution-any-field\" \"\")") != 0) { - gchar *restriction_expr = NULL; - GSList *mailboxes = NULL, *contacts = NULL, *found_infos = NULL; - gboolean includes_last_item = TRUE; - - success = ebb_ews_build_restriction (expr, &restriction_expr) && - e_book_meta_backend_ensure_connected_sync (meta_backend, cancellable, error) && - e_ews_connection_resolve_names_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, restriction_expr, - EWS_SEARCH_AD, NULL, TRUE, &mailboxes, &contacts, &includes_last_item, cancellable, error); - - if (success) { - GSList *mlink, *clink; - - for (mlink = mailboxes, clink = contacts; mlink; mlink = g_slist_next (mlink), clink = g_slist_next (clink)) { - EwsMailbox *mb = mlink->data; - EEwsItem *contact_item = clink ? clink->data : NULL; - EBookMetaBackendInfo *nfo; - EContact *contact = NULL; - gboolean is_public_dl = FALSE; - const gchar *str; - - if (g_strcmp0 (mb->mailbox_type, "PublicDL") == 0) { - contact = e_contact_new (); - - if (!ebb_ews_get_dl_info_gal (bbews, contact, mb, cancellable, NULL)) { - g_clear_object (&contact); - } else { - is_public_dl = TRUE; - } - } - - if (!contact && contact_item && e_ews_item_get_item_type (contact_item) == E_EWS_ITEM_TYPE_CONTACT) - contact = ebb_ews_item_to_contact (bbews, contact_item, cancellable, NULL); - - if (!contact) - contact = e_contact_new (); - - /* We do not get an id from the server, so just using email_id as uid for now */ - e_contact_set (contact, E_CONTACT_UID, mb->email); - - /* There is no ChangeKey provided either, thus make up some revision, - to have the contact always updated in the local cache. */ - ebews_populate_rev (contact, NULL); - - str = e_contact_get_const (contact, E_CONTACT_FULL_NAME); - if (!str || !*str) - e_contact_set (contact, E_CONTACT_FULL_NAME, mb->name); - - str = e_contact_get_const (contact, E_CONTACT_EMAIL_1); - if (!str || !*str || (!is_public_dl && contact_item && e_ews_item_get_item_type (contact_item) == E_EWS_ITEM_TYPE_CONTACT)) { - /* Cleanup first, then re-add only SMTP addresses */ - e_contact_set (contact, E_CONTACT_EMAIL_1, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_2, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_3, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_4, NULL); - e_contact_set (contact, E_CONTACT_EMAIL, NULL); - - ebews_populate_emails_ex (bbews, contact, contact_item, TRUE); - } - - str = e_contact_get_const (contact, E_CONTACT_EMAIL_1); - if (!str || !*str) { - e_contact_set (contact, E_CONTACT_EMAIL_1, mb->email); - } else if (!is_public_dl && mb->email && (!mb->routing_type || g_ascii_strcasecmp (mb->routing_type, "SMTP") == 0)) { - EContactField fields[3] = { E_CONTACT_EMAIL_2, E_CONTACT_EMAIL_3, E_CONTACT_EMAIL_4 }; - gchar *emails[3]; - gint ii, ff = 0; - - emails[0] = e_contact_get (contact, E_CONTACT_EMAIL_1); - emails[1] = e_contact_get (contact, E_CONTACT_EMAIL_2); - emails[2] = e_contact_get (contact, E_CONTACT_EMAIL_3); - - /* Make the mailbox email the primary email and skip duplicates */ - e_contact_set (contact, E_CONTACT_EMAIL_1, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_2, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_3, NULL); - e_contact_set (contact, E_CONTACT_EMAIL_4, NULL); - e_contact_set (contact, E_CONTACT_EMAIL, NULL); - - e_contact_set (contact, E_CONTACT_EMAIL_1, mb->email); - - for (ii = 0; ii < 3; ii++) { - if (emails[ii] && g_ascii_strcasecmp (emails[ii], mb->email) != 0) { - e_contact_set (contact, fields[ff], emails[ii]); - ff++; - } - - g_free (emails[ii]); - } - } - - ebb_ews_store_original_vcard (contact); - - nfo = e_book_meta_backend_info_new (e_contact_get_const (contact, E_CONTACT_UID), - e_contact_get_const (contact, E_CONTACT_REV), NULL, NULL); - nfo->object = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30); - - found_infos = g_slist_prepend (found_infos, nfo); - - g_object_unref (contact); - } - } - - g_slist_free_full (mailboxes, (GDestroyNotify) e_ews_mailbox_free); - e_util_free_nullable_object_slist (contacts); - - if (success) { - GSList *created_objects = NULL, *modified_objects = NULL; - - success = e_book_meta_backend_split_changes_sync (meta_backend, found_infos, &created_objects, - &modified_objects, NULL, cancellable, error); - if (success) - success = e_book_meta_backend_process_changes_sync (meta_backend, created_objects, - modified_objects, NULL, cancellable, error); - - g_slist_free_full (created_objects, e_book_meta_backend_info_free); - g_slist_free_full (modified_objects, e_book_meta_backend_info_free); - } - - g_slist_free_full (found_infos, e_book_meta_backend_info_free); - g_free (restriction_expr); - } - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - ebb_ews_convert_error_to_edb_error (error); - ebb_ews_maybe_disconnect_sync (bbews, error, cancellable); - - return success; -} - -static GSList * /* the possibly modified 'in_items' */ -ebb_ews_verify_changes (EBookCache *book_cache, - GSList *in_items, /* EEwsItem * */ - GCancellable *cancellable) -{ - GSList *items = NULL, *link; - - g_return_val_if_fail (E_IS_BOOK_CACHE (book_cache), in_items); - - for (link = in_items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (!g_cancellable_is_cancelled (cancellable) && ( - type == E_EWS_ITEM_TYPE_CONTACT || - type == E_EWS_ITEM_TYPE_GROUP)) { - EContact *existing = NULL; - - if (e_book_cache_get_contact (book_cache, id->id, TRUE, &existing, cancellable, NULL) && - existing) { - gchar *change_key; - - change_key = e_vcard_util_dup_x_attribute (E_VCARD (existing), X_EWS_CHANGEKEY); - if (!change_key) - change_key = e_contact_get (existing, E_CONTACT_REV); - - if (g_strcmp0 (change_key, id->change_key) == 0) { - g_object_unref (item); - } else { - items = g_slist_prepend (items, item); - } - - g_free (change_key); - } else { - items = g_slist_prepend (items, item); - } - - g_clear_object (&existing); - } else { - items = g_slist_prepend (items, item); - } - } - - g_slist_free (in_items); - - return items; -} - -static GSList * /* EBookMetaBackendInfo */ -ebb_ews_contacts_to_infos (const GSList *contacts) /* EContact * */ -{ - GSList *nfos = NULL, *link; - - for (link = (GSList *) contacts; link; link = g_slist_next (link)) { - EContact *contact = link->data; - EBookMetaBackendInfo *nfo; - - if (!E_IS_CONTACT (contact)) - continue; - - ebb_ews_store_original_vcard (contact); - - nfo = e_book_meta_backend_info_new ( - e_contact_get_const (contact, E_CONTACT_UID), - e_contact_get_const (contact, E_CONTACT_REV), - NULL, NULL); - nfo->object = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30); - - nfos = g_slist_prepend (nfos, nfo); - } - - return nfos; -} - -static gboolean -ebb_ews_connect_sync (EBookMetaBackend *meta_backend, - const ENamedParameters *credentials, - ESourceAuthenticationResult *out_auth_result, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - CamelEwsSettings *ews_settings; - gchar *hosturl; - gboolean success = FALSE; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_auth_result != NULL, FALSE); - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (bbews->priv->cnc) { - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - *out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED; - - return TRUE; - } - - ews_settings = ebb_ews_get_collection_settings (bbews); - hosturl = camel_ews_settings_dup_hosturl (ews_settings); - - bbews->priv->cnc = e_ews_connection_new_for_backend (E_BACKEND (bbews), e_book_backend_get_registry (E_BOOK_BACKEND (bbews)), hosturl, ews_settings); - - e_binding_bind_property ( - bbews, "proxy-resolver", - bbews->priv->cnc, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - *out_auth_result = e_ews_connection_try_credentials_sync (bbews->priv->cnc, credentials, cancellable, error); - - if (*out_auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) { - ESource *source = e_backend_get_source (E_BACKEND (bbews)); - ESourceEwsFolder *ews_folder; - gchar *gal_uid; - - ews_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER); - - g_free (bbews->priv->folder_id); - bbews->priv->folder_id = e_source_ews_folder_dup_id (ews_folder); - - gal_uid = camel_ews_settings_dup_gal_uid (ews_settings); - bbews->priv->is_gal = g_strcmp0 (e_source_get_uid (source), gal_uid) == 0; - - g_free (gal_uid); - - g_signal_connect_swapped (bbews->priv->cnc, "server-notification", - G_CALLBACK (ebb_ews_server_notification_cb), bbews); - - if (!bbews->priv->is_gal && - camel_ews_settings_get_listen_notifications (ews_settings) && - e_ews_connection_satisfies_server_version (bbews->priv->cnc, E_EWS_EXCHANGE_2010_SP1)) { - GSList *folders = NULL; - - folders = g_slist_prepend (folders, bbews->priv->folder_id); - - e_ews_connection_enable_notifications_sync (bbews->priv->cnc, - folders, &bbews->priv->subscription_key); - - g_slist_free (folders); - } - - e_book_backend_set_writable (E_BOOK_BACKEND (bbews), !bbews->priv->is_gal); - success = TRUE; - } else { - ebb_ews_convert_error_to_edb_error (error); - g_clear_object (&bbews->priv->cnc); - } - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - g_free (hosturl); - - return success; -} - -static gboolean -ebb_ews_disconnect_sync (EBookMetaBackend *meta_backend, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - ebb_ews_unset_connection (bbews); - - return TRUE; -} - -static gboolean -ebb_ews_get_changes_sync (EBookMetaBackend *meta_backend, - const gchar *last_sync_tag, - gboolean is_repeat, - gchar **out_new_sync_tag, - gboolean *out_repeat, - GSList **out_created_objects, - GSList **out_modified_objects, - GSList **out_removed_objects, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - EBookCache *book_cache; - gboolean success = TRUE; - GError *local_error = NULL; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_new_sync_tag != NULL, FALSE); - g_return_val_if_fail (out_repeat != NULL, FALSE); - g_return_val_if_fail (out_created_objects != NULL, FALSE); - g_return_val_if_fail (out_modified_objects != NULL, FALSE); - g_return_val_if_fail (out_removed_objects != NULL, FALSE); - - *out_created_objects = NULL; - *out_modified_objects = NULL; - *out_removed_objects = NULL; - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - book_cache = e_book_meta_backend_ref_cache (meta_backend); - g_return_val_if_fail (E_IS_BOOK_CACHE (book_cache), FALSE); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (bbews->priv->is_gal) { - CamelEwsSettings *ews_settings; - gchar *oab_url; - - ews_settings = ebb_ews_get_collection_settings (bbews); - oab_url = camel_ews_settings_dup_oaburl (ews_settings); - - if (oab_url && *oab_url && - camel_ews_settings_get_oab_offline (ews_settings)) { - EEwsConnection *oab_cnc; - GSList *full_l = NULL, *deltas = NULL, *link; - EwsOALDetails *full = NULL; - gchar *password, *etag = NULL; - gint sequence; - - sequence = e_cache_get_key_int (E_CACHE (book_cache), "gal-sequence", NULL); - if (sequence == -1) - sequence = 0; - - oab_cnc = e_ews_connection_new_for_backend (E_BACKEND (bbews), e_book_backend_get_registry (E_BOOK_BACKEND (bbews)), oab_url, ews_settings); - - e_binding_bind_property ( - bbews, "proxy-resolver", - oab_cnc, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - password = e_ews_connection_dup_password (bbews->priv->cnc); - e_ews_connection_set_password (oab_cnc, password); - e_util_safe_free_string (password); - - d (printf ("Ewsgal: Fetching oal full details file\n")); - if (!e_ews_connection_get_oal_detail_sync (oab_cnc, bbews->priv->folder_id, NULL, last_sync_tag, &full_l, &etag, cancellable, &local_error)) { - if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_NOT_MODIFIED)) { - g_clear_error (&local_error); - } else { - success = FALSE; - } - } - - if (success && full_l) { - guint32 delta_size = 0; - - for (link = full_l; link; link = g_slist_next (link)) { - EwsOALDetails *det = link->data; - - /* Throw away anything older than we already have */ - if (det->seq <= sequence) { - ews_oal_details_free (det); - } else if (!g_strcmp0 (det->type, "Full")) { - if (full) - ews_oal_details_free (full); - full = det; - } else if (sequence > 0 && !g_strcmp0 (det->type, "Diff")) { - delta_size += det->size; - deltas = g_slist_insert_sorted (deltas, det, det_sort_func); - } else { - ews_oal_details_free (det); - } - } - - g_slist_free (full_l); - full_l = NULL; - - /* If the deltas would be bigger, just download the new full file */ - if (full && delta_size > full->size) { - g_slist_free_full (deltas, (GDestroyNotify) ews_oal_details_free); - deltas = NULL; - } - } - - if (full) { - gchar *uncompressed_filename; - - uncompressed_filename = ebb_ews_download_gal (bbews, book_cache, full, deltas, sequence, cancellable, &local_error); - if (!uncompressed_filename) { - success = FALSE; - } else { - d (printf ("Ewsgal: Removing old gal\n")); - /* remove old_gal_file */ - ebb_ews_remove_old_gal_file (book_cache); - - d (printf ("Ewsgal: Check for changes in GAL\n")); - success = ebb_ews_check_gal_changes (bbews, book_cache, uncompressed_filename, - out_created_objects, out_modified_objects, out_removed_objects, cancellable, &local_error); - - if (success) { - if (e_cache_set_key (E_CACHE (book_cache), "oab-filename", uncompressed_filename, NULL)) { - /* Don't let it get deleted */ - g_free (uncompressed_filename); - uncompressed_filename = NULL; - } - - e_cache_set_key_int (E_CACHE (book_cache), "gal-sequence", full->seq, NULL); - - d (printf ("Ewsgal: sync successfully completed\n")); - } - - ews_oal_details_free (full); - } - - if (uncompressed_filename) { - /* preserve the oab file once we are able to decode the differential updates */ - g_unlink (uncompressed_filename); - g_free (uncompressed_filename); - } - } - - g_slist_free_full (full_l, (GDestroyNotify) ews_oal_details_free); - g_slist_free_full (deltas, (GDestroyNotify) ews_oal_details_free); - g_clear_object (&oab_cnc); - - if (success) - *out_new_sync_tag = etag; - else - g_free (etag); - - if (local_error) { - g_prefix_error (&local_error, "%s", _("Failed to update GAL:")); - g_propagate_error (error, local_error); - } - } - - g_free (oab_url); - } else { - GSList *items_created = NULL, *items_modified = NULL, *items_deleted = NULL, *link; - gboolean includes_last_item = TRUE; - - success = e_ews_connection_sync_folder_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, - last_sync_tag, bbews->priv->folder_id, "IdOnly", NULL, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - - if (!success && - g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) { - g_clear_error (&local_error); - - e_book_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL); - - success = e_ews_connection_sync_folder_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, - NULL, bbews->priv->folder_id, "IdOnly", NULL, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - } - - if (success) { - GSList *contacts_created = NULL, *contacts_modified = NULL; - - /* The sync state doesn't cover changes made by save_contact_sync(), - thus verify the changes, instead of re-donwloading the contacts again */ - items_created = ebb_ews_verify_changes (book_cache, items_created, cancellable); - items_modified = ebb_ews_verify_changes (book_cache, items_modified, cancellable); - - if (items_created) { - success = ebb_ews_fetch_items_sync (bbews, items_created, &contacts_created, cancellable, error); - if (success) - *out_created_objects = ebb_ews_contacts_to_infos (contacts_created); - } - - if (items_modified) { - success = ebb_ews_fetch_items_sync (bbews, items_modified, &contacts_modified, cancellable, error); - if (success) - *out_modified_objects = ebb_ews_contacts_to_infos (contacts_modified); - } - - for (link = items_deleted; link; link = g_slist_next (link)) { - const gchar *uid = link->data; - - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_book_meta_backend_info_new (uid, NULL, NULL, NULL)); - } - - g_slist_free_full (contacts_created, g_object_unref); - g_slist_free_full (contacts_modified, g_object_unref); - - *out_repeat = !includes_last_item; - } else if (local_error) { - g_propagate_error (error, local_error); - } - - g_slist_free_full (items_created, g_object_unref); - g_slist_free_full (items_modified, g_object_unref); - g_slist_free_full (items_deleted, g_free); - } - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - ebb_ews_convert_error_to_edb_error (error); - ebb_ews_maybe_disconnect_sync (bbews, error, cancellable); - - g_clear_object (&book_cache); - - return success; -} - -static gboolean -ebb_ews_load_contact_sync (EBookMetaBackend *meta_backend, - const gchar *uid, - const gchar *extra, - EContact **out_contact, - gchar **out_extra, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - GSList *ids, *items = NULL; - gboolean success; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (uid != NULL, FALSE); - g_return_val_if_fail (out_contact, FALSE); - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - ids = g_slist_prepend (NULL, (gpointer) uid); - - success = e_ews_connection_get_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, ids, "IdOnly", - NULL, FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &items, NULL, NULL, cancellable, error); - - g_slist_free (ids); - - if (!items) - success = FALSE; - - if (success) { - GSList *contacts = NULL; - - success = ebb_ews_fetch_items_sync (bbews, items, &contacts, cancellable, error); - if (success && contacts) { - *out_contact = g_object_ref (contacts->data); - - ebb_ews_store_original_vcard (*out_contact); - } - - g_slist_free_full (contacts, g_object_unref); - } - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - g_slist_free_full (items, g_object_unref); - - ebb_ews_convert_error_to_edb_error (error); - ebb_ews_maybe_disconnect_sync (bbews, error, cancellable); - - return success; -} - -static gboolean -ebb_ews_save_contact_sync (EBookMetaBackend *meta_backend, - gboolean overwrite_existing, - EConflictResolution conflict_resolution, - /* const */ EContact *contact, - const gchar *extra, - gchar **out_new_uid, - gchar **out_new_extra, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - EwsFolderId *fid; - GSList *items = NULL; - gboolean is_dl = FALSE; - gboolean success; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (E_IS_CONTACT (contact), FALSE); - g_return_val_if_fail (out_new_uid != NULL, FALSE); - g_return_val_if_fail (out_new_extra != NULL, FALSE); - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - if (e_contact_get (contact, E_CONTACT_IS_LIST)) { - if (!e_ews_connection_satisfies_server_version (bbews->priv->cnc, E_EWS_EXCHANGE_2010)) { - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - g_propagate_error (error, EDB_ERROR_EX (NOT_SUPPORTED, - _("Cannot save contact list, it’s only supported on EWS Server 2010 or later"))); - return FALSE; - } - - is_dl = TRUE; - } - - fid = e_ews_folder_id_new (bbews->priv->folder_id, NULL, FALSE); - if (overwrite_existing) { - EBookCache *book_cache; - EContact *old_contact = NULL; - - book_cache = e_book_meta_backend_ref_cache (meta_backend); - - success = e_book_cache_get_contact (book_cache, e_contact_get_const (contact, E_CONTACT_UID), FALSE, &old_contact, cancellable, error); - if (success) { - const gchar *original_vcard; - - /* This is for offline changes, where the EContact in the cache - is already modified, while the original, the one on the server, - is different. Using the cached EContact in this case generates - empty UpdateItem request and nothing is saved. */ - original_vcard = ebb_ews_get_original_vcard (old_contact); - if (original_vcard) { - EContact *tmp; - - tmp = e_contact_new_from_vcard (original_vcard); - if (tmp) { - g_object_unref (old_contact); - old_contact = tmp; - } - } - } - - if (success) { - ConvertData cd; - const gchar *conflict_res = "AlwaysOverwrite"; - - cd.bbews = bbews; - cd.cancellable = cancellable; - cd.error = error; - cd.old_contact = old_contact; - cd.new_contact = contact; - cd.change_key = NULL; - - if (conflict_resolution == E_CONFLICT_RESOLUTION_FAIL) - conflict_res = "NeverOverwrite"; - - success = e_ews_connection_update_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, - conflict_res, "SendAndSaveCopy", "SendToAllAndSaveCopy", - bbews->priv->folder_id, is_dl ? ebb_ews_convert_dl_to_updatexml_cb : ebb_ews_convert_contact_to_updatexml_cb, - &cd, &items, cancellable, error); - - g_free (cd.change_key); - } - - g_clear_object (&old_contact); - g_clear_object (&book_cache); - } else { - success = e_ews_connection_create_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, NULL, NULL, - fid, is_dl ? ebb_ews_convert_dl_to_xml_cb : ebb_ews_convert_contact_to_xml_cb, contact, - &items, cancellable, error); - } - - if (success && items) { - EEwsItem *item = items->data; - const EwsId *item_id; - - item_id = e_ews_item_get_id (item); - *out_new_uid = g_strdup (item_id->id); - - /* - * Support for ContactPhoto was added in Exchange 2010 SP2. - * We don't want to try to set/get this property if we are running in older version of the server. - */ - if (!overwrite_existing && - e_ews_connection_satisfies_server_version (bbews->priv->cnc, E_EWS_EXCHANGE_2010_SP2)) { - EContactPhoto *photo; - - /* - * The contact photo is basically an attachment with a special name. - * Considering this, we only can set the contact photo after create the contact itself. - * Then we are able to attach the picture to the "Contact Item". - */ - photo = e_contact_get (contact, E_CONTACT_PHOTO); - if (photo) { - GError *local_error = NULL; - - set_photo (bbews, item_id, contact, photo, NULL, cancellable, &local_error); - e_contact_photo_free (photo); - - if (local_error) { - g_propagate_error (error, local_error); - g_prefix_error (error, "%s", _("Failed to set contact photo:")); - success = FALSE; - } - } - } - } - - g_slist_free_full (items, g_object_unref); - e_ews_folder_id_free (fid); - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - ebb_ews_convert_error_to_edb_error (error); - ebb_ews_maybe_disconnect_sync (bbews, error, cancellable); - - return success; -} - -static gboolean -ebb_ews_remove_contact_sync (EBookMetaBackend *meta_backend, - EConflictResolution conflict_resolution, - const gchar *uid, - const gchar *extra, - const gchar *object, - GCancellable *cancellable, - GError **error) -{ - EBookBackendEws *bbews; - GSList *ids; - gboolean success; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - - bbews = E_BOOK_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - ids = g_slist_prepend (NULL, (gpointer) uid); - - success = e_ews_connection_delete_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, ids, EWS_HARD_DELETE, 0, FALSE, cancellable, error); - - g_slist_free (ids); - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - ebb_ews_convert_error_to_edb_error (error); - ebb_ews_maybe_disconnect_sync (bbews, error, cancellable); - - return success; -} - -static gboolean -ebb_ews_search_sync (EBookMetaBackend *meta_backend, - const gchar *expr, - gboolean meta_contact, - GSList **out_contacts, - GCancellable *cancellable, - GError **error) -{ - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - - /* Ignore errors, just try its best */ - ebb_ews_update_cache_for_expression (E_BOOK_BACKEND_EWS (meta_backend), expr, cancellable, NULL); - - /* Chain up to parent's method */ - return E_BOOK_META_BACKEND_CLASS (e_book_backend_ews_parent_class)->search_sync (meta_backend, expr, meta_contact, - out_contacts, cancellable, error); -} - -static gboolean -ebb_ews_search_uids_sync (EBookMetaBackend *meta_backend, - const gchar *expr, - GSList **out_uids, - GCancellable *cancellable, - GError **error) -{ - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE); - - /* Ignore errors, just try its best */ - ebb_ews_update_cache_for_expression (E_BOOK_BACKEND_EWS (meta_backend), expr, cancellable, NULL); - - /* Chain up to parent's method */ - return E_BOOK_META_BACKEND_CLASS (e_book_backend_ews_parent_class)->search_uids_sync (meta_backend, expr, - out_uids, cancellable, error); -} - -static gchar * -ebb_ews_get_backend_property (EBookBackend *book_backend, - const gchar *prop_name) -{ - EBookBackendEws *bbews; - - g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (book_backend), NULL); - g_return_val_if_fail (prop_name != NULL, NULL); - - bbews = E_BOOK_BACKEND_EWS (book_backend); - - if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) { - CamelEwsSettings *ews_settings; - - ews_settings = ebb_ews_get_collection_settings (bbews); - - return g_strjoin (",", - "net", - "contact-lists", - e_book_meta_backend_get_capabilities (E_BOOK_META_BACKEND (book_backend)), - (!bbews->priv->is_gal || camel_ews_settings_get_oab_offline (ews_settings)) ? "do-initial-query" : NULL, - NULL); - } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) { - return g_strdup (e_contact_field_name (E_CONTACT_FILE_AS)); - } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) { - GString *buffer; - gchar *fields; - gint ii; - - buffer = g_string_sized_new (1024); - - for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) { - if (mappings[ii].element_type != ELEMENT_TYPE_SIMPLE) - continue; - - if (buffer->len > 0) - g_string_append_c (buffer, ','); - g_string_append (buffer, e_contact_field_name (mappings[ii].field_id)); - } - - for (ii = 0; ii < G_N_ELEMENTS (phone_field_map); ii++) { - if (buffer->len > 0) - g_string_append_c (buffer, ','); - g_string_append (buffer, e_contact_field_name (phone_field_map[ii].field)); - } - - fields = g_strjoin ( - ",", - buffer->str, - e_contact_field_name (E_CONTACT_FULL_NAME), - e_contact_field_name (E_CONTACT_NICKNAME), - e_contact_field_name (E_CONTACT_FAMILY_NAME), - e_contact_field_name (E_CONTACT_EMAIL_1), - e_contact_field_name (E_CONTACT_EMAIL_2), - e_contact_field_name (E_CONTACT_EMAIL_3), - e_contact_field_name (E_CONTACT_ADDRESS_WORK), - e_contact_field_name (E_CONTACT_ADDRESS_HOME), - e_contact_field_name (E_CONTACT_ADDRESS_OTHER), - e_contact_field_name (E_CONTACT_BIRTH_DATE), - e_contact_field_name (E_CONTACT_NOTE), - e_contact_field_name (E_CONTACT_PHOTO), - NULL); - - g_string_free (buffer, TRUE); - - return fields; - } - - /* Chain up to parent's method. */ - return E_BOOK_BACKEND_CLASS (e_book_backend_ews_parent_class)->get_backend_property (book_backend, prop_name); -} - -static gboolean -ebb_ews_get_destination_address (EBackend *backend, - gchar **host, - guint16 *port) -{ - CamelEwsSettings *ews_settings; - SoupURI *soup_uri; - gchar *host_url; - gboolean result = FALSE; - - g_return_val_if_fail (port != NULL, FALSE); - g_return_val_if_fail (host != NULL, FALSE); - - /* Sanity checking */ - if (!e_book_backend_get_registry (E_BOOK_BACKEND (backend)) || - !e_backend_get_source (backend)) - return FALSE; - - ews_settings = ebb_ews_get_collection_settings (E_BOOK_BACKEND_EWS (backend)); - g_return_val_if_fail (ews_settings != NULL, FALSE); - - host_url = camel_ews_settings_dup_hosturl (ews_settings); - g_return_val_if_fail (host_url != NULL, FALSE); - - soup_uri = soup_uri_new (host_url); - if (soup_uri) { - *host = g_strdup (soup_uri_get_host (soup_uri)); - *port = soup_uri_get_port (soup_uri); - - result = *host && **host; - if (!result) { - g_free (*host); - *host = NULL; - } - - soup_uri_free (soup_uri); - } - - g_free (host_url); - - return result; -} - -static void -e_book_backend_ews_constructed (GObject *object) -{ - EBookBackendEws *bbews = E_BOOK_BACKEND_EWS (object); - EBookCache *book_cache; - gchar *cache_dirname; - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_book_backend_ews_parent_class)->constructed (object); - - book_cache = e_book_meta_backend_ref_cache (E_BOOK_META_BACKEND (bbews)); - - cache_dirname = g_path_get_dirname (e_cache_get_filename (E_CACHE (book_cache))); - - g_clear_object (&book_cache); - - bbews->priv->attachments_dir = g_build_filename (cache_dirname, "attachments", NULL); - g_mkdir_with_parents (bbews->priv->attachments_dir, 0777); - - g_free (cache_dirname); -} - -static void -e_book_backend_ews_dispose (GObject *object) -{ - EBookBackendEws *bbews = E_BOOK_BACKEND_EWS (object); - - g_rec_mutex_lock (&bbews->priv->cnc_lock); - - g_clear_object (&bbews->priv->cnc); - - g_rec_mutex_unlock (&bbews->priv->cnc_lock); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_book_backend_ews_parent_class)->dispose (object); -} - -static void -e_book_backend_ews_finalize (GObject *object) -{ - EBookBackendEws *bbews = E_BOOK_BACKEND_EWS (object); - - g_free (bbews->priv->folder_id); - g_free (bbews->priv->attachments_dir); - - g_rec_mutex_clear (&bbews->priv->cnc_lock); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_book_backend_ews_parent_class)->finalize (object); -} - -static void -e_book_backend_ews_init (EBookBackendEws *bbews) -{ - bbews->priv = G_TYPE_INSTANCE_GET_PRIVATE (bbews, E_TYPE_BOOK_BACKEND_EWS, EBookBackendEwsPrivate); - - g_rec_mutex_init (&bbews->priv->cnc_lock); -} - -static void -e_book_backend_ews_class_init (EBookBackendEwsClass *klass) -{ - GObjectClass *object_class; - EBackendClass *backend_class; - EBookBackendClass *book_backend_class; - EBookMetaBackendClass *book_meta_backend_class; - - g_type_class_add_private (klass, sizeof (EBookBackendEwsPrivate)); - - book_meta_backend_class = E_BOOK_META_BACKEND_CLASS (klass); - book_meta_backend_class->backend_module_filename = "libebookbackendews.so"; - book_meta_backend_class->backend_factory_type_name = "EBookBackendEwsFactory"; - book_meta_backend_class->connect_sync = ebb_ews_connect_sync; - book_meta_backend_class->disconnect_sync = ebb_ews_disconnect_sync; - book_meta_backend_class->get_changes_sync = ebb_ews_get_changes_sync; - book_meta_backend_class->load_contact_sync = ebb_ews_load_contact_sync; - book_meta_backend_class->save_contact_sync = ebb_ews_save_contact_sync; - book_meta_backend_class->remove_contact_sync = ebb_ews_remove_contact_sync; - book_meta_backend_class->search_sync = ebb_ews_search_sync; - book_meta_backend_class->search_uids_sync = ebb_ews_search_uids_sync; - - book_backend_class = E_BOOK_BACKEND_CLASS (klass); - book_backend_class->get_backend_property = ebb_ews_get_backend_property; - - backend_class = E_BACKEND_CLASS (klass); - backend_class->get_destination_address = ebb_ews_get_destination_address; - - object_class = G_OBJECT_CLASS (klass); - object_class->constructed = e_book_backend_ews_constructed; - object_class->dispose = e_book_backend_ews_dispose; - object_class->finalize = e_book_backend_ews_finalize; -} diff --git a/src/calendar/e-cal-backend-ews-utils.c b/src/calendar/e-cal-backend-ews-utils.c index f5fd837..ebf4270 100644 --- a/src/calendar/e-cal-backend-ews-utils.c +++ b/src/calendar/e-cal-backend-ews-utils.c @@ -178,8 +178,7 @@ e_cal_backend_ews_tz_util_get_msdn_equivalent (const gchar *ical_tz_location) { const gchar *msdn_tz_location = NULL; - if (!ical_tz_location || !*ical_tz_location) - return NULL; + g_return_val_if_fail (ical_tz_location != NULL, NULL); g_rec_mutex_lock (&tz_mutex); if (ical_to_msdn == NULL) { @@ -200,8 +199,7 @@ e_cal_backend_ews_tz_util_get_ical_equivalent (const gchar *msdn_tz_location) { const gchar *ical_tz_location = NULL; - if (!msdn_tz_location || !*msdn_tz_location) - return NULL; + g_return_val_if_fail (msdn_tz_location != NULL, NULL); g_rec_mutex_lock (&tz_mutex); if (msdn_to_ical == NULL) { @@ -1115,11 +1113,11 @@ convert_vevent_calcomp_to_xml (ESoapMessage *msg, e_ews_message_write_string_parameter (msg, "ReminderIsSet", NULL, "false"); /* start time, end time and meeting time zone */ - dtstart = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); + dtstart = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); tzid_start = (icaltimezone *) (dtstart.zone ? dtstart.zone : convert_data->default_zone); ical_location_start = icaltimezone_get_location (tzid_start); - dtend = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); + dtend = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); tzid_end = (icaltimezone *) (dtend.zone ? dtend.zone : convert_data->default_zone); ical_location_end = icaltimezone_get_location (tzid_end); @@ -1274,7 +1272,7 @@ convert_vtodo_calcomp_to_xml (ESoapMessage *msg, prop = icalcomponent_get_first_property (icalcomp, ICAL_DUE_PROPERTY); if (prop) { - dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp, ICAL_DUE_PROPERTY, icalproperty_get_due); + dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DUE_PROPERTY, icalproperty_get_due); e_ews_cal_utils_set_time (msg, "DueDate", &dt, TRUE); } @@ -1287,7 +1285,7 @@ convert_vtodo_calcomp_to_xml (ESoapMessage *msg, prop = icalcomponent_get_first_property (icalcomp, ICAL_DTSTART_PROPERTY); if (prop) { - dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); + dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); e_ews_cal_utils_set_time (msg, "StartDate", &dt, TRUE); } @@ -1538,8 +1536,8 @@ convert_vevent_component_to_updatexml (ESoapMessage *msg, } /* Update other properties allowed only for meeting organizers*/ /*meeting dates*/ - dtstart = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - dtstart_old = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp_old, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); + dtstart = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); + dtstart_old = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp_old, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); dt_start_changed = icaltime_compare (dtstart, dtstart_old) != 0; if (dtstart.zone != NULL) { tzid_start = (icaltimezone *) dtstart.zone; @@ -1550,8 +1548,8 @@ convert_vevent_component_to_updatexml (ESoapMessage *msg, dt_start_changed_timezone_name = TRUE; } - dtend = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); - dtend_old = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp_old, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); + dtend = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); + dtend_old = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp_old, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); dt_end_changed = icaltime_compare (dtend, dtend_old) != 0; if (dtend.zone != NULL) { tzid_end = (icaltimezone *) dtend.zone; @@ -1770,7 +1768,7 @@ convert_vtodo_component_to_updatexml (ESoapMessage *msg, prop = icalcomponent_get_first_property (icalcomp, ICAL_DUE_PROPERTY); if (prop) { - dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp, ICAL_DUE_PROPERTY, icalproperty_get_due); + dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DUE_PROPERTY, icalproperty_get_due); e_ews_message_start_set_item_field (msg, "DueDate", "task", "Task"); e_ews_cal_utils_set_time (msg, "DueDate", &dt, TRUE); e_ews_message_end_set_item_field (msg); @@ -1789,7 +1787,7 @@ convert_vtodo_component_to_updatexml (ESoapMessage *msg, prop = icalcomponent_get_first_property (icalcomp, ICAL_DTSTART_PROPERTY); if (prop) { - dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, convert_data->vcalendar, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); + dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); e_ews_message_start_set_item_field (msg, "StartDate", "task", "Task"); e_ews_cal_utils_set_time (msg, "StartDate", &dt, TRUE); e_ews_message_end_set_item_field (msg); @@ -2013,7 +2011,6 @@ e_cal_backend_ews_prepare_accept_item_request (ESoapMessage *msg, struct icaltimetype e_cal_backend_ews_get_datetime_with_zone (ETimezoneCache *timezone_cache, - icalcomponent *vcalendar, icalcomponent *comp, icalproperty_kind prop_kind, struct icaltimetype (* get_func) (const icalproperty *prop)) @@ -2021,7 +2018,7 @@ e_cal_backend_ews_get_datetime_with_zone (ETimezoneCache *timezone_cache, struct icaltimetype dt = icaltime_null_time (); icalproperty *prop; icalparameter *param; - const gchar *tzid, *eqv_tzid; + const gchar *tzid; g_return_val_if_fail (E_IS_TIMEZONE_CACHE (timezone_cache), dt); g_return_val_if_fail (comp != NULL, dt); @@ -2037,8 +2034,6 @@ e_cal_backend_ews_get_datetime_with_zone (ETimezoneCache *timezone_cache, icaltime_is_null_time (dt)) return dt; - dt.zone = NULL; - param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER); if (!param) return dt; @@ -2047,23 +2042,7 @@ e_cal_backend_ews_get_datetime_with_zone (ETimezoneCache *timezone_cache, if (!tzid || !*tzid) return dt; - eqv_tzid = e_cal_backend_ews_tz_util_get_ical_equivalent (tzid); - - if (!eqv_tzid) { - /* Unlikely to work, but just in case */ - eqv_tzid = e_cal_backend_ews_tz_util_get_msdn_equivalent (tzid); - if (eqv_tzid) - eqv_tzid = e_cal_backend_ews_tz_util_get_ical_equivalent (eqv_tzid); - } - - if (eqv_tzid) - dt.zone = e_timezone_cache_get_timezone (timezone_cache, eqv_tzid); - - if (!dt.zone) - dt.zone = e_timezone_cache_get_timezone (timezone_cache, tzid); - - if (!dt.zone) - dt.zone = vcalendar ? icalcomponent_get_timezone (vcalendar, tzid) : NULL; + dt.zone = e_timezone_cache_get_timezone (timezone_cache, tzid); return dt; } diff --git a/src/calendar/e-cal-backend-ews-utils.c.import-event-timezone b/src/calendar/e-cal-backend-ews-utils.c.import-event-timezone deleted file mode 100644 index ebf4270..0000000 --- a/src/calendar/e-cal-backend-ews-utils.c.import-event-timezone +++ /dev/null @@ -1,2048 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * 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. - * - * 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include -#include - -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include - -#include "server/e-ews-calendar-utils.h" -#include "server/e-ews-connection.h" -#include "server/e-ews-message.h" -#include "server/e-ews-item-change.h" - -#include "e-cal-backend-ews-utils.h" - -/* - * A bunch of global variables used to map the icaltimezone to MSDN[0] format. - * Also, some auxiliar functions to translate from one tz type to another. - * - * [0]: http://msdn.microsoft.com/en-us/library/ms912391(v=winembedded.11).aspx - */ -static GRecMutex tz_mutex; - -static GHashTable *ical_to_msdn = NULL; -static GHashTable *msdn_to_ical = NULL; -static guint tables_counter = 0; - -void -e_cal_backend_ews_populate_windows_zones (void) -{ - const gchar *xpath_eval_exp; - gchar *filename = NULL; - xmlDocPtr doc; - xmlXPathContextPtr xpath_ctxt; - xmlXPathObjectPtr xpath_obj; - xmlNodeSetPtr nodes; - gint i, len; - - g_rec_mutex_lock (&tz_mutex); - if (ical_to_msdn != NULL && msdn_to_ical != NULL) { - g_hash_table_ref (ical_to_msdn); - g_hash_table_ref (msdn_to_ical); - tables_counter++; - - g_rec_mutex_unlock (&tz_mutex); - return; - } - - filename = g_build_filename (EXCHANGE_EWS_DATADIR, "windowsZones.xml", NULL); - doc = xmlReadFile (filename, NULL, 0); - - if (doc == NULL) { - g_warning (G_STRLOC "Could not map %s file.", filename); - g_free (filename); - - g_rec_mutex_unlock (&tz_mutex); - return; - } - - xpath_eval_exp = "/supplementalData/windowsZones/mapTimezones/mapZone"; - - xpath_ctxt = xmlXPathNewContext (doc); - xpath_obj = xmlXPathEvalExpression (BAD_CAST xpath_eval_exp, xpath_ctxt); - - if (xpath_obj == NULL) { - g_warning (G_STRLOC "Unable to evaluate xpath expression \"%s\".", xpath_eval_exp); - xmlXPathFreeContext (xpath_ctxt); - xmlFreeDoc (doc); - g_free (filename); - - g_rec_mutex_unlock (&tz_mutex); - return; - } - - nodes = xpath_obj->nodesetval; - len = nodes->nodeNr; - - msdn_to_ical = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - ical_to_msdn = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - tables_counter++; - - for (i = 0; i < len; i++) { - xmlChar *msdn = xmlGetProp (nodes->nodeTab[i], BAD_CAST "other"); - xmlChar *ical = xmlGetProp (nodes->nodeTab[i], BAD_CAST "type"); - gchar **tokens; - gint tokens_len; - - tokens = g_strsplit ((gchar *) ical, " ", 0); - tokens_len = g_strv_length (tokens); - if (tokens_len == 1) { - if (!g_hash_table_lookup (msdn_to_ical, msdn)) - g_hash_table_insert (msdn_to_ical, g_strdup ((gchar *) msdn), g_strdup ((gchar *) ical)); - - if (!g_hash_table_lookup (ical_to_msdn, ical)) - g_hash_table_insert (ical_to_msdn, g_strdup ((gchar *) ical), g_strdup ((gchar *) msdn)); - } else { - gint j; - for (j = 0; j < tokens_len; j++) { - if (!g_hash_table_lookup (msdn_to_ical, msdn)) - g_hash_table_insert (msdn_to_ical, g_strdup ((gchar *) msdn), g_strdup (tokens[j])); - - if (!g_hash_table_lookup (ical_to_msdn, tokens[j])) - g_hash_table_insert (ical_to_msdn, g_strdup (tokens[j]), g_strdup ((gchar *) msdn)); - } - } - - g_strfreev (tokens); - xmlFree (ical); - xmlFree (msdn); - } - - xmlXPathFreeObject (xpath_obj); - xmlXPathFreeContext (xpath_ctxt); - xmlFreeDoc (doc); - g_free (filename); - - g_rec_mutex_unlock (&tz_mutex); -} - -void -e_cal_backend_ews_unref_windows_zones (void) -{ - g_rec_mutex_lock (&tz_mutex); - if (ical_to_msdn != NULL) - g_hash_table_unref (ical_to_msdn); - - if (msdn_to_ical != NULL) - g_hash_table_unref (msdn_to_ical); - - if (tables_counter > 0) { - tables_counter--; - - if (tables_counter == 0) { - ical_to_msdn = NULL; - msdn_to_ical = NULL; - } - } - - g_rec_mutex_unlock (&tz_mutex); -} - -const gchar * -e_cal_backend_ews_tz_util_get_msdn_equivalent (const gchar *ical_tz_location) -{ - const gchar *msdn_tz_location = NULL; - - g_return_val_if_fail (ical_tz_location != NULL, NULL); - - g_rec_mutex_lock (&tz_mutex); - if (ical_to_msdn == NULL) { - g_rec_mutex_unlock (&tz_mutex); - - g_warn_if_reached (); - return NULL; - } - - msdn_tz_location = g_hash_table_lookup (ical_to_msdn, ical_tz_location); - g_rec_mutex_unlock (&tz_mutex); - - return msdn_tz_location; -} - -const gchar * -e_cal_backend_ews_tz_util_get_ical_equivalent (const gchar *msdn_tz_location) -{ - const gchar *ical_tz_location = NULL; - - g_return_val_if_fail (msdn_tz_location != NULL, NULL); - - g_rec_mutex_lock (&tz_mutex); - if (msdn_to_ical == NULL) { - g_rec_mutex_unlock (&tz_mutex); - - g_warn_if_reached (); - return NULL; - } - - ical_tz_location = g_hash_table_lookup (msdn_to_ical, msdn_tz_location); - g_rec_mutex_unlock (&tz_mutex); - - return ical_tz_location; -} - -/* - * Iterate over the icalcomponent properties and collect attendees - */ -void -e_ews_collect_attendees (icalcomponent *comp, - GSList **required, - GSList **optional, - GSList **resource, - gboolean *out_rsvp_requested) -{ - icalproperty *prop; - icalparameter *param; - const gchar *str = NULL; - const gchar *org_email_address = NULL; - - if (out_rsvp_requested) - *out_rsvp_requested = TRUE; - - /* we need to know who the orgenizer is so we wont duplicate him/her */ - org_email_address = e_ews_collect_organizer (comp); - - /* iterate over every attendee property */ - for (prop = icalcomponent_get_first_property (comp, ICAL_ATTENDEE_PROPERTY); - prop != NULL; - prop = icalcomponent_get_next_property (comp, ICAL_ATTENDEE_PROPERTY)) { - - str = icalproperty_get_attendee (prop); - - if (!str || !*str) - continue; - - /* figure the email address of the attendee, discard "mailto:" if it's there */ - if (!g_ascii_strncasecmp (str, "mailto:", 7)) - str = (str) + 7; - - if (!*str) - continue; - - /* if this attenddee is the orgenizer - dont add him/her - in some cases there is no maito for email if meeting orginazer */ - if (g_ascii_strcasecmp (org_email_address, str) == 0) continue; - - /* figure type of attendee, add to relevant list */ - param = icalproperty_get_first_parameter (prop, ICAL_ROLE_PARAMETER); - - /*in case of new time proposal the role parameter is not a part of ical*/ - if (!param) continue; - - switch (icalparameter_get_role (param)) { - case ICAL_ROLE_OPTPARTICIPANT: - *optional = g_slist_append (*optional, (gpointer)str); - - if (out_rsvp_requested && *out_rsvp_requested) { - icalparameter *rsvp; - - rsvp = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER); - if (rsvp && icalparameter_get_rsvp (rsvp) == ICAL_RSVP_FALSE) - *out_rsvp_requested = FALSE; - } - break; - case ICAL_ROLE_CHAIR: - case ICAL_ROLE_REQPARTICIPANT: - *required = g_slist_append (*required, (gpointer)str); - - if (out_rsvp_requested && *out_rsvp_requested) { - icalparameter *rsvp; - - rsvp = icalproperty_get_first_parameter (prop, ICAL_RSVP_PARAMETER); - if (rsvp && icalparameter_get_rsvp (rsvp) == ICAL_RSVP_FALSE) - *out_rsvp_requested = FALSE; - } - break; - case ICAL_ROLE_NONPARTICIPANT: - *resource = g_slist_append (*resource, (gpointer)str); - break; - case ICAL_ROLE_X: - case ICAL_ROLE_NONE: - /* Ignore these for now */ - break; - } - } - - if (*required == NULL && *optional == NULL && *resource == NULL && org_email_address != NULL) - *required = g_slist_prepend (*required, (gpointer) org_email_address); -} - -gint -ews_get_alarm (ECalComponent *comp) -{ - GList *alarm_uids = e_cal_component_get_alarm_uids (comp); - ECalComponentAlarm *alarm = e_cal_component_get_alarm (comp, (const gchar *) (alarm_uids->data)); - ECalComponentAlarmAction action; - ECalComponentAlarmTrigger trigger; - gint dur_int = 0; - - e_cal_component_alarm_get_action (alarm, &action); - if (action == E_CAL_COMPONENT_ALARM_DISPLAY) { - e_cal_component_alarm_get_trigger (alarm, &trigger); - switch (trigger.type) { - case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START: - dur_int = ((icaldurationtype_as_int (trigger.u.rel_duration)) / SECS_IN_MINUTE) * -1; - break; - default: - break; - } - } - e_cal_component_alarm_free (alarm); - cal_obj_uid_list_free (alarm_uids); - return dur_int; -} - -void -ews_set_alarm (ESoapMessage *msg, - ECalComponent *comp) -{ - /* We know there would be only a single alarm in EWS calendar item */ - GList *alarm_uids = e_cal_component_get_alarm_uids (comp); - ECalComponentAlarm *alarm = e_cal_component_get_alarm (comp, (const gchar *) (alarm_uids->data)); - ECalComponentAlarmAction action; - - e_ews_message_write_string_parameter (msg, "ReminderIsSet", NULL, "true"); - e_cal_component_alarm_get_action (alarm, &action); - if (action == E_CAL_COMPONENT_ALARM_DISPLAY) { - ECalComponentAlarmTrigger trigger; - gchar buf[20]; - gint dur_int = 0; - e_cal_component_alarm_get_trigger (alarm, &trigger); - switch (trigger.type) { - case E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START: - dur_int = ((icaldurationtype_as_int (trigger.u.rel_duration)) / SECS_IN_MINUTE) * -1; - snprintf (buf, 20, "%d", dur_int); - e_ews_message_write_string_parameter (msg, "ReminderMinutesBeforeStart", NULL, buf); - break; - default: - break; - } - } - e_cal_component_alarm_free (alarm); - cal_obj_uid_list_free (alarm_uids); - -} - -static void -ewscal_set_date (ESoapMessage *msg, - const gchar *name, - icaltimetype *t) -{ - gchar *str; - - str = g_strdup_printf ( - "%04d-%02d-%02d", - t->year, t->month, t->day); - - e_ews_message_write_string_parameter (msg, name, NULL, str); - g_free (str); -} - -static const gchar *number_to_month (gint num) { - static const gchar *months[] = { - "January", "February", "March", "April", "May", "June", "July", - "August", "September", "October", "November", "December" - }; - - return months[num - 1]; -} - -static const gchar *number_to_weekday (gint num) { - static const gchar *days[] = { - "Sunday", "Monday", "Tuesday", "Wednesday", - "Thursday", "Friday", "Saturday", - "Day", "Weekday", "WeekendDay" - }; - - return days[num - 1]; -} - -static const gchar *weekindex_to_ical (gint index) { - static struct { - const gchar *exch; - gint index; - } table[] = { - { "First", 1 }, - { "Second", 2 }, - { "Third", 3 }, - { "Fourth", 4 }, - { "Fifth", 5 }, - { "Last", -1 } - }; - gint i; - - for (i = 0; i < 6; i++) { - if (index == table[i].index) - return table[i].exch; - } - - return 0; -} - -static void -ewscal_add_rrule (ESoapMessage *msg, - icalproperty *prop) -{ - struct icalrecurrencetype recur = icalproperty_get_rrule (prop); - - e_soap_message_start_element (msg, "RelativeYearlyRecurrence", NULL, NULL); - - e_ews_message_write_string_parameter (msg, "DaysOfWeek", NULL, number_to_weekday (icalrecurrencetype_day_day_of_week (recur.by_day[0]))); - e_ews_message_write_string_parameter (msg, "DayOfWeekIndex", NULL, weekindex_to_ical (icalrecurrencetype_day_position (recur.by_day[0]))); - e_ews_message_write_string_parameter (msg, "Month", NULL, number_to_month (recur.by_month[0])); - - e_soap_message_end_element (msg); /* "RelativeYearlyRecurrence" */ -} - -static void -ewscal_add_timechange (ESoapMessage *msg, - icalcomponent *comp, - gint baseoffs) -{ - gchar buffer[16], *offset; - const gchar *tzname; - icalproperty *prop; - struct icaltimetype dtstart; - gint utcoffs; - - prop = icalcomponent_get_first_property (comp, ICAL_TZNAME_PROPERTY); - if (prop) { - tzname = icalproperty_get_tzname (prop); - e_soap_message_add_attribute (msg, "TimeZoneName", tzname, NULL, NULL); - } - - /* Calculate zone Offset from BaseOffset */ - prop = icalcomponent_get_first_property (comp, ICAL_TZOFFSETTO_PROPERTY); - if (prop) { - utcoffs = -icalproperty_get_tzoffsetto (prop); - utcoffs -= baseoffs; - offset = icaldurationtype_as_ical_string_r (icaldurationtype_from_int (utcoffs)); - e_ews_message_write_string_parameter (msg, "Offset", NULL, offset); - free (offset); - } - - prop = icalcomponent_get_first_property (comp, ICAL_RRULE_PROPERTY); - if (prop) - ewscal_add_rrule (msg, prop); - - prop = icalcomponent_get_first_property (comp, ICAL_DTSTART_PROPERTY); - if (prop) { - dtstart = icalproperty_get_dtstart (prop); - snprintf (buffer, 16, "%02d:%02d:%02d", dtstart.hour, dtstart.minute, dtstart.second); - e_ews_message_write_string_parameter (msg, "Time", NULL, buffer); - } -} - -static void -ewscal_set_absolute_date_transitions (ESoapMessage *msg, - GSList *absolute_date_transitions) -{ - GSList *l; - - if (absolute_date_transitions == NULL) - return; - - for (l = absolute_date_transitions; l != NULL; l = l->next) { - EEwsCalendarAbsoluteDateTransition *adt = l->data; - - e_soap_message_start_element (msg, "AbsoluteDateTransition", NULL, NULL); - - e_ews_message_write_string_parameter_with_attribute ( - msg, - "To", NULL, adt->to->value, - "Kind", adt->to->kind); - e_ews_message_write_string_parameter (msg, "DateTime", NULL, adt->date_time); - - e_soap_message_end_element (msg); /* "AbsoluteDateTransition" */ - } -} - -static void -ewscal_set_recurring_day_transitions (ESoapMessage *msg, - GSList *recurring_day_transitions) -{ - GSList *l; - - if (recurring_day_transitions == NULL) - return; - - for (l = recurring_day_transitions; l != NULL; l = l->next) { - EEwsCalendarRecurringDayTransition *rdt = l->data; - - e_soap_message_start_element (msg, "RecurringDayTransition", NULL, NULL); - - e_ews_message_write_string_parameter_with_attribute ( - msg, - "To", NULL, rdt->to->value, - "Kind", rdt->to->kind); - e_ews_message_write_string_parameter (msg, "TimeOffset", NULL, rdt->time_offset); - e_ews_message_write_string_parameter (msg, "Month", NULL, rdt->month); - e_ews_message_write_string_parameter (msg, "DayOfWeek", NULL, rdt->day_of_week); - e_ews_message_write_string_parameter (msg, "Occurrence", NULL, rdt->occurrence); - - e_soap_message_end_element (msg); /* "RecurringDayTransition" */ - } -} - -static void -ewscal_set_recurring_date_transitions (ESoapMessage *msg, - GSList *recurring_date_transitions) -{ - GSList *l; - - if (recurring_date_transitions == NULL) - return; - - for (l = recurring_date_transitions; l != NULL; l = l->next) { - EEwsCalendarRecurringDateTransition *rdt = l->data; - - e_soap_message_start_element (msg, "RecurringDateTransition", NULL, NULL); - - e_ews_message_write_string_parameter_with_attribute ( - msg, - "To", NULL, rdt->to->value, - "Kind", rdt->to->kind); - e_ews_message_write_string_parameter (msg, "TimeOffset", NULL, rdt->time_offset); - e_ews_message_write_string_parameter (msg, "Month", NULL, rdt->month); - e_ews_message_write_string_parameter (msg, "Day", NULL, rdt->day); - - e_soap_message_end_element (msg); /* "RecurringDateTransition" */ - } -} - -void -ewscal_set_timezone (ESoapMessage *msg, - const gchar *name, - EEwsCalendarTimeZoneDefinition *tzd) -{ - GSList *l; - - if (name == NULL || tzd == NULL) - return; - - e_soap_message_start_element (msg, name, NULL, NULL); - e_soap_message_add_attribute (msg, "Id", tzd->id, NULL, NULL); - e_soap_message_add_attribute (msg, "Name", tzd->name, NULL, NULL); - - e_soap_message_start_element (msg, "Periods", NULL, NULL); - for (l = tzd->periods; l != NULL; l = l->next) { - EEwsCalendarPeriod *period = l->data; - - e_soap_message_start_element (msg, "Period", NULL, NULL); - e_soap_message_add_attribute (msg, "Bias", period->bias, NULL, NULL); - e_soap_message_add_attribute (msg, "Name", period->name, NULL, NULL); - e_soap_message_add_attribute (msg, "Id", period->id, NULL, NULL); - e_soap_message_end_element (msg); /* "Period" */ - } - e_soap_message_end_element (msg); /* "Periods" */ - - e_soap_message_start_element (msg, "TransitionsGroups", NULL, NULL); - for (l = tzd->transitions_groups; l != NULL; l = l->next) { - EEwsCalendarTransitionsGroup *tg = l->data; - - e_soap_message_start_element (msg, "TransitionsGroup", NULL, NULL); - e_soap_message_add_attribute (msg, "Id", tg->id, NULL, NULL); - - if (tg->transition != NULL) { - e_soap_message_start_element (msg, "Transition", NULL, NULL); - e_ews_message_write_string_parameter_with_attribute ( - msg, - "To", NULL, tg->transition->value, - "Kind", tg->transition->kind); - e_soap_message_end_element (msg); /* "Transition" */ - } - - ewscal_set_absolute_date_transitions (msg, tg->absolute_date_transitions); - ewscal_set_recurring_day_transitions (msg, tg->recurring_day_transitions); - ewscal_set_recurring_date_transitions (msg, tg->recurring_date_transitions); - - e_soap_message_end_element (msg); /* "TransitionsGroup" */ - } - e_soap_message_end_element (msg); /* "TransitionsGroups" */ - - e_soap_message_start_element (msg, "Transitions", NULL, NULL); - e_soap_message_start_element (msg, "Transition", NULL, NULL); - e_ews_message_write_string_parameter_with_attribute ( - msg, - "To", NULL, tzd->transitions->transition->value, - "Kind", tzd->transitions->transition->kind); - e_soap_message_end_element (msg); /* "Transition" */ - ewscal_set_absolute_date_transitions (msg, tzd->transitions->absolute_date_transitions); - ewscal_set_recurring_day_transitions (msg, tzd->transitions->recurring_day_transitions); - ewscal_set_recurring_date_transitions (msg, tzd->transitions->recurring_date_transitions); - e_soap_message_end_element (msg); /* "Transitions" */ - - e_soap_message_end_element (msg); /* "StartTimeZone" */ -} - -void -ewscal_set_meeting_timezone (ESoapMessage *msg, - icaltimezone *icaltz) -{ - icalcomponent *comp; - icalproperty *prop; - const gchar *location; - icalcomponent *xstd, *xdaylight; - gint std_utcoffs; - gchar *offset; - - if (!icaltz) - return; - - comp = icaltimezone_get_component (icaltz); - - /* Exchange needs a BaseOffset, followed by either *both* - * Standard and Daylight zones, or neither of them. If there's - * more than one STANDARD or DAYLIGHT component in the VTIMEZONE, - * we ignore the extra. So fully-specified timezones including - * historical DST rules cannot be handled by Exchange. */ - - /* FIXME: Walk through them all to find the *latest* ones, like - * icaltimezone_get_tznames_from_vtimezone() does. */ - xstd = icalcomponent_get_first_component (comp, ICAL_XSTANDARD_COMPONENT); - xdaylight = icalcomponent_get_first_component (comp, ICAL_XDAYLIGHT_COMPONENT); - - /* If there was only a DAYLIGHT component, swap them over and pretend - * it was the STANDARD component. We're only going to give the server - * the BaseOffset anyway. */ - if (!xstd) { - xstd = xdaylight; - xdaylight = NULL; - } - - /* Find a suitable string to use for the TimeZoneName */ - location = icaltimezone_get_location (icaltz); - if (!location) - location = icaltimezone_get_tzid (icaltz); - if (!location) - location = icaltimezone_get_tznames (icaltz); - - e_soap_message_start_element (msg, "MeetingTimeZone", NULL, NULL); - e_soap_message_add_attribute (msg, "TimeZoneName", location, NULL, NULL); - - /* Fetch the timezone offsets for the standard (or only) zone. - * Negate it, because Exchange does it backwards */ - if (xstd) { - prop = icalcomponent_get_first_property (xstd, ICAL_TZOFFSETTO_PROPERTY); - std_utcoffs = -icalproperty_get_tzoffsetto (prop); - } else { - /* UTC has no properties at all, so just set manually */ - std_utcoffs = 0; - } - - /* This is the overall BaseOffset tag, which the Standard and Daylight - * zones are offset from. It's redundant, but Exchange always sets it - * to the offset of the Standard zone, and the Offset in the Standard - * zone to zero. So try to avoid problems by doing the same. */ - offset = icaldurationtype_as_ical_string_r (icaldurationtype_from_int (std_utcoffs)); - e_ews_message_write_string_parameter (msg, "BaseOffset", NULL, offset); - free (offset); - - /* Only write the full TimeChangeType information, including the - * recurrence rules for the DST changes, if there is more than - * one. */ - if (xdaylight) { - /* Standard */ - e_soap_message_start_element (msg, "Standard", NULL, NULL); - ewscal_add_timechange (msg, xstd, std_utcoffs); - e_soap_message_end_element (msg); /* "Standard" */ - - /* DayLight */ - e_soap_message_start_element (msg, "Daylight", NULL, NULL); - ewscal_add_timechange (msg, xdaylight, std_utcoffs); - e_soap_message_end_element (msg); /* "Daylight" */ - } - e_soap_message_end_element (msg); /* "MeetingTimeZone" */ -} - -void -ewscal_set_reccurence (ESoapMessage *msg, - icalproperty *rrule, - icaltimetype *dtstart) -{ - gchar buffer[256]; - gint i, len; - - /* MSDN reference: http://msdn.microsoft.com/en-us/library/aa580471%28v=EXCHG.80%29.aspx - */ - struct icalrecurrencetype recur = icalproperty_get_rrule (rrule); - - e_soap_message_start_element (msg, "Recurrence", NULL, NULL); - - switch (recur.freq) { - case ICAL_DAILY_RECURRENCE: - e_soap_message_start_element (msg, "DailyRecurrence", NULL, NULL); - snprintf (buffer, 32, "%d", recur.interval); - e_ews_message_write_string_parameter (msg, "Interval", NULL, buffer); - e_soap_message_end_element (msg); /* "DailyRecurrence" */ - break; - - case ICAL_WEEKLY_RECURRENCE: - e_soap_message_start_element (msg, "WeeklyRecurrence", NULL, NULL); - - snprintf (buffer, 32, "%d", recur.interval); - e_ews_message_write_string_parameter (msg, "Interval", NULL, buffer); - - len = snprintf ( - buffer, 256, "%s", - number_to_weekday (icalrecurrencetype_day_day_of_week (recur.by_day[0]))); - for (i = 1; recur.by_day[i] != ICAL_RECURRENCE_ARRAY_MAX; i++) { - len += snprintf ( - buffer + len, 256 - len, " %s", - number_to_weekday (icalrecurrencetype_day_day_of_week (recur.by_day[i]))); - } - e_ews_message_write_string_parameter (msg, "DaysOfWeek", NULL, buffer); - - e_soap_message_end_element (msg); /* "WeeklyRecurrence" */ - break; - - case ICAL_MONTHLY_RECURRENCE: - if (recur.by_month_day[0] == ICAL_RECURRENCE_ARRAY_MAX) { - e_soap_message_start_element (msg, "RelativeMonthlyRecurrence", NULL, NULL); - - /* For now this is what got implemented since this is the only - relative monthly recurrence evolution can set. - TODO: extend the code with all possible monthly recurrence settings */ - snprintf (buffer, 32, "%d", recur.interval); - e_ews_message_write_string_parameter (msg, "Interval", NULL, buffer); - - e_ews_message_write_string_parameter ( - msg, "DaysOfWeek", NULL, - number_to_weekday (icalrecurrencetype_day_day_of_week (recur.by_day[0]))); - - e_ews_message_write_string_parameter (msg, "DayOfWeekIndex", NULL, weekindex_to_ical ((recur.by_set_pos[0] == 5 ? -1 : recur.by_set_pos[0]))); - - e_soap_message_end_element (msg); /* "RelativeMonthlyRecurrence" */ - - } else { - e_soap_message_start_element (msg, "AbsoluteMonthlyRecurrence", NULL, NULL); - - snprintf (buffer, 256, "%d", recur.interval); - e_ews_message_write_string_parameter (msg, "Interval", NULL, buffer); - - snprintf (buffer, 256, "%d", recur.by_month_day[0] == -1 ? 31 : recur.by_month_day[0]); - e_ews_message_write_string_parameter (msg, "DayOfMonth", NULL, buffer); - - e_soap_message_end_element (msg); /* "AbsoluteMonthlyRecurrence" */ - - } - break; - - case ICAL_YEARLY_RECURRENCE: - #if 0 /* FIXME */ - if (is_relative) { - ewscal_add_rrule (msg, rrule); - - } else - #endif - { - e_soap_message_start_element (msg, "AbsoluteYearlyRecurrence", NULL, NULL); - - /* work according to RFC5545 §3.3.10 - * dtstart is the default, give preference to by_month & by_month_day if they are set - */ - if (recur.by_month_day[0] != ICAL_RECURRENCE_ARRAY_MAX) { - snprintf (buffer, 256, "%d", recur.by_month_day[0] == -1 ? 31 : recur.by_month_day[0]); - } else { - snprintf (buffer, 256, "%d", dtstart->day); - } - e_ews_message_write_string_parameter (msg, "DayOfMonth", NULL, buffer); - - if (recur.by_month[0] != ICAL_RECURRENCE_ARRAY_MAX) { - snprintf (buffer, 256, "%d", recur.by_month_day[0]); - e_ews_message_write_string_parameter ( - msg, "Month", NULL, - number_to_month (recur.by_month[0])); - } else { - e_ews_message_write_string_parameter ( - msg, "Month", NULL, - number_to_month (dtstart->month)); - } - - e_soap_message_end_element (msg); /* "AbsoluteYearlyRecurrence" */ - - } - break; - - case ICAL_SECONDLY_RECURRENCE: - case ICAL_MINUTELY_RECURRENCE: - case ICAL_HOURLY_RECURRENCE: - default: - /* TODO: remove the "Recurrence" element somehow */ - g_warning ("EWS cant handle recurrence with frequency higher than DAILY\n"); - goto exit; - } - - if (recur.count > 0) { - e_soap_message_start_element (msg, "NumberedRecurrence", NULL, NULL); - ewscal_set_date (msg, "StartDate", dtstart); - snprintf (buffer, 32, "%d", recur.count); - e_ews_message_write_string_parameter (msg, "NumberOfOccurrences", NULL, buffer); - e_soap_message_end_element (msg); /* "NumberedRecurrence" */ - - } else if (!icaltime_is_null_time (recur.until)) { - e_soap_message_start_element (msg, "EndDateRecurrence", NULL, NULL); - ewscal_set_date (msg, "StartDate", dtstart); - ewscal_set_date (msg, "EndDate", &recur.until); - e_soap_message_end_element (msg); /* "EndDateRecurrence" */ - - } else { - e_soap_message_start_element (msg, "NoEndRecurrence", NULL, NULL); - ewscal_set_date (msg, "StartDate", dtstart); - e_soap_message_end_element (msg); /* "NoEndRecurrence" */ - } - -exit: - e_soap_message_end_element (msg); /* "Recurrence" */ -} - -static struct icaltimetype -icalcomponent_get_datetime (icalcomponent *comp, - icalproperty *prop) -{ - /* Extract datetime with proper timezone */ - icalcomponent *c; - icalparameter *param; - struct icaltimetype ret; - - ret = icalvalue_get_datetime (icalproperty_get_value (prop)); - - if ((param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER)) != NULL) { - const gchar *tzid = icalparameter_get_tzid (param); - icaltimezone *tz = NULL; - - for (c = comp; c != NULL; c = icalcomponent_get_parent (c)) { - tz = icalcomponent_get_timezone (c, tzid); - if (tz != NULL) break; - } - - if (tz == NULL) - tz = icaltimezone_get_builtin_timezone_from_tzid (tzid); - - if (tz != NULL) - ret = icaltime_set_timezone (&ret, tz); - } - - return ret; -} - -void -ewscal_set_reccurence_exceptions (ESoapMessage *msg, - icalcomponent *comp) -{ - icalproperty *exdate; - - /* Make sure we have at least 1 excluded occurrence */ - exdate = icalcomponent_get_first_property (comp,ICAL_EXDATE_PROPERTY); - if (!exdate) return; - - e_soap_message_start_element (msg, "DeletedOccurrences", NULL, NULL); - - for (; exdate; exdate = icalcomponent_get_next_property (comp, ICAL_EXDATE_PROPERTY)) { - struct icaltimetype exdatetime = icalcomponent_get_datetime (comp, exdate); - - e_soap_message_start_element (msg, "DeletedOccurrence", NULL, NULL); - - ewscal_set_date (msg, "Start", &exdatetime); - - e_soap_message_end_element (msg); /* "DeletedOccurrence" */ - } - - e_soap_message_end_element (msg); /* "DeletedOccurrences" */ -} - -/* - * get meeting organizer e-mail address - */ -const gchar * -e_ews_collect_organizer (icalcomponent *comp) -{ - icalproperty *org_prop = NULL; - const gchar *org = NULL; - const gchar *org_email_address = NULL; - - org_prop = icalcomponent_get_first_property (comp, ICAL_ORGANIZER_PROPERTY); - org = icalproperty_get_organizer (org_prop); - if (!org) - return NULL; - - if (g_ascii_strncasecmp (org, "MAILTO:", 7) == 0) - org = org + 7; - - org_email_address = org; - - if (org_email_address && !*org_email_address) - org_email_address = NULL; - - return org_email_address; -} - -gchar * -e_ews_extract_attachment_id_from_uri (const gchar *uri) -{ - gchar *attachment_id, *filepath = g_filename_from_uri (uri, NULL, NULL); - gchar **dirs = g_strsplit (filepath, "/", 0); - gint n = 0; - - while (dirs[n]) n++; - - attachment_id = g_strdup (dirs[n - 1]); - - g_strfreev (dirs); - - return attachment_id; -} - -void -e_ews_clean_icalcomponent (icalcomponent *icalcomp) -{ - icalproperty *prop, *item_id_prop = NULL, *changekey_prop = NULL; - - prop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); - while (prop) { - const gchar *x_name = icalproperty_get_x_name (prop); - if (!g_ascii_strcasecmp (x_name, "X-EVOLUTION-ITEMID")) - item_id_prop = prop; - else if (!g_ascii_strcasecmp (x_name, "X-EVOLUTION-CHANGEKEY")) - changekey_prop = prop; - - prop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY); - } - - if (item_id_prop != NULL) - icalcomponent_remove_property (icalcomp, item_id_prop); - - if (changekey_prop != NULL) - icalcomponent_remove_property (icalcomp, changekey_prop); -} - -static void -add_attendees_list_to_message (ESoapMessage *msg, - const gchar *listname, - GSList *list) -{ - GSList *item; - - e_soap_message_start_element (msg, listname, NULL, NULL); - - for (item = list; item != NULL; item = item->next) { - e_soap_message_start_element (msg, "Attendee", NULL, NULL); - e_soap_message_start_element (msg, "Mailbox", NULL, NULL); - - e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, item->data); - - e_soap_message_end_element (msg); /* "Mailbox" */ - e_soap_message_end_element (msg); /* "Attendee" */ - } - - e_soap_message_end_element (msg); -} - -static void -convert_sensitivity_calcomp_to_xml (ESoapMessage *msg, - icalcomponent *icalcomp) -{ - icalproperty *prop; - - g_return_if_fail (msg != NULL); - g_return_if_fail (icalcomp != NULL); - - prop = icalcomponent_get_first_property (icalcomp, ICAL_CLASS_PROPERTY); - if (prop) { - icalproperty_class classify = icalproperty_get_class (prop); - if (classify == ICAL_CLASS_PUBLIC) { - e_ews_message_write_string_parameter (msg, "Sensitivity", NULL, "Normal"); - } else if (classify == ICAL_CLASS_PRIVATE) { - e_ews_message_write_string_parameter (msg, "Sensitivity", NULL, "Private"); - } else if (classify == ICAL_CLASS_CONFIDENTIAL) { - e_ews_message_write_string_parameter (msg, "Sensitivity", NULL, "Personal"); - } - } -} - -static void -convert_categories_calcomp_to_xml (ESoapMessage *msg, - ECalComponent *comp, - icalcomponent *icalcomp) -{ - GSList *categ_list, *citer; - - g_return_if_fail (msg != NULL); - g_return_if_fail (icalcomp != NULL); - - if (comp) { - g_object_ref (comp); - } else { - icalcomponent *clone = icalcomponent_new_clone (icalcomp); - - comp = e_cal_component_new (); - if (!e_cal_component_set_icalcomponent (comp, clone)) { - icalcomponent_free (clone); - g_object_unref (comp); - - return; - } - } - - e_cal_component_get_categories_list (comp, &categ_list); - - g_object_unref (comp); - - if (!categ_list) - return; - - /* Categories cannot be empty, thus first verify they are not */ - for (citer = categ_list; citer; citer = g_slist_next (citer)) { - const gchar *category = citer->data; - - if (category && *category) - break; - } - - if (citer) { - e_soap_message_start_element (msg, "Categories", NULL, NULL); - - for (citer = categ_list; citer; citer = g_slist_next (citer)) { - const gchar *category = citer->data; - - if (!category || !*category) - continue; - - e_ews_message_write_string_parameter (msg, "String", NULL, category); - } - - e_soap_message_end_element (msg); /* Categories */ - } - - e_cal_component_free_categories_list (categ_list); -} - -static gboolean -check_is_all_day_event (const struct icaltimetype dtstart, - icaltimezone *zone_start, - const struct icaltimetype dtend, - icaltimezone *zone_end) -{ - gint64 secs_start, secs_end; - - if (icaltime_is_date (dtstart) && icaltime_is_date (dtend)) - return TRUE; - - secs_start = (gint64) (zone_start ? icaltime_as_timet_with_zone (dtstart, zone_start) : icaltime_as_timet (dtstart)); - secs_end = (gint64) (zone_end ? icaltime_as_timet_with_zone (dtend, zone_end) : icaltime_as_timet (dtend)); - - /* takes whole day(s) and starts on midnight in the zone_start */ - return ((secs_end - secs_start) % (24 * 60 * 60)) == 0 && (secs_start % 24 * 60 * 60) == 0; -} - -static void -convert_vevent_calcomp_to_xml (ESoapMessage *msg, - gpointer user_data) -{ - EwsCalendarConvertData *convert_data = user_data; - icalcomponent *icalcomp = convert_data->icalcomp; - ECalComponent *comp = e_cal_component_new (); - GSList *required = NULL, *optional = NULL, *resource = NULL; - icaltimetype dtstart, dtend; - icaltimezone *tzid_start, *tzid_end; - icalproperty *prop; - gboolean has_alarms, satisfies, rsvp_requested = TRUE, is_all_day_event; - const gchar *ical_location_start, *ical_location_end, *value; - const gchar *msdn_location_start, *msdn_location_end; - - e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp)); - - /* FORMAT OF A SAMPLE SOAP MESSAGE: http://msdn.microsoft.com/en-us/library/aa564690.aspx */ - - /* Prepare CalendarItem node in the SOAP message */ - e_soap_message_start_element (msg, "CalendarItem", NULL, NULL); - - /* subject */ - value = icalcomponent_get_summary (icalcomp); - if (value) - e_ews_message_write_string_parameter (msg, "Subject", NULL, value); - - convert_sensitivity_calcomp_to_xml (msg, icalcomp); - - /* description */ - value = icalcomponent_get_description (icalcomp); - if (value) - e_ews_message_write_string_parameter_with_attribute (msg, "Body", NULL, value, "BodyType", "Text"); - - convert_categories_calcomp_to_xml (msg, comp, icalcomp); - - /* set alarms */ - has_alarms = e_cal_component_has_alarms (comp); - if (has_alarms) - ews_set_alarm (msg, comp); - else - e_ews_message_write_string_parameter (msg, "ReminderIsSet", NULL, "false"); - - /* start time, end time and meeting time zone */ - dtstart = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - tzid_start = (icaltimezone *) (dtstart.zone ? dtstart.zone : convert_data->default_zone); - ical_location_start = icaltimezone_get_location (tzid_start); - - dtend = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); - tzid_end = (icaltimezone *) (dtend.zone ? dtend.zone : convert_data->default_zone); - ical_location_end = icaltimezone_get_location (tzid_end); - - satisfies = e_ews_connection_satisfies_server_version (convert_data->connection, E_EWS_EXCHANGE_2010); - if (satisfies && ical_location_start != NULL && ical_location_end != NULL) { - /* set iana timezone info as an extended property */ - e_ews_message_add_extended_property_distinguished_name_string ( - msg, - "PublicStrings", - "EvolutionEWSStartTimeZone", - ical_location_start); - - e_ews_message_add_extended_property_distinguished_name_string ( - msg, - "PublicStrings", - "EvolutionEWSEndTimeZone", - ical_location_end); - } - - is_all_day_event = check_is_all_day_event (dtstart, tzid_start, dtend, tzid_end); - - e_ews_cal_utils_set_time (msg, "Start", &dtstart, is_all_day_event && dtstart.is_date); - - /* Cover components without DTEND */ - if (icaltime_is_valid_time (dtend) && - !icaltime_is_null_time (dtend)) - e_ews_cal_utils_set_time (msg, "End", &dtend, is_all_day_event && dtend.is_date); - else - e_ews_cal_utils_set_time (msg, "End", &dtstart, is_all_day_event && dtstart.is_date); - - /* We have to do the time zone(s) later, or the server rejects the request */ - - /* All day event ? */ - if (is_all_day_event) - e_ews_message_write_string_parameter (msg, "IsAllDayEvent", NULL, "true"); - - /*freebusy*/ - prop = icalcomponent_get_first_property (icalcomp, ICAL_TRANSP_PROPERTY); - if (!g_strcmp0 (icalproperty_get_value_as_string (prop), "TRANSPARENT")) - e_ews_message_write_string_parameter (msg, "LegacyFreeBusyStatus",NULL,"Free"); - else - e_ews_message_write_string_parameter (msg, "LegacyFreeBusyStatus",NULL,"Busy"); - - /* location */ - value = icalcomponent_get_location (icalcomp); - if (value) - e_ews_message_write_string_parameter (msg, "Location", NULL, value); - - /* collect attendees */ - e_ews_collect_attendees (icalcomp, &required, &optional, &resource, &rsvp_requested); - - e_ews_message_write_string_parameter (msg, "IsResponseRequested", NULL, rsvp_requested ? "true" : "false"); - - if (required != NULL) { - add_attendees_list_to_message (msg, "RequiredAttendees", required); - g_slist_free (required); - } - if (optional != NULL) { - add_attendees_list_to_message (msg, "OptionalAttendees", optional); - g_slist_free (optional); - } - if (resource != NULL) { - add_attendees_list_to_message (msg, "Resources", resource); - g_slist_free (resource); - } - /* end of attendees */ - - /* Recurrence */ - prop = icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY); - if (prop != NULL) { - ewscal_set_reccurence (msg, prop, &dtstart); - } - - msdn_location_start = e_cal_backend_ews_tz_util_get_msdn_equivalent (ical_location_start); - msdn_location_end = e_cal_backend_ews_tz_util_get_msdn_equivalent (ical_location_end); - satisfies = e_ews_connection_satisfies_server_version (convert_data->connection, E_EWS_EXCHANGE_2010); - - /* We have to cast these because libical puts a const pointer into the - * icaltimetype, but its basic read-only icaltimezone_foo() functions - * take a non-const pointer! */ - if (satisfies && msdn_location_start != NULL && msdn_location_end != NULL) { - GSList *msdn_locations = NULL; - GSList *tzds = NULL; - - msdn_locations = g_slist_append (msdn_locations, (gchar *) msdn_location_start); - msdn_locations = g_slist_append (msdn_locations, (gchar *) msdn_location_end); - - if (e_ews_connection_get_server_time_zones_sync ( - convert_data->connection, - EWS_PRIORITY_MEDIUM, - msdn_locations, - &tzds, - NULL, - NULL)) { - ewscal_set_timezone (msg, "StartTimeZone", tzds->data); - ewscal_set_timezone (msg, "EndTimeZone", tzds->data); - } - - g_slist_free (msdn_locations); - g_slist_free_full (tzds, (GDestroyNotify) e_ews_calendar_time_zone_definition_free); - } else { - e_ews_message_replace_server_version (msg, E_EWS_EXCHANGE_2007_SP1); - - ewscal_set_meeting_timezone (msg, tzid_start); - } - - e_soap_message_end_element (msg); /* "CalendarItem" */ - - g_object_unref (comp); -} - -static const gchar * -ews_priority_to_string (gint priority) -{ - if (priority <= 3) - return "High"; - - if (priority >= 7) - return "Low"; - - return "Normal"; -} - -static void -convert_vtodo_calcomp_to_xml (ESoapMessage *msg, - gpointer user_data) -{ - EwsCalendarConvertData *convert_data = user_data; - icalcomponent *icalcomp = convert_data->icalcomp; - icalproperty *prop; - icaltimetype dt; - gint value; - gchar buffer[16]; - - e_soap_message_start_element (msg, "Task", NULL, NULL); - - e_ews_message_write_string_parameter (msg, "Subject", NULL, icalcomponent_get_summary (icalcomp)); - - convert_sensitivity_calcomp_to_xml (msg, icalcomp); - - e_ews_message_write_string_parameter_with_attribute (msg, "Body", NULL, icalcomponent_get_description (icalcomp), "BodyType", "Text"); - - convert_categories_calcomp_to_xml (msg, NULL, icalcomp); - - prop = icalcomponent_get_first_property (icalcomp, ICAL_PRIORITY_PROPERTY); - if (prop) { - gint priority; - - priority = icalproperty_get_priority (prop); - e_ews_message_write_string_parameter (msg, "Importance", NULL, ews_priority_to_string (priority)); - } - - prop = icalcomponent_get_first_property (icalcomp, ICAL_DUE_PROPERTY); - if (prop) { - dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DUE_PROPERTY, icalproperty_get_due); - e_ews_cal_utils_set_time (msg, "DueDate", &dt, TRUE); - } - - prop = icalcomponent_get_first_property (icalcomp, ICAL_PERCENTCOMPLETE_PROPERTY); - if (prop) { - value = icalproperty_get_percentcomplete (prop); - snprintf (buffer, 16, "%d", value); - e_ews_message_write_string_parameter (msg, "PercentComplete", NULL, buffer); - } - - prop = icalcomponent_get_first_property (icalcomp, ICAL_DTSTART_PROPERTY); - if (prop) { - dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - e_ews_cal_utils_set_time (msg, "StartDate", &dt, TRUE); - } - - prop = icalcomponent_get_first_property (icalcomp, ICAL_STATUS_PROPERTY); - if (prop) { - switch (icalproperty_get_status (prop)) { - case ICAL_STATUS_INPROCESS: - e_ews_message_write_string_parameter (msg, "Status", NULL, "InProgress"); - break; - case ICAL_STATUS_COMPLETED: - e_ews_message_write_string_parameter (msg, "Status", NULL, "Completed"); - break; - default: - break; - } - } - - e_soap_message_end_element (msg); /* "Task" */ -} - -static void -convert_vjournal_calcomp_to_xml (ESoapMessage *msg, - gpointer user_data) -{ - EwsCalendarConvertData *convert_data = user_data; - icalcomponent *icalcomp = convert_data->icalcomp; - const gchar *text; - - e_soap_message_start_element (msg, "Message", NULL, NULL); - e_ews_message_write_string_parameter (msg, "ItemClass", NULL, "IPM.StickyNote"); - - e_ews_message_write_string_parameter (msg, "Subject", NULL, icalcomponent_get_summary (icalcomp)); - - convert_sensitivity_calcomp_to_xml (msg, icalcomp); - - text = icalcomponent_get_description (icalcomp); - if (!text || !*text) - text = icalcomponent_get_summary (icalcomp); - e_ews_message_write_string_parameter_with_attribute (msg, "Body", NULL, text, "BodyType", "Text"); - - convert_categories_calcomp_to_xml (msg, NULL, icalcomp); - - e_soap_message_end_element (msg); /* Message */ -} - -void -e_cal_backend_ews_convert_calcomp_to_xml (ESoapMessage *msg, - gpointer user_data) -{ - EwsCalendarConvertData *convert_data = user_data; - - switch (icalcomponent_isa (convert_data->icalcomp)) { - case ICAL_VEVENT_COMPONENT: - convert_vevent_calcomp_to_xml (msg, convert_data); - break; - case ICAL_VTODO_COMPONENT: - convert_vtodo_calcomp_to_xml (msg, convert_data); - break; - case ICAL_VJOURNAL_COMPONENT: - convert_vjournal_calcomp_to_xml (msg, convert_data); - break; - default: - g_warn_if_reached (); - break; - } -} - -static void -convert_component_categories_to_updatexml (ECalComponent *comp, - ESoapMessage *msg, - const gchar *base_elem_name) -{ - GSList *categ_list = NULL, *citer; - - g_return_if_fail (comp != NULL); - g_return_if_fail (msg != NULL); - g_return_if_fail (base_elem_name != NULL); - - e_cal_component_get_categories_list (comp, &categ_list); - - /* Categories cannot be empty, thus first verify they are not */ - - for (citer = categ_list; citer; citer = g_slist_next (citer)) { - const gchar *category = citer->data; - - if (category && *category) - break; - } - - if (citer) { - e_ews_message_start_set_item_field (msg, "Categories", "item", base_elem_name); - e_soap_message_start_element (msg, "Categories", NULL, NULL); - - for (citer = categ_list; citer; citer = g_slist_next (citer)) { - const gchar *category = citer->data; - - if (!category || !*category) - continue; - - e_ews_message_write_string_parameter (msg, "String", NULL, category); - } - - e_soap_message_end_element (msg); /* Categories */ - e_ews_message_end_set_item_field (msg); - } else { - e_ews_message_add_delete_item_field (msg, "Categories", "item"); - } - - e_cal_component_free_categories_list (categ_list); -} - -static void -convert_vevent_property_to_updatexml (ESoapMessage *msg, - const gchar *name, - const gchar *value, - const gchar *prefix, - const gchar *attr_name, - const gchar *attr_value) -{ - e_ews_message_start_set_item_field (msg, name, prefix, "CalendarItem"); - e_ews_message_write_string_parameter_with_attribute (msg, name, NULL, value, attr_name, attr_value); - e_ews_message_end_set_item_field (msg); -} - -static void -convert_vevent_component_to_updatexml (ESoapMessage *msg, - gpointer user_data) -{ - EwsCalendarConvertData *convert_data = user_data; - icalcomponent *icalcomp = e_cal_component_get_icalcomponent (convert_data->comp); - icalcomponent *icalcomp_old = e_cal_component_get_icalcomponent (convert_data->old_comp); - GSList *required = NULL, *optional = NULL, *resource = NULL; - icaltimetype dtstart, dtend, dtstart_old, dtend_old; - icaltimezone *tzid_start = NULL, *tzid_end = NULL; - icalproperty *prop, *transp; - const gchar *org_email_address = NULL, *value = NULL, *old_value = NULL; - const gchar *ical_location_start = NULL, *ical_location_end = NULL; - const gchar *old_ical_location_start = NULL, *old_ical_location_end = NULL; - const gchar *old_msdn_location_start = NULL, *old_msdn_location_end = NULL; - const gchar *msdn_location_start = NULL, *msdn_location_end = NULL; - gboolean has_alarms, has_alarms_old; - gboolean dt_start_changed = FALSE, dt_end_changed = FALSE, dt_changed; - gboolean dt_start_changed_timezone_name = FALSE, dt_end_changed_timezone_name = FALSE; - gboolean satisfies, rsvp_requested = TRUE, is_all_day_event = FALSE; - gint alarm = 0, alarm_old = 0; - gchar *recid; - GError *error = NULL; - - /* Modifying a recurring meeting ? */ - if (icalcomponent_get_first_property (icalcomp_old, ICAL_RRULE_PROPERTY) != NULL) { - /* A single occurrence ? */ - prop = icalcomponent_get_first_property (icalcomp, ICAL_RECURRENCEID_PROPERTY); - if (prop != NULL) { - recid = icalproperty_get_value_as_string_r (prop); - e_ews_message_start_item_change ( - msg, - E_EWS_ITEMCHANGE_TYPE_OCCURRENCEITEM, - convert_data->item_id, - convert_data->change_key, - e_cal_backend_ews_rid_to_index ( - convert_data->default_zone, - recid, - icalcomp_old, - &error)); - g_free (recid); - } else { - e_ews_message_start_item_change ( - msg, E_EWS_ITEMCHANGE_TYPE_ITEM, - convert_data->item_id, convert_data->change_key, 0); - } - } else e_ews_message_start_item_change (msg, E_EWS_ITEMCHANGE_TYPE_ITEM, - convert_data->item_id, convert_data->change_key, 0); - - /* subject */ - value = icalcomponent_get_summary (icalcomp); - old_value = icalcomponent_get_summary (icalcomp_old); - if ((value && old_value && g_ascii_strcasecmp (value, old_value)) || - (value && old_value == NULL)) { - convert_vevent_property_to_updatexml (msg, "Subject", value, "item", NULL, NULL); - } else if (!value && old_value) - convert_vevent_property_to_updatexml (msg, "Subject", "", "item", NULL, NULL); - - prop = icalcomponent_get_first_property (icalcomp, ICAL_CLASS_PROPERTY); - if (prop) { - icalproperty_class classify = icalproperty_get_class (prop); - if (classify == ICAL_CLASS_PUBLIC) { - convert_vevent_property_to_updatexml (msg, "Sensitivity", "Normal", "item", NULL, NULL); - } else if (classify == ICAL_CLASS_PRIVATE) { - convert_vevent_property_to_updatexml (msg, "Sensitivity", "Private", "item", NULL, NULL); - } else if (classify == ICAL_CLASS_CONFIDENTIAL) { - convert_vevent_property_to_updatexml (msg, "Sensitivity", "Personal", "item", NULL, NULL); - } - } - - /*description*/ - value = icalcomponent_get_description (icalcomp); - old_value = icalcomponent_get_description (icalcomp_old); - if ((value && old_value && g_ascii_strcasecmp (value, old_value)) || - (value && old_value == NULL)) { - convert_vevent_property_to_updatexml (msg, "Body", value, "item", "BodyType", "Text"); - } else if (!value && old_value) - convert_vevent_property_to_updatexml (msg, "Body", "", "item", "BodyType", "Text"); - - /*update alarm items*/ - has_alarms = e_cal_component_has_alarms (convert_data->comp); - if (has_alarms) { - alarm = ews_get_alarm (convert_data->comp); - has_alarms_old = e_cal_component_has_alarms (convert_data->old_comp); - if (has_alarms_old) - alarm_old = ews_get_alarm (convert_data->old_comp); - if (!(alarm == alarm_old)) { - gchar buf[20]; - snprintf (buf, 20, "%d", alarm); - convert_vevent_property_to_updatexml (msg, "ReminderIsSet", "true", "item", NULL, NULL); - convert_vevent_property_to_updatexml (msg, "ReminderMinutesBeforeStart", buf, "item", NULL, NULL); - } - } - else convert_vevent_property_to_updatexml (msg, "ReminderIsSet", "false", "item", NULL, NULL); - - /* Categories */ - convert_component_categories_to_updatexml (convert_data->comp, msg, "CalendarItem"); - - /*location*/ - value = icalcomponent_get_location (icalcomp); - old_value = icalcomponent_get_location (icalcomp_old); - if ((value && old_value && g_ascii_strcasecmp (value, old_value)) || - (value && old_value == NULL)) { - convert_vevent_property_to_updatexml (msg, "Location", value, "calendar", NULL, NULL); - } else if (!value && old_value) - convert_vevent_property_to_updatexml (msg, "Location", "", "calendar", NULL, NULL); - - /*freebusy*/ - transp = icalcomponent_get_first_property (icalcomp, ICAL_TRANSP_PROPERTY); - value = icalproperty_get_value_as_string (transp); - transp = icalcomponent_get_first_property (icalcomp_old, ICAL_TRANSP_PROPERTY); - old_value = icalproperty_get_value_as_string (transp); - if (g_strcmp0 (value, old_value)) { - if (!g_strcmp0 (value, "TRANSPARENT")) - convert_vevent_property_to_updatexml (msg, "LegacyFreeBusyStatus","Free" , "calendar", NULL, NULL); - else - convert_vevent_property_to_updatexml (msg, "LegacyFreeBusyStatus","Busy" , "calendar", NULL, NULL); - } - - org_email_address = e_ews_collect_organizer (icalcomp); - if (org_email_address && convert_data->user_email && g_ascii_strcasecmp (org_email_address, convert_data->user_email)) { - e_ews_message_end_item_change (msg); - return; - } - /* Update other properties allowed only for meeting organizers*/ - /*meeting dates*/ - dtstart = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - dtstart_old = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp_old, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - dt_start_changed = icaltime_compare (dtstart, dtstart_old) != 0; - if (dtstart.zone != NULL) { - tzid_start = (icaltimezone *) dtstart.zone; - ical_location_start = icaltimezone_get_location (tzid_start); - - old_ical_location_start = icaltimezone_get_location ((icaltimezone *)dtstart_old.zone); - if (g_strcmp0 (ical_location_start, old_ical_location_start) != 0) - dt_start_changed_timezone_name = TRUE; - } - - dtend = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); - dtend_old = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp_old, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); - dt_end_changed = icaltime_compare (dtend, dtend_old) != 0; - if (dtend.zone != NULL) { - tzid_end = (icaltimezone *) dtend.zone; - ical_location_end = icaltimezone_get_location (tzid_end); - - old_ical_location_end = icaltimezone_get_location ((icaltimezone *)dtend_old.zone); - if (g_strcmp0 (ical_location_end, old_ical_location_end) != 0) - dt_end_changed_timezone_name = TRUE; - } - - satisfies = e_ews_connection_satisfies_server_version (convert_data->connection, E_EWS_EXCHANGE_2010); - - if (satisfies) { - if (old_ical_location_start != NULL) { - old_msdn_location_start = e_cal_backend_ews_tz_util_get_msdn_equivalent (old_ical_location_start); - msdn_location_start = e_cal_backend_ews_tz_util_get_msdn_equivalent (ical_location_start); - - if (g_strcmp0 (old_msdn_location_start, msdn_location_start) != 0) - dt_start_changed = TRUE; - } - - if (old_ical_location_end != NULL) { - old_msdn_location_end = e_cal_backend_ews_tz_util_get_msdn_equivalent (old_ical_location_end); - msdn_location_end = e_cal_backend_ews_tz_util_get_msdn_equivalent (ical_location_end); - - if (g_strcmp0 (old_msdn_location_end, msdn_location_end) != 0) - dt_end_changed = TRUE; - } - - if ((dt_start_changed || dt_start_changed_timezone_name) && ical_location_start != NULL) - e_ews_message_add_set_item_field_extended_distinguished_name_string ( - msg, - NULL, - "CalendarItem", - "PublicStrings", - "EvolutionEWSStartTimeZone", - ical_location_start); - - if ((dt_end_changed || dt_end_changed_timezone_name) && ical_location_end != NULL) - e_ews_message_add_set_item_field_extended_distinguished_name_string ( - msg, - NULL, - "CalendarItem", - "PublicStrings", - "EvolutionEWSEndTimeZone", - ical_location_end); - } - - dt_changed = dt_start_changed || dt_end_changed; - - if (dt_changed) - is_all_day_event = check_is_all_day_event (dtstart, tzid_start, dtend, tzid_end); - - if (dt_start_changed) { - e_ews_message_start_set_item_field (msg, "Start", "calendar","CalendarItem"); - e_ews_cal_utils_set_time (msg, "Start", &dtstart, is_all_day_event && dtstart.is_date); - e_ews_message_end_set_item_field (msg); - } - - if (dt_end_changed) { - e_ews_message_start_set_item_field (msg, "End", "calendar", "CalendarItem"); - e_ews_cal_utils_set_time (msg, "End", &dtend, is_all_day_event && dtend.is_date); - e_ews_message_end_set_item_field (msg); - } - - /*Check for All Day Event*/ - if (dt_changed) { - if (is_all_day_event) - convert_vevent_property_to_updatexml (msg, "IsAllDayEvent", "true", "calendar", NULL, NULL); - else - convert_vevent_property_to_updatexml (msg, "IsAllDayEvent", "false", "calendar", NULL, NULL); - } - - e_ews_collect_attendees (icalcomp, &required, &optional, &resource, &rsvp_requested); - - convert_vevent_property_to_updatexml (msg, "IsResponseRequested", rsvp_requested ? "true" : "false", "calendar", NULL, NULL); - - if (required != NULL) { - e_ews_message_start_set_item_field (msg, "RequiredAttendees", "calendar", "CalendarItem"); - - add_attendees_list_to_message (msg, "RequiredAttendees", required); - g_slist_free (required); - - e_ews_message_end_set_item_field (msg); - } - if (optional != NULL) { - e_ews_message_start_set_item_field (msg, "OptionalAttendees", "calendar", "CalendarItem"); - - add_attendees_list_to_message (msg, "OptionalAttendees", optional); - g_slist_free (optional); - - e_ews_message_end_set_item_field (msg); - } - if (resource != NULL) { - e_ews_message_start_set_item_field (msg, "Resources", "calendar", "CalendarItem"); - - add_attendees_list_to_message (msg, "Resources", resource); - g_slist_free (resource); - - e_ews_message_end_set_item_field (msg); - } - - /* Recurrence */ - value = NULL; old_value = NULL; - prop = icalcomponent_get_first_property (icalcomp_old, ICAL_RRULE_PROPERTY); - if (prop != NULL) - old_value = icalproperty_get_value_as_string (prop); - prop = icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY); - if (prop != NULL) - value = icalproperty_get_value_as_string (prop); - - if (prop != NULL && g_strcmp0 (value, old_value)) { - e_ews_message_start_set_item_field (msg, "Recurrence", "calendar", "CalendarItem"); - ewscal_set_reccurence (msg, prop, &dtstart); - e_ews_message_end_set_item_field (msg); - } - - if (dt_changed && satisfies) { - if (msdn_location_start != NULL || msdn_location_end != NULL) { - GSList *msdn_locations = NULL; - GSList *tzds = NULL; - - if (msdn_location_start != NULL) - msdn_locations = g_slist_append (msdn_locations, (gchar *) msdn_location_start); - - if (msdn_location_end != NULL) - msdn_locations = g_slist_append (msdn_locations, (gchar *) msdn_location_end); - - if (e_ews_connection_get_server_time_zones_sync ( - convert_data->connection, - EWS_PRIORITY_MEDIUM, - msdn_locations, - &tzds, - NULL, - NULL)) { - GSList *tmp; - - tmp = tzds; - if (tzid_start != NULL) { - e_ews_message_start_set_item_field (msg, "StartTimeZone", "calendar", "CalendarItem"); - ewscal_set_timezone (msg, "StartTimeZone", tmp->data); - e_ews_message_end_set_item_field (msg); - - /* - * Exchange server is smart enough to return the list of - * ServerTimeZone without repeated elements - */ - if (tmp->next != NULL) - tmp = tmp->next; - } - - if (tzid_end != NULL) { - e_ews_message_start_set_item_field (msg, "EndTimeZone", "calendar", "CalendarItem"); - ewscal_set_timezone (msg, "EndTimeZone", tmp->data); - e_ews_message_end_set_item_field (msg); - } - } - - g_slist_free (msdn_locations); - g_slist_free_full (tzds, (GDestroyNotify) e_ews_calendar_time_zone_definition_free); - } - } else if (dt_changed) { - e_ews_message_replace_server_version (msg, E_EWS_EXCHANGE_2007_SP1); - - e_ews_message_start_set_item_field (msg, "MeetingTimeZone", "calendar", "CalendarItem"); - ewscal_set_meeting_timezone (msg, tzid_start ? tzid_start : convert_data->default_zone); - e_ews_message_end_set_item_field (msg); - } - - e_ews_message_end_item_change (msg); -} - -static void -convert_vtodo_property_to_updatexml (ESoapMessage *msg, - const gchar *name, - const gchar *value, - const gchar *prefix, - const gchar *attr_name, - const gchar *attr_value) -{ - e_ews_message_start_set_item_field (msg, name, prefix, "Task"); - e_ews_message_write_string_parameter_with_attribute (msg, name, NULL, value, attr_name, attr_value); - e_ews_message_end_set_item_field (msg); -} - -static void -convert_vtodo_component_to_updatexml (ESoapMessage *msg, - gpointer user_data) -{ - EwsCalendarConvertData *convert_data = user_data; - icalcomponent *icalcomp = e_cal_component_get_icalcomponent (convert_data->comp); - icalproperty *prop; - icaltimetype dt; - gint value; - gchar buffer[16]; - - e_ews_message_start_item_change ( - msg, E_EWS_ITEMCHANGE_TYPE_ITEM, - convert_data->item_id, convert_data->change_key, 0); - - convert_vtodo_property_to_updatexml (msg, "Subject", icalcomponent_get_summary (icalcomp), "item", NULL, NULL); - - prop = icalcomponent_get_first_property (icalcomp, ICAL_CLASS_PROPERTY); - if (prop) { - icalproperty_class classify = icalproperty_get_class (prop); - if (classify == ICAL_CLASS_PUBLIC) { - convert_vtodo_property_to_updatexml (msg, "Sensitivity", "Normal", "item", NULL, NULL); - } else if (classify == ICAL_CLASS_PRIVATE) { - convert_vtodo_property_to_updatexml (msg, "Sensitivity", "Private", "item", NULL, NULL); - } else if (classify == ICAL_CLASS_CONFIDENTIAL) { - convert_vtodo_property_to_updatexml (msg, "Sensitivity", "Personal", "item", NULL, NULL); - } - } - - convert_vtodo_property_to_updatexml (msg, "Body", icalcomponent_get_description (icalcomp), "item", "BodyType", "Text"); - - prop = icalcomponent_get_first_property (icalcomp, ICAL_DUE_PROPERTY); - if (prop) { - dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DUE_PROPERTY, icalproperty_get_due); - e_ews_message_start_set_item_field (msg, "DueDate", "task", "Task"); - e_ews_cal_utils_set_time (msg, "DueDate", &dt, TRUE); - e_ews_message_end_set_item_field (msg); - } else { - e_ews_message_add_delete_item_field (msg, "DueDate", "task"); - } - - prop = icalcomponent_get_first_property (icalcomp, ICAL_PERCENTCOMPLETE_PROPERTY); - if (prop) { - value = icalproperty_get_percentcomplete (prop); - snprintf (buffer, 16, "%d", value); - e_ews_message_start_set_item_field (msg, "PercentComplete", "task", "Task"); - e_ews_message_write_string_parameter (msg, "PercentComplete", NULL, buffer); - e_ews_message_end_set_item_field (msg); - } - - prop = icalcomponent_get_first_property (icalcomp, ICAL_DTSTART_PROPERTY); - if (prop) { - dt = e_cal_backend_ews_get_datetime_with_zone (convert_data->timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - e_ews_message_start_set_item_field (msg, "StartDate", "task", "Task"); - e_ews_cal_utils_set_time (msg, "StartDate", &dt, TRUE); - e_ews_message_end_set_item_field (msg); - } else { - e_ews_message_add_delete_item_field (msg, "StartDate", "task"); - } - - prop = icalcomponent_get_first_property (icalcomp, ICAL_STATUS_PROPERTY); - if (prop) { - switch (icalproperty_get_status (prop)) { - case ICAL_STATUS_INPROCESS: - convert_vtodo_property_to_updatexml (msg, "Status", "InProgress", "task", NULL, NULL); - break; - case ICAL_STATUS_COMPLETED: - convert_vtodo_property_to_updatexml (msg, "Status", "Completed", "task", NULL, NULL); - break; - case ICAL_STATUS_NONE: - case ICAL_STATUS_NEEDSACTION: - convert_vtodo_property_to_updatexml (msg, "Status", "NotStarted", "task", NULL, NULL); - break; - default: - break; - } - } - - /* Categories */ - convert_component_categories_to_updatexml (convert_data->comp, msg, "Task"); - - prop = icalcomponent_get_first_property (icalcomp, ICAL_PRIORITY_PROPERTY); - if (prop) { - gint priority; - - priority = icalproperty_get_priority (prop); - convert_vtodo_property_to_updatexml (msg, "Importance", ews_priority_to_string (priority), "item", NULL, NULL); - } - - e_ews_message_end_item_change (msg); -} - -static void -convert_vjournal_property_to_updatexml (ESoapMessage *msg, - const gchar *name, - const gchar *value, - const gchar *prefix, - const gchar *attr_name, - const gchar *attr_value) -{ - e_ews_message_start_set_item_field (msg, name, prefix, "Message"); - e_ews_message_write_string_parameter_with_attribute (msg, name, NULL, value, attr_name, attr_value); - e_ews_message_end_set_item_field (msg); -} - -static void -convert_vjournal_component_to_updatexml (ESoapMessage *msg, - gpointer user_data) -{ - EwsCalendarConvertData *convert_data = user_data; - icalcomponent *icalcomp = e_cal_component_get_icalcomponent (convert_data->comp); - icalproperty *prop; - const gchar *text; - - e_ews_message_start_item_change ( - msg, E_EWS_ITEMCHANGE_TYPE_ITEM, - convert_data->item_id, convert_data->change_key, 0); - - convert_vjournal_property_to_updatexml (msg, "ItemClass", "IPM.StickyNote", "item", NULL, NULL); - convert_vjournal_property_to_updatexml (msg, "Subject", icalcomponent_get_summary (icalcomp), "item", NULL, NULL); - - prop = icalcomponent_get_first_property (icalcomp, ICAL_CLASS_PROPERTY); - if (prop) { - icalproperty_class classify = icalproperty_get_class (prop); - if (classify == ICAL_CLASS_PUBLIC) { - convert_vjournal_property_to_updatexml (msg, "Sensitivity", "Normal", "item", NULL, NULL); - } else if (classify == ICAL_CLASS_PRIVATE) { - convert_vjournal_property_to_updatexml (msg, "Sensitivity", "Private", "item", NULL, NULL); - } else if (classify == ICAL_CLASS_CONFIDENTIAL) { - convert_vjournal_property_to_updatexml (msg, "Sensitivity", "Personal", "item", NULL, NULL); - } - } - - text = icalcomponent_get_description (icalcomp); - if (!text || !*text) - text = icalcomponent_get_summary (icalcomp); - - convert_vjournal_property_to_updatexml (msg, "Body", text, "item", "BodyType", "Text"); - - /* Categories */ - convert_component_categories_to_updatexml (convert_data->comp, msg, "Message"); - - e_ews_message_end_item_change (msg); -} - -void -e_cal_backend_ews_convert_component_to_updatexml (ESoapMessage *msg, - gpointer user_data) -{ - EwsCalendarConvertData *convert_data = user_data; - icalcomponent *icalcomp = e_cal_component_get_icalcomponent (convert_data->comp); - - switch (icalcomponent_isa (icalcomp)) { - case ICAL_VEVENT_COMPONENT: - convert_vevent_component_to_updatexml (msg, user_data); - break; - case ICAL_VTODO_COMPONENT: - convert_vtodo_component_to_updatexml (msg, user_data); - break; - case ICAL_VJOURNAL_COMPONENT: - convert_vjournal_component_to_updatexml (msg, user_data); - break; - default: - break; - } -} - -guint -e_cal_backend_ews_rid_to_index (icaltimezone *timezone, - const gchar *rid, - icalcomponent *comp, - GError **error) -{ - guint index = 1; - icalproperty *prop = icalcomponent_get_first_property (comp, ICAL_RRULE_PROPERTY); - struct icalrecurrencetype rule = icalproperty_get_rrule (prop); - struct icaltimetype dtstart = icalcomponent_get_dtstart (comp); - icalrecur_iterator * ritr; - icaltimetype next, o_time; - - /* icalcomponent_get_datetime needs a fix to initialize ret.zone to NULL. If a timezone is not - * found in libical, it remains uninitialized in that function causing invalid read or crash. so - * we set the timezone as we cannot identify if it has a valid timezone or not */ - dtstart.zone = timezone; - ritr = icalrecur_iterator_new (rule, dtstart); - next = icalrecur_iterator_next (ritr); - o_time = icaltime_from_string (rid); - o_time.zone = dtstart.zone; - - for (; !icaltime_is_null_time (next); next = icalrecur_iterator_next (ritr), index++) { - if (icaltime_compare_date_only (o_time, next) == 0) - break; - } - - icalrecur_iterator_free (ritr); - - if (icaltime_is_null_time (next)) { - g_propagate_error ( - error, EDC_ERROR_EX (OtherError, - "Invalid occurrence ID")); - index = 0; - } - - return index; -} - -void -e_cal_backend_ews_clear_reminder_is_set (ESoapMessage *msg, - gpointer user_data) -{ - EwsCalendarConvertData *convert_data = user_data; - - e_ews_message_start_item_change ( - msg, - convert_data->change_type, - convert_data->item_id, - convert_data->change_key, - convert_data->index); - - e_ews_message_start_set_item_field (msg, "ReminderIsSet","item", "CalendarItem"); - - e_ews_message_write_string_parameter (msg, "ReminderIsSet", NULL, "false"); - - e_ews_message_end_set_item_field (msg); - - e_ews_message_end_item_change (msg); -} - -void -e_cal_backend_ews_prepare_set_free_busy_status (ESoapMessage *msg, - gpointer user_data) -{ - EwsCalendarConvertData *data = user_data; - - e_ews_message_start_item_change (msg, E_EWS_ITEMCHANGE_TYPE_ITEM, data->item_id, data->change_key, 0); - - e_ews_message_start_set_item_field (msg, "LegacyFreeBusyStatus", "calendar", "CalendarItem"); - - e_ews_message_write_string_parameter (msg, "LegacyFreeBusyStatus", NULL, "Free"); - - e_ews_message_end_set_item_field (msg); - - e_ews_message_end_item_change (msg); -} - -void -e_cal_backend_ews_prepare_accept_item_request (ESoapMessage *msg, - gpointer user_data) -{ - EwsCalendarConvertData *data = user_data; - const gchar *response_type = data->response_type; - - /* FORMAT OF A SAMPLE SOAP MESSAGE: http://msdn.microsoft.com/en-us/library/aa566464%28v=exchg.140%29.aspx - * Accept and Decline meeting have same method code (10032) - * The real status is reflected at Attendee property PARTSTAT - * need to find current user as attendee and make a decision what to do. - * Prepare AcceptItem node in the SOAP message */ - - if (response_type && !g_ascii_strcasecmp (response_type, "ACCEPTED")) - e_soap_message_start_element (msg, "AcceptItem", NULL, NULL); - else if (response_type && !g_ascii_strcasecmp (response_type, "DECLINED")) - e_soap_message_start_element (msg, "DeclineItem", NULL, NULL); - else - e_soap_message_start_element (msg, "TentativelyAcceptItem", NULL, NULL); - - e_soap_message_start_element (msg, "ReferenceItemId", NULL, NULL); - e_soap_message_add_attribute (msg, "Id", data->item_id, NULL, NULL); - e_soap_message_add_attribute (msg, "ChangeKey", data->change_key, NULL, NULL); - e_soap_message_end_element (msg); /* "ReferenceItemId" */ - - /* end of "AcceptItem" */ - e_soap_message_end_element (msg); -} - -struct icaltimetype -e_cal_backend_ews_get_datetime_with_zone (ETimezoneCache *timezone_cache, - icalcomponent *comp, - icalproperty_kind prop_kind, - struct icaltimetype (* get_func) (const icalproperty *prop)) -{ - struct icaltimetype dt = icaltime_null_time (); - icalproperty *prop; - icalparameter *param; - const gchar *tzid; - - g_return_val_if_fail (E_IS_TIMEZONE_CACHE (timezone_cache), dt); - g_return_val_if_fail (comp != NULL, dt); - g_return_val_if_fail (get_func != NULL, dt); - - prop = icalcomponent_get_first_property (comp, prop_kind); - if (!prop) - return dt; - - dt = get_func (prop); - - if (!icaltime_is_valid_time (dt) || - icaltime_is_null_time (dt)) - return dt; - - param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER); - if (!param) - return dt; - - tzid = icalparameter_get_tzid (param); - if (!tzid || !*tzid) - return dt; - - dt.zone = e_timezone_cache_get_timezone (timezone_cache, tzid); - - return dt; -} diff --git a/src/calendar/e-cal-backend-ews-utils.h b/src/calendar/e-cal-backend-ews-utils.h index 560200d..ace3cf1 100644 --- a/src/calendar/e-cal-backend-ews-utils.h +++ b/src/calendar/e-cal-backend-ews-utils.h @@ -47,7 +47,6 @@ typedef struct { ECalComponent *comp; ECalComponent *old_comp; icalcomponent *icalcomp; - icalcomponent *vcalendar; /* can be NULL, parent of icalcomp, where timezones can be eventually found */ gchar *item_id; gchar *change_key; EEwsItemChangeType change_type; @@ -83,7 +82,6 @@ guint e_cal_backend_ews_rid_to_index (icaltimezone *timezone, const gchar *rid, struct icaltimetype e_cal_backend_ews_get_datetime_with_zone (ETimezoneCache *timezone_cache, - icalcomponent *vcalendar, icalcomponent *comp, icalproperty_kind prop_kind, struct icaltimetype (* get_func) (const icalproperty *prop)); diff --git a/src/calendar/e-cal-backend-ews-utils.h.import-event-timezone b/src/calendar/e-cal-backend-ews-utils.h.import-event-timezone deleted file mode 100644 index ace3cf1..0000000 --- a/src/calendar/e-cal-backend-ews-utils.h.import-event-timezone +++ /dev/null @@ -1,91 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - * 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. - * - * 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -#ifndef E_CAL_BACKEND_EWS_UTILS_H -#define E_CAL_BACKEND_EWS_UTILS_H - -#include -#include -#include - -#include "server/e-ews-connection.h" -#include "server/e-ews-item-change.h" - -#include "e-cal-backend-ews.h" - -G_BEGIN_DECLS -#define EDC_ERROR(_code) e_data_cal_create_error (_code, NULL) -#define EDC_ERROR_EX(_code, _msg) e_data_cal_create_error (_code, _msg) - -#define MINUTES_IN_HOUR 60 -#define SECS_IN_MINUTE 60 - -typedef struct { - EEwsConnection *connection; - ETimezoneCache *timezone_cache; - icaltimezone *default_zone; - gchar *user_email; - gchar *response_type; /* Accept */ - GSList *users; - ECalComponent *comp; - ECalComponent *old_comp; - icalcomponent *icalcomp; - gchar *item_id; - gchar *change_key; - EEwsItemChangeType change_type; - gint index; - time_t start; - time_t end; -} EwsCalendarConvertData; - -const gchar *e_ews_collect_organizer (icalcomponent *comp); -void e_ews_collect_attendees (icalcomponent *comp, GSList **required, GSList **optional, GSList **resource, gboolean *out_rsvp_requested); - -void ewscal_set_timezone (ESoapMessage *msg, const gchar *name, EEwsCalendarTimeZoneDefinition *tzd); -void ewscal_set_meeting_timezone (ESoapMessage *msg, icaltimezone *icaltz); -void ewscal_set_reccurence (ESoapMessage *msg, icalproperty *rrule, icaltimetype *dtstart); -void ewscal_set_reccurence_exceptions (ESoapMessage *msg, icalcomponent *comp); -gchar *e_ews_extract_attachment_id_from_uri (const gchar *uri); -void ews_set_alarm (ESoapMessage *msg, ECalComponent *comp); -gint ews_get_alarm (ECalComponent *comp); -void e_ews_clean_icalcomponent (icalcomponent *icalcomp); - -const gchar *e_cal_backend_ews_tz_util_get_msdn_equivalent (const gchar *ical_tz_location); -const gchar *e_cal_backend_ews_tz_util_get_ical_equivalent (const gchar *msdn_tz_location); -void e_cal_backend_ews_populate_windows_zones (void); -void e_cal_backend_ews_unref_windows_zones (void); - -void e_cal_backend_ews_convert_calcomp_to_xml (ESoapMessage *msg, gpointer user_data); -void e_cal_backend_ews_convert_component_to_updatexml (ESoapMessage *msg, gpointer user_data); -void e_cal_backend_ews_clear_reminder_is_set (ESoapMessage *msg, gpointer user_data); -void e_cal_backend_ews_prepare_set_free_busy_status (ESoapMessage *msg,gpointer user_data); -void e_cal_backend_ews_prepare_accept_item_request (ESoapMessage *msg, gpointer user_data); - -guint e_cal_backend_ews_rid_to_index (icaltimezone *timezone, const gchar *rid, icalcomponent *comp, GError **error); - -struct icaltimetype - e_cal_backend_ews_get_datetime_with_zone (ETimezoneCache *timezone_cache, - icalcomponent *comp, - icalproperty_kind prop_kind, - struct icaltimetype (* get_func) (const icalproperty *prop)); - -G_END_DECLS - -#endif diff --git a/src/calendar/e-cal-backend-ews.c b/src/calendar/e-cal-backend-ews.c index 7f9334c..1f7ab52 100644 --- a/src/calendar/e-cal-backend-ews.c +++ b/src/calendar/e-cal-backend-ews.c @@ -147,41 +147,6 @@ ecb_ews_get_collection_settings (ECalBackendEws *cbews) return CAMEL_EWS_SETTINGS (settings); } -static GHashTable * -ecb_ews_get_mail_aliases (ECalBackendEws *cbews) -{ - ESource *source; - ESourceRegistry *registry; - GHashTable *aliases = NULL; - GList *identities, *link; - const gchar *parent_uid; - - source = e_backend_get_source (E_BACKEND (cbews)); - parent_uid = e_source_get_parent (source); - - if (!parent_uid || !*parent_uid) - return NULL; - - registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbews)); - identities = e_source_registry_list_enabled (registry, E_SOURCE_EXTENSION_MAIL_IDENTITY); - - for (link = identities; link; link = g_list_next (link)) { - ESource *mail_identity = link->data; - - if (g_strcmp0 (parent_uid, e_source_get_parent (mail_identity)) == 0) { - ESourceMailIdentity *extension; - - extension = e_source_get_extension (mail_identity, E_SOURCE_EXTENSION_MAIL_IDENTITY); - aliases = e_source_mail_identity_get_aliases_as_hash_table (extension); - break; - } - } - - g_list_free_full (identities, g_object_unref); - - return aliases; -} - static void ecb_ews_convert_error_to_edc_error (GError **perror) { @@ -346,7 +311,7 @@ ecb_ews_responsetype_to_partstat (const gchar *responsetype) else if (g_ascii_strcasecmp (responsetype, "NoResponseReceived") == 0) param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION); else if (g_ascii_strcasecmp (responsetype, "Unknown") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION); + param = icalparameter_new_partstat (ICAL_PARTSTAT_NONE); if (!param) param = icalparameter_new_partstat (ICAL_PARTSTAT_NONE); @@ -614,7 +579,7 @@ ecb_ews_item_to_component_sync (ECalBackendEws *cbews, if (start_zone != NULL) { icalcomp = icalcomponent_get_first_component (vcomp, kind); - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); + dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); dt = icaltime_convert_to_zone (dt, start_zone); icalcomponent_set_dtstart (icalcomp, dt); @@ -622,7 +587,7 @@ ecb_ews_item_to_component_sync (ECalBackendEws *cbews, e_timezone_cache_add_timezone (timezone_cache, start_zone); if (end_zone != NULL) { - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); + dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); dt = icaltime_convert_to_zone (dt, end_zone); icalcomponent_set_dtend (icalcomp, dt); @@ -690,11 +655,11 @@ ecb_ews_item_to_component_sync (ECalBackendEws *cbews, zone = icaltimezone_get_builtin_timezone (tzid); if (zone != NULL) { - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); + dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); dt = icaltime_convert_to_zone (dt, zone); icalcomponent_set_dtstart (icalcomp, dt); - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); + dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); dt = icaltime_convert_to_zone (dt, zone); icalcomponent_set_dtend (icalcomp, dt); } @@ -1385,18 +1350,6 @@ ecb_ews_is_organizer (ECalBackendEws *cbews, is_organizer = user_email && g_ascii_strcasecmp (email, user_email) == 0; g_free (user_email); - - if (!is_organizer) { - GHashTable *aliases; - - aliases = ecb_ews_get_mail_aliases (cbews); - - if (aliases) { - is_organizer = g_hash_table_contains (aliases, email); - - g_hash_table_unref (aliases); - } - } } return is_organizer; @@ -1441,8 +1394,7 @@ ecb_ews_connect_sync (ECalMetaBackend *meta_backend, cbews->priv->cnc, "proxy-resolver", G_BINDING_SYNC_CREATE); - *out_auth_result = e_ews_connection_try_credentials_sync (cbews->priv->cnc, credentials, NULL, - out_certificate_pem, out_certificate_errors, cancellable, error); + *out_auth_result = e_ews_connection_try_credentials_sync (cbews->priv->cnc, credentials, cancellable, error); if (*out_auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) { ESource *source = e_backend_get_source (E_BACKEND (cbews)); @@ -2642,15 +2594,11 @@ ecb_ews_save_component_sync (ECalMetaBackend *meta_backend, g_slist_free_full (existing, g_object_unref); g_slist_free_full (changed_instances, change_data_free); g_slist_free_full (removed_instances, g_object_unref); - } else if (e_cal_component_has_organizer (master) && - !ecb_ews_is_organizer (cbews, master)) { - success = FALSE; - g_propagate_error (error, EDC_ERROR_EX (PermissionDenied, _("Cannot create meetings organized by other users in an Exchange Web Services calendar."))); } else { GHashTable *removed_indexes; EwsCalendarConvertData convert_data = { 0 }; EEwsItem *item = NULL; - EwsId *ews_id = NULL; + const EwsId *ews_id = NULL; const gchar *send_meeting_invitations; icalcomponent *icalcomp; icalproperty *prop; @@ -2694,7 +2642,7 @@ ecb_ews_save_component_sync (ECalMetaBackend *meta_backend, if (item) { g_object_ref (item); - ews_id = e_ews_id_copy (e_ews_item_get_id (item)); + ews_id = e_ews_item_get_id (item); } } @@ -2718,8 +2666,6 @@ ecb_ews_save_component_sync (ECalMetaBackend *meta_backend, g_clear_object (&item); item = items_req->data; - - e_ews_id_free (ews_id); ews_id = NULL; if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) { @@ -2728,7 +2674,7 @@ ecb_ews_save_component_sync (ECalMetaBackend *meta_backend, success = FALSE; } else { item = g_object_ref (item); - ews_id = e_ews_id_copy (e_ews_item_get_id (item)); + ews_id = e_ews_item_get_id (item); } } @@ -2743,21 +2689,13 @@ ecb_ews_save_component_sync (ECalMetaBackend *meta_backend, g_warn_if_fail (ews_id != NULL); if (ews_id && ecb_ews_extract_attachments (icalcomp, &info_attachments)) { - gchar *changekey = NULL; GSList *ids = NULL; success = e_ews_connection_create_attachments_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - ews_id, info_attachments, FALSE, &changekey, &ids, cancellable, error); + ews_id, info_attachments, FALSE, NULL, &ids, cancellable, error); g_slist_free_full (info_attachments, (GDestroyNotify) e_ews_attachment_info_free); g_slist_free_full (ids, g_free); - - if (success && changekey) { - g_free (ews_id->change_key); - ews_id->change_key = changekey; - } else { - g_free (changekey); - } } } @@ -2792,7 +2730,6 @@ ecb_ews_save_component_sync (ECalMetaBackend *meta_backend, } icalcomponent_free (icalcomp); - e_ews_id_free (ews_id); g_clear_object (&item); for (link = (GSList *) instances; link && success; link = g_slist_next (link)) { @@ -2993,7 +2930,7 @@ ecb_ews_send_cancellation_email_sync (ECalBackendEws *cbews, icalcomponent_add_property (vevent, icalproperty_new_status (ICAL_STATUS_CANCELLED)); prop = icalcomponent_get_first_property (vevent, ICAL_METHOD_PROPERTY); if (prop != NULL) icalcomponent_remove_property (vevent, prop); - dt = e_cal_backend_ews_get_datetime_with_zone (E_TIMEZONE_CACHE (cbews), NULL, vevent, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); + dt = e_cal_backend_ews_get_datetime_with_zone (E_TIMEZONE_CACHE (cbews), vevent, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); icaltz = (icaltimezone *) (dt.zone ? dt.zone : ecb_ews_get_timezone_from_ical_component (cbews, vevent)); vtz = icaltimezone_get_component (icaltz); @@ -3036,7 +2973,6 @@ ecb_ews_send_cancellation_email_sync (ECalBackendEws *cbews, static void ecb_ews_receive_objects_no_exchange_mail (ECalBackendEws *cbews, - icalcomponent *vcalendar, icalcomponent *subcomp, GSList **ids, GCancellable *cancellable, @@ -3048,7 +2984,6 @@ ecb_ews_receive_objects_no_exchange_mail (ECalBackendEws *cbews, convert_data.connection = cbews->priv->cnc; convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); convert_data.icalcomp = subcomp; - convert_data.vcalendar = vcalendar; convert_data.default_zone = icaltimezone_get_utc_timezone (); fid = e_ews_folder_id_new (cbews->priv->folder_id, NULL, FALSE); @@ -3293,7 +3228,6 @@ ecb_ews_get_item_accept_id (ECalComponent *comp, static gboolean ecb_ews_do_method_request_publish_reply (ECalBackendEws *cbews, - icalcomponent *vcalendar, ECalComponent *comp, icalcomponent *subcomp, const gchar *response_type, @@ -3324,7 +3258,7 @@ ecb_ews_do_method_request_publish_reply (ECalBackendEws *cbews, while (pass < 2) { /*in case we do not have item id we will create item with mime content only*/ if (!item_id || (response_type && g_ascii_strcasecmp (response_type, "NEEDS-ACTION") == 0)) { - ecb_ews_receive_objects_no_exchange_mail (cbews, vcalendar, subcomp, &ids, cancellable, &local_error); + ecb_ews_receive_objects_no_exchange_mail (cbews, subcomp, &ids, cancellable, &local_error); } else { EwsCalendarConvertData convert_data = { 0 }; @@ -3433,7 +3367,6 @@ ecb_ews_do_method_request_publish_reply (ECalBackendEws *cbews, } convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.vcalendar = vcalendar; e_ews_connection_update_items_sync ( cbews->priv->cnc, @@ -3519,7 +3452,7 @@ ecb_ews_receive_objects_sync (ECalBackendSync *sync_backend, comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (subcomp)); - success = ecb_ews_do_method_request_publish_reply (cbews, icalcomp, comp, subcomp, response_type, user_email, rsvp_requested, cancellable, error); + success = ecb_ews_do_method_request_publish_reply (cbews, comp, subcomp, response_type, user_email, rsvp_requested, cancellable, error); do_refresh = TRUE; @@ -3742,8 +3675,6 @@ ecb_ews_get_backend_property (ECalBackend *cal_backend, return g_strjoin ( ",", CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS, - CAL_STATIC_CAPABILITY_NO_AUDIO_ALARMS, - CAL_STATIC_CAPABILITY_NO_PROCEDURE_ALARMS, CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY, CAL_STATIC_CAPABILITY_REMOVE_ALARMS, CAL_STATIC_CAPABILITY_NO_THISANDPRIOR, diff --git a/src/calendar/e-cal-backend-ews.c.cve-2019-3890 b/src/calendar/e-cal-backend-ews.c.cve-2019-3890 deleted file mode 100644 index 9e8bad8..0000000 --- a/src/calendar/e-cal-backend-ews.c.cve-2019-3890 +++ /dev/null @@ -1,3914 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com) - * - * 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. - * - * 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#include -#include -#include -#include - -#include -#include - -#include "server/e-source-ews-folder.h" -#include "server/e-ews-calendar-utils.h" -#include "server/e-ews-connection-utils.h" -#include "server/e-ews-camel-common.h" - -#include "e-cal-backend-ews.h" -#include "e-cal-backend-ews-utils.h" - -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -#ifdef G_OS_WIN32 -#ifdef gmtime_r -#undef gmtime_r -#endif - -/* The gmtime() in Microsoft's C library is MT-safe */ -#define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0) -#endif - -/* Private part of the CalBackendEws structure */ -struct _ECalBackendEwsPrivate { - GRecMutex cnc_lock; - EEwsConnection *cnc; - gchar *folder_id; - - guint subscription_key; - gboolean is_freebusy_calendar; - - gchar *attachments_dir; -}; - -#define X_EWS_ORIGINAL_COMP "X-EWS-ORIGINAL-COMP" - -#define EWS_MAX_FETCH_COUNT 100 - -#define GET_ITEMS_SYNC_PROPERTIES \ - "item:Attachments" \ - " item:Categories" \ - " item:HasAttachments" \ - " item:MimeContent" \ - " calendar:UID" \ - " calendar:Resources" \ - " calendar:ModifiedOccurrences" \ - " calendar:IsMeeting" \ - " calendar:IsResponseRequested" \ - " calendar:MyResponseType" \ - " calendar:RequiredAttendees" \ - " calendar:OptionalAttendees" - -#define GET_ITEMS_SYNC_PROPERTIES_2007 \ - GET_ITEMS_SYNC_PROPERTIES \ - " calendar:TimeZone" - -#define GET_ITEMS_SYNC_PROPERTIES_2010 \ - GET_ITEMS_SYNC_PROPERTIES \ - " calendar:StartTimeZone" \ - " calendar:EndTimeZone" - -#define e_data_cal_error_if_fail(expr, _code) \ - G_STMT_START { \ - if (G_LIKELY (expr)) { \ - } else { \ - g_log (G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "file %s: line %d (%s): assertion `%s' failed", \ - __FILE__, __LINE__, G_STRFUNC, #expr); \ - g_set_error (&error, E_DATA_CAL_ERROR, (_code), \ - "file %s: line %d (%s): assertion `%s' failed", \ - __FILE__, __LINE__, G_STRFUNC, #expr); \ - goto exit; \ - } \ - } G_STMT_END - -G_DEFINE_TYPE (ECalBackendEws, e_cal_backend_ews, E_TYPE_CAL_META_BACKEND) - -static CamelEwsSettings * -ecb_ews_get_collection_settings (ECalBackendEws *cbews) -{ - ESource *source; - ESource *collection; - ESourceCamel *extension; - ESourceRegistry *registry; - CamelSettings *settings; - const gchar *extension_name; - - source = e_backend_get_source (E_BACKEND (cbews)); - registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbews)); - - extension_name = e_source_camel_get_extension_name ("ews"); - e_source_camel_generate_subtype ("ews", CAMEL_TYPE_EWS_SETTINGS); - - /* The collection settings live in our parent data source. */ - collection = e_source_registry_find_extension ( - registry, source, extension_name); - g_return_val_if_fail (collection != NULL, NULL); - - extension = e_source_get_extension (collection, extension_name); - settings = e_source_camel_get_settings (extension); - - g_object_unref (collection); - - return CAMEL_EWS_SETTINGS (settings); -} - -static void -ecb_ews_convert_error_to_edc_error (GError **perror) -{ - GError *error = NULL; - - if (!perror || !*perror || (*perror)->domain == E_DATA_CAL_ERROR) - return; - - if ((*perror)->domain == EWS_CONNECTION_ERROR) { - switch ((*perror)->code) { - case EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED: - error = EDC_ERROR_EX (AuthenticationFailed, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_FOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_MANAGEDFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PARENTFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PUBLICFOLDERSERVERNOTFOUND: - error = EDC_ERROR_EX (NoSuchCal, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_EVENTNOTFOUND: - case EWS_CONNECTION_ERROR_ITEMNOTFOUND: - error = EDC_ERROR_EX (ObjectNotFound, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_UNAVAILABLE: - g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND, (*perror)->message); - break; - } - - if (!error) - error = EDC_ERROR_EX (OtherError, (*perror)->message); - } - - if (error) { - g_error_free (*perror); - *perror = error; - } -} - -static void -ecb_ews_maybe_disconnect_sync (ECalBackendEws *cbews, - GError **in_perror, - GCancellable *cancellable) -{ - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cbews)); - - if (in_perror && g_error_matches (*in_perror, E_DATA_CAL_ERROR, AuthenticationFailed)) { - e_cal_meta_backend_disconnect_sync (E_CAL_META_BACKEND (cbews), cancellable, NULL); - e_backend_schedule_credentials_required (E_BACKEND (cbews), E_SOURCE_CREDENTIALS_REASON_REJECTED, NULL, 0, NULL, NULL, G_STRFUNC); - } -} - -static void -ecb_ews_server_notification_cb (ECalBackendEws *cbews, - GSList *events, - EEwsConnection *cnc) -{ - GSList *link; - gboolean update_folder = FALSE; - - g_return_if_fail (cbews != NULL); - g_return_if_fail (cbews->priv != NULL); - - for (link = events; link && !update_folder; link = g_slist_next (link)) { - EEwsNotificationEvent *event = link->data; - - switch (event->type) { - case E_EWS_NOTIFICATION_EVENT_CREATED: - case E_EWS_NOTIFICATION_EVENT_DELETED: - case E_EWS_NOTIFICATION_EVENT_MODIFIED: - g_rec_mutex_lock (&cbews->priv->cnc_lock); - if (g_strcmp0 (event->folder_id, cbews->priv->folder_id) == 0) - update_folder = TRUE; - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - break; - case E_EWS_NOTIFICATION_EVENT_MOVED: - case E_EWS_NOTIFICATION_EVENT_COPIED: - g_rec_mutex_lock (&cbews->priv->cnc_lock); - if (g_strcmp0 (event->folder_id, cbews->priv->folder_id) == 0 || - g_strcmp0 (event->old_folder_id, cbews->priv->folder_id) == 0) - update_folder = TRUE; - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - break; - default: - return; - } - } - - if (update_folder) - e_cal_meta_backend_schedule_refresh (E_CAL_META_BACKEND (cbews)); -} - -static void -ecb_ews_unset_connection (ECalBackendEws *cbews) -{ - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cbews)); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->cnc) { - e_ews_connection_set_disconnected_flag (cbews->priv->cnc, TRUE); - - g_signal_handlers_disconnect_by_func (cbews->priv->cnc, ecb_ews_server_notification_cb, cbews); - - if (cbews->priv->subscription_key != 0) { - e_ews_connection_disable_notifications_sync ( - cbews->priv->cnc, - cbews->priv->subscription_key); - cbews->priv->subscription_key = 0; - } - } - - g_clear_object (&cbews->priv->cnc); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); -} - -static icaltimezone * -ecb_ews_get_timezone (ETimezoneCache *timezone_cache, - const gchar *msdn_tzid, - const gchar *tzid, - const gchar *evo_ews_tzid) -{ - icaltimezone *zone = NULL; - const gchar *evo_ews_msdn_tzid; - - zone = e_timezone_cache_get_timezone (timezone_cache, tzid); - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (tzid); - - if (g_strcmp0 (tzid, evo_ews_tzid) == 0) - return zone; - - if (evo_ews_tzid != NULL) { - evo_ews_msdn_tzid = e_cal_backend_ews_tz_util_get_msdn_equivalent (evo_ews_tzid); - - if (g_strcmp0 (msdn_tzid, evo_ews_msdn_tzid) == 0) { - zone = e_timezone_cache_get_timezone (timezone_cache, evo_ews_tzid); - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (evo_ews_tzid); - } - } - - return zone; -} - -static icalparameter * -ecb_ews_responsetype_to_partstat (const gchar *responsetype) -{ - icalparameter *param = NULL; - - if (!responsetype) - return icalparameter_new_partstat (ICAL_PARTSTAT_NONE); - - if (g_ascii_strcasecmp (responsetype, "Organizer") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_ACCEPTED); - else if (g_ascii_strcasecmp (responsetype, "Tentative") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_TENTATIVE); - else if (g_ascii_strcasecmp (responsetype, "Accept") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_ACCEPTED); - else if (g_ascii_strcasecmp (responsetype, "Decline") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_DECLINED); - else if (g_ascii_strcasecmp (responsetype, "NoResponseReceived") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION); - else if (g_ascii_strcasecmp (responsetype, "Unknown") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION); - - if (!param) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NONE); - - return param; -} - -static ECalComponent * -ecb_ews_item_to_component_sync (ECalBackendEws *cbews, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *res_component = NULL; - ETimezoneCache *timezone_cache; - icalcomponent_kind kind; - EEwsItemType item_type; - icalcomponent *icalcomp, *vcomp; - icaltimezone *utc_zone = icaltimezone_get_utc_timezone (); - CamelEwsSettings *ews_settings; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), NULL); - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - timezone_cache = E_TIMEZONE_CACHE (cbews); - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - ews_settings = ecb_ews_get_collection_settings (cbews); - - item_type = e_ews_item_get_item_type (item); - if (item_type == E_EWS_ITEM_TYPE_TASK || item_type == E_EWS_ITEM_TYPE_MEMO) { - icalproperty *icalprop; - icaltimetype due_date, start_date, complete_date, created; - icalproperty_status status = ICAL_STATUS_NONE; - icalproperty_class class = ICAL_CLASS_NONE; - const gchar *ews_task_status, *sensitivity; - EwsImportance item_importance; - gint priority = 5; - gboolean has_this_date = FALSE; - - vcomp = icalcomponent_new (ICAL_VCALENDAR_COMPONENT); - /*subject*/ - icalcomp = icalcomponent_new (item_type == E_EWS_ITEM_TYPE_TASK ? ICAL_VTODO_COMPONENT : ICAL_VJOURNAL_COMPONENT); - icalprop = icalproperty_new_summary (e_ews_item_get_subject (item)); - icalcomponent_add_property (icalcomp, icalprop); - - /*date time created*/ - created = icaltime_from_timet_with_zone (e_ews_item_get_date_created (item), 0, utc_zone); - icalprop = icalproperty_new_created (created); - icalcomponent_add_property (icalcomp, icalprop); - - /*sensitivity*/ - sensitivity = e_ews_item_get_sensitivity (item); - if (g_strcmp0 (sensitivity, "Normal") == 0) - class = ICAL_CLASS_PUBLIC; - else if (g_strcmp0 (sensitivity, "Private") == 0) - class = ICAL_CLASS_PRIVATE; - else if ((g_strcmp0 (sensitivity, "Confidential") == 0) || - (g_strcmp0 (sensitivity, "Personal") == 0)) - class = ICAL_CLASS_CONFIDENTIAL; - icalprop = icalproperty_new_class (class); - icalcomponent_add_property (icalcomp, icalprop); - - /*description*/ - icalprop = icalproperty_new_description (e_ews_item_get_body (item)); - icalcomponent_add_property (icalcomp, icalprop); - - /*task assaingments*/ - if (e_ews_item_get_delegator (item) != NULL) { - const gchar *task_owner = e_ews_item_get_delegator (item); - GSList *mailboxes = NULL, *l; - gboolean includes_last_item; - gchar *mailtoname, *user_email; - icalparameter *param; - - /*The task owner according to Exchange is current user, even that the task was assigned by - *someone else. I'm making the current user attendee and task delegator will be a task organizer */ - - user_email = camel_ews_settings_dup_email (ews_settings); - mailtoname = g_strdup_printf ("mailto:%s", user_email); - icalprop = icalproperty_new_attendee (mailtoname); - g_free (mailtoname); - g_free (user_email); - - param = icalparameter_new_cn (e_ews_item_get_owner (item)); - icalproperty_add_parameter (icalprop, param); - icalcomponent_add_property (icalcomp, icalprop); - - /* get delegator mail box*/ - e_ews_connection_resolve_names_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, task_owner, - EWS_SEARCH_AD, NULL, FALSE, &mailboxes, NULL, - &includes_last_item, cancellable, error); - - for (l = mailboxes; l != NULL; l = g_slist_next (l)) { - EwsMailbox *mb = l->data; - - mailtoname = g_strdup_printf ("mailto:%s", mb->email); - icalprop = icalproperty_new_organizer (mailtoname); - param = icalparameter_new_cn (mb->name); - icalproperty_add_parameter (icalprop, param); - icalcomponent_add_property (icalcomp, icalprop); - - g_free (mailtoname); - e_ews_mailbox_free (mb); - } - g_slist_free (mailboxes); - } - - if (item_type == E_EWS_ITEM_TYPE_TASK) { - icaltimezone *user_timezone = calendar_config_get_icaltimezone (); - const gchar *percent_complete; - - /*start date*/ - has_this_date = FALSE; - e_ews_item_task_has_start_date (item, &has_this_date); - if (has_this_date) { - start_date = icaltime_from_timet_with_zone (e_ews_item_get_start_date (item), 0, user_timezone); - start_date.is_date = 1; - icalprop = icalproperty_new_dtstart (start_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*status*/ - ews_task_status = e_ews_item_get_status (item); - if (g_strcmp0 (ews_task_status, "NotStarted") != 0) { - if (g_strcmp0 (ews_task_status, "Completed") == 0) - status = ICAL_STATUS_COMPLETED; - else if (g_strcmp0 (ews_task_status, "InProgress") == 0) - status = ICAL_STATUS_INPROCESS; - else if (g_strcmp0 (ews_task_status, "WaitingOnOthers") == 0) - status = ICAL_STATUS_NEEDSACTION; - else if (g_strcmp0 (ews_task_status, "Deferred") == 0) - status = ICAL_STATUS_CANCELLED; - icalprop = icalproperty_new_status (status); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*precent complete*/ - percent_complete = e_ews_item_get_percent_complete (item); - icalprop = icalproperty_new_percentcomplete (atoi (percent_complete ? percent_complete : "0")); - icalcomponent_add_property (icalcomp, icalprop); - - /*due date*/ - e_ews_item_task_has_due_date (item, &has_this_date); - if (has_this_date) { - due_date = icaltime_from_timet_with_zone (e_ews_item_get_due_date (item), 0, user_timezone); - due_date.is_date = 1; - icalprop = icalproperty_new_due (due_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*complete date*/ - has_this_date = FALSE; - e_ews_item_task_has_complete_date (item, &has_this_date); - if (has_this_date) { - complete_date = icaltime_from_timet_with_zone (e_ews_item_get_complete_date (item), 0, user_timezone); - icalprop = icalproperty_new_completed (complete_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*priority*/ - item_importance = e_ews_item_get_importance (item); - if (item_importance == EWS_ITEM_HIGH) - priority = 3; - else if (item_importance == EWS_ITEM_LOW) - priority = 7; - icalprop = icalproperty_new_priority (priority); - icalcomponent_add_property (icalcomp, icalprop); - } - - icalcomponent_add_component (vcomp, icalcomp); - } else { - struct icaltimetype dt; - const gchar *mime_content; - const gchar *tzid; - gboolean timezone_set = FALSE; - - mime_content = e_ews_item_get_mime_content (item); - vcomp = mime_content && *mime_content ? icalparser_parse_string (mime_content) : NULL; - - if (!vcomp && mime_content && *mime_content) { - const gchar *begin_vcalendar, *end_vcalendar; - - /* Workaround Exchange 2016 error, which returns invalid iCalendar object (without 'END:VCALENDAR'), - when the event has at least one detached instance. */ - begin_vcalendar = camel_strstrcase (mime_content, "BEGIN:VCALENDAR"); - end_vcalendar = camel_strstrcase (mime_content, "END:VCALENDAR"); - - /* If it exists, then it should be alone on a separate line */ - if (!(begin_vcalendar && (begin_vcalendar == mime_content || begin_vcalendar[-1] == '\n') && - (begin_vcalendar[15 /* strlen ("BEGIN:VCALENDAR") */] == '\r' || begin_vcalendar[15] == '\n'))) - begin_vcalendar = NULL; - - /* If it exists, then it should be alone on a separate line and not at the very beginning of the mime_content */ - if (!(end_vcalendar && end_vcalendar > mime_content && end_vcalendar[-1] == '\n' && - (end_vcalendar[13 /* strlen ("END:VCALENDAR") */] == '\r' || end_vcalendar[13] == '\n' || end_vcalendar[13] == '\0'))) - end_vcalendar = NULL; - - if (begin_vcalendar && !end_vcalendar) { - gchar *str; - - str = g_strconcat (mime_content, "\r\n", "END:VCALENDAR", "\r\n", NULL); - vcomp = icalparser_parse_string (str); - g_free (str); - } - } - - if (!vcomp) { - if (mime_content) - g_warning ("%s: Failed to parse mime content:---%s---", G_STRFUNC, mime_content); - return NULL; - } - - tzid = e_ews_item_get_tzid (item); - if (tzid == NULL) { - /* - * When we are working with Exchange server 2010 or newer, we have to handle a few - * things more than we do working old servers. These things are: - * - MSDN timezone names: - * Used setting StartTimeZone and EndTimeZone. MSDN timezone names are not - * the same used in libical, so we need to have a table of equivalence to - * convert from one to another and avoid show the MSDN timezone name to the - * user and save it in the ETimezoneCache. - * - EvoEWSStartTimeZone/EvoEWSEndTimeZone - * Used to keep track if the timezone shown to the user is the same one set - * by him/her. As we have a table of equivalence, sometimes the user sets a - * timezone but without EvoEWSStartTiemZone property, another timezone name, - * in the same offset, can be shown. And we want to avoid this. - * - DTEND property: - * As we have to work with DTEND setting an event when using EWS server 2010 or - * newer, we have to care about set it properly here, instead of use the same - * as is used in DTSTART. - */ - icaltimezone *start_zone, *end_zone; - const gchar *start_tzid, *end_tzid; - const gchar *ical_start_tzid, *ical_end_tzid; - const gchar *evo_ews_start_tzid, *evo_ews_end_tzid; - - start_tzid = e_ews_item_get_start_tzid (item); - end_tzid = e_ews_item_get_end_tzid (item); - - ical_start_tzid = e_cal_backend_ews_tz_util_get_ical_equivalent (start_tzid); - ical_end_tzid = e_cal_backend_ews_tz_util_get_ical_equivalent (end_tzid); - - evo_ews_start_tzid = e_ews_item_get_iana_start_time_zone (item); - evo_ews_end_tzid = e_ews_item_get_iana_end_time_zone (item); - - /* - * We have a few timezones that don't have an equivalent MSDN timezone. - * For those, we will get ical_start_tzid being NULL and then we need to use - * start_tzid, which one has the libical's expected name. - */ - start_zone = ecb_ews_get_timezone ( - timezone_cache, - start_tzid, - ical_start_tzid != NULL ? ical_start_tzid : start_tzid, - evo_ews_start_tzid); - end_zone = ecb_ews_get_timezone ( - timezone_cache, - end_tzid, - ical_end_tzid != NULL ? ical_end_tzid : end_tzid, - evo_ews_end_tzid); - - if (start_zone != NULL) { - icalcomp = icalcomponent_get_first_component (vcomp, kind); - - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - dt = icaltime_convert_to_zone (dt, start_zone); - icalcomponent_set_dtstart (icalcomp, dt); - - timezone_set = TRUE; - e_timezone_cache_add_timezone (timezone_cache, start_zone); - - if (end_zone != NULL) { - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); - dt = icaltime_convert_to_zone (dt, end_zone); - icalcomponent_set_dtend (icalcomp, dt); - - e_timezone_cache_add_timezone (timezone_cache, end_zone); - } - } - - if (!timezone_set) - tzid = start_tzid; - } - - if (!timezone_set && tzid) { - /* - * When we are working with Exchange server older than 2010, we don't set different - * DTSTART and DTEND properties in VTIMEZONE. The reason of that is we don't use - * those properties settings/changing a meeting timezone. - * So, for older servers, here, we only set the DTSTART and DTEND properties with - * the same values. - */ - icaltimezone *zone; - gchar *new_tzid = NULL; - - icalcomp = icalcomponent_get_first_component (vcomp, kind); - - if (!icaltimezone_get_builtin_timezone (tzid) && - icalcomponent_get_uid (icalcomp)) { - icalcomponent *vtimezone; - - /* Add the timezone */ - vtimezone = icalcomponent_get_first_component (vcomp, ICAL_VTIMEZONE_COMPONENT); - if (vtimezone != NULL) { - icalproperty *prop; - - new_tzid = g_strconcat ("/evolution/ews/tzid/", icalcomponent_get_uid (icalcomp), NULL); - - zone = icaltimezone_new (); - vtimezone = icalcomponent_new_clone (vtimezone); - prop = icalcomponent_get_first_property (vtimezone, ICAL_TZID_PROPERTY); - if (prop) { - icalproperty_set_tzid (prop, new_tzid); - - prop = icalcomponent_get_first_property (vtimezone, ICAL_LOCATION_PROPERTY); - if (!prop) { - /* Use the original tzid as the timezone Location, to not expose - evolution-ews TZID. */ - prop = icalproperty_new_location (tzid); - icalcomponent_add_property (vtimezone, prop); - } - } else { - g_free (new_tzid); - new_tzid = NULL; - } - icaltimezone_set_component (zone, vtimezone); - e_timezone_cache_add_timezone (timezone_cache, zone); - icaltimezone_free (zone, TRUE); - } - } - - zone = e_timezone_cache_get_timezone (timezone_cache, new_tzid ? new_tzid : tzid); - - if (!zone && new_tzid) - zone = e_timezone_cache_get_timezone (timezone_cache, tzid); - - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (tzid); - - if (zone != NULL) { - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - dt = icaltime_convert_to_zone (dt, zone); - icalcomponent_set_dtstart (icalcomp, dt); - - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); - dt = icaltime_convert_to_zone (dt, zone); - icalcomponent_set_dtend (icalcomp, dt); - } - - g_free (new_tzid); - } - } - - /* Vevent or Vtodo */ - icalcomp = icalcomponent_get_first_component (vcomp, kind); - if (icalcomp) { - icalproperty *icalprop, *freebusy; - struct icaltimetype itt; - const EwsId *item_id; - const GSList *l = NULL; - const gchar *uid = e_ews_item_get_uid (item); - - item_id = e_ews_item_get_id (item); - - if (e_ews_item_get_is_meeting (item)) { - gboolean is_response_requested = e_ews_item_get_is_response_requested (item); - gchar *user_email; - - user_email = camel_ews_settings_dup_email (ews_settings); - - /* Attendees */ - for (l = e_ews_item_get_attendees (item); l != NULL; l = g_slist_next (l)) { - icalparameter *param, *cu_type; - gchar *mailtoname; - const gchar *email = NULL; - EwsAttendee *attendee = (EwsAttendee *) l->data; - - if (!attendee->mailbox) - continue; - - if (g_strcmp0 (attendee->mailbox->routing_type, "EX") == 0) - email = e_ews_item_util_strip_ex_address (attendee->mailbox->email); - - mailtoname = g_strdup_printf ("mailto:%s", email ? email : attendee->mailbox->email); - icalprop = icalproperty_new_attendee (mailtoname); - g_free (mailtoname); - - param = icalparameter_new_cn (attendee->mailbox->name); - icalproperty_add_parameter (icalprop, param); - - if (g_ascii_strcasecmp (attendee->attendeetype, "Required") == 0) { - param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL); - } - else if (g_ascii_strcasecmp (attendee->attendeetype, "Resource") == 0) { - param = icalparameter_new_role (ICAL_ROLE_NONPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_RESOURCE); - } - else { - param = icalparameter_new_role ( ICAL_ROLE_OPTPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL); - } - icalproperty_add_parameter (icalprop, cu_type); - icalproperty_add_parameter (icalprop, param); - - if (is_response_requested) { - param = icalparameter_new_rsvp (ICAL_RSVP_TRUE); - icalproperty_add_parameter (icalprop, param); - } - - if (user_email && (email || attendee->mailbox->email) && e_ews_item_get_my_response_type (item) && - g_ascii_strcasecmp (email ? email : attendee->mailbox->email, user_email) == 0) { - param = ecb_ews_responsetype_to_partstat (e_ews_item_get_my_response_type (item)); - } else { - param = ecb_ews_responsetype_to_partstat (attendee->responsetype); - } - icalproperty_add_parameter (icalprop, param); - - icalcomponent_add_property (icalcomp, icalprop); - } - - g_free (user_email); - } - - /* Free/Busy */ - freebusy = icalcomponent_get_first_property (icalcomp, ICAL_TRANSP_PROPERTY); - if (!freebusy && (e_ews_item_get_item_type (item) != E_EWS_ITEM_TYPE_TASK)) { - /* Busy by default */ - freebusy = icalproperty_new_transp (ICAL_TRANSP_OPAQUE); - icalcomponent_add_property (icalcomp, freebusy); - } - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); - icalprop != NULL; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) { - if (g_strcmp0 (icalproperty_get_x_name (icalprop), "X-MICROSOFT-CDO-BUSYSTATUS") == 0) { - if (g_strcmp0 (icalproperty_get_value_as_string (icalprop), "BUSY") == 0) { - icalproperty_set_transp (freebusy, ICAL_TRANSP_OPAQUE); - } else { - icalproperty_set_transp (freebusy, ICAL_TRANSP_TRANSPARENT); - } - - break; - } - } - - /* AllDayEvent */ - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); - icalprop != NULL; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) { - if (g_strcmp0 (icalproperty_get_x_name (icalprop), "X-MICROSOFT-CDO-ALLDAYEVENT") == 0) { - if (g_strcmp0 (icalproperty_get_value_as_string (icalprop), "TRUE") == 0) { - struct icaltimetype dtend, dtstart; - dtstart = icalcomponent_get_dtstart (icalcomp); - dtstart.is_date = 1; - icalcomponent_set_dtstart (icalcomp, dtstart); - - dtend = icalcomponent_get_dtend (icalcomp); - dtend.is_date = 1; - icalcomponent_set_dtend (icalcomp, dtend); - } - break; - } - } - - if (icalcomponent_get_first_property (icalcomp, ICAL_RECURRENCEID_PROPERTY)) { - /* Exchange sets RRULE even on the children, which is broken */ - icalprop = icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY); - if (icalprop) { - icalcomponent_remove_property (icalcomp, icalprop); - icalproperty_free (icalprop); - } - } - - /* The EXDATE sent by the server can be date-time format with timezone, while - the event start time can be date-only. This breaks the rules, thus correct - it and make also EXDATE date-only. */ - itt = icalcomponent_get_dtstart (icalcomp); - if (icaltime_is_valid_time (itt) && itt.is_date) { - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_EXDATE_PROPERTY); - icalprop; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_EXDATE_PROPERTY)) { - itt = icalproperty_get_exdate (icalprop); - itt.is_date = 1; - icalproperty_set_exdate (icalprop, itt); - - icalproperty_remove_parameter_by_kind (icalprop, ICAL_TZID_PARAMETER); - } - } - - /* Exchange sets an ORGANIZER on all events. RFC2445 says: - * - * This property MUST NOT be specified in an iCalendar - * object that specifies only a time zone definition or - * that defines calendar entities that are not group - * scheduled entities, but are entities only on a single - * user's calendar. - */ - if (!icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) { - if ((icalprop = icalcomponent_get_first_property (icalcomp, ICAL_ORGANIZER_PROPERTY))) { - icalcomponent_remove_property (icalcomp, icalprop); - icalproperty_free (icalprop); - } - } - - icalcomponent_set_uid (icalcomp, uid ? uid : item_id->id); - - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-ITEMID", item_id->id); - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY", item_id->change_key); - - res_component = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (icalcomp)); - - /* Categories */ - e_cal_component_set_categories_list (res_component, (GSList *) e_ews_item_get_categories (item)); - - /* - * There is no API to set/get alarm description on the server side. - * However, for some reason, the alarm description has been set to "REMINDER" - * automatically (and with no i18n). Instead of show it to the user, let's - * set the summary as the alarm description. - */ - if (e_cal_component_has_alarms (res_component)) { - GList *alarm_uids, *l; - - alarm_uids = e_cal_component_get_alarm_uids (res_component); - for (l = alarm_uids; l != NULL; l = l->next) { - ECalComponentAlarm *alarm; - ECalComponentText text; - - alarm = e_cal_component_get_alarm (res_component, l->data); - e_cal_component_get_summary (res_component, &text); - e_cal_component_alarm_set_description (alarm, &text); - - e_cal_component_alarm_free (alarm); - } - cal_obj_uid_list_free (alarm_uids); - } - } - - icalcomponent_free (vcomp); - - if (res_component) { - const GSList *attachment_ids, *aid, *l; - const gchar *uid = NULL; - GSList *info_attachments = NULL, *uris = NULL; - gboolean has_attachment = FALSE; - - e_ews_item_has_attachments (item, &has_attachment); - if (!has_attachment) - return res_component; - - e_cal_component_get_uid (res_component, &uid); - - attachment_ids = e_ews_item_get_attachments_ids (item); - - if (e_ews_connection_get_attachments_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - uid, - attachment_ids, - cbews->priv->attachments_dir, - TRUE, - &info_attachments, - NULL, NULL, - cancellable, - NULL)) { - icalcomponent *icalcomp; - icalproperty *icalprop; - icalparameter *icalparam; - - for (l = info_attachments; l; l = l->next) { - EEwsAttachmentInfo *info = l->data; - - /* ignore non-uri attachments, because it's an exception */ - if (e_ews_attachment_info_get_type (info) == E_EWS_ATTACHMENT_INFO_TYPE_URI) { - const gchar *uri = e_ews_attachment_info_get_uri (info); - - if (uri) - uris = g_slist_append (uris, g_strdup (uri)); - } - } - - e_cal_component_set_attachment_list (res_component, uris); - - icalcomp = e_cal_component_get_icalcomponent (res_component); - icalprop = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY); - for (aid = attachment_ids; aid && icalprop; aid = aid->next, icalprop = icalcomponent_get_next_property (icalcomp, ICAL_ATTACH_PROPERTY)) { - icalparam = icalparameter_new_x (aid->data); - icalparameter_set_xname (icalparam, "X-EWS-ATTACHMENTID"); - icalproperty_add_parameter (icalprop, icalparam); - } - - g_slist_free_full (uris, g_free); - g_slist_free_full (info_attachments, (GDestroyNotify) e_ews_attachment_info_free); - } - } - - return res_component; -} - -static void -ecb_ews_store_original_comp (ECalComponent *comp) -{ - gchar *comp_str; - gchar *base64; - - g_return_if_fail (E_IS_CAL_COMPONENT (comp)); - - comp_str = e_cal_component_get_as_string (comp); - g_return_if_fail (comp_str != NULL); - - /* Include NUL-terminator */ - base64 = g_base64_encode ((const guchar *) comp_str, strlen (comp_str) + 1); - - e_cal_util_set_x_property (e_cal_component_get_icalcomponent (comp), - X_EWS_ORIGINAL_COMP, base64); - - g_free (base64); - g_free (comp_str); -} - -static ECalComponent * /* free with g_object_unref(), if not NULL */ -ecb_ews_restore_original_comp (ECalComponent *from_comp) -{ - ECalComponent *comp = NULL; - const gchar *original_base64; - guchar *decoded; - gsize len = -1; - - g_return_val_if_fail (E_IS_CAL_COMPONENT (from_comp), NULL); - - original_base64 = e_cal_util_get_x_property (e_cal_component_get_icalcomponent (from_comp), X_EWS_ORIGINAL_COMP); - - if (!original_base64 || !*original_base64) - return NULL; - - decoded = g_base64_decode (original_base64, &len); - if (!decoded || !*decoded || len <= 0) { - g_free (decoded); - return NULL; - } - - if (decoded[len - 1] != '\0') { - gchar *tmp; - - tmp = g_strndup ((const gchar *) decoded, len); - - g_free (decoded); - decoded = (guchar *) tmp; - } - - if (decoded && *decoded) - comp = e_cal_component_new_from_string ((const gchar *) decoded); - - g_free (decoded); - - return comp; -} - -static gboolean -ecb_ews_get_items_sync (ECalBackendEws *cbews, - const GSList *item_ids, /* gchar * */ - const gchar *default_props, - const EEwsAdditionalProps *add_props, - GSList **out_components, /* ECalComponent * */ - GCancellable *cancellable, - GError **error) -{ - GSList *items = NULL, *link; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (out_components != NULL, FALSE); - - success = e_ews_connection_get_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - item_ids, - default_props, - add_props, - FALSE, - NULL, - E_EWS_BODY_TYPE_TEXT, - &items, - NULL, NULL, - cancellable, - error); - - if (!success) - return FALSE; - - /* fetch modified occurrences */ - for (link = items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const GSList *modified_occurrences; - - if (!item || e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - modified_occurrences = e_ews_item_get_modified_occurrences (item); - if (modified_occurrences) { - EEwsAdditionalProps *modified_add_props; - - modified_add_props = e_ews_additional_props_new (); - if (e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) { - EEwsExtendedFieldURI *ext_uri; - - modified_add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2010); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSStartTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - modified_add_props->extended_furis = g_slist_append (modified_add_props->extended_furis, ext_uri); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSEndTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - modified_add_props->extended_furis = g_slist_append (modified_add_props->extended_furis, ext_uri); - } else { - modified_add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2007); - } - - success = ecb_ews_get_items_sync (cbews, modified_occurrences, "IdOnly", modified_add_props, out_components, cancellable, error); - - e_ews_additional_props_free (modified_add_props); - - if (!success) - goto exit; - } - } - - for (link = items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - ECalComponent *comp; - GError *local_error = NULL; - - if (!item || e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - comp = ecb_ews_item_to_component_sync (cbews, item, cancellable, &local_error); - if (!comp) { - if (!local_error) - continue; - - g_propagate_error (error, local_error); - success = FALSE; - break; - } - - ecb_ews_store_original_comp (comp); - - *out_components = g_slist_prepend (*out_components, comp); - } - - exit: - g_slist_free_full (items, g_object_unref); - - return success; -} - -static gboolean -ecb_ews_fetch_items_sync (ECalBackendEws *cbews, - const GSList *items, /* EEwsItem * */ - GSList **out_components, /* ECalComponent * */ - GCancellable *cancellable, - GError **error) -{ - GSList *event_ids = NULL, *task_memo_ids = NULL, *link; - gboolean success = TRUE; - - for (link = (GSList *) items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (type == E_EWS_ITEM_TYPE_EVENT) - event_ids = g_slist_prepend (event_ids, g_strdup (id->id)); - else if (type == E_EWS_ITEM_TYPE_TASK || type == E_EWS_ITEM_TYPE_MEMO) { - task_memo_ids = g_slist_prepend (task_memo_ids, g_strdup (id->id)); - } - } - - if (event_ids) { - EEwsAdditionalProps *add_props; - - add_props = e_ews_additional_props_new (); - if (e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) { - EEwsExtendedFieldURI *ext_uri; - - add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2010); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSStartTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - add_props->extended_furis = g_slist_append (add_props->extended_furis, ext_uri); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSEndTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - add_props->extended_furis = g_slist_append (add_props->extended_furis, ext_uri); - } else { - add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2007); - } - - success = ecb_ews_get_items_sync (cbews, event_ids, "IdOnly", add_props, out_components, cancellable, error); - - e_ews_additional_props_free (add_props); - } - - if (task_memo_ids && success) - success = ecb_ews_get_items_sync (cbews, task_memo_ids, "AllProperties", NULL, out_components, cancellable, error); - - g_slist_free_full (event_ids, g_free); - g_slist_free_full (task_memo_ids, g_free); - - return success; -} - -static gboolean -ecb_ews_freebusy_ecomp_changed (ECalComponent *ecomp, - icalcomponent *vevent) -{ - icalcomponent *icomp; - gboolean changed = FALSE; - - g_return_val_if_fail (vevent != NULL, FALSE); - - if (!ecomp) - return TRUE; - - icomp = e_cal_component_get_icalcomponent (ecomp); - if (!icomp) - return TRUE; - - if (!changed) - changed = g_strcmp0 (icalcomponent_get_summary (icomp), icalcomponent_get_summary (vevent)) != 0; - if (!changed) - changed = g_strcmp0 (icalcomponent_get_location (icomp), icalcomponent_get_location (vevent)) != 0; - if (!changed) - changed = icaltime_compare (icalcomponent_get_dtstart (icomp), icalcomponent_get_dtstart (vevent)) != 0; - if (!changed) - changed = icaltime_compare (icalcomponent_get_dtend (icomp), icalcomponent_get_dtend (vevent)) != 0; - - return changed; -} - -static GSList * /* the possibly modified 'in_items' */ -ecb_ews_verify_changes (ECalCache *cal_cache, - icalcomponent_kind kind, - GSList *in_items, /* EEwsItem * */ - GCancellable *cancellable) -{ - GSList *items = NULL, *link; - - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), in_items); - - for (link = in_items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (!g_cancellable_is_cancelled (cancellable) && ( - (type == E_EWS_ITEM_TYPE_EVENT && kind == ICAL_VEVENT_COMPONENT) || - (type == E_EWS_ITEM_TYPE_MEMO && kind == ICAL_VJOURNAL_COMPONENT) || - (type == E_EWS_ITEM_TYPE_TASK && kind == ICAL_VTODO_COMPONENT) )) { - ECalComponent *existing = NULL; - - if (e_cal_cache_get_component (cal_cache, id->id, NULL, &existing, cancellable, NULL) && - existing && g_strcmp0 (e_cal_util_get_x_property (e_cal_component_get_icalcomponent (existing), - "X-EVOLUTION-CHANGEKEY"), id->change_key) == 0) { - g_object_unref (item); - } else { - items = g_slist_prepend (items, item); - } - - g_clear_object (&existing); - } else if (type == E_EWS_ITEM_TYPE_EVENT || - type == E_EWS_ITEM_TYPE_MEMO || - type == E_EWS_ITEM_TYPE_TASK) { - g_object_unref (item); - } else { - items = g_slist_prepend (items, item); - } - } - - g_slist_free (in_items); - - return items; -} - -static GSList * /* ECalMetaBackendInfo */ -ecb_ews_components_to_infos (ECalMetaBackend *meta_backend, - const GSList *components, /* ECalComponent * */ - icalcomponent_kind kind) -{ - GSList *nfos = NULL, *link; - GHashTable *sorted_by_uids; /* gchar * ~> GSList { ECalComponent * } */ - GHashTableIter iter; - gpointer key, value; - - sorted_by_uids = g_hash_table_new (g_str_hash, g_str_equal); - - for (link = (GSList *) components; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - icalcomponent *icomp; - const gchar *uid; - GSList *instances; - - if (!comp) - continue; - - icomp = e_cal_component_get_icalcomponent (comp); - uid = icalcomponent_get_uid (icomp); - - if (!uid) - continue; - - ecb_ews_store_original_comp (comp); - - instances = g_hash_table_lookup (sorted_by_uids, uid); - g_hash_table_insert (sorted_by_uids, (gpointer) uid, g_slist_prepend (instances, comp)); - } - - g_hash_table_iter_init (&iter, sorted_by_uids); - while (g_hash_table_iter_next (&iter, &key, &value)) { - const gchar *uid = key; - GSList *instances = value, *link; - icalcomponent *icomp, *merged; - ECalComponent *comp; - ECalMetaBackendInfo *nfo; - const gchar *revision, *itemid; - - if (!uid || !instances) { - g_slist_free (instances); - continue; - } - - /* Try to find master object, to have seves marter's itemid in ECalMetaBackendInfo::extra, - thus the re-load of the event is done for the whole series and not for a detached instance */ - comp = NULL; - for (link = instances; link && !comp; link = g_slist_next (link)) { - comp = link->data; - - if (!comp) - continue; - - if (e_cal_component_is_instance (comp)) - comp = NULL; - } - - if (!comp) - comp = instances->data; - - if (!comp) { - g_slist_free (instances); - continue; - } - - icomp = e_cal_component_get_icalcomponent (comp); - itemid = e_cal_util_get_x_property (icomp, "X-EVOLUTION-ITEMID"); - revision = e_cal_util_get_x_property (icomp, "X-EVOLUTION-CHANGEKEY"); - merged = e_cal_meta_backend_merge_instances (meta_backend, instances, FALSE); - - if (!merged) { - g_warn_if_fail (merged != NULL); - g_slist_free (instances); - continue; - } - - nfo = e_cal_meta_backend_info_new (uid, revision, NULL, itemid); - nfo->object = icalcomponent_as_ical_string_r (merged); - - nfos = g_slist_prepend (nfos, nfo); - - icalcomponent_free (merged); - g_slist_free (instances); - } - - g_hash_table_destroy (sorted_by_uids); - - return nfos; -} - -static void -ecb_ews_extract_item_id (ECalComponent *comp, - gchar **out_id, - gchar **out_change_key) -{ - icalcomponent *icalcomp; - - g_return_if_fail (E_IS_CAL_COMPONENT (comp)); - - icalcomp = e_cal_component_get_icalcomponent (comp); - g_return_if_fail (icalcomp != NULL); - - if (out_id) - *out_id = e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-ITEMID"); - if (out_change_key) - *out_change_key = e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY"); -} - -static gboolean -ecb_ews_is_organizer (ECalBackendEws *cbews, - ECalComponent *comp) -{ - ECalComponentOrganizer organizer; - gboolean is_organizer = FALSE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE); - - if (!e_cal_component_has_organizer (comp)) - return FALSE; - - organizer.value = NULL; - - e_cal_component_get_organizer (comp, &organizer); - if (organizer.value) { - CamelEwsSettings *ews_settings; - const gchar *email = organizer.value; - gchar *user_email; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - user_email = camel_ews_settings_dup_email (ews_settings); - - if (!g_ascii_strncasecmp (email, "mailto:", 7)) - email += 7; - - is_organizer = user_email && g_ascii_strcasecmp (email, user_email) == 0; - - g_free (user_email); - } - - return is_organizer; -} - -static gboolean -ecb_ews_connect_sync (ECalMetaBackend *meta_backend, - const ENamedParameters *credentials, - ESourceAuthenticationResult *out_auth_result, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - CamelEwsSettings *ews_settings; - gchar *hosturl; - gboolean success = FALSE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_auth_result != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->cnc) { - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - *out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED; - - return TRUE; - } - - ews_settings = ecb_ews_get_collection_settings (cbews); - hosturl = camel_ews_settings_dup_hosturl (ews_settings); - - cbews->priv->cnc = e_ews_connection_new_for_backend (E_BACKEND (cbews), e_cal_backend_get_registry (E_CAL_BACKEND (cbews)), hosturl, ews_settings); - - e_binding_bind_property ( - cbews, "proxy-resolver", - cbews->priv->cnc, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - *out_auth_result = e_ews_connection_try_credentials_sync (cbews->priv->cnc, credentials, cancellable, error); - - if (*out_auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) { - ESource *source = e_backend_get_source (E_BACKEND (cbews)); - ESourceEwsFolder *ews_folder; - - ews_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER); - - g_free (cbews->priv->folder_id); - cbews->priv->folder_id = e_source_ews_folder_dup_id (ews_folder); - cbews->priv->is_freebusy_calendar = cbews->priv->folder_id && g_str_has_prefix (cbews->priv->folder_id, "freebusy-calendar::"); - - g_signal_connect_swapped (cbews->priv->cnc, "server-notification", - G_CALLBACK (ecb_ews_server_notification_cb), cbews); - - if (!cbews->priv->is_freebusy_calendar && - camel_ews_settings_get_listen_notifications (ews_settings) && - e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010_SP1)) { - GSList *folders = NULL; - - folders = g_slist_prepend (folders, cbews->priv->folder_id); - - e_ews_connection_enable_notifications_sync (cbews->priv->cnc, - folders, &cbews->priv->subscription_key); - - g_slist_free (folders); - } - - e_cal_backend_set_writable (E_CAL_BACKEND (cbews), !cbews->priv->is_freebusy_calendar); - success = TRUE; - } else { - ecb_ews_convert_error_to_edc_error (error); - g_clear_object (&cbews->priv->cnc); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - g_free (hosturl); - - return success; -} - -static gboolean -ecb_ews_disconnect_sync (ECalMetaBackend *meta_backend, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - ecb_ews_unset_connection (cbews); - - return TRUE; -} - -static gboolean -ecb_ews_get_changes_sync (ECalMetaBackend *meta_backend, - const gchar *last_sync_tag, - gboolean is_repeat, - gchar **out_new_sync_tag, - gboolean *out_repeat, - GSList **out_created_objects, - GSList **out_modified_objects, - GSList **out_removed_objects, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - gboolean success = TRUE; - GError *local_error = NULL; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_new_sync_tag != NULL, FALSE); - g_return_val_if_fail (out_repeat != NULL, FALSE); - g_return_val_if_fail (out_created_objects != NULL, FALSE); - g_return_val_if_fail (out_modified_objects != NULL, FALSE); - g_return_val_if_fail (out_removed_objects != NULL, FALSE); - - *out_created_objects = NULL; - *out_modified_objects = NULL; - *out_removed_objects = NULL; - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->is_freebusy_calendar) { - ESourceEwsFolder *ews_folder; - EEWSFreeBusyData fbdata; - GSList *free_busy = NULL, *link; - gboolean success; - time_t today; - - ews_folder = e_source_get_extension (e_backend_get_source (E_BACKEND (cbews)), E_SOURCE_EXTENSION_EWS_FOLDER); - - today = time_day_begin (time (NULL)); - - fbdata.period_start = time_add_week (today, -e_source_ews_folder_get_freebusy_weeks_before (ews_folder)); - fbdata.period_end = time_day_end (time_add_week (today, e_source_ews_folder_get_freebusy_weeks_after (ews_folder))); - fbdata.user_mails = g_slist_prepend (NULL, e_source_ews_folder_dup_foreign_mail (ews_folder)); - - success = e_ews_connection_get_free_busy_sync (cbews->priv->cnc, G_PRIORITY_DEFAULT, - e_ews_cal_utils_prepare_free_busy_request, &fbdata, - &free_busy, cancellable, &local_error); - - if (success) { - icaltimezone *utc_zone = icaltimezone_get_utc_timezone (); - GSList *comps = NULL; - GHashTable *known; - GHashTableIter iter; - gpointer key; - - known = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - - if (e_cal_cache_search_components (cal_cache, NULL, &comps, cancellable, NULL)) { - for (link = comps; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - icalcomponent *icomp; - const gchar *uid; - - if (!comp) - continue; - - icomp = e_cal_component_get_icalcomponent (comp); - if (!icomp) - continue; - - uid = icalcomponent_get_uid (icomp); - - if (uid && *uid) - g_hash_table_insert (known, g_strdup (uid), g_object_ref (comp)); - } - - g_slist_free_full (comps, g_object_unref); - } - - for (link = free_busy; link; link = g_slist_next (link)) { - icalcomponent *fbcomp = link->data; - icalproperty *fbprop; - icalparameter *param; - struct icalperiodtype fb; - icalparameter_fbtype fbtype; - - if (!fbcomp || icalcomponent_isa (fbcomp) != ICAL_VFREEBUSY_COMPONENT) - continue; - - for (fbprop = icalcomponent_get_first_property (fbcomp, ICAL_FREEBUSY_PROPERTY); - fbprop; - fbprop = icalcomponent_get_next_property (fbcomp, ICAL_FREEBUSY_PROPERTY)) { - ECalComponent *ecomp; - icalcomponent *vevent; - const gchar *id, *summary, *location; - - param = icalproperty_get_first_parameter (fbprop, ICAL_FBTYPE_PARAMETER); - if (!param) - continue; - - fbtype = icalparameter_get_fbtype (param); - - if (fbtype != ICAL_FBTYPE_FREE && - fbtype != ICAL_FBTYPE_BUSY && - fbtype != ICAL_FBTYPE_BUSYUNAVAILABLE && - fbtype != ICAL_FBTYPE_BUSYTENTATIVE) - continue; - - fb = icalproperty_get_freebusy (fbprop); - id = icalproperty_get_parameter_as_string (fbprop, "X-EWS-ID"); - summary = icalproperty_get_parameter_as_string (fbprop, "X-SUMMARY"); - location = icalproperty_get_parameter_as_string (fbprop, "X-LOCATION"); - - vevent = icalcomponent_new_vevent (); - - if (id && *id) { - icalcomponent_set_uid (vevent, id); - } else { - gchar *uid; - - uid = g_strdup_printf ("%s-%s-%d", - icaltime_as_ical_string (fb.start), - icaltime_as_ical_string (fb.end), - (gint) fbtype); - - icalcomponent_set_uid (vevent, uid); - - g_free (uid); - } - - fb.start.zone = utc_zone; - fb.end.zone = utc_zone; - - icalcomponent_set_dtstart (vevent, fb.start); - icalcomponent_set_dtend (vevent, fb.end); - - icalcomponent_add_property (vevent, icalproperty_new_created (icaltime_current_time_with_zone (utc_zone))); - - if (fbtype == ICAL_FBTYPE_FREE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Free")); - icalcomponent_add_property (vevent, icalproperty_new_transp (ICAL_TRANSP_TRANSPARENT)); - } else if (fbtype == ICAL_FBTYPE_BUSY) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Busy")); - } else if (fbtype == ICAL_FBTYPE_BUSYUNAVAILABLE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Out of Office")); - } else if (fbtype == ICAL_FBTYPE_BUSYTENTATIVE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Tentative")); - } - - if (summary && *summary) - icalcomponent_set_summary (vevent, summary); - - if (location && *location) - icalcomponent_set_location (vevent, location); - - ecomp = g_hash_table_lookup (known, icalcomponent_get_uid (vevent)); - if (ecomp) { - g_object_ref (ecomp); - - /* This dereferences the ecomp, thus the ref() call above to keep it alive */ - g_hash_table_remove (known, icalcomponent_get_uid (vevent)); - - if (ecb_ews_freebusy_ecomp_changed (ecomp, vevent)) { - ECalMetaBackendInfo *nfo; - gchar *revision = e_util_generate_uid (); - - e_cal_util_set_x_property (vevent, "X-EVOLUTION-CHANGEKEY", revision); - - nfo = e_cal_meta_backend_info_new (icalcomponent_get_uid (vevent), NULL, NULL, NULL); - nfo->revision = revision; - nfo->object = icalcomponent_as_ical_string_r (vevent); - - *out_created_objects = g_slist_prepend (*out_created_objects, nfo); - } else { - icalcomponent_free (vevent); - } - - g_clear_object (&ecomp); - } else { - ECalMetaBackendInfo *nfo; - gchar *revision = e_util_generate_uid (); - - e_cal_util_set_x_property (vevent, "X-EVOLUTION-CHANGEKEY", revision); - - nfo = e_cal_meta_backend_info_new (icalcomponent_get_uid (vevent), NULL, NULL, NULL); - nfo->revision = revision; - nfo->object = icalcomponent_as_ical_string_r (vevent); - - *out_modified_objects = g_slist_prepend (*out_modified_objects, nfo); - } - } - } - - g_hash_table_iter_init (&iter, known); - while (g_hash_table_iter_next (&iter, &key, NULL)) { - const gchar *uid = key; - - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_cal_meta_backend_info_new (uid, NULL, NULL, NULL)); - } - - g_hash_table_destroy (known); - } else if (g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NOFREEBUSYACCESS)) { - e_cal_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL); - - e_cal_backend_notify_error (E_CAL_BACKEND (cbews), local_error->message); - g_clear_error (&local_error); - } else { - g_propagate_error (error, local_error); - } - - g_slist_free_full (free_busy, (GDestroyNotify) icalcomponent_free); - g_slist_free_full (fbdata.user_mails, g_free); - } else { - GSList *items_created = NULL, *items_modified = NULL, *items_deleted = NULL, *link; - EEwsAdditionalProps *add_props; - gboolean includes_last_item = TRUE; - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("item:ItemClass"); - - success = e_ews_connection_sync_folder_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - last_sync_tag, cbews->priv->folder_id, "IdOnly", add_props, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - - if (!success && - g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) { - g_clear_error (&local_error); - - e_cal_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL); - - success = e_ews_connection_sync_folder_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - NULL, cbews->priv->folder_id, "IdOnly", add_props, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - } - - e_ews_additional_props_free (add_props); - - if (success) { - GSList *components_created = NULL, *components_modified = NULL; - icalcomponent_kind kind; - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - - /* The sync state doesn't cover changes made by save_component_sync(), - thus verify the changes, instead of re-donwloading the component again */ - items_created = ecb_ews_verify_changes (cal_cache, kind, items_created, cancellable); - items_modified = ecb_ews_verify_changes (cal_cache, kind, items_modified, cancellable); - - if (items_created) { - success = ecb_ews_fetch_items_sync (cbews, items_created, &components_created, cancellable, error); - if (success) - *out_created_objects = ecb_ews_components_to_infos (meta_backend, components_created, kind); - } - - if (items_modified) { - success = ecb_ews_fetch_items_sync (cbews, items_modified, &components_modified, cancellable, error); - if (success) - *out_modified_objects = ecb_ews_components_to_infos (meta_backend, components_modified, kind); - } - - for (link = items_deleted; link; link = g_slist_next (link)) { - const gchar *item_id = link->data; - GSList *ids = NULL, *ilink; - - if (!e_cal_cache_get_ids_with_extra (cal_cache, item_id, &ids, cancellable, NULL)) - continue; - - for (ilink = ids; ilink; ilink = g_slist_next (ilink)) { - ECalComponentId *id = ilink->data; - - /* Use the master object */ - if (id && id->uid && *id->uid && (!id->rid || !*id->rid)) { - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_cal_meta_backend_info_new (id->uid, NULL, NULL, NULL)); - break; - } - } - - g_slist_free_full (ids, (GDestroyNotify) e_cal_component_free_id); - } - - g_slist_free_full (components_created, g_object_unref); - g_slist_free_full (components_modified, g_object_unref); - - *out_repeat = !includes_last_item; - } else if (local_error) { - g_propagate_error (error, local_error); - } - - g_slist_free_full (items_created, g_object_unref); - g_slist_free_full (items_modified, g_object_unref); - g_slist_free_full (items_deleted, g_free); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_clear_object (&cal_cache); - - return success; -} - -static gboolean -ecb_ews_load_component_sync (ECalMetaBackend *meta_backend, - const gchar *uid, - const gchar *extra, - icalcomponent **out_component, - gchar **out_extra, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - GSList *ids, *items = NULL, *components = NULL; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (uid != NULL, FALSE); - g_return_val_if_fail (out_component != NULL, FALSE); - g_return_val_if_fail (out_extra != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - ids = g_slist_prepend (NULL, (gpointer) (extra && *extra ? extra : uid)); - - success = e_ews_connection_get_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, ids, "IdOnly", - NULL, FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &items, NULL, NULL, cancellable, error); - - g_slist_free (ids); - - if (success && items) { - success = ecb_ews_fetch_items_sync (cbews, items, &components, cancellable, error); - - if (components) { - const EwsId *ews_id = e_ews_item_get_id (items->data); - - if (ews_id) - *out_extra = g_strdup (ews_id->id); - - if (components->next) { - GSList *link; - - *out_component = icalcomponent_new_vcalendar (); - - for (link = components; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - if (!comp) - continue; - - icalcomponent_add_component (*out_component, - icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp))); - } - } else { - *out_component = icalcomponent_new_clone (e_cal_component_get_icalcomponent (components->data)); - } - } else { - success = FALSE; - } - } - - if (!components && e_cal_meta_backend_refresh_sync (meta_backend, cancellable, NULL)) { - ECalCache *cal_cache; - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - if (cal_cache) { - success = e_cal_cache_get_components_by_uid (cal_cache, uid, &components, cancellable, NULL); - if (success) { - *out_component = e_cal_meta_backend_merge_instances (meta_backend, components, FALSE); - - if (!e_cal_cache_get_component_extra (cal_cache, uid, NULL, out_extra, cancellable, NULL)) - *out_extra = NULL; - - g_clear_error (error); - } - g_object_unref (cal_cache); - } - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_slist_free_full (components, g_object_unref); - g_slist_free_full (items, g_object_unref); - - return success; -} - -/* Very simple and naive component comparator, to avoid - unnecessary uploads and changes on the server. */ -static gboolean -ecb_ews_components_equal (ECalComponent *comp1, - ECalComponent *comp2) -{ - icalcomponent *icomp1, *icomp2; - icalproperty *prop1; - GHashTable *processed_props; - gboolean equal = TRUE; - - if (!comp1 && !comp2) - return TRUE; - else if (!comp1 || !comp2) - return FALSE; - - icomp1 = e_cal_component_get_icalcomponent (comp1); - icomp2 = e_cal_component_get_icalcomponent (comp2); - - if (!icomp1 || !icomp2) - return FALSE; - - if (g_strcmp0 (icalcomponent_get_uid (icomp1), icalcomponent_get_uid (icomp2)) != 0) - return FALSE; - - if (icalcomponent_count_properties (icomp1, ICAL_ANY_PROPERTY) != - icalcomponent_count_properties (icomp2, ICAL_ANY_PROPERTY)) - return FALSE; - - processed_props = g_hash_table_new (g_direct_hash, g_direct_equal); - - for (prop1 = icalcomponent_get_first_property (icomp1, ICAL_ANY_PROPERTY); - prop1 && equal; - prop1 = icalcomponent_get_next_property (icomp1, ICAL_ANY_PROPERTY)) { - icalproperty_kind kind = icalproperty_isa (prop1); - icalproperty *prop2; - - for (prop2 = icalcomponent_get_first_property (icomp2, kind); - prop2; - prop2 = icalcomponent_get_next_property (icomp2, kind)) { - gchar *str1, *str2; - gboolean same; - - if (g_hash_table_contains (processed_props, prop2)) - continue; - - if (icalproperty_count_parameters (prop1) != icalproperty_count_parameters (prop2)) - continue; - - str1 = icalproperty_as_ical_string_r (prop1); - str2 = icalproperty_as_ical_string_r (prop2); - - same = g_strcmp0 (str1, str2) == 0; - - g_free (str1); - g_free (str2); - - if (same) { - g_hash_table_insert (processed_props, prop2, NULL); - break; - } - } - - if (!prop2) - equal = FALSE; - } - - g_hash_table_destroy (processed_props); - - return equal; -} - -typedef struct _ChangeData { - ECalComponent *old_component; - ECalComponent *new_component; -} ChangeData; - -static void -change_data_free (gpointer ptr) -{ - ChangeData *cd = ptr; - - if (cd) { - g_clear_object (&cd->old_component); - g_clear_object (&cd->new_component); - g_free (cd); - } -} - -static void -ecb_ews_filter_out_unchanged_instances (const GSList *to_save_instances, - const GSList *existing_instances, - GSList **out_changed_instances, /* ChangeData * */ - GSList **out_removed_instances) /* ECalComponent * */ -{ - GSList *link = NULL; - GHashTable *existing_hash; - GHashTableIter iter; - gpointer value; - - g_return_if_fail (to_save_instances != NULL); - g_return_if_fail (existing_instances != NULL); - g_return_if_fail (out_changed_instances != NULL); - g_return_if_fail (out_removed_instances != NULL); - - *out_changed_instances = NULL; - *out_removed_instances = NULL; - - existing_hash = g_hash_table_new_full ((GHashFunc)e_cal_component_id_hash, (GEqualFunc) e_cal_component_id_equal, - (GDestroyNotify) e_cal_component_free_id, NULL); - - for (link = (GSList *) existing_instances; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id; - - id = e_cal_component_get_id (comp); - if (id) - g_hash_table_insert (existing_hash, id, comp); - } - - for (link = (GSList *) to_save_instances; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id = NULL; - - id = e_cal_component_get_id (comp); - if (id) { - ECalComponent *old_comp; - - old_comp = g_hash_table_lookup (existing_hash, id); - - if (!ecb_ews_components_equal (comp, old_comp)) { - ChangeData *cd; - - cd = g_new0 (ChangeData, 1); - cd->old_component = old_comp ? g_object_ref (old_comp) : NULL; - cd->new_component = g_object_ref (comp); - - *out_changed_instances = g_slist_prepend (*out_changed_instances, cd); - } - - g_hash_table_remove (existing_hash, id); - e_cal_component_free_id (id); - } - } - - g_hash_table_iter_init (&iter, existing_hash); - while (g_hash_table_iter_next (&iter, NULL, &value)) { - *out_removed_instances = g_slist_prepend (*out_removed_instances, g_object_ref (value)); - } - - g_hash_table_destroy (existing_hash); -} - -static gboolean -ecb_ews_extract_attachments (icalcomponent *icalcomp, - GSList **out_attachments) /* EEwsAttachmentInfo * */ -{ - icalproperty *prop; - GSList *props = NULL, *link; - - g_return_val_if_fail (icalcomp != NULL, FALSE); - g_return_val_if_fail (out_attachments != NULL, FALSE); - - *out_attachments = NULL; - - for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY); - prop; - prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTACH_PROPERTY)) { - props = g_slist_prepend (props, prop); - } - - for (link = props; link; link = g_slist_next (link)) { - EEwsAttachmentInfo *info; - icalattach *attach; - icalparameter *param; - const gchar *stored_filename; - - prop = link->data; - param = icalproperty_get_first_parameter (prop, ICAL_FILENAME_PARAMETER); - stored_filename = param ? icalparameter_get_filename (param) : NULL; - - attach = icalproperty_get_attach (prop); - if (icalattach_get_is_url (attach)) { - const gchar *uri; - - uri = icalattach_get_url (attach); - - if (!uri || !*uri) - continue; - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_URI); - - e_ews_attachment_info_set_uri (info, uri); - if (stored_filename && *stored_filename) { - e_ews_attachment_info_set_prefer_filename (info, stored_filename); - } else { - gchar *uri_filename; - - uri_filename = g_filename_from_uri (uri, NULL, NULL); - if (uri_filename && *uri_filename) { - gchar *basename; - - basename = g_path_get_basename (uri_filename); - if (basename && *basename && basename[0] != '.' && basename[0] != G_DIR_SEPARATOR) { - const gchar *uid; - - uid = icalcomponent_get_uid (icalcomp); - - if (uid && g_str_has_prefix (basename, uid) && basename[strlen (uid)] == '-') { - e_ews_attachment_info_set_prefer_filename (info, basename + strlen (uid) + 1); - } - } - - g_free (basename); - } - - g_free (uri_filename); - } - } else { - gsize len = -1; - guchar *decoded = NULL; - const gchar *content; - - content = (const gchar *) icalattach_get_data (attach); - decoded = g_base64_decode (content, &len); - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_INLINED); - e_ews_attachment_info_set_inlined_data (info, decoded, len); - - if (stored_filename && *stored_filename) - e_ews_attachment_info_set_prefer_filename (info, stored_filename); - - g_free (decoded); - } - - e_ews_attachment_info_set_id (info, icalproperty_get_parameter_as_string (prop, "X-EWS-ATTACHMENTID")); - *out_attachments = g_slist_prepend (*out_attachments, info); - } - - g_slist_free (props); - - return *out_attachments != NULL; -} - -static icaltimezone * -ecb_ews_get_timezone_from_ical_component (ECalBackendEws *cbews, - icalcomponent *icalcomp) -{ - ETimezoneCache *timezone_cache; - icalproperty *prop = NULL; - const gchar *tzid = NULL; - - timezone_cache = E_TIMEZONE_CACHE (cbews); - - prop = icalcomponent_get_first_property (icalcomp, ICAL_DTSTART_PROPERTY); - if (prop != NULL) { - icalparameter *param = NULL; - - param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER); - if (param) { - tzid = icalparameter_get_tzid (param); - } else { - struct icaltimetype dtstart; - - dtstart = icalproperty_get_dtstart (prop); - if (icaltime_is_utc (dtstart)) - tzid = "UTC"; - } - } - - if (tzid) - return e_timezone_cache_get_timezone (timezone_cache, tzid); - - return NULL; -} - -static gboolean -ecb_ews_remove_item_sync (ECalBackendEws *cbews, - ECalCache *cal_cache, - GHashTable *removed_indexes, - const gchar *uid, - const gchar *rid, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *comp = NULL, *parent = NULL; - EwsId item_id = { 0 }; - gint index = 0; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE); - g_return_val_if_fail (uid != NULL, FALSE); - - if (rid && !*rid) - rid = NULL; - - if (!e_cal_cache_get_component (cal_cache, uid, rid, &comp, cancellable, error) || - (rid && !e_cal_cache_get_component (cal_cache, uid, NULL, &parent, cancellable, error))) { - if (!parent && !comp) { - g_propagate_error (error, EDC_ERROR (ObjectNotFound)); - return FALSE; - } - } - - ecb_ews_extract_item_id (comp ? comp : parent, &item_id.id, &item_id.change_key); - - if (!item_id.id) { - g_propagate_error (error, EDC_ERROR_EX (OtherError, "Cannot determine EWS ItemId")); - success = FALSE; - } else { - if (parent) { - index = e_cal_backend_ews_rid_to_index ( - ecb_ews_get_timezone_from_ical_component (cbews, - e_cal_component_get_icalcomponent (parent)), - rid, - e_cal_component_get_icalcomponent (parent), - error); - if (index == 0) - success = comp != NULL; - } - - if (index && removed_indexes && g_hash_table_contains (removed_indexes, GINT_TO_POINTER (index))) { - /* Do nothing, it's already deleted from the server */ - } else { - if (removed_indexes && index) - g_hash_table_insert (removed_indexes, GINT_TO_POINTER (index), NULL); - - success = success && e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, index, EWS_HARD_DELETE, - ecb_ews_is_organizer (cbews, comp) ? EWS_SEND_TO_ALL_AND_SAVE_COPY : EWS_SEND_TO_NONE, - EWS_ALL_OCCURRENCES, cancellable, error); - } - } - - g_free (item_id.id); - g_free (item_id.change_key); - - g_clear_object (&comp); - g_clear_object (&parent); - - return success; -} - -static void -ecb_ews_get_attach_differences (ECalComponent *oldcomp, - ECalComponent *newcomp, - GSList **out_removed_attachment_ids, /* gchar * */ - GSList **out_added_attachments) /* EEwsAttachmentInfo * */ -{ - GSList *old_attachments = NULL, *new_attachments = NULL, *link; - - g_return_if_fail (out_removed_attachment_ids != NULL); - g_return_if_fail (out_added_attachments != NULL); - - *out_removed_attachment_ids = NULL; - *out_added_attachments = NULL; - - if (!ecb_ews_extract_attachments (e_cal_component_get_icalcomponent (oldcomp), &old_attachments)) - old_attachments = NULL; - - if (!ecb_ews_extract_attachments (e_cal_component_get_icalcomponent (newcomp), &new_attachments)) - new_attachments = NULL; - - for (link = old_attachments; link; link = g_slist_next (link)) { - EEwsAttachmentInfo *old_nfo = link->data; - GSList *nlink; - - if (!old_nfo) - continue; - - for (nlink = new_attachments; nlink; nlink = g_slist_next (nlink)) { - EEwsAttachmentInfo *new_nfo = nlink->data; - gboolean same = FALSE; - - if (!new_nfo || - e_ews_attachment_info_get_type (old_nfo) != e_ews_attachment_info_get_type (new_nfo)) - continue; - - if (e_ews_attachment_info_get_type (old_nfo) == E_EWS_ATTACHMENT_INFO_TYPE_INLINED) { - const gchar *old_data, *new_data; - gsize old_len = -1, new_len = -1; - - old_data = e_ews_attachment_info_get_inlined_data (old_nfo, &old_len); - new_data = e_ews_attachment_info_get_inlined_data (new_nfo, &new_len); - - same = old_len == new_len && (old_len == 0 || - (old_len > 0 && old_data && new_data && memcmp (old_data, new_data, old_len) == 0)); - } else if (e_ews_attachment_info_get_type (old_nfo) == E_EWS_ATTACHMENT_INFO_TYPE_URI) { - same = g_strcmp0 (e_ews_attachment_info_get_uri (old_nfo), e_ews_attachment_info_get_uri (new_nfo)) == 0; - } - - if (same) { - new_attachments = g_slist_remove (new_attachments, new_nfo); - e_ews_attachment_info_free (new_nfo); - break; - } - } - - if (!nlink) { - /* Did not find in the new_attachments, thus it's removed */ - g_warn_if_fail (e_ews_attachment_info_get_id (old_nfo) != NULL); - *out_removed_attachment_ids = g_slist_prepend (*out_removed_attachment_ids, - g_strdup (e_ews_attachment_info_get_id (old_nfo))); - } - } - - *out_added_attachments = new_attachments; - - g_slist_free_full (old_attachments, (GDestroyNotify) e_ews_attachment_info_free); -} - -struct TzidCbData { - icalcomponent *comp; - ECalBackendEws *cbews; -}; - -static void -tzid_cb (icalparameter *param, - gpointer data) -{ - struct TzidCbData *cbd = data; - const gchar *tzid; - icaltimezone *zone; - icalcomponent *new_comp; - - tzid = icalparameter_get_tzid (param); - if (!tzid) - return; - - zone = e_timezone_cache_get_timezone (E_TIMEZONE_CACHE (cbd->cbews), tzid); - if (!zone) - return; - - new_comp = icaltimezone_get_component (zone); - if (!new_comp) - return; - - icalcomponent_add_component (cbd->comp, icalcomponent_new_clone (new_comp)); -} - -static void -ecb_ews_pick_all_tzids_out (ECalBackendEws *cbews, - icalcomponent *icalcomp) -{ - - /* pick all the tzids out of the component and resolve - * them using the vtimezones in the current calendar */ - struct TzidCbData cbd; - - cbd.cbews = cbews; - cbd.comp = icalcomp; - - icalcomponent_foreach_tzid (icalcomp, tzid_cb, &cbd); -} - -static gboolean -ecb_ews_modify_item_sync (ECalBackendEws *cbews, - GHashTable *removed_indexes, - icalcomponent *old_icalcomp, - icalcomponent *new_icalcomp, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *comp = NULL, *oldcomp = NULL; - icalcomponent *icalcomp; - gchar *itemid = NULL, *changekey = NULL; - GSList *added_attachments = NULL, *removed_attachment_ids = NULL; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (new_icalcomp != NULL, FALSE); - - icalcomp = icalcomponent_new_clone (new_icalcomp); - - ecb_ews_pick_all_tzids_out (cbews, icalcomp); - - comp = e_cal_component_new_from_icalcomponent (icalcomp); - if (!comp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - ecb_ews_extract_item_id (comp, &itemid, &changekey); - if (!itemid) { - g_propagate_error (error, EDC_ERROR_EX (OtherError, "Cannot determine EWS ItemId")); - g_object_unref (comp); - return FALSE; - } - - if (old_icalcomp) { - oldcomp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (old_icalcomp)); - } else { - oldcomp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (new_icalcomp)); - } - - ecb_ews_pick_all_tzids_out (cbews, e_cal_component_get_icalcomponent (oldcomp)); - - /* In case we have updated attachments we have to run update attachments - * before update items so attendees will receive mails with already updated attachments */ - - ecb_ews_get_attach_differences (oldcomp, comp, &removed_attachment_ids, &added_attachments); - - /* preform sync delete attachemnt operation*/ - if (removed_attachment_ids) { - g_free (changekey); - changekey = NULL; - - success = e_ews_connection_delete_attachments_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - removed_attachment_ids, &changekey, cancellable, error); - - g_slist_free_full (removed_attachment_ids, g_free); - } - - /* in case we have a new attachments add them before update */ - if (added_attachments && success) { - EwsId item_id; - - item_id.id = itemid; - item_id.change_key = changekey; - - changekey = NULL; - - success = e_ews_connection_create_attachments_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - &item_id, added_attachments, - FALSE, &changekey, NULL, cancellable, error); - - g_free (item_id.change_key); - } - - if (success && old_icalcomp && - icalcomponent_get_first_property (new_icalcomp, ICAL_RRULE_PROPERTY) && - !icalcomponent_get_first_property (new_icalcomp, ICAL_RECURRENCEID_PROPERTY)) { - icalproperty *prop, *old_prop; - GSList *exceptions = NULL, *link; - EwsId item_id; - - item_id.id = itemid; - item_id.change_key = changekey; - - /* Excluded occurrences */ - for (prop = icalcomponent_get_first_property (new_icalcomp, ICAL_EXDATE_PROPERTY); - prop; - prop = icalcomponent_get_next_property (new_icalcomp, ICAL_EXDATE_PROPERTY)) { - const gchar *new_rid; - - new_rid = icalproperty_get_value_as_string (prop); - - for (old_prop = icalcomponent_get_first_property (old_icalcomp, ICAL_EXDATE_PROPERTY); - old_prop; - old_prop = icalcomponent_get_next_property (old_icalcomp, ICAL_EXDATE_PROPERTY)) { - if (g_strcmp0 (new_rid, icalproperty_get_value_as_string (old_prop)) == 0) - break; - } - - if (!old_prop) - exceptions = g_slist_prepend (exceptions, prop); - } - - exceptions = g_slist_reverse (exceptions); - - for (link = exceptions; link && success; link = g_slist_next (link)) { - guint index; - - prop = link->data; - - index = e_cal_backend_ews_rid_to_index ( - ecb_ews_get_timezone_from_ical_component (cbews, new_icalcomp), - icalproperty_get_value_as_string (prop), - new_icalcomp, - error); - - if (index == 0) { - success = FALSE; - } else if (!removed_indexes || !g_hash_table_contains (removed_indexes, GINT_TO_POINTER (index))) { - if (removed_indexes) - g_hash_table_insert (removed_indexes, GINT_TO_POINTER (index), NULL); - - success = e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, index, - EWS_HARD_DELETE, EWS_SEND_TO_NONE, EWS_ALL_OCCURRENCES, cancellable, error); - } - } - - g_slist_free (exceptions); - } - - if (success) { - EwsCalendarConvertData convert_data = { 0 }; - CamelEwsSettings *ews_settings; - const gchar *send_meeting_invitations; - const gchar *send_or_save; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.user_email = camel_ews_settings_dup_email (ews_settings); - convert_data.comp = comp; - convert_data.old_comp = oldcomp; - convert_data.item_id = itemid; - convert_data.change_key = changekey; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - if (e_cal_component_has_attendees (comp)) { - send_meeting_invitations = "SendToAllAndSaveCopy"; - send_or_save = "SendAndSaveCopy"; - } else { - /*In case of appointment we have to set SendMeetingInvites to SendToNone */ - send_meeting_invitations = "SendToNone"; - send_or_save = "SaveOnly"; - } - - success = e_ews_connection_update_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", send_or_save, send_meeting_invitations, cbews->priv->folder_id, - e_cal_backend_ews_convert_component_to_updatexml, &convert_data, - NULL, cancellable, error); - - g_free (convert_data.user_email); - } - - g_slist_free_full (added_attachments, (GDestroyNotify) e_ews_attachment_info_free); - g_clear_object (&oldcomp); - g_clear_object (&comp); - g_free (changekey); - g_free (itemid); - - return success; -} - -static gboolean -ecb_ews_save_component_sync (ECalMetaBackend *meta_backend, - gboolean overwrite_existing, - EConflictResolution conflict_resolution, - const GSList *instances, - const gchar *extra, - gchar **out_new_uid, - gchar **out_new_extra, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - ECalComponent *master = NULL; - EwsFolderId *fid; - GSList *link; - const gchar *uid = NULL; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - for (link = (GSList *) instances; link && !master; link = g_slist_next (link)) { - master = link->data; - - if (!master) - continue; - - if (e_cal_component_is_instance (master)) - master = NULL; - } - - if (!master) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - g_return_val_if_fail (cal_cache != NULL, FALSE); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - e_cal_component_get_uid (master, &uid); - fid = e_ews_folder_id_new (cbews->priv->folder_id, NULL, FALSE); - - if (overwrite_existing) { - GSList *existing = NULL, *changed_instances = NULL, *removed_instances = NULL; - - success = uid && e_cal_cache_get_components_by_uid (cal_cache, uid, &existing, cancellable, error) && existing; - - if (success) { - GSList *link; - - /* This is for offline changes, where the component in the cache - is already modified, while the original, the one on the server, - is different. Using the cached component in this case generates - empty UpdateItem request and nothing is saved. */ - for (link = existing; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - comp = ecb_ews_restore_original_comp (comp); - if (comp) { - g_object_unref (link->data); - link->data = comp; - } - } - } - - if (success) - ecb_ews_filter_out_unchanged_instances (instances, existing, &changed_instances, &removed_instances); - - if (success) { - GHashTable *removed_indexes; - - removed_indexes = g_hash_table_new (g_direct_hash, g_direct_equal); - - for (link = changed_instances; link && success; link = g_slist_next (link)) { - ChangeData *cd = link->data; - - if (!cd) - continue; - - success = ecb_ews_modify_item_sync (cbews, removed_indexes, - e_cal_component_get_icalcomponent (cd->old_component ? cd->old_component : master), - e_cal_component_get_icalcomponent (cd->new_component), - cancellable, error); - } - - for (link = removed_instances; link && success; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id = NULL; - - if (!comp) - continue; - - id = e_cal_component_get_id (comp); - - if (id) { - success = ecb_ews_remove_item_sync (cbews, cal_cache, removed_indexes, id->uid, id->rid, cancellable, error); - e_cal_component_free_id (id); - } - } - - g_hash_table_destroy (removed_indexes); - } - - if (success) - ecb_ews_extract_item_id (master, out_new_uid, NULL); - - g_slist_free_full (existing, g_object_unref); - g_slist_free_full (changed_instances, change_data_free); - g_slist_free_full (removed_instances, g_object_unref); - } else { - GHashTable *removed_indexes; - EwsCalendarConvertData convert_data = { 0 }; - EEwsItem *item = NULL; - EwsId *ews_id = NULL; - const gchar *send_meeting_invitations; - icalcomponent *icalcomp; - icalproperty *prop; - GSList *items = NULL; - - icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (master)); - - e_ews_clean_icalcomponent (icalcomp); - - if (!e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) - ecb_ews_pick_all_tzids_out (cbews, icalcomp); - - /* - * In case we are creating a meeting with attendees and attachments. - * We have to preform 3 steps in order to allow attendees to receive attachments in their invite mails. - * 1. create meeting and do not send invites - * 2. create attachments - * 3. dummy update meeting and send invites to all - */ - if (e_cal_component_has_attendees (master)) { - if (e_cal_component_has_attachments (master)) - send_meeting_invitations = "SendToNone"; - else - send_meeting_invitations = "SendToAllAndSaveCopy"; - } else { - /* In case of appointment we have to set SendMeetingInvites to SendToNone */ - send_meeting_invitations = "SendToNone"; - } - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.icalcomp = icalcomp; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - success = e_ews_connection_create_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, "SaveOnly", send_meeting_invitations, - fid, e_cal_backend_ews_convert_calcomp_to_xml, &convert_data, - &items, cancellable, error); - - if (success && items) { - item = items->data; - if (item) { - g_object_ref (item); - - ews_id = e_ews_id_copy (e_ews_item_get_id (item)); - } - } - - if (success && item && e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_EVENT) { - EEwsAdditionalProps *add_props; - GSList *items, *items_req = NULL; - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("calendar:UID"); - - items = g_slist_append (NULL, ews_id->id); - - /* get calender uid from server*/ - success = e_ews_connection_get_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - items, "IdOnly", add_props, FALSE, NULL, E_EWS_BODY_TYPE_TEXT, - &items_req, NULL, NULL, cancellable, error) && items_req != NULL; - - e_ews_additional_props_free (add_props); - - if (success) { - g_clear_object (&item); - - item = items_req->data; - - e_ews_id_free (ews_id); - ews_id = NULL; - - if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) { - g_propagate_error (error, g_error_copy (e_ews_item_get_error (item))); - item = NULL; - success = FALSE; - } else { - item = g_object_ref (item); - ews_id = e_ews_id_copy (e_ews_item_get_id (item)); - } - } - - g_slist_free_full (items_req, g_object_unref); - g_slist_free (items); - } - - /* attachments */ - if (success && e_cal_component_has_attachments (master) > 0) { - GSList *info_attachments = NULL; - - g_warn_if_fail (ews_id != NULL); - - if (ews_id && ecb_ews_extract_attachments (icalcomp, &info_attachments)) { - gchar *changekey = NULL; - GSList *ids = NULL; - - success = e_ews_connection_create_attachments_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - ews_id, info_attachments, FALSE, &changekey, &ids, cancellable, error); - - g_slist_free_full (info_attachments, (GDestroyNotify) e_ews_attachment_info_free); - g_slist_free_full (ids, g_free); - - if (success && changekey) { - g_free (ews_id->change_key); - ews_id->change_key = changekey; - } else { - g_free (changekey); - } - } - } - - removed_indexes = g_hash_table_new (g_direct_hash, g_direct_equal); - - if (success && icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY)) { - GSList *exceptions = NULL; - - /* Excluded occurrences */ - for (prop = icalcomponent_get_first_property (icalcomp, ICAL_EXDATE_PROPERTY); - prop; - prop = icalcomponent_get_next_property (icalcomp, ICAL_EXDATE_PROPERTY)) { - exceptions = g_slist_prepend (exceptions, g_strdup (icalproperty_get_value_as_string (prop))); - } - - for (link = exceptions; link && success; link = g_slist_next (link)) { - success = ecb_ews_remove_item_sync (cbews, cal_cache, removed_indexes, uid, link->data, cancellable, error); - } - - g_slist_free_full (exceptions, g_free); - } - - if (success && e_cal_component_has_attendees (master) && e_cal_component_has_attachments (master)) { - if (ews_id) { - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-ITEMID", ews_id->id); - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY", ews_id->change_key); - } - - /* In case we have attendees and atachemnts we have to fake update items, - * this is the only way to pass attachments in meeting invite mail */ - success = ecb_ews_modify_item_sync (cbews, removed_indexes, NULL, icalcomp, cancellable, error); - } - - icalcomponent_free (icalcomp); - e_ews_id_free (ews_id); - g_clear_object (&item); - - for (link = (GSList *) instances; link && success; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - if (comp == master) - continue; - - icalcomp = e_cal_component_get_icalcomponent (comp); - - success = ecb_ews_modify_item_sync (cbews, removed_indexes, NULL, icalcomp, cancellable, error); - } - - if (success && items) { - EEwsItem *item = items->data; - const EwsId *item_id; - - item_id = e_ews_item_get_id (item); - *out_new_uid = g_strdup (item_id->id); - } - - g_slist_free_full (items, g_object_unref); - g_hash_table_destroy (removed_indexes); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - g_clear_object (&cal_cache); - e_ews_folder_id_free (fid); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - - return success; -} - -static gboolean -ecb_ews_remove_component_sync (ECalMetaBackend *meta_backend, - EConflictResolution conflict_resolution, - const gchar *uid, - const gchar *extra, - const gchar *object, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalComponent *comp; - EwsId item_id; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (object != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - comp = e_cal_component_new_from_string (object); - if (!comp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - ecb_ews_extract_item_id (comp, &item_id.id, &item_id.change_key); - - success = e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, 0, EWS_HARD_DELETE, - ecb_ews_is_organizer (cbews, comp) ? EWS_SEND_TO_ALL_AND_SAVE_COPY : EWS_SEND_TO_NONE, - EWS_ALL_OCCURRENCES, cancellable, error); - - g_free (item_id.id); - g_free (item_id.change_key); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_object_unref (comp); - - return success; -} - -static void -ecb_ews_discard_alarm_sync (ECalBackendSync *cal_backend_sync, - EDataCal *cal, - GCancellable *cancellable, - const gchar *uid, - const gchar *rid, - const gchar *auid, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - ECalComponent *comp = NULL; - EwsCalendarConvertData convert_data = { 0 }; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cal_backend_sync)); - - cbews = E_CAL_BACKEND_EWS (cal_backend_sync); - - cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbews)); - g_return_if_fail (cal_cache != NULL); - - if (!e_cal_cache_get_component (cal_cache, uid, NULL, &comp, cancellable, NULL) || !comp) { - g_object_unref (cal_cache); - g_propagate_error (error, EDC_ERROR (ObjectNotFound)); - return; - } - - g_object_unref (cal_cache); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) { - g_clear_object (&comp); - return; - } - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - - if (e_cal_component_has_recurrences (comp)) { - gint *index; - - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_OCCURRENCEITEM; - e_cal_component_get_sequence (comp, &index); - - if (index != NULL) { - /*Microsoft is counting the occurrences starting from 1 - where EcalComponent is starting from zerro */ - convert_data.index = *index + 1; - e_cal_component_free_sequence (index); - } else { - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_ITEM; - convert_data.index = -1; - } - } else { - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_ITEM; - convert_data.index = -1; - } - - ecb_ews_extract_item_id (comp, &convert_data.item_id, &convert_data.change_key); - - if (e_ews_connection_update_items_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", NULL, - "SendToNone", NULL, - e_cal_backend_ews_clear_reminder_is_set, - &convert_data, - NULL, - cancellable, - error)) { - icalcomponent *icomp = e_cal_component_get_icalcomponent (comp); - GSList *modified_objects; - - modified_objects = g_slist_prepend (NULL, - e_cal_meta_backend_info_new (icalcomponent_get_uid (icomp), NULL, NULL, - e_cal_util_get_x_property (icomp, "X-EVOLUTION-ITEMID"))); - - /* Refresh the local cache, to have up-to-date ChangeKey */ - e_cal_meta_backend_process_changes_sync (E_CAL_META_BACKEND (cbews), NULL, modified_objects, NULL, cancellable, error); - - g_slist_free_full (modified_objects, e_cal_meta_backend_info_free); - } - - g_object_unref (comp); - g_free (convert_data.item_id); - g_free (convert_data.change_key); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static gboolean -ecb_ews_send_cancellation_email_sync (ECalBackendEws *cbews, - CamelAddress *from, - CamelInternetAddress *recipient, - const gchar *subject, - const gchar *body, - const gchar *calobj, - GCancellable *cancellable, - GError **error) -{ - CamelMimeMessage *message; - CamelContentType *mime_type; - CamelMultipart *multi; - CamelMimePart *text_part, *vcal_part; - gchar *ical_str; - icalcomponent *vcal, *vevent, *vtz; - icalproperty *prop; - icaltimezone *icaltz; - struct icaltimetype dt; - gboolean success; - - vcal = icalcomponent_new (ICAL_VCALENDAR_COMPONENT); - icalcomponent_add_property (vcal, icalproperty_new_version ("2.0")); - icalcomponent_add_property (vcal, icalproperty_new_prodid ("-//Evolution EWS backend//EN")); - icalcomponent_add_property (vcal, icalproperty_new_method (ICAL_METHOD_CANCEL)); - vevent = icalcomponent_new_from_string (calobj); - prop = icalcomponent_get_first_property (vevent, ICAL_STATUS_PROPERTY); - if (prop != NULL) icalcomponent_remove_property (vevent, prop); - icalcomponent_add_property (vevent, icalproperty_new_status (ICAL_STATUS_CANCELLED)); - prop = icalcomponent_get_first_property (vevent, ICAL_METHOD_PROPERTY); - if (prop != NULL) icalcomponent_remove_property (vevent, prop); - dt = e_cal_backend_ews_get_datetime_with_zone (E_TIMEZONE_CACHE (cbews), NULL, vevent, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - icaltz = (icaltimezone *) - (dt.zone ? dt.zone : ecb_ews_get_timezone_from_ical_component (cbews, vevent)); - vtz = icaltimezone_get_component (icaltz); - icalcomponent_add_component (vcal, icalcomponent_new_clone (vtz)); - icalcomponent_add_component (vcal, vevent); - text_part = camel_mime_part_new (); - camel_mime_part_set_content (text_part, body, strlen (body), "text/plain"); - - vcal_part = camel_mime_part_new (); - mime_type = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (vcal_part)); - camel_content_type_set_param (mime_type, "charset", "utf-8"); - camel_content_type_set_param (mime_type, "method", "CANCEL"); - ical_str = icalcomponent_as_ical_string_r ((icalcomponent *) vcal); - camel_mime_part_set_content (vcal_part, ical_str, strlen (ical_str), "text/calendar; method=CANCEL"); - free (ical_str); - - multi = camel_multipart_new (); - camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multi), "multipart/alternative"); - camel_multipart_add_part (multi, text_part); - camel_multipart_set_boundary (multi, NULL); - camel_multipart_add_part (multi, vcal_part); - g_object_unref (text_part); - g_object_unref (vcal_part); - - message = camel_mime_message_new (); - camel_mime_message_set_subject (message, subject); - camel_mime_message_set_from (message, CAMEL_INTERNET_ADDRESS (from)); - camel_mime_message_set_recipients (message, CAMEL_RECIPIENT_TYPE_TO, recipient); - - camel_medium_set_content ((CamelMedium *) message, (CamelDataWrapper *) multi); - g_object_unref (multi); - - success = camel_ews_utils_create_mime_message (cbews->priv->cnc, "SendOnly", NULL, message, NULL, from, NULL, NULL, NULL, cancellable, error); - - g_object_unref (message); - icalcomponent_free (vcal); - - return success; -} - -static void -ecb_ews_receive_objects_no_exchange_mail (ECalBackendEws *cbews, - icalcomponent *vcalendar, - icalcomponent *subcomp, - GSList **ids, - GCancellable *cancellable, - GError **error) -{ - EwsCalendarConvertData convert_data = { 0 }; - EwsFolderId *fid; - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.icalcomp = subcomp; - convert_data.vcalendar = vcalendar; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - fid = e_ews_folder_id_new (cbews->priv->folder_id, NULL, FALSE); - - e_ews_connection_create_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - "SaveOnly", - "SendToNone", - fid, - e_cal_backend_ews_convert_calcomp_to_xml, - &convert_data, - ids, - cancellable, - error); - - e_ews_folder_id_free (fid); -} - -static icalproperty * -find_attendee (icalcomponent *ical_comp, - const gchar *address) -{ - icalproperty *prop; - - if (address == NULL) - return NULL; - - for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY); - prop != NULL; - prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) { - gchar *attendee; - gchar *text; - - attendee = icalproperty_get_value_as_string_r (prop); - - if (!attendee) - continue; - - text = g_strdup (itip_strip_mailto (attendee)); - text = g_strstrip (text); - if (text && !g_ascii_strcasecmp (address, text)) { - g_free (text); - g_free (attendee); - break; - } - g_free (text); - g_free (attendee); - } - - return prop; -} - -static icalproperty * -find_attendee_if_sentby (icalcomponent *ical_comp, - const gchar *address) -{ - icalproperty *prop; - - if (address == NULL) - return NULL; - - for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY); - prop != NULL; - prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) { - icalparameter *param; - const gchar *attendee_sentby; - gchar *text; - - param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER); - if (!param) - continue; - - attendee_sentby = icalparameter_get_sentby (param); - - if (!attendee_sentby) - continue; - - text = g_strdup (itip_strip_mailto (attendee_sentby)); - text = g_strstrip (text); - if (text && !g_ascii_strcasecmp (address, text)) { - g_free (text); - break; - } - g_free (text); - } - - return prop; -} - -static void -ecb_ews_get_rsvp (icalproperty *attendee, - gboolean *out_rsvp_requested) -{ - if (out_rsvp_requested) - *out_rsvp_requested = FALSE; - else - return; - - if (attendee) { - icalparameter *rsvp; - - rsvp = icalproperty_get_first_parameter (attendee, ICAL_RSVP_PARAMETER); - if (rsvp) { - *out_rsvp_requested = icalparameter_get_rsvp (rsvp) == ICAL_RSVP_TRUE; - } - } -} - -static const gchar * -ecb_ews_get_current_user_meeting_reponse (ECalBackendEws *cbews, - icalcomponent *icalcomp, - const gchar *current_user_mail, - gboolean *out_rsvp_requested) -{ - icalproperty *attendee; - const gchar *attendee_str = NULL, *attendee_mail = NULL; - gint attendees_count = 0; - const gchar *response = NULL; - gboolean found = FALSE; - - if (out_rsvp_requested) - *out_rsvp_requested = FALSE; - - attendee = icalcomponent_get_first_property (icalcomp, ICAL_ORGANIZER_PROPERTY); - if (attendee) { - attendee_str = icalproperty_get_organizer (attendee); - - if (attendee_str) { - if (!strncasecmp (attendee_str, "MAILTO:", 7)) - attendee_mail = attendee_str + 7; - else - attendee_mail = attendee_str; - if (attendee_mail && current_user_mail && g_ascii_strcasecmp (attendee_mail, current_user_mail) == 0) { - /* Empty string means it's an organizer, NULL is when not found */ - return ""; - } - } - } - - for (attendee = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY); - attendee != NULL; - attendee = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY), attendees_count++) { - attendee_str = icalproperty_get_attendee (attendee); - - if (attendee_str != NULL) { - if (!strncasecmp (attendee_str, "MAILTO:", 7)) - attendee_mail = attendee_str + 7; - else - attendee_mail = attendee_str; - if (attendee_mail && current_user_mail && g_ascii_strcasecmp (attendee_mail, current_user_mail) == 0) { - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } - } - } - - /* this should not happen, but if the user's configured email does not match the one - used in the invitation, like when the invitation comes to a mailing list... */ - if (!found && attendees_count == 1) { - attendee = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY); - g_return_val_if_fail (attendee != NULL, NULL); - - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } else if (!found) { - ESourceRegistry *registry; - ECalComponent *comp; - - registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbews)); - comp = e_cal_component_new (); - if (e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp))) { - gchar *my_address; - - my_address = itip_get_comp_attendee (registry, comp, NULL); - - attendee = find_attendee (icalcomp, my_address); - if (!attendee) - attendee = find_attendee_if_sentby (icalcomp, my_address); - - if (attendee) { - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } - - g_free (my_address); - } - - g_object_unref (comp); - } - - if (found && !response) { - response = "NEEDS-ACTION"; - } - - return response; -} - -/* changekey can be NULL if you don't want it. itemid cannot. */ -static void -ecb_ews_get_item_accept_id (ECalComponent *comp, - gchar **itemid, - gchar **changekey, - gchar **mail_id) -{ - icalproperty *prop; - gchar *id_item = NULL; - gchar *id_accept = NULL; - gchar *ck = NULL; - - prop = icalcomponent_get_first_property ( - e_cal_component_get_icalcomponent (comp), - ICAL_X_PROPERTY); - while (prop) { - const gchar *x_name, *x_val; - - x_name = icalproperty_get_x_name (prop); - x_val = icalproperty_get_x (prop); - if (!id_item && g_ascii_strcasecmp (x_name, "X-EVOLUTION-ITEMID") == 0) - id_item = g_strdup (x_val); - else if (!id_accept && g_ascii_strcasecmp (x_name, "X-EVOLUTION-ACCEPT-ID") == 0) - id_accept = g_strdup (x_val); - else if (changekey && !ck && !g_ascii_strcasecmp (x_name, "X-EVOLUTION-CHANGEKEY")) - ck = g_strdup (x_val); - - prop = icalcomponent_get_next_property ( - e_cal_component_get_icalcomponent (comp), - ICAL_X_PROPERTY); - } - - if (!id_item) - id_item = g_strdup (id_accept); - - *itemid = id_item; - *mail_id = id_accept; - if (changekey) - *changekey = ck; -} - -static gboolean -ecb_ews_do_method_request_publish_reply (ECalBackendEws *cbews, - icalcomponent *vcalendar, - ECalComponent *comp, - icalcomponent *subcomp, - const gchar *response_type, - const gchar *user_email, - gboolean rsvp_requested, - GCancellable *cancellable, - GError **error) -{ - GError *local_error = NULL; - gchar *item_id = NULL; - gchar *change_key = NULL; - gchar *mail_id = NULL; - gint pass = 0; - GSList *ids = NULL; - - if (!response_type && - e_cal_util_component_has_organizer (subcomp) && - e_cal_util_component_has_attendee (subcomp)) { - g_set_error (error, E_DATA_CAL_ERROR, UnknownUser, _("Cannot find user “%s” between attendees"), user_email ? user_email : "NULL"); - return FALSE; - } - - if (response_type && *response_type) - ecb_ews_get_item_accept_id (comp, &item_id, &change_key, &mail_id); - else - response_type = NULL; - - while (pass < 2) { - /*in case we do not have item id we will create item with mime content only*/ - if (!item_id || (response_type && g_ascii_strcasecmp (response_type, "NEEDS-ACTION") == 0)) { - ecb_ews_receive_objects_no_exchange_mail (cbews, vcalendar, subcomp, &ids, cancellable, &local_error); - } else { - EwsCalendarConvertData convert_data = { 0 }; - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.response_type = (gchar *) response_type; - convert_data.item_id = item_id; - convert_data.change_key = change_key; - - e_ews_connection_create_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - rsvp_requested ? "SendAndSaveCopy" : "SaveOnly", - rsvp_requested ? NULL : "SendToNone", - NULL, - e_cal_backend_ews_prepare_accept_item_request, - &convert_data, - &ids, - cancellable, - &local_error); - } - - if (pass == 0 && mail_id != NULL && item_id != NULL && - g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_ITEMNOTFOUND)) { - /* - * maybe the associated accept calendar item changed - * on the server, thus retry with updated values - */ - GSList *my_ids = NULL; - - g_clear_error (&local_error); - - my_ids = g_slist_append (my_ids, mail_id); - - if (e_ews_connection_get_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - my_ids, - "AllProperties", - NULL, - FALSE, - NULL, - E_EWS_BODY_TYPE_ANY, - &ids, - NULL, - NULL, - cancellable, - &local_error) && - ids != NULL && - ids->data != NULL) { - EEwsItem *item = ids->data; - const EwsId *id = e_ews_item_get_id (item); - - if (id != NULL && g_strcmp0 (id->id, mail_id) == 0) { - const EwsId *cal_item_accepted_id; - - cal_item_accepted_id = e_ews_item_get_calendar_item_accept_id (item); - if (cal_item_accepted_id != NULL) { - g_clear_error (&local_error); - pass++; - - g_free (item_id); - g_free (change_key); - - item_id = g_strdup (cal_item_accepted_id->id); - change_key = g_strdup (cal_item_accepted_id->change_key); - } - } - } - - g_slist_free (my_ids); - - if (pass == 0) - break; - } else { - break; - } - - g_slist_free_full (ids, g_object_unref); - ids = NULL; - } - - if (local_error == NULL) { - icalproperty *transp; - - transp = icalcomponent_get_first_property (subcomp, ICAL_TRANSP_PROPERTY); - - if (g_strcmp0 (icalproperty_get_value_as_string (transp), "TRANSPARENT") == 0 && - g_strcmp0 (response_type, "ACCEPTED") == 0) { - EwsCalendarConvertData convert_data = { 0 }; - GSList *l; - - /* - * user can accept meeting but mark it as free in it's calendar - * the following code is updating the exchange meeting status to free - */ - for (l = ids; l != NULL; l = g_slist_next (l)) { - EEwsItem *item = l->data; - - if (item != NULL) { - const EwsId *id = e_ews_item_get_id (item); - - convert_data.item_id = id->id; - convert_data.change_key = id->change_key; - break; - } - } - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.vcalendar = vcalendar; - - e_ews_connection_update_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", - NULL, - "SendToNone", - NULL, - e_cal_backend_ews_prepare_set_free_busy_status, - &convert_data, - NULL, - cancellable, - &local_error); - } - } - - if (local_error != NULL) - g_propagate_error (error, local_error); - - g_free (item_id); - g_free (change_key); - g_free (mail_id); - g_slist_free_full (ids, g_object_unref); - - return !local_error; -} - -static void -ecb_ews_receive_objects_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *calobj, - GError **error) -{ - ECalBackendEws *cbews; - ECalBackend *cal_backend; - CamelEwsSettings *ews_settings; - icalcomponent *icalcomp, *subcomp; - icalcomponent_kind kind; - gchar *user_email; - gboolean success = TRUE, do_refresh = FALSE; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - icalcomp = calobj ? icalparser_parse_string (calobj) : NULL; - - if (!icalcomp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - /* make sure ical data we parse is actually a vCalendar component */ - if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) { - icalcomponent_free (icalcomp); - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - cal_backend = E_CAL_BACKEND (cbews); - kind = e_cal_backend_get_kind (cal_backend); - - ews_settings = ecb_ews_get_collection_settings (cbews); - user_email = camel_ews_settings_dup_email (ews_settings); - - switch (icalcomponent_get_method (icalcomp)) { - case ICAL_METHOD_REQUEST: - case ICAL_METHOD_PUBLISH: - case ICAL_METHOD_REPLY: - for (subcomp = icalcomponent_get_first_component (icalcomp, kind); - subcomp && success; - subcomp = icalcomponent_get_next_component (icalcomp, kind)) { - ECalComponent *comp; - const gchar *response_type; - gboolean rsvp_requested = FALSE; - - /* getting a data for meeting request response */ - response_type = ecb_ews_get_current_user_meeting_reponse (cbews, subcomp, user_email, &rsvp_requested); - - comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (subcomp)); - - success = ecb_ews_do_method_request_publish_reply (cbews, icalcomp, comp, subcomp, response_type, user_email, rsvp_requested, cancellable, error); - - do_refresh = TRUE; - - g_object_unref (comp); - } - break; - case ICAL_METHOD_COUNTER: - /* - * this is a new time proposal mail from one of the attendees - * if we decline the proposal, nothing have to be done - * if we accept it we will call to modify_object - */ - for (subcomp = icalcomponent_get_first_component (icalcomp, kind); - subcomp && success; - subcomp = icalcomponent_get_next_component (icalcomp, kind)) { - const gchar *response_type; - - /* getting a data for meeting request response */ - response_type = ecb_ews_get_current_user_meeting_reponse (cbews, subcomp, user_email, NULL); - - if (g_strcmp0 (response_type, "ACCEPTED") == 0) { - gchar **split_subject; - icalproperty *summary; - - /* we have to edit the meeting subject to remove exchange header */ - summary = icalcomponent_get_first_property (subcomp, ICAL_SUMMARY_PROPERTY); - split_subject = - g_strsplit (icalproperty_get_value_as_string (summary), ":", -1); - icalproperty_set_value_from_string (summary, split_subject[1] , "NO"); - g_strfreev (split_subject); - - success = ecb_ews_modify_item_sync (cbews, NULL, NULL, subcomp, cancellable, error); - - do_refresh = TRUE; - } - } - break; - default: - break; - } - - icalcomponent_free (icalcomp); - g_free (user_email); - - if (success && do_refresh) - e_cal_meta_backend_schedule_refresh (E_CAL_META_BACKEND (cbews)); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static void -ecb_ews_send_objects_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *calobj, - GSList **users, - gchar **modified_calobj, - GError **error) -{ - ECalBackendEws *cbews; - icalcomponent_kind kind; - icalcomponent *icalcomp, *subcomp = NULL; - gchar *subcalobj; - gboolean success = TRUE; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - icalcomp = calobj ? icalparser_parse_string (calobj) : NULL; - - /* make sure data was parsed properly */ - if (!icalcomp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - /* make sure ical data we parse is actually an vcal component */ - if ((icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) && (icalcomponent_isa (icalcomp) != ICAL_VEVENT_COMPONENT)) { - icalcomponent_free (icalcomp); - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - - if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) { - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - subcomp = icalcomponent_get_first_component (icalcomp, kind); - } - if (icalcomponent_isa (icalcomp) == ICAL_VEVENT_COMPONENT) - subcomp = icalcomp; - while (subcomp && success) { - const gchar *new_body_content = NULL, *subject = NULL, *org_email = NULL; - const gchar *org = NULL, *attendee = NULL; - icalproperty *prop, *org_prop = NULL; - CamelInternetAddress *org_addr = camel_internet_address_new (); - - new_body_content = e_cal_util_get_x_property (subcomp, "X-EVOLUTION-RETRACT-COMMENT"); - subject = icalproperty_get_value_as_string (icalcomponent_get_first_property (subcomp, ICAL_SUMMARY_PROPERTY)); - - org_prop = icalcomponent_get_first_property (subcomp, ICAL_ORGANIZER_PROPERTY); - org = icalproperty_get_organizer (org_prop); - if (!g_ascii_strncasecmp (org, "MAILTO:", 7)) - org_email = (org) + 7; - else - org_email = org; - - camel_internet_address_add (org_addr, icalproperty_get_parameter_as_string (org_prop, "CN"), org_email); - - /* iterate over every attendee property */ - for (prop = icalcomponent_get_first_property (subcomp, ICAL_ATTENDEE_PROPERTY); - prop && success; - prop = icalcomponent_get_next_property (subcomp, ICAL_ATTENDEE_PROPERTY)) { - CamelInternetAddress *attendee_addr = camel_internet_address_new (); - attendee = icalproperty_get_attendee (prop); - if (g_ascii_strcasecmp (org_email, attendee) == 0) continue; - if (!g_ascii_strncasecmp (attendee, "mailto:", 7)) attendee = (attendee) + 7; - - subcalobj = icalcomponent_as_ical_string_r (subcomp); - camel_internet_address_add (attendee_addr, icalproperty_get_parameter_as_string (prop, "CN"), attendee); - success = ecb_ews_send_cancellation_email_sync (cbews, CAMEL_ADDRESS (org_addr), attendee_addr, - subject, new_body_content, subcalobj, cancellable, error); - g_object_unref (attendee_addr); - free (subcalobj); - } - - g_object_unref (org_addr); - subcomp = icalcomponent_get_next_component (icalcomp, kind); - } - - icalcomponent_free (icalcomp); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static void -ecb_ews_get_free_busy_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const GSList *users, - time_t start, - time_t end, - GSList **freebusyobjs, - GError **error) -{ - ECalBackendEws *cbews; - EEWSFreeBusyData fbdata = { 0 }; - GSList *freebusy = NULL; - gboolean success; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - g_return_if_fail (freebusyobjs != NULL); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - *freebusyobjs = NULL; - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - /* EWS can support only 100 identities, which is the maximum number of identities that the Web service method can request - see http://msdn.microsoft.com / en - us / library / aa564001 % 28v = EXCHG.140 % 29.aspx */ - if (g_slist_length ((GSList *) users) > 100) { - g_propagate_error (error, EDC_ERROR (SearchSizeLimitExceeded)); - return; - } - - fbdata.period_start = start; - fbdata.period_end = end; - fbdata.user_mails = (GSList *) users; - - success = e_ews_connection_get_free_busy_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - e_ews_cal_utils_prepare_free_busy_request, &fbdata, - &freebusy, cancellable, error); - - if (success) { - GSList *fblink, *ulink; - - for (fblink = freebusy, ulink = (GSList *) users; - fblink && ulink; - fblink = g_slist_next (fblink), ulink = g_slist_next (ulink)) { - icalcomponent *icalcomp = fblink->data; - gchar *mailto; - - /* add attendee property */ - mailto = g_strconcat ("mailto:", ulink->data, NULL); - icalcomponent_add_property (icalcomp, icalproperty_new_attendee (mailto)); - g_free (mailto); - - *freebusyobjs = g_slist_prepend (*freebusyobjs, icalcomponent_as_ical_string_r (icalcomp)); - } - - *freebusyobjs = g_slist_reverse (*freebusyobjs); - } - - g_slist_free_full (freebusy, (GDestroyNotify) icalcomponent_free); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static gchar * -ecb_ews_get_backend_property (ECalBackend *cal_backend, - const gchar *prop_name) -{ - ECalBackendEws *cbews; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cal_backend), NULL); - g_return_val_if_fail (prop_name != NULL, NULL); - - cbews = E_CAL_BACKEND_EWS (cal_backend); - - if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) { - return g_strjoin ( - ",", - CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS, - CAL_STATIC_CAPABILITY_NO_AUDIO_ALARMS, - CAL_STATIC_CAPABILITY_NO_PROCEDURE_ALARMS, - CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY, - CAL_STATIC_CAPABILITY_REMOVE_ALARMS, - CAL_STATIC_CAPABILITY_NO_THISANDPRIOR, - CAL_STATIC_CAPABILITY_NO_THISANDFUTURE, - CAL_STATIC_CAPABILITY_NO_CONV_TO_ASSIGN_TASK, - CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT, - CAL_STATIC_CAPABILITY_SAVE_SCHEDULES, - CAL_STATIC_CAPABILITY_NO_ALARM_AFTER_START, - CAL_STATIC_CAPABILITY_NO_MEMO_START_DATE, - CAL_STATIC_CAPABILITY_ALL_DAY_EVENT_AS_TIME, - CAL_STATIC_CAPABILITY_TASK_DATE_ONLY, - e_cal_meta_backend_get_capabilities (E_CAL_META_BACKEND (cbews)), - NULL); - } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS)) { - /* return email address of the person who opened the calendar */ - CamelEwsSettings *ews_settings; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - return camel_ews_settings_dup_email (ews_settings); - } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS)) { - /* ews does not support email based alarms */ - return NULL; - } - - /* Chain up to parent's method. */ - return E_CAL_BACKEND_CLASS (e_cal_backend_ews_parent_class)->get_backend_property (cal_backend, prop_name); -} - -static void -ecb_ews_get_timezone_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *tzid, - gchar **tzobject, - GError **error) -{ - GError *local_error = NULL; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - g_return_if_fail (tzid != NULL); - g_return_if_fail (tzobject != NULL); - - *tzobject = NULL; - - E_CAL_BACKEND_SYNC_CLASS (e_cal_backend_ews_parent_class)->get_timezone_sync (sync_backend, cal, cancellable, tzid, tzobject, &local_error); - - if (!*tzobject) { - /* The timezone can be sometimes the Windows zone, try to convert it to libical */ - const gchar *ical_location = e_cal_backend_ews_tz_util_get_ical_equivalent (tzid); - - if (ical_location) - E_CAL_BACKEND_SYNC_CLASS (e_cal_backend_ews_parent_class)->get_timezone_sync (sync_backend, cal, cancellable, ical_location, tzobject, NULL); - } - - if (*tzobject) - g_clear_error (&local_error); - else if (local_error) - g_propagate_error (error, local_error); -} - -static gboolean -ecb_ews_get_destination_address (EBackend *backend, - gchar **host, - guint16 *port) -{ - CamelEwsSettings *ews_settings; - SoupURI *soup_uri; - gchar *host_url; - gboolean result = FALSE; - - g_return_val_if_fail (port != NULL, FALSE); - g_return_val_if_fail (host != NULL, FALSE); - - /* Sanity checking */ - if (!e_cal_backend_get_registry (E_CAL_BACKEND (backend)) || - !e_backend_get_source (backend)) - return FALSE; - - ews_settings = ecb_ews_get_collection_settings (E_CAL_BACKEND_EWS (backend)); - g_return_val_if_fail (ews_settings != NULL, FALSE); - - host_url = camel_ews_settings_dup_hosturl (ews_settings); - g_return_val_if_fail (host_url != NULL, FALSE); - - soup_uri = soup_uri_new (host_url); - if (soup_uri) { - *host = g_strdup (soup_uri_get_host (soup_uri)); - *port = soup_uri_get_port (soup_uri); - - result = *host && **host; - if (!result) { - g_free (*host); - *host = NULL; - } - - soup_uri_free (soup_uri); - } - - g_free (host_url); - - return result; -} - -static gchar * -ecb_ews_dup_component_revision (ECalCache *cal_cache, - icalcomponent *icalcomp, - gpointer user_data) -{ - g_return_val_if_fail (icalcomp != NULL, NULL); - - return e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY"); -} - -static void -ecb_ews_constructed (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - ECalCache *cal_cache; - gchar *cache_dirname; - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->constructed (object); - - /* Reset the connectable, it steals data from Authentication extension, - where is written incorrect address */ - e_backend_set_connectable (E_BACKEND (object), NULL); - - cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbews)); - g_return_if_fail (cal_cache != NULL); - - cache_dirname = g_path_get_dirname (e_cache_get_filename (E_CACHE (cal_cache))); - g_signal_connect (cal_cache, "dup-component-revision", G_CALLBACK (ecb_ews_dup_component_revision), NULL); - - g_clear_object (&cal_cache); - - cbews->priv->attachments_dir = g_build_filename (cache_dirname, "attachments", NULL); - g_mkdir_with_parents (cbews->priv->attachments_dir, 0777); - - g_free (cache_dirname); -} - -static void -ecb_ews_dispose (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - g_clear_object (&cbews->priv->cnc); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->dispose (object); -} - -static void -ecb_ews_finalize (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - - g_free (cbews->priv->folder_id); - g_free (cbews->priv->attachments_dir); - - g_rec_mutex_clear (&cbews->priv->cnc_lock); - - e_cal_backend_ews_unref_windows_zones (); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->finalize (object); -} - -static void -e_cal_backend_ews_init (ECalBackendEws *cbews) -{ - cbews->priv = G_TYPE_INSTANCE_GET_PRIVATE (cbews, E_TYPE_CAL_BACKEND_EWS, ECalBackendEwsPrivate); - - g_rec_mutex_init (&cbews->priv->cnc_lock); - - e_cal_backend_ews_populate_windows_zones (); -} - -static void -e_cal_backend_ews_class_init (ECalBackendEwsClass *klass) -{ - GObjectClass *object_class; - EBackendClass *backend_class; - ECalBackendClass *cal_backend_class; - ECalBackendSyncClass *cal_backend_sync_class; - ECalMetaBackendClass *cal_meta_backend_class; - - g_type_class_add_private (klass, sizeof (ECalBackendEwsPrivate)); - - cal_meta_backend_class = E_CAL_META_BACKEND_CLASS (klass); - cal_meta_backend_class->connect_sync = ecb_ews_connect_sync; - cal_meta_backend_class->disconnect_sync = ecb_ews_disconnect_sync; - cal_meta_backend_class->get_changes_sync = ecb_ews_get_changes_sync; - cal_meta_backend_class->load_component_sync = ecb_ews_load_component_sync; - cal_meta_backend_class->save_component_sync = ecb_ews_save_component_sync; - cal_meta_backend_class->remove_component_sync = ecb_ews_remove_component_sync; - - cal_backend_sync_class = E_CAL_BACKEND_SYNC_CLASS (klass); - cal_backend_sync_class->discard_alarm_sync = ecb_ews_discard_alarm_sync; - cal_backend_sync_class->receive_objects_sync = ecb_ews_receive_objects_sync; - cal_backend_sync_class->send_objects_sync = ecb_ews_send_objects_sync; - cal_backend_sync_class->get_free_busy_sync = ecb_ews_get_free_busy_sync; - cal_backend_sync_class->get_timezone_sync = ecb_ews_get_timezone_sync; - - cal_backend_class = E_CAL_BACKEND_CLASS (klass); - cal_backend_class->get_backend_property = ecb_ews_get_backend_property; - - backend_class = E_BACKEND_CLASS (klass); - backend_class->get_destination_address = ecb_ews_get_destination_address; - - object_class = G_OBJECT_CLASS (klass); - object_class->constructed = ecb_ews_constructed; - object_class->dispose = ecb_ews_dispose; - object_class->finalize = ecb_ews_finalize; -} diff --git a/src/calendar/e-cal-backend-ews.c.disable-reminder-types b/src/calendar/e-cal-backend-ews.c.disable-reminder-types deleted file mode 100644 index ca6cc3e..0000000 --- a/src/calendar/e-cal-backend-ews.c.disable-reminder-types +++ /dev/null @@ -1,3901 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com) - * - * 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. - * - * 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#include -#include -#include -#include - -#include -#include - -#include "server/e-source-ews-folder.h" -#include "server/e-ews-calendar-utils.h" -#include "server/e-ews-connection-utils.h" -#include "server/e-ews-camel-common.h" - -#include "e-cal-backend-ews.h" -#include "e-cal-backend-ews-utils.h" - -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -#ifdef G_OS_WIN32 -#ifdef gmtime_r -#undef gmtime_r -#endif - -/* The gmtime() in Microsoft's C library is MT-safe */ -#define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0) -#endif - -/* Private part of the CalBackendEws structure */ -struct _ECalBackendEwsPrivate { - GRecMutex cnc_lock; - EEwsConnection *cnc; - gchar *folder_id; - - guint subscription_key; - gboolean is_freebusy_calendar; - - gchar *attachments_dir; -}; - -#define X_EWS_ORIGINAL_COMP "X-EWS-ORIGINAL-COMP" - -#define EWS_MAX_FETCH_COUNT 100 - -#define GET_ITEMS_SYNC_PROPERTIES \ - "item:Attachments" \ - " item:Categories" \ - " item:HasAttachments" \ - " item:MimeContent" \ - " calendar:UID" \ - " calendar:Resources" \ - " calendar:ModifiedOccurrences" \ - " calendar:IsMeeting" \ - " calendar:IsResponseRequested" \ - " calendar:MyResponseType" \ - " calendar:RequiredAttendees" \ - " calendar:OptionalAttendees" - -#define GET_ITEMS_SYNC_PROPERTIES_2007 \ - GET_ITEMS_SYNC_PROPERTIES \ - " calendar:TimeZone" - -#define GET_ITEMS_SYNC_PROPERTIES_2010 \ - GET_ITEMS_SYNC_PROPERTIES \ - " calendar:StartTimeZone" \ - " calendar:EndTimeZone" - -#define e_data_cal_error_if_fail(expr, _code) \ - G_STMT_START { \ - if (G_LIKELY (expr)) { \ - } else { \ - g_log (G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "file %s: line %d (%s): assertion `%s' failed", \ - __FILE__, __LINE__, G_STRFUNC, #expr); \ - g_set_error (&error, E_DATA_CAL_ERROR, (_code), \ - "file %s: line %d (%s): assertion `%s' failed", \ - __FILE__, __LINE__, G_STRFUNC, #expr); \ - goto exit; \ - } \ - } G_STMT_END - -G_DEFINE_TYPE (ECalBackendEws, e_cal_backend_ews, E_TYPE_CAL_META_BACKEND) - -static CamelEwsSettings * -ecb_ews_get_collection_settings (ECalBackendEws *cbews) -{ - ESource *source; - ESource *collection; - ESourceCamel *extension; - ESourceRegistry *registry; - CamelSettings *settings; - const gchar *extension_name; - - source = e_backend_get_source (E_BACKEND (cbews)); - registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbews)); - - extension_name = e_source_camel_get_extension_name ("ews"); - e_source_camel_generate_subtype ("ews", CAMEL_TYPE_EWS_SETTINGS); - - /* The collection settings live in our parent data source. */ - collection = e_source_registry_find_extension ( - registry, source, extension_name); - g_return_val_if_fail (collection != NULL, NULL); - - extension = e_source_get_extension (collection, extension_name); - settings = e_source_camel_get_settings (extension); - - g_object_unref (collection); - - return CAMEL_EWS_SETTINGS (settings); -} - -static void -ecb_ews_convert_error_to_edc_error (GError **perror) -{ - GError *error = NULL; - - if (!perror || !*perror || (*perror)->domain == E_DATA_CAL_ERROR) - return; - - if ((*perror)->domain == EWS_CONNECTION_ERROR) { - switch ((*perror)->code) { - case EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED: - error = EDC_ERROR_EX (AuthenticationFailed, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_FOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_MANAGEDFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PARENTFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PUBLICFOLDERSERVERNOTFOUND: - error = EDC_ERROR_EX (NoSuchCal, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_EVENTNOTFOUND: - case EWS_CONNECTION_ERROR_ITEMNOTFOUND: - error = EDC_ERROR_EX (ObjectNotFound, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_UNAVAILABLE: - g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND, (*perror)->message); - break; - } - - if (!error) - error = EDC_ERROR_EX (OtherError, (*perror)->message); - } - - if (error) { - g_error_free (*perror); - *perror = error; - } -} - -static void -ecb_ews_maybe_disconnect_sync (ECalBackendEws *cbews, - GError **in_perror, - GCancellable *cancellable) -{ - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cbews)); - - if (in_perror && g_error_matches (*in_perror, E_DATA_CAL_ERROR, AuthenticationFailed)) { - e_cal_meta_backend_disconnect_sync (E_CAL_META_BACKEND (cbews), cancellable, NULL); - e_backend_schedule_credentials_required (E_BACKEND (cbews), E_SOURCE_CREDENTIALS_REASON_REJECTED, NULL, 0, NULL, NULL, G_STRFUNC); - } -} - -static void -ecb_ews_server_notification_cb (ECalBackendEws *cbews, - GSList *events, - EEwsConnection *cnc) -{ - GSList *link; - gboolean update_folder = FALSE; - - g_return_if_fail (cbews != NULL); - g_return_if_fail (cbews->priv != NULL); - - for (link = events; link && !update_folder; link = g_slist_next (link)) { - EEwsNotificationEvent *event = link->data; - - switch (event->type) { - case E_EWS_NOTIFICATION_EVENT_CREATED: - case E_EWS_NOTIFICATION_EVENT_DELETED: - case E_EWS_NOTIFICATION_EVENT_MODIFIED: - g_rec_mutex_lock (&cbews->priv->cnc_lock); - if (g_strcmp0 (event->folder_id, cbews->priv->folder_id) == 0) - update_folder = TRUE; - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - break; - case E_EWS_NOTIFICATION_EVENT_MOVED: - case E_EWS_NOTIFICATION_EVENT_COPIED: - g_rec_mutex_lock (&cbews->priv->cnc_lock); - if (g_strcmp0 (event->folder_id, cbews->priv->folder_id) == 0 || - g_strcmp0 (event->old_folder_id, cbews->priv->folder_id) == 0) - update_folder = TRUE; - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - break; - default: - return; - } - } - - if (update_folder) - e_cal_meta_backend_schedule_refresh (E_CAL_META_BACKEND (cbews)); -} - -static void -ecb_ews_unset_connection (ECalBackendEws *cbews) -{ - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cbews)); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->cnc) { - e_ews_connection_set_disconnected_flag (cbews->priv->cnc, TRUE); - - g_signal_handlers_disconnect_by_func (cbews->priv->cnc, ecb_ews_server_notification_cb, cbews); - - if (cbews->priv->subscription_key != 0) { - e_ews_connection_disable_notifications_sync ( - cbews->priv->cnc, - cbews->priv->subscription_key); - cbews->priv->subscription_key = 0; - } - } - - g_clear_object (&cbews->priv->cnc); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); -} - -static icaltimezone * -ecb_ews_get_timezone (ETimezoneCache *timezone_cache, - const gchar *msdn_tzid, - const gchar *tzid, - const gchar *evo_ews_tzid) -{ - icaltimezone *zone = NULL; - const gchar *evo_ews_msdn_tzid; - - zone = e_timezone_cache_get_timezone (timezone_cache, tzid); - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (tzid); - - if (g_strcmp0 (tzid, evo_ews_tzid) == 0) - return zone; - - if (evo_ews_tzid != NULL) { - evo_ews_msdn_tzid = e_cal_backend_ews_tz_util_get_msdn_equivalent (evo_ews_tzid); - - if (g_strcmp0 (msdn_tzid, evo_ews_msdn_tzid) == 0) { - zone = e_timezone_cache_get_timezone (timezone_cache, evo_ews_tzid); - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (evo_ews_tzid); - } - } - - return zone; -} - -static icalparameter * -ecb_ews_responsetype_to_partstat (const gchar *responsetype) -{ - icalparameter *param = NULL; - - if (!responsetype) - return icalparameter_new_partstat (ICAL_PARTSTAT_NONE); - - if (g_ascii_strcasecmp (responsetype, "Organizer") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_ACCEPTED); - else if (g_ascii_strcasecmp (responsetype, "Tentative") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_TENTATIVE); - else if (g_ascii_strcasecmp (responsetype, "Accept") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_ACCEPTED); - else if (g_ascii_strcasecmp (responsetype, "Decline") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_DECLINED); - else if (g_ascii_strcasecmp (responsetype, "NoResponseReceived") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION); - else if (g_ascii_strcasecmp (responsetype, "Unknown") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NONE); - - if (!param) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NONE); - - return param; -} - -static ECalComponent * -ecb_ews_item_to_component_sync (ECalBackendEws *cbews, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *res_component = NULL; - ETimezoneCache *timezone_cache; - icalcomponent_kind kind; - EEwsItemType item_type; - icalcomponent *icalcomp, *vcomp; - icaltimezone *utc_zone = icaltimezone_get_utc_timezone (); - CamelEwsSettings *ews_settings; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), NULL); - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - timezone_cache = E_TIMEZONE_CACHE (cbews); - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - ews_settings = ecb_ews_get_collection_settings (cbews); - - item_type = e_ews_item_get_item_type (item); - if (item_type == E_EWS_ITEM_TYPE_TASK || item_type == E_EWS_ITEM_TYPE_MEMO) { - icalproperty *icalprop; - icaltimetype due_date, start_date, complete_date, created; - icalproperty_status status = ICAL_STATUS_NONE; - icalproperty_class class = ICAL_CLASS_NONE; - const gchar *ews_task_status, *sensitivity; - EwsImportance item_importance; - gint priority = 5; - gboolean has_this_date = FALSE; - - vcomp = icalcomponent_new (ICAL_VCALENDAR_COMPONENT); - /*subject*/ - icalcomp = icalcomponent_new (item_type == E_EWS_ITEM_TYPE_TASK ? ICAL_VTODO_COMPONENT : ICAL_VJOURNAL_COMPONENT); - icalprop = icalproperty_new_summary (e_ews_item_get_subject (item)); - icalcomponent_add_property (icalcomp, icalprop); - - /*date time created*/ - created = icaltime_from_timet_with_zone (e_ews_item_get_date_created (item), 0, utc_zone); - icalprop = icalproperty_new_created (created); - icalcomponent_add_property (icalcomp, icalprop); - - /*sensitivity*/ - sensitivity = e_ews_item_get_sensitivity (item); - if (g_strcmp0 (sensitivity, "Normal") == 0) - class = ICAL_CLASS_PUBLIC; - else if (g_strcmp0 (sensitivity, "Private") == 0) - class = ICAL_CLASS_PRIVATE; - else if ((g_strcmp0 (sensitivity, "Confidential") == 0) || - (g_strcmp0 (sensitivity, "Personal") == 0)) - class = ICAL_CLASS_CONFIDENTIAL; - icalprop = icalproperty_new_class (class); - icalcomponent_add_property (icalcomp, icalprop); - - /*description*/ - icalprop = icalproperty_new_description (e_ews_item_get_body (item)); - icalcomponent_add_property (icalcomp, icalprop); - - /*task assaingments*/ - if (e_ews_item_get_delegator (item) != NULL) { - const gchar *task_owner = e_ews_item_get_delegator (item); - GSList *mailboxes = NULL, *l; - gboolean includes_last_item; - gchar *mailtoname, *user_email; - icalparameter *param; - - /*The task owner according to Exchange is current user, even that the task was assigned by - *someone else. I'm making the current user attendee and task delegator will be a task organizer */ - - user_email = camel_ews_settings_dup_email (ews_settings); - mailtoname = g_strdup_printf ("mailto:%s", user_email); - icalprop = icalproperty_new_attendee (mailtoname); - g_free (mailtoname); - g_free (user_email); - - param = icalparameter_new_cn (e_ews_item_get_owner (item)); - icalproperty_add_parameter (icalprop, param); - icalcomponent_add_property (icalcomp, icalprop); - - /* get delegator mail box*/ - e_ews_connection_resolve_names_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, task_owner, - EWS_SEARCH_AD, NULL, FALSE, &mailboxes, NULL, - &includes_last_item, cancellable, error); - - for (l = mailboxes; l != NULL; l = g_slist_next (l)) { - EwsMailbox *mb = l->data; - - mailtoname = g_strdup_printf ("mailto:%s", mb->email); - icalprop = icalproperty_new_organizer (mailtoname); - param = icalparameter_new_cn (mb->name); - icalproperty_add_parameter (icalprop, param); - icalcomponent_add_property (icalcomp, icalprop); - - g_free (mailtoname); - e_ews_mailbox_free (mb); - } - g_slist_free (mailboxes); - } - - if (item_type == E_EWS_ITEM_TYPE_TASK) { - icaltimezone *user_timezone = calendar_config_get_icaltimezone (); - const gchar *percent_complete; - - /*start date*/ - has_this_date = FALSE; - e_ews_item_task_has_start_date (item, &has_this_date); - if (has_this_date) { - start_date = icaltime_from_timet_with_zone (e_ews_item_get_start_date (item), 0, user_timezone); - start_date.is_date = 1; - icalprop = icalproperty_new_dtstart (start_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*status*/ - ews_task_status = e_ews_item_get_status (item); - if (g_strcmp0 (ews_task_status, "NotStarted") != 0) { - if (g_strcmp0 (ews_task_status, "Completed") == 0) - status = ICAL_STATUS_COMPLETED; - else if (g_strcmp0 (ews_task_status, "InProgress") == 0) - status = ICAL_STATUS_INPROCESS; - else if (g_strcmp0 (ews_task_status, "WaitingOnOthers") == 0) - status = ICAL_STATUS_NEEDSACTION; - else if (g_strcmp0 (ews_task_status, "Deferred") == 0) - status = ICAL_STATUS_CANCELLED; - icalprop = icalproperty_new_status (status); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*precent complete*/ - percent_complete = e_ews_item_get_percent_complete (item); - icalprop = icalproperty_new_percentcomplete (atoi (percent_complete ? percent_complete : "0")); - icalcomponent_add_property (icalcomp, icalprop); - - /*due date*/ - e_ews_item_task_has_due_date (item, &has_this_date); - if (has_this_date) { - due_date = icaltime_from_timet_with_zone (e_ews_item_get_due_date (item), 0, user_timezone); - due_date.is_date = 1; - icalprop = icalproperty_new_due (due_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*complete date*/ - has_this_date = FALSE; - e_ews_item_task_has_complete_date (item, &has_this_date); - if (has_this_date) { - complete_date = icaltime_from_timet_with_zone (e_ews_item_get_complete_date (item), 0, user_timezone); - icalprop = icalproperty_new_completed (complete_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*priority*/ - item_importance = e_ews_item_get_importance (item); - if (item_importance == EWS_ITEM_HIGH) - priority = 3; - else if (item_importance == EWS_ITEM_LOW) - priority = 7; - icalprop = icalproperty_new_priority (priority); - icalcomponent_add_property (icalcomp, icalprop); - } - - icalcomponent_add_component (vcomp, icalcomp); - } else { - struct icaltimetype dt; - const gchar *mime_content; - const gchar *tzid; - gboolean timezone_set = FALSE; - - mime_content = e_ews_item_get_mime_content (item); - vcomp = mime_content && *mime_content ? icalparser_parse_string (mime_content) : NULL; - - if (!vcomp && mime_content && *mime_content) { - const gchar *begin_vcalendar, *end_vcalendar; - - /* Workaround Exchange 2016 error, which returns invalid iCalendar object (without 'END:VCALENDAR'), - when the event has at least one detached instance. */ - begin_vcalendar = camel_strstrcase (mime_content, "BEGIN:VCALENDAR"); - end_vcalendar = camel_strstrcase (mime_content, "END:VCALENDAR"); - - /* If it exists, then it should be alone on a separate line */ - if (!(begin_vcalendar && (begin_vcalendar == mime_content || begin_vcalendar[-1] == '\n') && - (begin_vcalendar[15 /* strlen ("BEGIN:VCALENDAR") */] == '\r' || begin_vcalendar[15] == '\n'))) - begin_vcalendar = NULL; - - /* If it exists, then it should be alone on a separate line and not at the very beginning of the mime_content */ - if (!(end_vcalendar && end_vcalendar > mime_content && end_vcalendar[-1] == '\n' && - (end_vcalendar[13 /* strlen ("END:VCALENDAR") */] == '\r' || end_vcalendar[13] == '\n' || end_vcalendar[13] == '\0'))) - end_vcalendar = NULL; - - if (begin_vcalendar && !end_vcalendar) { - gchar *str; - - str = g_strconcat (mime_content, "\r\n", "END:VCALENDAR", "\r\n", NULL); - vcomp = icalparser_parse_string (str); - g_free (str); - } - } - - if (!vcomp) { - if (mime_content) - g_warning ("%s: Failed to parse mime content:---%s---", G_STRFUNC, mime_content); - return NULL; - } - - tzid = e_ews_item_get_tzid (item); - if (tzid == NULL) { - /* - * When we are working with Exchange server 2010 or newer, we have to handle a few - * things more than we do working old servers. These things are: - * - MSDN timezone names: - * Used setting StartTimeZone and EndTimeZone. MSDN timezone names are not - * the same used in libical, so we need to have a table of equivalence to - * convert from one to another and avoid show the MSDN timezone name to the - * user and save it in the ETimezoneCache. - * - EvoEWSStartTimeZone/EvoEWSEndTimeZone - * Used to keep track if the timezone shown to the user is the same one set - * by him/her. As we have a table of equivalence, sometimes the user sets a - * timezone but without EvoEWSStartTiemZone property, another timezone name, - * in the same offset, can be shown. And we want to avoid this. - * - DTEND property: - * As we have to work with DTEND setting an event when using EWS server 2010 or - * newer, we have to care about set it properly here, instead of use the same - * as is used in DTSTART. - */ - icaltimezone *start_zone, *end_zone; - const gchar *start_tzid, *end_tzid; - const gchar *ical_start_tzid, *ical_end_tzid; - const gchar *evo_ews_start_tzid, *evo_ews_end_tzid; - - start_tzid = e_ews_item_get_start_tzid (item); - end_tzid = e_ews_item_get_end_tzid (item); - - ical_start_tzid = e_cal_backend_ews_tz_util_get_ical_equivalent (start_tzid); - ical_end_tzid = e_cal_backend_ews_tz_util_get_ical_equivalent (end_tzid); - - evo_ews_start_tzid = e_ews_item_get_iana_start_time_zone (item); - evo_ews_end_tzid = e_ews_item_get_iana_end_time_zone (item); - - /* - * We have a few timezones that don't have an equivalent MSDN timezone. - * For those, we will get ical_start_tzid being NULL and then we need to use - * start_tzid, which one has the libical's expected name. - */ - start_zone = ecb_ews_get_timezone ( - timezone_cache, - start_tzid, - ical_start_tzid != NULL ? ical_start_tzid : start_tzid, - evo_ews_start_tzid); - end_zone = ecb_ews_get_timezone ( - timezone_cache, - end_tzid, - ical_end_tzid != NULL ? ical_end_tzid : end_tzid, - evo_ews_end_tzid); - - if (start_zone != NULL) { - icalcomp = icalcomponent_get_first_component (vcomp, kind); - - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - dt = icaltime_convert_to_zone (dt, start_zone); - icalcomponent_set_dtstart (icalcomp, dt); - - timezone_set = TRUE; - e_timezone_cache_add_timezone (timezone_cache, start_zone); - - if (end_zone != NULL) { - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); - dt = icaltime_convert_to_zone (dt, end_zone); - icalcomponent_set_dtend (icalcomp, dt); - - e_timezone_cache_add_timezone (timezone_cache, end_zone); - } - } - - if (!timezone_set) - tzid = start_tzid; - } - - if (!timezone_set && tzid) { - /* - * When we are working with Exchange server older than 2010, we don't set different - * DTSTART and DTEND properties in VTIMEZONE. The reason of that is we don't use - * those properties settings/changing a meeting timezone. - * So, for older servers, here, we only set the DTSTART and DTEND properties with - * the same values. - */ - icaltimezone *zone; - gchar *new_tzid = NULL; - - icalcomp = icalcomponent_get_first_component (vcomp, kind); - - if (!icaltimezone_get_builtin_timezone (tzid) && - icalcomponent_get_uid (icalcomp)) { - icalcomponent *vtimezone; - - /* Add the timezone */ - vtimezone = icalcomponent_get_first_component (vcomp, ICAL_VTIMEZONE_COMPONENT); - if (vtimezone != NULL) { - icalproperty *prop; - - new_tzid = g_strconcat ("/evolution/ews/tzid/", icalcomponent_get_uid (icalcomp), NULL); - - zone = icaltimezone_new (); - vtimezone = icalcomponent_new_clone (vtimezone); - prop = icalcomponent_get_first_property (vtimezone, ICAL_TZID_PROPERTY); - if (prop) { - icalproperty_set_tzid (prop, new_tzid); - - prop = icalcomponent_get_first_property (vtimezone, ICAL_LOCATION_PROPERTY); - if (!prop) { - /* Use the original tzid as the timezone Location, to not expose - evolution-ews TZID. */ - prop = icalproperty_new_location (tzid); - icalcomponent_add_property (vtimezone, prop); - } - } else { - g_free (new_tzid); - new_tzid = NULL; - } - icaltimezone_set_component (zone, vtimezone); - e_timezone_cache_add_timezone (timezone_cache, zone); - icaltimezone_free (zone, TRUE); - } - } - - zone = e_timezone_cache_get_timezone (timezone_cache, new_tzid ? new_tzid : tzid); - - if (!zone && new_tzid) - zone = e_timezone_cache_get_timezone (timezone_cache, tzid); - - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (tzid); - - if (zone != NULL) { - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - dt = icaltime_convert_to_zone (dt, zone); - icalcomponent_set_dtstart (icalcomp, dt); - - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); - dt = icaltime_convert_to_zone (dt, zone); - icalcomponent_set_dtend (icalcomp, dt); - } - - g_free (new_tzid); - } - } - - /* Vevent or Vtodo */ - icalcomp = icalcomponent_get_first_component (vcomp, kind); - if (icalcomp) { - icalproperty *icalprop, *freebusy; - struct icaltimetype itt; - const EwsId *item_id; - const GSList *l = NULL; - const gchar *uid = e_ews_item_get_uid (item); - - item_id = e_ews_item_get_id (item); - - if (e_ews_item_get_is_meeting (item)) { - gboolean is_response_requested = e_ews_item_get_is_response_requested (item); - gchar *user_email; - - user_email = camel_ews_settings_dup_email (ews_settings); - - /* Attendees */ - for (l = e_ews_item_get_attendees (item); l != NULL; l = g_slist_next (l)) { - icalparameter *param, *cu_type; - gchar *mailtoname; - const gchar *email = NULL; - EwsAttendee *attendee = (EwsAttendee *) l->data; - - if (!attendee->mailbox) - continue; - - if (g_strcmp0 (attendee->mailbox->routing_type, "EX") == 0) - email = e_ews_item_util_strip_ex_address (attendee->mailbox->email); - - mailtoname = g_strdup_printf ("mailto:%s", email ? email : attendee->mailbox->email); - icalprop = icalproperty_new_attendee (mailtoname); - g_free (mailtoname); - - param = icalparameter_new_cn (attendee->mailbox->name); - icalproperty_add_parameter (icalprop, param); - - if (g_ascii_strcasecmp (attendee->attendeetype, "Required") == 0) { - param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL); - } - else if (g_ascii_strcasecmp (attendee->attendeetype, "Resource") == 0) { - param = icalparameter_new_role (ICAL_ROLE_NONPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_RESOURCE); - } - else { - param = icalparameter_new_role ( ICAL_ROLE_OPTPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL); - } - icalproperty_add_parameter (icalprop, cu_type); - icalproperty_add_parameter (icalprop, param); - - if (is_response_requested) { - param = icalparameter_new_rsvp (ICAL_RSVP_TRUE); - icalproperty_add_parameter (icalprop, param); - } - - if (user_email && (email || attendee->mailbox->email) && e_ews_item_get_my_response_type (item) && - g_ascii_strcasecmp (email ? email : attendee->mailbox->email, user_email) == 0) { - param = ecb_ews_responsetype_to_partstat (e_ews_item_get_my_response_type (item)); - } else { - param = ecb_ews_responsetype_to_partstat (attendee->responsetype); - } - icalproperty_add_parameter (icalprop, param); - - icalcomponent_add_property (icalcomp, icalprop); - } - - g_free (user_email); - } - - /* Free/Busy */ - freebusy = icalcomponent_get_first_property (icalcomp, ICAL_TRANSP_PROPERTY); - if (!freebusy && (e_ews_item_get_item_type (item) != E_EWS_ITEM_TYPE_TASK)) { - /* Busy by default */ - freebusy = icalproperty_new_transp (ICAL_TRANSP_OPAQUE); - icalcomponent_add_property (icalcomp, freebusy); - } - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); - icalprop != NULL; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) { - if (g_strcmp0 (icalproperty_get_x_name (icalprop), "X-MICROSOFT-CDO-BUSYSTATUS") == 0) { - if (g_strcmp0 (icalproperty_get_value_as_string (icalprop), "BUSY") == 0) { - icalproperty_set_transp (freebusy, ICAL_TRANSP_OPAQUE); - } else { - icalproperty_set_transp (freebusy, ICAL_TRANSP_TRANSPARENT); - } - - break; - } - } - - /* AllDayEvent */ - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); - icalprop != NULL; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) { - if (g_strcmp0 (icalproperty_get_x_name (icalprop), "X-MICROSOFT-CDO-ALLDAYEVENT") == 0) { - if (g_strcmp0 (icalproperty_get_value_as_string (icalprop), "TRUE") == 0) { - struct icaltimetype dtend, dtstart; - dtstart = icalcomponent_get_dtstart (icalcomp); - dtstart.is_date = 1; - icalcomponent_set_dtstart (icalcomp, dtstart); - - dtend = icalcomponent_get_dtend (icalcomp); - dtend.is_date = 1; - icalcomponent_set_dtend (icalcomp, dtend); - } - break; - } - } - - if (icalcomponent_get_first_property (icalcomp, ICAL_RECURRENCEID_PROPERTY)) { - /* Exchange sets RRULE even on the children, which is broken */ - icalprop = icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY); - if (icalprop) { - icalcomponent_remove_property (icalcomp, icalprop); - icalproperty_free (icalprop); - } - } - - /* The EXDATE sent by the server can be date-time format with timezone, while - the event start time can be date-only. This breaks the rules, thus correct - it and make also EXDATE date-only. */ - itt = icalcomponent_get_dtstart (icalcomp); - if (icaltime_is_valid_time (itt) && itt.is_date) { - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_EXDATE_PROPERTY); - icalprop; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_EXDATE_PROPERTY)) { - itt = icalproperty_get_exdate (icalprop); - itt.is_date = 1; - icalproperty_set_exdate (icalprop, itt); - - icalproperty_remove_parameter_by_kind (icalprop, ICAL_TZID_PARAMETER); - } - } - - /* Exchange sets an ORGANIZER on all events. RFC2445 says: - * - * This property MUST NOT be specified in an iCalendar - * object that specifies only a time zone definition or - * that defines calendar entities that are not group - * scheduled entities, but are entities only on a single - * user's calendar. - */ - if (!icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) { - if ((icalprop = icalcomponent_get_first_property (icalcomp, ICAL_ORGANIZER_PROPERTY))) { - icalcomponent_remove_property (icalcomp, icalprop); - icalproperty_free (icalprop); - } - } - - icalcomponent_set_uid (icalcomp, uid ? uid : item_id->id); - - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-ITEMID", item_id->id); - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY", item_id->change_key); - - res_component = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (icalcomp)); - - /* Categories */ - e_cal_component_set_categories_list (res_component, (GSList *) e_ews_item_get_categories (item)); - - /* - * There is no API to set/get alarm description on the server side. - * However, for some reason, the alarm description has been set to "REMINDER" - * automatically (and with no i18n). Instead of show it to the user, let's - * set the summary as the alarm description. - */ - if (e_cal_component_has_alarms (res_component)) { - GList *alarm_uids, *l; - - alarm_uids = e_cal_component_get_alarm_uids (res_component); - for (l = alarm_uids; l != NULL; l = l->next) { - ECalComponentAlarm *alarm; - ECalComponentText text; - - alarm = e_cal_component_get_alarm (res_component, l->data); - e_cal_component_get_summary (res_component, &text); - e_cal_component_alarm_set_description (alarm, &text); - - e_cal_component_alarm_free (alarm); - } - cal_obj_uid_list_free (alarm_uids); - } - } - - icalcomponent_free (vcomp); - - if (res_component) { - const GSList *attachment_ids, *aid, *l; - const gchar *uid = NULL; - GSList *info_attachments = NULL, *uris = NULL; - gboolean has_attachment = FALSE; - - e_ews_item_has_attachments (item, &has_attachment); - if (!has_attachment) - return res_component; - - e_cal_component_get_uid (res_component, &uid); - - attachment_ids = e_ews_item_get_attachments_ids (item); - - if (e_ews_connection_get_attachments_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - uid, - attachment_ids, - cbews->priv->attachments_dir, - TRUE, - &info_attachments, - NULL, NULL, - cancellable, - NULL)) { - icalcomponent *icalcomp; - icalproperty *icalprop; - icalparameter *icalparam; - - for (l = info_attachments; l; l = l->next) { - EEwsAttachmentInfo *info = l->data; - - /* ignore non-uri attachments, because it's an exception */ - if (e_ews_attachment_info_get_type (info) == E_EWS_ATTACHMENT_INFO_TYPE_URI) { - const gchar *uri = e_ews_attachment_info_get_uri (info); - - if (uri) - uris = g_slist_append (uris, g_strdup (uri)); - } - } - - e_cal_component_set_attachment_list (res_component, uris); - - icalcomp = e_cal_component_get_icalcomponent (res_component); - icalprop = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY); - for (aid = attachment_ids; aid && icalprop; aid = aid->next, icalprop = icalcomponent_get_next_property (icalcomp, ICAL_ATTACH_PROPERTY)) { - icalparam = icalparameter_new_x (aid->data); - icalparameter_set_xname (icalparam, "X-EWS-ATTACHMENTID"); - icalproperty_add_parameter (icalprop, icalparam); - } - - g_slist_free_full (uris, g_free); - g_slist_free_full (info_attachments, (GDestroyNotify) e_ews_attachment_info_free); - } - } - - return res_component; -} - -static void -ecb_ews_store_original_comp (ECalComponent *comp) -{ - gchar *comp_str; - gchar *base64; - - g_return_if_fail (E_IS_CAL_COMPONENT (comp)); - - comp_str = e_cal_component_get_as_string (comp); - g_return_if_fail (comp_str != NULL); - - /* Include NUL-terminator */ - base64 = g_base64_encode ((const guchar *) comp_str, strlen (comp_str) + 1); - - e_cal_util_set_x_property (e_cal_component_get_icalcomponent (comp), - X_EWS_ORIGINAL_COMP, base64); - - g_free (base64); - g_free (comp_str); -} - -static ECalComponent * /* free with g_object_unref(), if not NULL */ -ecb_ews_restore_original_comp (ECalComponent *from_comp) -{ - ECalComponent *comp = NULL; - const gchar *original_base64; - guchar *decoded; - gsize len = -1; - - g_return_val_if_fail (E_IS_CAL_COMPONENT (from_comp), NULL); - - original_base64 = e_cal_util_get_x_property (e_cal_component_get_icalcomponent (from_comp), X_EWS_ORIGINAL_COMP); - - if (!original_base64 || !*original_base64) - return NULL; - - decoded = g_base64_decode (original_base64, &len); - if (!decoded || !*decoded || len <= 0) { - g_free (decoded); - return NULL; - } - - if (decoded[len - 1] != '\0') { - gchar *tmp; - - tmp = g_strndup ((const gchar *) decoded, len); - - g_free (decoded); - decoded = (guchar *) tmp; - } - - if (decoded && *decoded) - comp = e_cal_component_new_from_string ((const gchar *) decoded); - - g_free (decoded); - - return comp; -} - -static gboolean -ecb_ews_get_items_sync (ECalBackendEws *cbews, - const GSList *item_ids, /* gchar * */ - const gchar *default_props, - const EEwsAdditionalProps *add_props, - GSList **out_components, /* ECalComponent * */ - GCancellable *cancellable, - GError **error) -{ - GSList *items = NULL, *link; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (out_components != NULL, FALSE); - - success = e_ews_connection_get_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - item_ids, - default_props, - add_props, - FALSE, - NULL, - E_EWS_BODY_TYPE_TEXT, - &items, - NULL, NULL, - cancellable, - error); - - if (!success) - return FALSE; - - /* fetch modified occurrences */ - for (link = items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const GSList *modified_occurrences; - - if (!item || e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - modified_occurrences = e_ews_item_get_modified_occurrences (item); - if (modified_occurrences) { - EEwsAdditionalProps *modified_add_props; - - modified_add_props = e_ews_additional_props_new (); - if (e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) { - EEwsExtendedFieldURI *ext_uri; - - modified_add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2010); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSStartTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - modified_add_props->extended_furis = g_slist_append (modified_add_props->extended_furis, ext_uri); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSEndTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - modified_add_props->extended_furis = g_slist_append (modified_add_props->extended_furis, ext_uri); - } else { - modified_add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2007); - } - - success = ecb_ews_get_items_sync (cbews, modified_occurrences, "IdOnly", modified_add_props, out_components, cancellable, error); - - e_ews_additional_props_free (modified_add_props); - - if (!success) - goto exit; - } - } - - for (link = items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - ECalComponent *comp; - GError *local_error = NULL; - - if (!item || e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - comp = ecb_ews_item_to_component_sync (cbews, item, cancellable, &local_error); - if (!comp) { - if (!local_error) - continue; - - g_propagate_error (error, local_error); - success = FALSE; - break; - } - - ecb_ews_store_original_comp (comp); - - *out_components = g_slist_prepend (*out_components, comp); - } - - exit: - g_slist_free_full (items, g_object_unref); - - return success; -} - -static gboolean -ecb_ews_fetch_items_sync (ECalBackendEws *cbews, - const GSList *items, /* EEwsItem * */ - GSList **out_components, /* ECalComponent * */ - GCancellable *cancellable, - GError **error) -{ - GSList *event_ids = NULL, *task_memo_ids = NULL, *link; - gboolean success = TRUE; - - for (link = (GSList *) items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (type == E_EWS_ITEM_TYPE_EVENT) - event_ids = g_slist_prepend (event_ids, g_strdup (id->id)); - else if (type == E_EWS_ITEM_TYPE_TASK || type == E_EWS_ITEM_TYPE_MEMO) { - task_memo_ids = g_slist_prepend (task_memo_ids, g_strdup (id->id)); - } - } - - if (event_ids) { - EEwsAdditionalProps *add_props; - - add_props = e_ews_additional_props_new (); - if (e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) { - EEwsExtendedFieldURI *ext_uri; - - add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2010); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSStartTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - add_props->extended_furis = g_slist_append (add_props->extended_furis, ext_uri); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSEndTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - add_props->extended_furis = g_slist_append (add_props->extended_furis, ext_uri); - } else { - add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2007); - } - - success = ecb_ews_get_items_sync (cbews, event_ids, "IdOnly", add_props, out_components, cancellable, error); - - e_ews_additional_props_free (add_props); - } - - if (task_memo_ids && success) - success = ecb_ews_get_items_sync (cbews, task_memo_ids, "AllProperties", NULL, out_components, cancellable, error); - - g_slist_free_full (event_ids, g_free); - g_slist_free_full (task_memo_ids, g_free); - - return success; -} - -static gboolean -ecb_ews_freebusy_ecomp_changed (ECalComponent *ecomp, - icalcomponent *vevent) -{ - icalcomponent *icomp; - gboolean changed = FALSE; - - g_return_val_if_fail (vevent != NULL, FALSE); - - if (!ecomp) - return TRUE; - - icomp = e_cal_component_get_icalcomponent (ecomp); - if (!icomp) - return TRUE; - - if (!changed) - changed = g_strcmp0 (icalcomponent_get_summary (icomp), icalcomponent_get_summary (vevent)) != 0; - if (!changed) - changed = g_strcmp0 (icalcomponent_get_location (icomp), icalcomponent_get_location (vevent)) != 0; - if (!changed) - changed = icaltime_compare (icalcomponent_get_dtstart (icomp), icalcomponent_get_dtstart (vevent)) != 0; - if (!changed) - changed = icaltime_compare (icalcomponent_get_dtend (icomp), icalcomponent_get_dtend (vevent)) != 0; - - return changed; -} - -static GSList * /* the possibly modified 'in_items' */ -ecb_ews_verify_changes (ECalCache *cal_cache, - icalcomponent_kind kind, - GSList *in_items, /* EEwsItem * */ - GCancellable *cancellable) -{ - GSList *items = NULL, *link; - - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), in_items); - - for (link = in_items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (!g_cancellable_is_cancelled (cancellable) && ( - (type == E_EWS_ITEM_TYPE_EVENT && kind == ICAL_VEVENT_COMPONENT) || - (type == E_EWS_ITEM_TYPE_MEMO && kind == ICAL_VJOURNAL_COMPONENT) || - (type == E_EWS_ITEM_TYPE_TASK && kind == ICAL_VTODO_COMPONENT) )) { - ECalComponent *existing = NULL; - - if (e_cal_cache_get_component (cal_cache, id->id, NULL, &existing, cancellable, NULL) && - existing && g_strcmp0 (e_cal_util_get_x_property (e_cal_component_get_icalcomponent (existing), - "X-EVOLUTION-CHANGEKEY"), id->change_key) == 0) { - g_object_unref (item); - } else { - items = g_slist_prepend (items, item); - } - - g_clear_object (&existing); - } else if (type == E_EWS_ITEM_TYPE_EVENT || - type == E_EWS_ITEM_TYPE_MEMO || - type == E_EWS_ITEM_TYPE_TASK) { - g_object_unref (item); - } else { - items = g_slist_prepend (items, item); - } - } - - g_slist_free (in_items); - - return items; -} - -static GSList * /* ECalMetaBackendInfo */ -ecb_ews_components_to_infos (ECalMetaBackend *meta_backend, - const GSList *components, /* ECalComponent * */ - icalcomponent_kind kind) -{ - GSList *nfos = NULL, *link; - GHashTable *sorted_by_uids; /* gchar * ~> GSList { ECalComponent * } */ - GHashTableIter iter; - gpointer key, value; - - sorted_by_uids = g_hash_table_new (g_str_hash, g_str_equal); - - for (link = (GSList *) components; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - icalcomponent *icomp; - const gchar *uid; - GSList *instances; - - if (!comp) - continue; - - icomp = e_cal_component_get_icalcomponent (comp); - uid = icalcomponent_get_uid (icomp); - - if (!uid) - continue; - - ecb_ews_store_original_comp (comp); - - instances = g_hash_table_lookup (sorted_by_uids, uid); - g_hash_table_insert (sorted_by_uids, (gpointer) uid, g_slist_prepend (instances, comp)); - } - - g_hash_table_iter_init (&iter, sorted_by_uids); - while (g_hash_table_iter_next (&iter, &key, &value)) { - const gchar *uid = key; - GSList *instances = value, *link; - icalcomponent *icomp, *merged; - ECalComponent *comp; - ECalMetaBackendInfo *nfo; - const gchar *revision, *itemid; - - if (!uid || !instances) { - g_slist_free (instances); - continue; - } - - /* Try to find master object, to have seves marter's itemid in ECalMetaBackendInfo::extra, - thus the re-load of the event is done for the whole series and not for a detached instance */ - comp = NULL; - for (link = instances; link && !comp; link = g_slist_next (link)) { - comp = link->data; - - if (!comp) - continue; - - if (e_cal_component_is_instance (comp)) - comp = NULL; - } - - if (!comp) - comp = instances->data; - - if (!comp) { - g_slist_free (instances); - continue; - } - - icomp = e_cal_component_get_icalcomponent (comp); - itemid = e_cal_util_get_x_property (icomp, "X-EVOLUTION-ITEMID"); - revision = e_cal_util_get_x_property (icomp, "X-EVOLUTION-CHANGEKEY"); - merged = e_cal_meta_backend_merge_instances (meta_backend, instances, FALSE); - - if (!merged) { - g_warn_if_fail (merged != NULL); - g_slist_free (instances); - continue; - } - - nfo = e_cal_meta_backend_info_new (uid, revision, NULL, itemid); - nfo->object = icalcomponent_as_ical_string_r (merged); - - nfos = g_slist_prepend (nfos, nfo); - - icalcomponent_free (merged); - g_slist_free (instances); - } - - g_hash_table_destroy (sorted_by_uids); - - return nfos; -} - -static void -ecb_ews_extract_item_id (ECalComponent *comp, - gchar **out_id, - gchar **out_change_key) -{ - icalcomponent *icalcomp; - - g_return_if_fail (E_IS_CAL_COMPONENT (comp)); - - icalcomp = e_cal_component_get_icalcomponent (comp); - g_return_if_fail (icalcomp != NULL); - - if (out_id) - *out_id = e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-ITEMID"); - if (out_change_key) - *out_change_key = e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY"); -} - -static gboolean -ecb_ews_is_organizer (ECalBackendEws *cbews, - ECalComponent *comp) -{ - ECalComponentOrganizer organizer; - gboolean is_organizer = FALSE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE); - - if (!e_cal_component_has_organizer (comp)) - return FALSE; - - organizer.value = NULL; - - e_cal_component_get_organizer (comp, &organizer); - if (organizer.value) { - CamelEwsSettings *ews_settings; - const gchar *email = organizer.value; - gchar *user_email; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - user_email = camel_ews_settings_dup_email (ews_settings); - - if (!g_ascii_strncasecmp (email, "mailto:", 7)) - email += 7; - - is_organizer = user_email && g_ascii_strcasecmp (email, user_email) == 0; - - g_free (user_email); - } - - return is_organizer; -} - -static gboolean -ecb_ews_connect_sync (ECalMetaBackend *meta_backend, - const ENamedParameters *credentials, - ESourceAuthenticationResult *out_auth_result, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - CamelEwsSettings *ews_settings; - gchar *hosturl; - gboolean success = FALSE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_auth_result != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->cnc) { - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - *out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED; - - return TRUE; - } - - ews_settings = ecb_ews_get_collection_settings (cbews); - hosturl = camel_ews_settings_dup_hosturl (ews_settings); - - cbews->priv->cnc = e_ews_connection_new_for_backend (E_BACKEND (cbews), e_cal_backend_get_registry (E_CAL_BACKEND (cbews)), hosturl, ews_settings); - - e_binding_bind_property ( - cbews, "proxy-resolver", - cbews->priv->cnc, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - *out_auth_result = e_ews_connection_try_credentials_sync (cbews->priv->cnc, credentials, cancellable, error); - - if (*out_auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) { - ESource *source = e_backend_get_source (E_BACKEND (cbews)); - ESourceEwsFolder *ews_folder; - - ews_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER); - - g_free (cbews->priv->folder_id); - cbews->priv->folder_id = e_source_ews_folder_dup_id (ews_folder); - cbews->priv->is_freebusy_calendar = cbews->priv->folder_id && g_str_has_prefix (cbews->priv->folder_id, "freebusy-calendar::"); - - g_signal_connect_swapped (cbews->priv->cnc, "server-notification", - G_CALLBACK (ecb_ews_server_notification_cb), cbews); - - if (!cbews->priv->is_freebusy_calendar && - camel_ews_settings_get_listen_notifications (ews_settings) && - e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010_SP1)) { - GSList *folders = NULL; - - folders = g_slist_prepend (folders, cbews->priv->folder_id); - - e_ews_connection_enable_notifications_sync (cbews->priv->cnc, - folders, &cbews->priv->subscription_key); - - g_slist_free (folders); - } - - e_cal_backend_set_writable (E_CAL_BACKEND (cbews), !cbews->priv->is_freebusy_calendar); - success = TRUE; - } else { - ecb_ews_convert_error_to_edc_error (error); - g_clear_object (&cbews->priv->cnc); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - g_free (hosturl); - - return success; -} - -static gboolean -ecb_ews_disconnect_sync (ECalMetaBackend *meta_backend, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - ecb_ews_unset_connection (cbews); - - return TRUE; -} - -static gboolean -ecb_ews_get_changes_sync (ECalMetaBackend *meta_backend, - const gchar *last_sync_tag, - gboolean is_repeat, - gchar **out_new_sync_tag, - gboolean *out_repeat, - GSList **out_created_objects, - GSList **out_modified_objects, - GSList **out_removed_objects, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - gboolean success = TRUE; - GError *local_error = NULL; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_new_sync_tag != NULL, FALSE); - g_return_val_if_fail (out_repeat != NULL, FALSE); - g_return_val_if_fail (out_created_objects != NULL, FALSE); - g_return_val_if_fail (out_modified_objects != NULL, FALSE); - g_return_val_if_fail (out_removed_objects != NULL, FALSE); - - *out_created_objects = NULL; - *out_modified_objects = NULL; - *out_removed_objects = NULL; - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->is_freebusy_calendar) { - ESourceEwsFolder *ews_folder; - EEWSFreeBusyData fbdata; - GSList *free_busy = NULL, *link; - gboolean success; - time_t today; - - ews_folder = e_source_get_extension (e_backend_get_source (E_BACKEND (cbews)), E_SOURCE_EXTENSION_EWS_FOLDER); - - today = time_day_begin (time (NULL)); - - fbdata.period_start = time_add_week (today, -e_source_ews_folder_get_freebusy_weeks_before (ews_folder)); - fbdata.period_end = time_day_end (time_add_week (today, e_source_ews_folder_get_freebusy_weeks_after (ews_folder))); - fbdata.user_mails = g_slist_prepend (NULL, e_source_ews_folder_dup_foreign_mail (ews_folder)); - - success = e_ews_connection_get_free_busy_sync (cbews->priv->cnc, G_PRIORITY_DEFAULT, - e_ews_cal_utils_prepare_free_busy_request, &fbdata, - &free_busy, cancellable, &local_error); - - if (success) { - icaltimezone *utc_zone = icaltimezone_get_utc_timezone (); - GSList *comps = NULL; - GHashTable *known; - GHashTableIter iter; - gpointer key; - - known = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - - if (e_cal_cache_search_components (cal_cache, NULL, &comps, cancellable, NULL)) { - for (link = comps; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - icalcomponent *icomp; - const gchar *uid; - - if (!comp) - continue; - - icomp = e_cal_component_get_icalcomponent (comp); - if (!icomp) - continue; - - uid = icalcomponent_get_uid (icomp); - - if (uid && *uid) - g_hash_table_insert (known, g_strdup (uid), g_object_ref (comp)); - } - - g_slist_free_full (comps, g_object_unref); - } - - for (link = free_busy; link; link = g_slist_next (link)) { - icalcomponent *fbcomp = link->data; - icalproperty *fbprop; - icalparameter *param; - struct icalperiodtype fb; - icalparameter_fbtype fbtype; - - if (!fbcomp || icalcomponent_isa (fbcomp) != ICAL_VFREEBUSY_COMPONENT) - continue; - - for (fbprop = icalcomponent_get_first_property (fbcomp, ICAL_FREEBUSY_PROPERTY); - fbprop; - fbprop = icalcomponent_get_next_property (fbcomp, ICAL_FREEBUSY_PROPERTY)) { - ECalComponent *ecomp; - icalcomponent *vevent; - const gchar *id, *summary, *location; - - param = icalproperty_get_first_parameter (fbprop, ICAL_FBTYPE_PARAMETER); - if (!param) - continue; - - fbtype = icalparameter_get_fbtype (param); - - if (fbtype != ICAL_FBTYPE_FREE && - fbtype != ICAL_FBTYPE_BUSY && - fbtype != ICAL_FBTYPE_BUSYUNAVAILABLE && - fbtype != ICAL_FBTYPE_BUSYTENTATIVE) - continue; - - fb = icalproperty_get_freebusy (fbprop); - id = icalproperty_get_parameter_as_string (fbprop, "X-EWS-ID"); - summary = icalproperty_get_parameter_as_string (fbprop, "X-SUMMARY"); - location = icalproperty_get_parameter_as_string (fbprop, "X-LOCATION"); - - vevent = icalcomponent_new_vevent (); - - if (id && *id) { - icalcomponent_set_uid (vevent, id); - } else { - gchar *uid; - - uid = g_strdup_printf ("%s-%s-%d", - icaltime_as_ical_string (fb.start), - icaltime_as_ical_string (fb.end), - (gint) fbtype); - - icalcomponent_set_uid (vevent, uid); - - g_free (uid); - } - - fb.start.zone = utc_zone; - fb.end.zone = utc_zone; - - icalcomponent_set_dtstart (vevent, fb.start); - icalcomponent_set_dtend (vevent, fb.end); - - icalcomponent_add_property (vevent, icalproperty_new_created (icaltime_current_time_with_zone (utc_zone))); - - if (fbtype == ICAL_FBTYPE_FREE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Free")); - icalcomponent_add_property (vevent, icalproperty_new_transp (ICAL_TRANSP_TRANSPARENT)); - } else if (fbtype == ICAL_FBTYPE_BUSY) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Busy")); - } else if (fbtype == ICAL_FBTYPE_BUSYUNAVAILABLE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Out of Office")); - } else if (fbtype == ICAL_FBTYPE_BUSYTENTATIVE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Tentative")); - } - - if (summary && *summary) - icalcomponent_set_summary (vevent, summary); - - if (location && *location) - icalcomponent_set_location (vevent, location); - - ecomp = g_hash_table_lookup (known, icalcomponent_get_uid (vevent)); - if (ecomp) { - g_object_ref (ecomp); - - /* This dereferences the ecomp, thus the ref() call above to keep it alive */ - g_hash_table_remove (known, icalcomponent_get_uid (vevent)); - - if (ecb_ews_freebusy_ecomp_changed (ecomp, vevent)) { - ECalMetaBackendInfo *nfo; - gchar *revision = e_util_generate_uid (); - - e_cal_util_set_x_property (vevent, "X-EVOLUTION-CHANGEKEY", revision); - - nfo = e_cal_meta_backend_info_new (icalcomponent_get_uid (vevent), NULL, NULL, NULL); - nfo->revision = revision; - nfo->object = icalcomponent_as_ical_string_r (vevent); - - *out_created_objects = g_slist_prepend (*out_created_objects, nfo); - } else { - icalcomponent_free (vevent); - } - - g_clear_object (&ecomp); - } else { - ECalMetaBackendInfo *nfo; - gchar *revision = e_util_generate_uid (); - - e_cal_util_set_x_property (vevent, "X-EVOLUTION-CHANGEKEY", revision); - - nfo = e_cal_meta_backend_info_new (icalcomponent_get_uid (vevent), NULL, NULL, NULL); - nfo->revision = revision; - nfo->object = icalcomponent_as_ical_string_r (vevent); - - *out_modified_objects = g_slist_prepend (*out_modified_objects, nfo); - } - } - } - - g_hash_table_iter_init (&iter, known); - while (g_hash_table_iter_next (&iter, &key, NULL)) { - const gchar *uid = key; - - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_cal_meta_backend_info_new (uid, NULL, NULL, NULL)); - } - - g_hash_table_destroy (known); - } else if (g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NOFREEBUSYACCESS)) { - e_cal_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL); - - e_cal_backend_notify_error (E_CAL_BACKEND (cbews), local_error->message); - g_clear_error (&local_error); - } else { - g_propagate_error (error, local_error); - } - - g_slist_free_full (free_busy, (GDestroyNotify) icalcomponent_free); - g_slist_free_full (fbdata.user_mails, g_free); - } else { - GSList *items_created = NULL, *items_modified = NULL, *items_deleted = NULL, *link; - EEwsAdditionalProps *add_props; - gboolean includes_last_item = TRUE; - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("item:ItemClass"); - - success = e_ews_connection_sync_folder_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - last_sync_tag, cbews->priv->folder_id, "IdOnly", add_props, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - - if (!success && - g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) { - g_clear_error (&local_error); - - e_cal_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL); - - success = e_ews_connection_sync_folder_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - NULL, cbews->priv->folder_id, "IdOnly", add_props, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - } - - e_ews_additional_props_free (add_props); - - if (success) { - GSList *components_created = NULL, *components_modified = NULL; - icalcomponent_kind kind; - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - - /* The sync state doesn't cover changes made by save_component_sync(), - thus verify the changes, instead of re-donwloading the component again */ - items_created = ecb_ews_verify_changes (cal_cache, kind, items_created, cancellable); - items_modified = ecb_ews_verify_changes (cal_cache, kind, items_modified, cancellable); - - if (items_created) { - success = ecb_ews_fetch_items_sync (cbews, items_created, &components_created, cancellable, error); - if (success) - *out_created_objects = ecb_ews_components_to_infos (meta_backend, components_created, kind); - } - - if (items_modified) { - success = ecb_ews_fetch_items_sync (cbews, items_modified, &components_modified, cancellable, error); - if (success) - *out_modified_objects = ecb_ews_components_to_infos (meta_backend, components_modified, kind); - } - - for (link = items_deleted; link; link = g_slist_next (link)) { - const gchar *item_id = link->data; - GSList *ids = NULL, *ilink; - - if (!e_cal_cache_get_ids_with_extra (cal_cache, item_id, &ids, cancellable, NULL)) - continue; - - for (ilink = ids; ilink; ilink = g_slist_next (ilink)) { - ECalComponentId *id = ilink->data; - - /* Use the master object */ - if (id && id->uid && *id->uid && (!id->rid || !*id->rid)) { - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_cal_meta_backend_info_new (id->uid, NULL, NULL, NULL)); - break; - } - } - - g_slist_free_full (ids, (GDestroyNotify) e_cal_component_free_id); - } - - g_slist_free_full (components_created, g_object_unref); - g_slist_free_full (components_modified, g_object_unref); - - *out_repeat = !includes_last_item; - } else if (local_error) { - g_propagate_error (error, local_error); - } - - g_slist_free_full (items_created, g_object_unref); - g_slist_free_full (items_modified, g_object_unref); - g_slist_free_full (items_deleted, g_free); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_clear_object (&cal_cache); - - return success; -} - -static gboolean -ecb_ews_load_component_sync (ECalMetaBackend *meta_backend, - const gchar *uid, - const gchar *extra, - icalcomponent **out_component, - gchar **out_extra, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - GSList *ids, *items = NULL, *components = NULL; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (uid != NULL, FALSE); - g_return_val_if_fail (out_component != NULL, FALSE); - g_return_val_if_fail (out_extra != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - ids = g_slist_prepend (NULL, (gpointer) (extra && *extra ? extra : uid)); - - success = e_ews_connection_get_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, ids, "IdOnly", - NULL, FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &items, NULL, NULL, cancellable, error); - - g_slist_free (ids); - - if (success && items) { - success = ecb_ews_fetch_items_sync (cbews, items, &components, cancellable, error); - - if (components) { - const EwsId *ews_id = e_ews_item_get_id (items->data); - - if (ews_id) - *out_extra = g_strdup (ews_id->id); - - if (components->next) { - GSList *link; - - *out_component = icalcomponent_new_vcalendar (); - - for (link = components; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - if (!comp) - continue; - - icalcomponent_add_component (*out_component, - icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp))); - } - } else { - *out_component = icalcomponent_new_clone (e_cal_component_get_icalcomponent (components->data)); - } - } else { - success = FALSE; - } - } - - if (!components && e_cal_meta_backend_refresh_sync (meta_backend, cancellable, NULL)) { - ECalCache *cal_cache; - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - if (cal_cache) { - success = e_cal_cache_get_components_by_uid (cal_cache, uid, &components, cancellable, NULL); - if (success) { - *out_component = e_cal_meta_backend_merge_instances (meta_backend, components, FALSE); - - if (!e_cal_cache_get_component_extra (cal_cache, uid, NULL, out_extra, cancellable, NULL)) - *out_extra = NULL; - - g_clear_error (error); - } - g_object_unref (cal_cache); - } - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_slist_free_full (components, g_object_unref); - g_slist_free_full (items, g_object_unref); - - return success; -} - -/* Very simple and naive component comparator, to avoid - unnecessary uploads and changes on the server. */ -static gboolean -ecb_ews_components_equal (ECalComponent *comp1, - ECalComponent *comp2) -{ - icalcomponent *icomp1, *icomp2; - icalproperty *prop1; - GHashTable *processed_props; - gboolean equal = TRUE; - - if (!comp1 && !comp2) - return TRUE; - else if (!comp1 || !comp2) - return FALSE; - - icomp1 = e_cal_component_get_icalcomponent (comp1); - icomp2 = e_cal_component_get_icalcomponent (comp2); - - if (!icomp1 || !icomp2) - return FALSE; - - if (g_strcmp0 (icalcomponent_get_uid (icomp1), icalcomponent_get_uid (icomp2)) != 0) - return FALSE; - - if (icalcomponent_count_properties (icomp1, ICAL_ANY_PROPERTY) != - icalcomponent_count_properties (icomp2, ICAL_ANY_PROPERTY)) - return FALSE; - - processed_props = g_hash_table_new (g_direct_hash, g_direct_equal); - - for (prop1 = icalcomponent_get_first_property (icomp1, ICAL_ANY_PROPERTY); - prop1 && equal; - prop1 = icalcomponent_get_next_property (icomp1, ICAL_ANY_PROPERTY)) { - icalproperty_kind kind = icalproperty_isa (prop1); - icalproperty *prop2; - - for (prop2 = icalcomponent_get_first_property (icomp2, kind); - prop2; - prop2 = icalcomponent_get_next_property (icomp2, kind)) { - gchar *str1, *str2; - gboolean same; - - if (g_hash_table_contains (processed_props, prop2)) - continue; - - if (icalproperty_count_parameters (prop1) != icalproperty_count_parameters (prop2)) - continue; - - str1 = icalproperty_as_ical_string_r (prop1); - str2 = icalproperty_as_ical_string_r (prop2); - - same = g_strcmp0 (str1, str2) == 0; - - g_free (str1); - g_free (str2); - - if (same) { - g_hash_table_insert (processed_props, prop2, NULL); - break; - } - } - - if (!prop2) - equal = FALSE; - } - - g_hash_table_destroy (processed_props); - - return equal; -} - -typedef struct _ChangeData { - ECalComponent *old_component; - ECalComponent *new_component; -} ChangeData; - -static void -change_data_free (gpointer ptr) -{ - ChangeData *cd = ptr; - - if (cd) { - g_clear_object (&cd->old_component); - g_clear_object (&cd->new_component); - g_free (cd); - } -} - -static void -ecb_ews_filter_out_unchanged_instances (const GSList *to_save_instances, - const GSList *existing_instances, - GSList **out_changed_instances, /* ChangeData * */ - GSList **out_removed_instances) /* ECalComponent * */ -{ - GSList *link = NULL; - GHashTable *existing_hash; - GHashTableIter iter; - gpointer value; - - g_return_if_fail (to_save_instances != NULL); - g_return_if_fail (existing_instances != NULL); - g_return_if_fail (out_changed_instances != NULL); - g_return_if_fail (out_removed_instances != NULL); - - *out_changed_instances = NULL; - *out_removed_instances = NULL; - - existing_hash = g_hash_table_new_full ((GHashFunc)e_cal_component_id_hash, (GEqualFunc) e_cal_component_id_equal, - (GDestroyNotify) e_cal_component_free_id, NULL); - - for (link = (GSList *) existing_instances; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id; - - id = e_cal_component_get_id (comp); - if (id) - g_hash_table_insert (existing_hash, id, comp); - } - - for (link = (GSList *) to_save_instances; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id = NULL; - - id = e_cal_component_get_id (comp); - if (id) { - ECalComponent *old_comp; - - old_comp = g_hash_table_lookup (existing_hash, id); - - if (!ecb_ews_components_equal (comp, old_comp)) { - ChangeData *cd; - - cd = g_new0 (ChangeData, 1); - cd->old_component = old_comp ? g_object_ref (old_comp) : NULL; - cd->new_component = g_object_ref (comp); - - *out_changed_instances = g_slist_prepend (*out_changed_instances, cd); - } - - g_hash_table_remove (existing_hash, id); - e_cal_component_free_id (id); - } - } - - g_hash_table_iter_init (&iter, existing_hash); - while (g_hash_table_iter_next (&iter, NULL, &value)) { - *out_removed_instances = g_slist_prepend (*out_removed_instances, g_object_ref (value)); - } - - g_hash_table_destroy (existing_hash); -} - -static gboolean -ecb_ews_extract_attachments (icalcomponent *icalcomp, - GSList **out_attachments) /* EEwsAttachmentInfo * */ -{ - icalproperty *prop; - GSList *props = NULL, *link; - - g_return_val_if_fail (icalcomp != NULL, FALSE); - g_return_val_if_fail (out_attachments != NULL, FALSE); - - *out_attachments = NULL; - - for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY); - prop; - prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTACH_PROPERTY)) { - props = g_slist_prepend (props, prop); - } - - for (link = props; link; link = g_slist_next (link)) { - EEwsAttachmentInfo *info; - icalattach *attach; - icalparameter *param; - const gchar *stored_filename; - - prop = link->data; - param = icalproperty_get_first_parameter (prop, ICAL_FILENAME_PARAMETER); - stored_filename = param ? icalparameter_get_filename (param) : NULL; - - attach = icalproperty_get_attach (prop); - if (icalattach_get_is_url (attach)) { - const gchar *uri; - - uri = icalattach_get_url (attach); - - if (!uri || !*uri) - continue; - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_URI); - - e_ews_attachment_info_set_uri (info, uri); - if (stored_filename && *stored_filename) { - e_ews_attachment_info_set_prefer_filename (info, stored_filename); - } else { - gchar *uri_filename; - - uri_filename = g_filename_from_uri (uri, NULL, NULL); - if (uri_filename && *uri_filename) { - gchar *basename; - - basename = g_path_get_basename (uri_filename); - if (basename && *basename && basename[0] != '.' && basename[0] != G_DIR_SEPARATOR) { - const gchar *uid; - - uid = icalcomponent_get_uid (icalcomp); - - if (uid && g_str_has_prefix (basename, uid) && basename[strlen (uid)] == '-') { - e_ews_attachment_info_set_prefer_filename (info, basename + strlen (uid) + 1); - } - } - - g_free (basename); - } - - g_free (uri_filename); - } - } else { - gsize len = -1; - guchar *decoded = NULL; - const gchar *content; - - content = (const gchar *) icalattach_get_data (attach); - decoded = g_base64_decode (content, &len); - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_INLINED); - e_ews_attachment_info_set_inlined_data (info, decoded, len); - - if (stored_filename && *stored_filename) - e_ews_attachment_info_set_prefer_filename (info, stored_filename); - - g_free (decoded); - } - - e_ews_attachment_info_set_id (info, icalproperty_get_parameter_as_string (prop, "X-EWS-ATTACHMENTID")); - *out_attachments = g_slist_prepend (*out_attachments, info); - } - - g_slist_free (props); - - return *out_attachments != NULL; -} - -static icaltimezone * -ecb_ews_get_timezone_from_ical_component (ECalBackendEws *cbews, - icalcomponent *icalcomp) -{ - ETimezoneCache *timezone_cache; - icalproperty *prop = NULL; - const gchar *tzid = NULL; - - timezone_cache = E_TIMEZONE_CACHE (cbews); - - prop = icalcomponent_get_first_property (icalcomp, ICAL_DTSTART_PROPERTY); - if (prop != NULL) { - icalparameter *param = NULL; - - param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER); - if (param) { - tzid = icalparameter_get_tzid (param); - } else { - struct icaltimetype dtstart; - - dtstart = icalproperty_get_dtstart (prop); - if (icaltime_is_utc (dtstart)) - tzid = "UTC"; - } - } - - if (tzid) - return e_timezone_cache_get_timezone (timezone_cache, tzid); - - return NULL; -} - -static gboolean -ecb_ews_remove_item_sync (ECalBackendEws *cbews, - ECalCache *cal_cache, - GHashTable *removed_indexes, - const gchar *uid, - const gchar *rid, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *comp = NULL, *parent = NULL; - EwsId item_id = { 0 }; - gint index = 0; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE); - g_return_val_if_fail (uid != NULL, FALSE); - - if (rid && !*rid) - rid = NULL; - - if (!e_cal_cache_get_component (cal_cache, uid, rid, &comp, cancellable, error) || - (rid && !e_cal_cache_get_component (cal_cache, uid, NULL, &parent, cancellable, error))) { - if (!parent && !comp) { - g_propagate_error (error, EDC_ERROR (ObjectNotFound)); - return FALSE; - } - } - - ecb_ews_extract_item_id (comp ? comp : parent, &item_id.id, &item_id.change_key); - - if (!item_id.id) { - g_propagate_error (error, EDC_ERROR_EX (OtherError, "Cannot determine EWS ItemId")); - success = FALSE; - } else { - if (parent) { - index = e_cal_backend_ews_rid_to_index ( - ecb_ews_get_timezone_from_ical_component (cbews, - e_cal_component_get_icalcomponent (parent)), - rid, - e_cal_component_get_icalcomponent (parent), - error); - if (index == 0) - success = comp != NULL; - } - - if (index && removed_indexes && g_hash_table_contains (removed_indexes, GINT_TO_POINTER (index))) { - /* Do nothing, it's already deleted from the server */ - } else { - if (removed_indexes && index) - g_hash_table_insert (removed_indexes, GINT_TO_POINTER (index), NULL); - - success = success && e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, index, EWS_HARD_DELETE, - ecb_ews_is_organizer (cbews, comp) ? EWS_SEND_TO_ALL_AND_SAVE_COPY : EWS_SEND_TO_NONE, - EWS_ALL_OCCURRENCES, cancellable, error); - } - } - - g_free (item_id.id); - g_free (item_id.change_key); - - g_clear_object (&comp); - g_clear_object (&parent); - - return success; -} - -static void -ecb_ews_get_attach_differences (ECalComponent *oldcomp, - ECalComponent *newcomp, - GSList **out_removed_attachment_ids, /* gchar * */ - GSList **out_added_attachments) /* EEwsAttachmentInfo * */ -{ - GSList *old_attachments = NULL, *new_attachments = NULL, *link; - - g_return_if_fail (out_removed_attachment_ids != NULL); - g_return_if_fail (out_added_attachments != NULL); - - *out_removed_attachment_ids = NULL; - *out_added_attachments = NULL; - - if (!ecb_ews_extract_attachments (e_cal_component_get_icalcomponent (oldcomp), &old_attachments)) - old_attachments = NULL; - - if (!ecb_ews_extract_attachments (e_cal_component_get_icalcomponent (newcomp), &new_attachments)) - new_attachments = NULL; - - for (link = old_attachments; link; link = g_slist_next (link)) { - EEwsAttachmentInfo *old_nfo = link->data; - GSList *nlink; - - if (!old_nfo) - continue; - - for (nlink = new_attachments; nlink; nlink = g_slist_next (nlink)) { - EEwsAttachmentInfo *new_nfo = nlink->data; - gboolean same = FALSE; - - if (!new_nfo || - e_ews_attachment_info_get_type (old_nfo) != e_ews_attachment_info_get_type (new_nfo)) - continue; - - if (e_ews_attachment_info_get_type (old_nfo) == E_EWS_ATTACHMENT_INFO_TYPE_INLINED) { - const gchar *old_data, *new_data; - gsize old_len = -1, new_len = -1; - - old_data = e_ews_attachment_info_get_inlined_data (old_nfo, &old_len); - new_data = e_ews_attachment_info_get_inlined_data (new_nfo, &new_len); - - same = old_len == new_len && (old_len == 0 || - (old_len > 0 && old_data && new_data && memcmp (old_data, new_data, old_len) == 0)); - } else if (e_ews_attachment_info_get_type (old_nfo) == E_EWS_ATTACHMENT_INFO_TYPE_URI) { - same = g_strcmp0 (e_ews_attachment_info_get_uri (old_nfo), e_ews_attachment_info_get_uri (new_nfo)) == 0; - } - - if (same) { - new_attachments = g_slist_remove (new_attachments, new_nfo); - e_ews_attachment_info_free (new_nfo); - break; - } - } - - if (!nlink) { - /* Did not find in the new_attachments, thus it's removed */ - g_warn_if_fail (e_ews_attachment_info_get_id (old_nfo) != NULL); - *out_removed_attachment_ids = g_slist_prepend (*out_removed_attachment_ids, - g_strdup (e_ews_attachment_info_get_id (old_nfo))); - } - } - - *out_added_attachments = new_attachments; - - g_slist_free_full (old_attachments, (GDestroyNotify) e_ews_attachment_info_free); -} - -struct TzidCbData { - icalcomponent *comp; - ECalBackendEws *cbews; -}; - -static void -tzid_cb (icalparameter *param, - gpointer data) -{ - struct TzidCbData *cbd = data; - const gchar *tzid; - icaltimezone *zone; - icalcomponent *new_comp; - - tzid = icalparameter_get_tzid (param); - if (!tzid) - return; - - zone = e_timezone_cache_get_timezone (E_TIMEZONE_CACHE (cbd->cbews), tzid); - if (!zone) - return; - - new_comp = icaltimezone_get_component (zone); - if (!new_comp) - return; - - icalcomponent_add_component (cbd->comp, icalcomponent_new_clone (new_comp)); -} - -static void -ecb_ews_pick_all_tzids_out (ECalBackendEws *cbews, - icalcomponent *icalcomp) -{ - - /* pick all the tzids out of the component and resolve - * them using the vtimezones in the current calendar */ - struct TzidCbData cbd; - - cbd.cbews = cbews; - cbd.comp = icalcomp; - - icalcomponent_foreach_tzid (icalcomp, tzid_cb, &cbd); -} - -static gboolean -ecb_ews_modify_item_sync (ECalBackendEws *cbews, - GHashTable *removed_indexes, - icalcomponent *old_icalcomp, - icalcomponent *new_icalcomp, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *comp = NULL, *oldcomp = NULL; - icalcomponent *icalcomp; - gchar *itemid = NULL, *changekey = NULL; - GSList *added_attachments = NULL, *removed_attachment_ids = NULL; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (new_icalcomp != NULL, FALSE); - - icalcomp = icalcomponent_new_clone (new_icalcomp); - - ecb_ews_pick_all_tzids_out (cbews, icalcomp); - - comp = e_cal_component_new_from_icalcomponent (icalcomp); - if (!comp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - ecb_ews_extract_item_id (comp, &itemid, &changekey); - if (!itemid) { - g_propagate_error (error, EDC_ERROR_EX (OtherError, "Cannot determine EWS ItemId")); - g_object_unref (comp); - return FALSE; - } - - if (old_icalcomp) { - oldcomp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (old_icalcomp)); - } else { - oldcomp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (new_icalcomp)); - } - - ecb_ews_pick_all_tzids_out (cbews, e_cal_component_get_icalcomponent (oldcomp)); - - /* In case we have updated attachments we have to run update attachments - * before update items so attendees will receive mails with already updated attachments */ - - ecb_ews_get_attach_differences (oldcomp, comp, &removed_attachment_ids, &added_attachments); - - /* preform sync delete attachemnt operation*/ - if (removed_attachment_ids) { - g_free (changekey); - changekey = NULL; - - success = e_ews_connection_delete_attachments_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - removed_attachment_ids, &changekey, cancellable, error); - - g_slist_free_full (removed_attachment_ids, g_free); - } - - /* in case we have a new attachments add them before update */ - if (added_attachments && success) { - EwsId item_id; - - item_id.id = itemid; - item_id.change_key = changekey; - - changekey = NULL; - - success = e_ews_connection_create_attachments_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - &item_id, added_attachments, - FALSE, &changekey, NULL, cancellable, error); - - g_free (item_id.change_key); - } - - if (success && old_icalcomp && - icalcomponent_get_first_property (new_icalcomp, ICAL_RRULE_PROPERTY) && - !icalcomponent_get_first_property (new_icalcomp, ICAL_RECURRENCEID_PROPERTY)) { - icalproperty *prop, *old_prop; - GSList *exceptions = NULL, *link; - EwsId item_id; - - item_id.id = itemid; - item_id.change_key = changekey; - - /* Excluded occurrences */ - for (prop = icalcomponent_get_first_property (new_icalcomp, ICAL_EXDATE_PROPERTY); - prop; - prop = icalcomponent_get_next_property (new_icalcomp, ICAL_EXDATE_PROPERTY)) { - const gchar *new_rid; - - new_rid = icalproperty_get_value_as_string (prop); - - for (old_prop = icalcomponent_get_first_property (old_icalcomp, ICAL_EXDATE_PROPERTY); - old_prop; - old_prop = icalcomponent_get_next_property (old_icalcomp, ICAL_EXDATE_PROPERTY)) { - if (g_strcmp0 (new_rid, icalproperty_get_value_as_string (old_prop)) == 0) - break; - } - - if (!old_prop) - exceptions = g_slist_prepend (exceptions, prop); - } - - exceptions = g_slist_reverse (exceptions); - - for (link = exceptions; link && success; link = g_slist_next (link)) { - guint index; - - prop = link->data; - - index = e_cal_backend_ews_rid_to_index ( - ecb_ews_get_timezone_from_ical_component (cbews, new_icalcomp), - icalproperty_get_value_as_string (prop), - new_icalcomp, - error); - - if (index == 0) { - success = FALSE; - } else if (!removed_indexes || !g_hash_table_contains (removed_indexes, GINT_TO_POINTER (index))) { - if (removed_indexes) - g_hash_table_insert (removed_indexes, GINT_TO_POINTER (index), NULL); - - success = e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, index, - EWS_HARD_DELETE, EWS_SEND_TO_NONE, EWS_ALL_OCCURRENCES, cancellable, error); - } - } - - g_slist_free (exceptions); - } - - if (success) { - EwsCalendarConvertData convert_data = { 0 }; - CamelEwsSettings *ews_settings; - const gchar *send_meeting_invitations; - const gchar *send_or_save; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.user_email = camel_ews_settings_dup_email (ews_settings); - convert_data.comp = comp; - convert_data.old_comp = oldcomp; - convert_data.item_id = itemid; - convert_data.change_key = changekey; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - if (e_cal_component_has_attendees (comp)) { - send_meeting_invitations = "SendToAllAndSaveCopy"; - send_or_save = "SendAndSaveCopy"; - } else { - /*In case of appointment we have to set SendMeetingInvites to SendToNone */ - send_meeting_invitations = "SendToNone"; - send_or_save = "SaveOnly"; - } - - success = e_ews_connection_update_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", send_or_save, send_meeting_invitations, cbews->priv->folder_id, - e_cal_backend_ews_convert_component_to_updatexml, &convert_data, - NULL, cancellable, error); - - g_free (convert_data.user_email); - } - - g_slist_free_full (added_attachments, (GDestroyNotify) e_ews_attachment_info_free); - g_clear_object (&oldcomp); - g_clear_object (&comp); - g_free (changekey); - g_free (itemid); - - return success; -} - -static gboolean -ecb_ews_save_component_sync (ECalMetaBackend *meta_backend, - gboolean overwrite_existing, - EConflictResolution conflict_resolution, - const GSList *instances, - const gchar *extra, - gchar **out_new_uid, - gchar **out_new_extra, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - ECalComponent *master = NULL; - EwsFolderId *fid; - GSList *link; - const gchar *uid = NULL; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - for (link = (GSList *) instances; link && !master; link = g_slist_next (link)) { - master = link->data; - - if (!master) - continue; - - if (e_cal_component_is_instance (master)) - master = NULL; - } - - if (!master) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - g_return_val_if_fail (cal_cache != NULL, FALSE); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - e_cal_component_get_uid (master, &uid); - fid = e_ews_folder_id_new (cbews->priv->folder_id, NULL, FALSE); - - if (overwrite_existing) { - GSList *existing = NULL, *changed_instances = NULL, *removed_instances = NULL; - - success = uid && e_cal_cache_get_components_by_uid (cal_cache, uid, &existing, cancellable, error) && existing; - - if (success) { - GSList *link; - - /* This is for offline changes, where the component in the cache - is already modified, while the original, the one on the server, - is different. Using the cached component in this case generates - empty UpdateItem request and nothing is saved. */ - for (link = existing; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - comp = ecb_ews_restore_original_comp (comp); - if (comp) { - g_object_unref (link->data); - link->data = comp; - } - } - } - - if (success) - ecb_ews_filter_out_unchanged_instances (instances, existing, &changed_instances, &removed_instances); - - if (success) { - GHashTable *removed_indexes; - - removed_indexes = g_hash_table_new (g_direct_hash, g_direct_equal); - - for (link = changed_instances; link && success; link = g_slist_next (link)) { - ChangeData *cd = link->data; - - if (!cd) - continue; - - success = ecb_ews_modify_item_sync (cbews, removed_indexes, - e_cal_component_get_icalcomponent (cd->old_component ? cd->old_component : master), - e_cal_component_get_icalcomponent (cd->new_component), - cancellable, error); - } - - for (link = removed_instances; link && success; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id = NULL; - - if (!comp) - continue; - - id = e_cal_component_get_id (comp); - - if (id) { - success = ecb_ews_remove_item_sync (cbews, cal_cache, removed_indexes, id->uid, id->rid, cancellable, error); - e_cal_component_free_id (id); - } - } - - g_hash_table_destroy (removed_indexes); - } - - if (success) - ecb_ews_extract_item_id (master, out_new_uid, NULL); - - g_slist_free_full (existing, g_object_unref); - g_slist_free_full (changed_instances, change_data_free); - g_slist_free_full (removed_instances, g_object_unref); - } else { - GHashTable *removed_indexes; - EwsCalendarConvertData convert_data = { 0 }; - EEwsItem *item = NULL; - const EwsId *ews_id = NULL; - const gchar *send_meeting_invitations; - icalcomponent *icalcomp; - icalproperty *prop; - GSList *items = NULL; - - icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (master)); - - e_ews_clean_icalcomponent (icalcomp); - - if (!e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) - ecb_ews_pick_all_tzids_out (cbews, icalcomp); - - /* - * In case we are creating a meeting with attendees and attachments. - * We have to preform 3 steps in order to allow attendees to receive attachments in their invite mails. - * 1. create meeting and do not send invites - * 2. create attachments - * 3. dummy update meeting and send invites to all - */ - if (e_cal_component_has_attendees (master)) { - if (e_cal_component_has_attachments (master)) - send_meeting_invitations = "SendToNone"; - else - send_meeting_invitations = "SendToAllAndSaveCopy"; - } else { - /* In case of appointment we have to set SendMeetingInvites to SendToNone */ - send_meeting_invitations = "SendToNone"; - } - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.icalcomp = icalcomp; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - success = e_ews_connection_create_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, "SaveOnly", send_meeting_invitations, - fid, e_cal_backend_ews_convert_calcomp_to_xml, &convert_data, - &items, cancellable, error); - - if (success && items) { - item = items->data; - if (item) { - g_object_ref (item); - - ews_id = e_ews_item_get_id (item); - } - } - - if (success && item && e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_EVENT) { - EEwsAdditionalProps *add_props; - GSList *items, *items_req = NULL; - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("calendar:UID"); - - items = g_slist_append (NULL, ews_id->id); - - /* get calender uid from server*/ - success = e_ews_connection_get_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - items, "IdOnly", add_props, FALSE, NULL, E_EWS_BODY_TYPE_TEXT, - &items_req, NULL, NULL, cancellable, error) && items_req != NULL; - - e_ews_additional_props_free (add_props); - - if (success) { - g_clear_object (&item); - - item = items_req->data; - ews_id = NULL; - - if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) { - g_propagate_error (error, g_error_copy (e_ews_item_get_error (item))); - item = NULL; - success = FALSE; - } else { - item = g_object_ref (item); - ews_id = e_ews_item_get_id (item); - } - } - - g_slist_free_full (items_req, g_object_unref); - g_slist_free (items); - } - - /* attachments */ - if (success && e_cal_component_has_attachments (master) > 0) { - GSList *info_attachments = NULL; - - g_warn_if_fail (ews_id != NULL); - - if (ews_id && ecb_ews_extract_attachments (icalcomp, &info_attachments)) { - GSList *ids = NULL; - - success = e_ews_connection_create_attachments_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - ews_id, info_attachments, FALSE, NULL, &ids, cancellable, error); - - g_slist_free_full (info_attachments, (GDestroyNotify) e_ews_attachment_info_free); - g_slist_free_full (ids, g_free); - } - } - - removed_indexes = g_hash_table_new (g_direct_hash, g_direct_equal); - - if (success && icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY)) { - GSList *exceptions = NULL; - - /* Excluded occurrences */ - for (prop = icalcomponent_get_first_property (icalcomp, ICAL_EXDATE_PROPERTY); - prop; - prop = icalcomponent_get_next_property (icalcomp, ICAL_EXDATE_PROPERTY)) { - exceptions = g_slist_prepend (exceptions, g_strdup (icalproperty_get_value_as_string (prop))); - } - - for (link = exceptions; link && success; link = g_slist_next (link)) { - success = ecb_ews_remove_item_sync (cbews, cal_cache, removed_indexes, uid, link->data, cancellable, error); - } - - g_slist_free_full (exceptions, g_free); - } - - if (success && e_cal_component_has_attendees (master) && e_cal_component_has_attachments (master)) { - if (ews_id) { - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-ITEMID", ews_id->id); - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY", ews_id->change_key); - } - - /* In case we have attendees and atachemnts we have to fake update items, - * this is the only way to pass attachments in meeting invite mail */ - success = ecb_ews_modify_item_sync (cbews, removed_indexes, NULL, icalcomp, cancellable, error); - } - - icalcomponent_free (icalcomp); - g_clear_object (&item); - - for (link = (GSList *) instances; link && success; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - if (comp == master) - continue; - - icalcomp = e_cal_component_get_icalcomponent (comp); - - success = ecb_ews_modify_item_sync (cbews, removed_indexes, NULL, icalcomp, cancellable, error); - } - - if (success && items) { - EEwsItem *item = items->data; - const EwsId *item_id; - - item_id = e_ews_item_get_id (item); - *out_new_uid = g_strdup (item_id->id); - } - - g_slist_free_full (items, g_object_unref); - g_hash_table_destroy (removed_indexes); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - g_clear_object (&cal_cache); - e_ews_folder_id_free (fid); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - - return success; -} - -static gboolean -ecb_ews_remove_component_sync (ECalMetaBackend *meta_backend, - EConflictResolution conflict_resolution, - const gchar *uid, - const gchar *extra, - const gchar *object, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalComponent *comp; - EwsId item_id; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (object != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - comp = e_cal_component_new_from_string (object); - if (!comp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - ecb_ews_extract_item_id (comp, &item_id.id, &item_id.change_key); - - success = e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, 0, EWS_HARD_DELETE, - ecb_ews_is_organizer (cbews, comp) ? EWS_SEND_TO_ALL_AND_SAVE_COPY : EWS_SEND_TO_NONE, - EWS_ALL_OCCURRENCES, cancellable, error); - - g_free (item_id.id); - g_free (item_id.change_key); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_object_unref (comp); - - return success; -} - -static void -ecb_ews_discard_alarm_sync (ECalBackendSync *cal_backend_sync, - EDataCal *cal, - GCancellable *cancellable, - const gchar *uid, - const gchar *rid, - const gchar *auid, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - ECalComponent *comp = NULL; - EwsCalendarConvertData convert_data = { 0 }; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cal_backend_sync)); - - cbews = E_CAL_BACKEND_EWS (cal_backend_sync); - - cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbews)); - g_return_if_fail (cal_cache != NULL); - - if (!e_cal_cache_get_component (cal_cache, uid, NULL, &comp, cancellable, NULL) || !comp) { - g_object_unref (cal_cache); - g_propagate_error (error, EDC_ERROR (ObjectNotFound)); - return; - } - - g_object_unref (cal_cache); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) { - g_clear_object (&comp); - return; - } - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - - if (e_cal_component_has_recurrences (comp)) { - gint *index; - - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_OCCURRENCEITEM; - e_cal_component_get_sequence (comp, &index); - - if (index != NULL) { - /*Microsoft is counting the occurrences starting from 1 - where EcalComponent is starting from zerro */ - convert_data.index = *index + 1; - e_cal_component_free_sequence (index); - } else { - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_ITEM; - convert_data.index = -1; - } - } else { - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_ITEM; - convert_data.index = -1; - } - - ecb_ews_extract_item_id (comp, &convert_data.item_id, &convert_data.change_key); - - if (e_ews_connection_update_items_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", NULL, - "SendToNone", NULL, - e_cal_backend_ews_clear_reminder_is_set, - &convert_data, - NULL, - cancellable, - error)) { - icalcomponent *icomp = e_cal_component_get_icalcomponent (comp); - GSList *modified_objects; - - modified_objects = g_slist_prepend (NULL, - e_cal_meta_backend_info_new (icalcomponent_get_uid (icomp), NULL, NULL, - e_cal_util_get_x_property (icomp, "X-EVOLUTION-ITEMID"))); - - /* Refresh the local cache, to have up-to-date ChangeKey */ - e_cal_meta_backend_process_changes_sync (E_CAL_META_BACKEND (cbews), NULL, modified_objects, NULL, cancellable, error); - - g_slist_free_full (modified_objects, e_cal_meta_backend_info_free); - } - - g_object_unref (comp); - g_free (convert_data.item_id); - g_free (convert_data.change_key); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static gboolean -ecb_ews_send_cancellation_email_sync (ECalBackendEws *cbews, - CamelAddress *from, - CamelInternetAddress *recipient, - const gchar *subject, - const gchar *body, - const gchar *calobj, - GCancellable *cancellable, - GError **error) -{ - CamelMimeMessage *message; - CamelContentType *mime_type; - CamelMultipart *multi; - CamelMimePart *text_part, *vcal_part; - gchar *ical_str; - icalcomponent *vcal, *vevent, *vtz; - icalproperty *prop; - icaltimezone *icaltz; - struct icaltimetype dt; - gboolean success; - - vcal = icalcomponent_new (ICAL_VCALENDAR_COMPONENT); - icalcomponent_add_property (vcal, icalproperty_new_version ("2.0")); - icalcomponent_add_property (vcal, icalproperty_new_prodid ("-//Evolution EWS backend//EN")); - icalcomponent_add_property (vcal, icalproperty_new_method (ICAL_METHOD_CANCEL)); - vevent = icalcomponent_new_from_string (calobj); - prop = icalcomponent_get_first_property (vevent, ICAL_STATUS_PROPERTY); - if (prop != NULL) icalcomponent_remove_property (vevent, prop); - icalcomponent_add_property (vevent, icalproperty_new_status (ICAL_STATUS_CANCELLED)); - prop = icalcomponent_get_first_property (vevent, ICAL_METHOD_PROPERTY); - if (prop != NULL) icalcomponent_remove_property (vevent, prop); - dt = e_cal_backend_ews_get_datetime_with_zone (E_TIMEZONE_CACHE (cbews), NULL, vevent, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - icaltz = (icaltimezone *) - (dt.zone ? dt.zone : ecb_ews_get_timezone_from_ical_component (cbews, vevent)); - vtz = icaltimezone_get_component (icaltz); - icalcomponent_add_component (vcal, icalcomponent_new_clone (vtz)); - icalcomponent_add_component (vcal, vevent); - text_part = camel_mime_part_new (); - camel_mime_part_set_content (text_part, body, strlen (body), "text/plain"); - - vcal_part = camel_mime_part_new (); - mime_type = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (vcal_part)); - camel_content_type_set_param (mime_type, "charset", "utf-8"); - camel_content_type_set_param (mime_type, "method", "CANCEL"); - ical_str = icalcomponent_as_ical_string_r ((icalcomponent *) vcal); - camel_mime_part_set_content (vcal_part, ical_str, strlen (ical_str), "text/calendar; method=CANCEL"); - free (ical_str); - - multi = camel_multipart_new (); - camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multi), "multipart/alternative"); - camel_multipart_add_part (multi, text_part); - camel_multipart_set_boundary (multi, NULL); - camel_multipart_add_part (multi, vcal_part); - g_object_unref (text_part); - g_object_unref (vcal_part); - - message = camel_mime_message_new (); - camel_mime_message_set_subject (message, subject); - camel_mime_message_set_from (message, CAMEL_INTERNET_ADDRESS (from)); - camel_mime_message_set_recipients (message, CAMEL_RECIPIENT_TYPE_TO, recipient); - - camel_medium_set_content ((CamelMedium *) message, (CamelDataWrapper *) multi); - g_object_unref (multi); - - success = camel_ews_utils_create_mime_message (cbews->priv->cnc, "SendOnly", NULL, message, NULL, from, NULL, NULL, NULL, cancellable, error); - - g_object_unref (message); - icalcomponent_free (vcal); - - return success; -} - -static void -ecb_ews_receive_objects_no_exchange_mail (ECalBackendEws *cbews, - icalcomponent *vcalendar, - icalcomponent *subcomp, - GSList **ids, - GCancellable *cancellable, - GError **error) -{ - EwsCalendarConvertData convert_data = { 0 }; - EwsFolderId *fid; - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.icalcomp = subcomp; - convert_data.vcalendar = vcalendar; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - fid = e_ews_folder_id_new (cbews->priv->folder_id, NULL, FALSE); - - e_ews_connection_create_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - "SaveOnly", - "SendToNone", - fid, - e_cal_backend_ews_convert_calcomp_to_xml, - &convert_data, - ids, - cancellable, - error); - - e_ews_folder_id_free (fid); -} - -static icalproperty * -find_attendee (icalcomponent *ical_comp, - const gchar *address) -{ - icalproperty *prop; - - if (address == NULL) - return NULL; - - for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY); - prop != NULL; - prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) { - gchar *attendee; - gchar *text; - - attendee = icalproperty_get_value_as_string_r (prop); - - if (!attendee) - continue; - - text = g_strdup (itip_strip_mailto (attendee)); - text = g_strstrip (text); - if (text && !g_ascii_strcasecmp (address, text)) { - g_free (text); - g_free (attendee); - break; - } - g_free (text); - g_free (attendee); - } - - return prop; -} - -static icalproperty * -find_attendee_if_sentby (icalcomponent *ical_comp, - const gchar *address) -{ - icalproperty *prop; - - if (address == NULL) - return NULL; - - for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY); - prop != NULL; - prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) { - icalparameter *param; - const gchar *attendee_sentby; - gchar *text; - - param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER); - if (!param) - continue; - - attendee_sentby = icalparameter_get_sentby (param); - - if (!attendee_sentby) - continue; - - text = g_strdup (itip_strip_mailto (attendee_sentby)); - text = g_strstrip (text); - if (text && !g_ascii_strcasecmp (address, text)) { - g_free (text); - break; - } - g_free (text); - } - - return prop; -} - -static void -ecb_ews_get_rsvp (icalproperty *attendee, - gboolean *out_rsvp_requested) -{ - if (out_rsvp_requested) - *out_rsvp_requested = FALSE; - else - return; - - if (attendee) { - icalparameter *rsvp; - - rsvp = icalproperty_get_first_parameter (attendee, ICAL_RSVP_PARAMETER); - if (rsvp) { - *out_rsvp_requested = icalparameter_get_rsvp (rsvp) == ICAL_RSVP_TRUE; - } - } -} - -static const gchar * -ecb_ews_get_current_user_meeting_reponse (ECalBackendEws *cbews, - icalcomponent *icalcomp, - const gchar *current_user_mail, - gboolean *out_rsvp_requested) -{ - icalproperty *attendee; - const gchar *attendee_str = NULL, *attendee_mail = NULL; - gint attendees_count = 0; - const gchar *response = NULL; - gboolean found = FALSE; - - if (out_rsvp_requested) - *out_rsvp_requested = FALSE; - - attendee = icalcomponent_get_first_property (icalcomp, ICAL_ORGANIZER_PROPERTY); - if (attendee) { - attendee_str = icalproperty_get_organizer (attendee); - - if (attendee_str) { - if (!strncasecmp (attendee_str, "MAILTO:", 7)) - attendee_mail = attendee_str + 7; - else - attendee_mail = attendee_str; - if (attendee_mail && current_user_mail && g_ascii_strcasecmp (attendee_mail, current_user_mail) == 0) { - /* Empty string means it's an organizer, NULL is when not found */ - return ""; - } - } - } - - for (attendee = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY); - attendee != NULL; - attendee = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY), attendees_count++) { - attendee_str = icalproperty_get_attendee (attendee); - - if (attendee_str != NULL) { - if (!strncasecmp (attendee_str, "MAILTO:", 7)) - attendee_mail = attendee_str + 7; - else - attendee_mail = attendee_str; - if (attendee_mail && current_user_mail && g_ascii_strcasecmp (attendee_mail, current_user_mail) == 0) { - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } - } - } - - /* this should not happen, but if the user's configured email does not match the one - used in the invitation, like when the invitation comes to a mailing list... */ - if (!found && attendees_count == 1) { - attendee = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY); - g_return_val_if_fail (attendee != NULL, NULL); - - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } else if (!found) { - ESourceRegistry *registry; - ECalComponent *comp; - - registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbews)); - comp = e_cal_component_new (); - if (e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp))) { - gchar *my_address; - - my_address = itip_get_comp_attendee (registry, comp, NULL); - - attendee = find_attendee (icalcomp, my_address); - if (!attendee) - attendee = find_attendee_if_sentby (icalcomp, my_address); - - if (attendee) { - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } - - g_free (my_address); - } - - g_object_unref (comp); - } - - if (found && !response) { - response = "NEEDS-ACTION"; - } - - return response; -} - -/* changekey can be NULL if you don't want it. itemid cannot. */ -static void -ecb_ews_get_item_accept_id (ECalComponent *comp, - gchar **itemid, - gchar **changekey, - gchar **mail_id) -{ - icalproperty *prop; - gchar *id_item = NULL; - gchar *id_accept = NULL; - gchar *ck = NULL; - - prop = icalcomponent_get_first_property ( - e_cal_component_get_icalcomponent (comp), - ICAL_X_PROPERTY); - while (prop) { - const gchar *x_name, *x_val; - - x_name = icalproperty_get_x_name (prop); - x_val = icalproperty_get_x (prop); - if (!id_item && g_ascii_strcasecmp (x_name, "X-EVOLUTION-ITEMID") == 0) - id_item = g_strdup (x_val); - else if (!id_accept && g_ascii_strcasecmp (x_name, "X-EVOLUTION-ACCEPT-ID") == 0) - id_accept = g_strdup (x_val); - else if (changekey && !ck && !g_ascii_strcasecmp (x_name, "X-EVOLUTION-CHANGEKEY")) - ck = g_strdup (x_val); - - prop = icalcomponent_get_next_property ( - e_cal_component_get_icalcomponent (comp), - ICAL_X_PROPERTY); - } - - if (!id_item) - id_item = g_strdup (id_accept); - - *itemid = id_item; - *mail_id = id_accept; - if (changekey) - *changekey = ck; -} - -static gboolean -ecb_ews_do_method_request_publish_reply (ECalBackendEws *cbews, - icalcomponent *vcalendar, - ECalComponent *comp, - icalcomponent *subcomp, - const gchar *response_type, - const gchar *user_email, - gboolean rsvp_requested, - GCancellable *cancellable, - GError **error) -{ - GError *local_error = NULL; - gchar *item_id = NULL; - gchar *change_key = NULL; - gchar *mail_id = NULL; - gint pass = 0; - GSList *ids = NULL; - - if (!response_type && - e_cal_util_component_has_organizer (subcomp) && - e_cal_util_component_has_attendee (subcomp)) { - g_set_error (error, E_DATA_CAL_ERROR, UnknownUser, _("Cannot find user “%s” between attendees"), user_email ? user_email : "NULL"); - return FALSE; - } - - if (response_type && *response_type) - ecb_ews_get_item_accept_id (comp, &item_id, &change_key, &mail_id); - else - response_type = NULL; - - while (pass < 2) { - /*in case we do not have item id we will create item with mime content only*/ - if (!item_id || (response_type && g_ascii_strcasecmp (response_type, "NEEDS-ACTION") == 0)) { - ecb_ews_receive_objects_no_exchange_mail (cbews, vcalendar, subcomp, &ids, cancellable, &local_error); - } else { - EwsCalendarConvertData convert_data = { 0 }; - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.response_type = (gchar *) response_type; - convert_data.item_id = item_id; - convert_data.change_key = change_key; - - e_ews_connection_create_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - rsvp_requested ? "SendAndSaveCopy" : "SaveOnly", - rsvp_requested ? NULL : "SendToNone", - NULL, - e_cal_backend_ews_prepare_accept_item_request, - &convert_data, - &ids, - cancellable, - &local_error); - } - - if (pass == 0 && mail_id != NULL && item_id != NULL && - g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_ITEMNOTFOUND)) { - /* - * maybe the associated accept calendar item changed - * on the server, thus retry with updated values - */ - GSList *my_ids = NULL; - - g_clear_error (&local_error); - - my_ids = g_slist_append (my_ids, mail_id); - - if (e_ews_connection_get_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - my_ids, - "AllProperties", - NULL, - FALSE, - NULL, - E_EWS_BODY_TYPE_ANY, - &ids, - NULL, - NULL, - cancellable, - &local_error) && - ids != NULL && - ids->data != NULL) { - EEwsItem *item = ids->data; - const EwsId *id = e_ews_item_get_id (item); - - if (id != NULL && g_strcmp0 (id->id, mail_id) == 0) { - const EwsId *cal_item_accepted_id; - - cal_item_accepted_id = e_ews_item_get_calendar_item_accept_id (item); - if (cal_item_accepted_id != NULL) { - g_clear_error (&local_error); - pass++; - - g_free (item_id); - g_free (change_key); - - item_id = g_strdup (cal_item_accepted_id->id); - change_key = g_strdup (cal_item_accepted_id->change_key); - } - } - } - - g_slist_free (my_ids); - - if (pass == 0) - break; - } else { - break; - } - - g_slist_free_full (ids, g_object_unref); - ids = NULL; - } - - if (local_error == NULL) { - icalproperty *transp; - - transp = icalcomponent_get_first_property (subcomp, ICAL_TRANSP_PROPERTY); - - if (g_strcmp0 (icalproperty_get_value_as_string (transp), "TRANSPARENT") == 0 && - g_strcmp0 (response_type, "ACCEPTED") == 0) { - EwsCalendarConvertData convert_data = { 0 }; - GSList *l; - - /* - * user can accept meeting but mark it as free in it's calendar - * the following code is updating the exchange meeting status to free - */ - for (l = ids; l != NULL; l = g_slist_next (l)) { - EEwsItem *item = l->data; - - if (item != NULL) { - const EwsId *id = e_ews_item_get_id (item); - - convert_data.item_id = id->id; - convert_data.change_key = id->change_key; - break; - } - } - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.vcalendar = vcalendar; - - e_ews_connection_update_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", - NULL, - "SendToNone", - NULL, - e_cal_backend_ews_prepare_set_free_busy_status, - &convert_data, - NULL, - cancellable, - &local_error); - } - } - - if (local_error != NULL) - g_propagate_error (error, local_error); - - g_free (item_id); - g_free (change_key); - g_free (mail_id); - g_slist_free_full (ids, g_object_unref); - - return !local_error; -} - -static void -ecb_ews_receive_objects_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *calobj, - GError **error) -{ - ECalBackendEws *cbews; - ECalBackend *cal_backend; - CamelEwsSettings *ews_settings; - icalcomponent *icalcomp, *subcomp; - icalcomponent_kind kind; - gchar *user_email; - gboolean success = TRUE, do_refresh = FALSE; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - icalcomp = calobj ? icalparser_parse_string (calobj) : NULL; - - if (!icalcomp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - /* make sure ical data we parse is actually a vCalendar component */ - if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) { - icalcomponent_free (icalcomp); - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - cal_backend = E_CAL_BACKEND (cbews); - kind = e_cal_backend_get_kind (cal_backend); - - ews_settings = ecb_ews_get_collection_settings (cbews); - user_email = camel_ews_settings_dup_email (ews_settings); - - switch (icalcomponent_get_method (icalcomp)) { - case ICAL_METHOD_REQUEST: - case ICAL_METHOD_PUBLISH: - case ICAL_METHOD_REPLY: - for (subcomp = icalcomponent_get_first_component (icalcomp, kind); - subcomp && success; - subcomp = icalcomponent_get_next_component (icalcomp, kind)) { - ECalComponent *comp; - const gchar *response_type; - gboolean rsvp_requested = FALSE; - - /* getting a data for meeting request response */ - response_type = ecb_ews_get_current_user_meeting_reponse (cbews, subcomp, user_email, &rsvp_requested); - - comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (subcomp)); - - success = ecb_ews_do_method_request_publish_reply (cbews, icalcomp, comp, subcomp, response_type, user_email, rsvp_requested, cancellable, error); - - do_refresh = TRUE; - - g_object_unref (comp); - } - break; - case ICAL_METHOD_COUNTER: - /* - * this is a new time proposal mail from one of the attendees - * if we decline the proposal, nothing have to be done - * if we accept it we will call to modify_object - */ - for (subcomp = icalcomponent_get_first_component (icalcomp, kind); - subcomp && success; - subcomp = icalcomponent_get_next_component (icalcomp, kind)) { - const gchar *response_type; - - /* getting a data for meeting request response */ - response_type = ecb_ews_get_current_user_meeting_reponse (cbews, subcomp, user_email, NULL); - - if (g_strcmp0 (response_type, "ACCEPTED") == 0) { - gchar **split_subject; - icalproperty *summary; - - /* we have to edit the meeting subject to remove exchange header */ - summary = icalcomponent_get_first_property (subcomp, ICAL_SUMMARY_PROPERTY); - split_subject = - g_strsplit (icalproperty_get_value_as_string (summary), ":", -1); - icalproperty_set_value_from_string (summary, split_subject[1] , "NO"); - g_strfreev (split_subject); - - success = ecb_ews_modify_item_sync (cbews, NULL, NULL, subcomp, cancellable, error); - - do_refresh = TRUE; - } - } - break; - default: - break; - } - - icalcomponent_free (icalcomp); - g_free (user_email); - - if (success && do_refresh) - e_cal_meta_backend_schedule_refresh (E_CAL_META_BACKEND (cbews)); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static void -ecb_ews_send_objects_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *calobj, - GSList **users, - gchar **modified_calobj, - GError **error) -{ - ECalBackendEws *cbews; - icalcomponent_kind kind; - icalcomponent *icalcomp, *subcomp = NULL; - gchar *subcalobj; - gboolean success = TRUE; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - icalcomp = calobj ? icalparser_parse_string (calobj) : NULL; - - /* make sure data was parsed properly */ - if (!icalcomp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - /* make sure ical data we parse is actually an vcal component */ - if ((icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) && (icalcomponent_isa (icalcomp) != ICAL_VEVENT_COMPONENT)) { - icalcomponent_free (icalcomp); - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - - if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) { - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - subcomp = icalcomponent_get_first_component (icalcomp, kind); - } - if (icalcomponent_isa (icalcomp) == ICAL_VEVENT_COMPONENT) - subcomp = icalcomp; - while (subcomp && success) { - const gchar *new_body_content = NULL, *subject = NULL, *org_email = NULL; - const gchar *org = NULL, *attendee = NULL; - icalproperty *prop, *org_prop = NULL; - CamelInternetAddress *org_addr = camel_internet_address_new (); - - new_body_content = e_cal_util_get_x_property (subcomp, "X-EVOLUTION-RETRACT-COMMENT"); - subject = icalproperty_get_value_as_string (icalcomponent_get_first_property (subcomp, ICAL_SUMMARY_PROPERTY)); - - org_prop = icalcomponent_get_first_property (subcomp, ICAL_ORGANIZER_PROPERTY); - org = icalproperty_get_organizer (org_prop); - if (!g_ascii_strncasecmp (org, "MAILTO:", 7)) - org_email = (org) + 7; - else - org_email = org; - - camel_internet_address_add (org_addr, icalproperty_get_parameter_as_string (org_prop, "CN"), org_email); - - /* iterate over every attendee property */ - for (prop = icalcomponent_get_first_property (subcomp, ICAL_ATTENDEE_PROPERTY); - prop && success; - prop = icalcomponent_get_next_property (subcomp, ICAL_ATTENDEE_PROPERTY)) { - CamelInternetAddress *attendee_addr = camel_internet_address_new (); - attendee = icalproperty_get_attendee (prop); - if (g_ascii_strcasecmp (org_email, attendee) == 0) continue; - if (!g_ascii_strncasecmp (attendee, "mailto:", 7)) attendee = (attendee) + 7; - - subcalobj = icalcomponent_as_ical_string_r (subcomp); - camel_internet_address_add (attendee_addr, icalproperty_get_parameter_as_string (prop, "CN"), attendee); - success = ecb_ews_send_cancellation_email_sync (cbews, CAMEL_ADDRESS (org_addr), attendee_addr, - subject, new_body_content, subcalobj, cancellable, error); - g_object_unref (attendee_addr); - free (subcalobj); - } - - g_object_unref (org_addr); - subcomp = icalcomponent_get_next_component (icalcomp, kind); - } - - icalcomponent_free (icalcomp); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static void -ecb_ews_get_free_busy_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const GSList *users, - time_t start, - time_t end, - GSList **freebusyobjs, - GError **error) -{ - ECalBackendEws *cbews; - EEWSFreeBusyData fbdata = { 0 }; - GSList *freebusy = NULL; - gboolean success; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - g_return_if_fail (freebusyobjs != NULL); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - *freebusyobjs = NULL; - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - /* EWS can support only 100 identities, which is the maximum number of identities that the Web service method can request - see http://msdn.microsoft.com / en - us / library / aa564001 % 28v = EXCHG.140 % 29.aspx */ - if (g_slist_length ((GSList *) users) > 100) { - g_propagate_error (error, EDC_ERROR (SearchSizeLimitExceeded)); - return; - } - - fbdata.period_start = start; - fbdata.period_end = end; - fbdata.user_mails = (GSList *) users; - - success = e_ews_connection_get_free_busy_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - e_ews_cal_utils_prepare_free_busy_request, &fbdata, - &freebusy, cancellable, error); - - if (success) { - GSList *fblink, *ulink; - - for (fblink = freebusy, ulink = (GSList *) users; - fblink && ulink; - fblink = g_slist_next (fblink), ulink = g_slist_next (ulink)) { - icalcomponent *icalcomp = fblink->data; - gchar *mailto; - - /* add attendee property */ - mailto = g_strconcat ("mailto:", ulink->data, NULL); - icalcomponent_add_property (icalcomp, icalproperty_new_attendee (mailto)); - g_free (mailto); - - *freebusyobjs = g_slist_prepend (*freebusyobjs, icalcomponent_as_ical_string_r (icalcomp)); - } - - *freebusyobjs = g_slist_reverse (*freebusyobjs); - } - - g_slist_free_full (freebusy, (GDestroyNotify) icalcomponent_free); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static gchar * -ecb_ews_get_backend_property (ECalBackend *cal_backend, - const gchar *prop_name) -{ - ECalBackendEws *cbews; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cal_backend), NULL); - g_return_val_if_fail (prop_name != NULL, NULL); - - cbews = E_CAL_BACKEND_EWS (cal_backend); - - if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) { - return g_strjoin ( - ",", - CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS, - CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY, - CAL_STATIC_CAPABILITY_REMOVE_ALARMS, - CAL_STATIC_CAPABILITY_NO_THISANDPRIOR, - CAL_STATIC_CAPABILITY_NO_THISANDFUTURE, - CAL_STATIC_CAPABILITY_NO_CONV_TO_ASSIGN_TASK, - CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT, - CAL_STATIC_CAPABILITY_SAVE_SCHEDULES, - CAL_STATIC_CAPABILITY_NO_ALARM_AFTER_START, - CAL_STATIC_CAPABILITY_NO_MEMO_START_DATE, - CAL_STATIC_CAPABILITY_ALL_DAY_EVENT_AS_TIME, - CAL_STATIC_CAPABILITY_TASK_DATE_ONLY, - e_cal_meta_backend_get_capabilities (E_CAL_META_BACKEND (cbews)), - NULL); - } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS)) { - /* return email address of the person who opened the calendar */ - CamelEwsSettings *ews_settings; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - return camel_ews_settings_dup_email (ews_settings); - } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS)) { - /* ews does not support email based alarms */ - return NULL; - } - - /* Chain up to parent's method. */ - return E_CAL_BACKEND_CLASS (e_cal_backend_ews_parent_class)->get_backend_property (cal_backend, prop_name); -} - -static void -ecb_ews_get_timezone_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *tzid, - gchar **tzobject, - GError **error) -{ - GError *local_error = NULL; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - g_return_if_fail (tzid != NULL); - g_return_if_fail (tzobject != NULL); - - *tzobject = NULL; - - E_CAL_BACKEND_SYNC_CLASS (e_cal_backend_ews_parent_class)->get_timezone_sync (sync_backend, cal, cancellable, tzid, tzobject, &local_error); - - if (!*tzobject) { - /* The timezone can be sometimes the Windows zone, try to convert it to libical */ - const gchar *ical_location = e_cal_backend_ews_tz_util_get_ical_equivalent (tzid); - - if (ical_location) - E_CAL_BACKEND_SYNC_CLASS (e_cal_backend_ews_parent_class)->get_timezone_sync (sync_backend, cal, cancellable, ical_location, tzobject, NULL); - } - - if (*tzobject) - g_clear_error (&local_error); - else if (local_error) - g_propagate_error (error, local_error); -} - -static gboolean -ecb_ews_get_destination_address (EBackend *backend, - gchar **host, - guint16 *port) -{ - CamelEwsSettings *ews_settings; - SoupURI *soup_uri; - gchar *host_url; - gboolean result = FALSE; - - g_return_val_if_fail (port != NULL, FALSE); - g_return_val_if_fail (host != NULL, FALSE); - - /* Sanity checking */ - if (!e_cal_backend_get_registry (E_CAL_BACKEND (backend)) || - !e_backend_get_source (backend)) - return FALSE; - - ews_settings = ecb_ews_get_collection_settings (E_CAL_BACKEND_EWS (backend)); - g_return_val_if_fail (ews_settings != NULL, FALSE); - - host_url = camel_ews_settings_dup_hosturl (ews_settings); - g_return_val_if_fail (host_url != NULL, FALSE); - - soup_uri = soup_uri_new (host_url); - if (soup_uri) { - *host = g_strdup (soup_uri_get_host (soup_uri)); - *port = soup_uri_get_port (soup_uri); - - result = *host && **host; - if (!result) { - g_free (*host); - *host = NULL; - } - - soup_uri_free (soup_uri); - } - - g_free (host_url); - - return result; -} - -static gchar * -ecb_ews_dup_component_revision (ECalCache *cal_cache, - icalcomponent *icalcomp, - gpointer user_data) -{ - g_return_val_if_fail (icalcomp != NULL, NULL); - - return e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY"); -} - -static void -ecb_ews_constructed (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - ECalCache *cal_cache; - gchar *cache_dirname; - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->constructed (object); - - /* Reset the connectable, it steals data from Authentication extension, - where is written incorrect address */ - e_backend_set_connectable (E_BACKEND (object), NULL); - - cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbews)); - g_return_if_fail (cal_cache != NULL); - - cache_dirname = g_path_get_dirname (e_cache_get_filename (E_CACHE (cal_cache))); - g_signal_connect (cal_cache, "dup-component-revision", G_CALLBACK (ecb_ews_dup_component_revision), NULL); - - g_clear_object (&cal_cache); - - cbews->priv->attachments_dir = g_build_filename (cache_dirname, "attachments", NULL); - g_mkdir_with_parents (cbews->priv->attachments_dir, 0777); - - g_free (cache_dirname); -} - -static void -ecb_ews_dispose (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - g_clear_object (&cbews->priv->cnc); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->dispose (object); -} - -static void -ecb_ews_finalize (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - - g_free (cbews->priv->folder_id); - g_free (cbews->priv->attachments_dir); - - g_rec_mutex_clear (&cbews->priv->cnc_lock); - - e_cal_backend_ews_unref_windows_zones (); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->finalize (object); -} - -static void -e_cal_backend_ews_init (ECalBackendEws *cbews) -{ - cbews->priv = G_TYPE_INSTANCE_GET_PRIVATE (cbews, E_TYPE_CAL_BACKEND_EWS, ECalBackendEwsPrivate); - - g_rec_mutex_init (&cbews->priv->cnc_lock); - - e_cal_backend_ews_populate_windows_zones (); -} - -static void -e_cal_backend_ews_class_init (ECalBackendEwsClass *klass) -{ - GObjectClass *object_class; - EBackendClass *backend_class; - ECalBackendClass *cal_backend_class; - ECalBackendSyncClass *cal_backend_sync_class; - ECalMetaBackendClass *cal_meta_backend_class; - - g_type_class_add_private (klass, sizeof (ECalBackendEwsPrivate)); - - cal_meta_backend_class = E_CAL_META_BACKEND_CLASS (klass); - cal_meta_backend_class->connect_sync = ecb_ews_connect_sync; - cal_meta_backend_class->disconnect_sync = ecb_ews_disconnect_sync; - cal_meta_backend_class->get_changes_sync = ecb_ews_get_changes_sync; - cal_meta_backend_class->load_component_sync = ecb_ews_load_component_sync; - cal_meta_backend_class->save_component_sync = ecb_ews_save_component_sync; - cal_meta_backend_class->remove_component_sync = ecb_ews_remove_component_sync; - - cal_backend_sync_class = E_CAL_BACKEND_SYNC_CLASS (klass); - cal_backend_sync_class->discard_alarm_sync = ecb_ews_discard_alarm_sync; - cal_backend_sync_class->receive_objects_sync = ecb_ews_receive_objects_sync; - cal_backend_sync_class->send_objects_sync = ecb_ews_send_objects_sync; - cal_backend_sync_class->get_free_busy_sync = ecb_ews_get_free_busy_sync; - cal_backend_sync_class->get_timezone_sync = ecb_ews_get_timezone_sync; - - cal_backend_class = E_CAL_BACKEND_CLASS (klass); - cal_backend_class->get_backend_property = ecb_ews_get_backend_property; - - backend_class = E_BACKEND_CLASS (klass); - backend_class->get_destination_address = ecb_ews_get_destination_address; - - object_class = G_OBJECT_CLASS (klass); - object_class->constructed = ecb_ews_constructed; - object_class->dispose = ecb_ews_dispose; - object_class->finalize = ecb_ews_finalize; -} diff --git a/src/calendar/e-cal-backend-ews.c.import-event-timezone b/src/calendar/e-cal-backend-ews.c.import-event-timezone deleted file mode 100644 index 1f7ab52..0000000 --- a/src/calendar/e-cal-backend-ews.c.import-event-timezone +++ /dev/null @@ -1,3897 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com) - * - * 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. - * - * 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#include -#include -#include -#include - -#include -#include - -#include "server/e-source-ews-folder.h" -#include "server/e-ews-calendar-utils.h" -#include "server/e-ews-connection-utils.h" -#include "server/e-ews-camel-common.h" - -#include "e-cal-backend-ews.h" -#include "e-cal-backend-ews-utils.h" - -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -#ifdef G_OS_WIN32 -#ifdef gmtime_r -#undef gmtime_r -#endif - -/* The gmtime() in Microsoft's C library is MT-safe */ -#define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0) -#endif - -/* Private part of the CalBackendEws structure */ -struct _ECalBackendEwsPrivate { - GRecMutex cnc_lock; - EEwsConnection *cnc; - gchar *folder_id; - - guint subscription_key; - gboolean is_freebusy_calendar; - - gchar *attachments_dir; -}; - -#define X_EWS_ORIGINAL_COMP "X-EWS-ORIGINAL-COMP" - -#define EWS_MAX_FETCH_COUNT 100 - -#define GET_ITEMS_SYNC_PROPERTIES \ - "item:Attachments" \ - " item:Categories" \ - " item:HasAttachments" \ - " item:MimeContent" \ - " calendar:UID" \ - " calendar:Resources" \ - " calendar:ModifiedOccurrences" \ - " calendar:IsMeeting" \ - " calendar:IsResponseRequested" \ - " calendar:MyResponseType" \ - " calendar:RequiredAttendees" \ - " calendar:OptionalAttendees" - -#define GET_ITEMS_SYNC_PROPERTIES_2007 \ - GET_ITEMS_SYNC_PROPERTIES \ - " calendar:TimeZone" - -#define GET_ITEMS_SYNC_PROPERTIES_2010 \ - GET_ITEMS_SYNC_PROPERTIES \ - " calendar:StartTimeZone" \ - " calendar:EndTimeZone" - -#define e_data_cal_error_if_fail(expr, _code) \ - G_STMT_START { \ - if (G_LIKELY (expr)) { \ - } else { \ - g_log (G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "file %s: line %d (%s): assertion `%s' failed", \ - __FILE__, __LINE__, G_STRFUNC, #expr); \ - g_set_error (&error, E_DATA_CAL_ERROR, (_code), \ - "file %s: line %d (%s): assertion `%s' failed", \ - __FILE__, __LINE__, G_STRFUNC, #expr); \ - goto exit; \ - } \ - } G_STMT_END - -G_DEFINE_TYPE (ECalBackendEws, e_cal_backend_ews, E_TYPE_CAL_META_BACKEND) - -static CamelEwsSettings * -ecb_ews_get_collection_settings (ECalBackendEws *cbews) -{ - ESource *source; - ESource *collection; - ESourceCamel *extension; - ESourceRegistry *registry; - CamelSettings *settings; - const gchar *extension_name; - - source = e_backend_get_source (E_BACKEND (cbews)); - registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbews)); - - extension_name = e_source_camel_get_extension_name ("ews"); - e_source_camel_generate_subtype ("ews", CAMEL_TYPE_EWS_SETTINGS); - - /* The collection settings live in our parent data source. */ - collection = e_source_registry_find_extension ( - registry, source, extension_name); - g_return_val_if_fail (collection != NULL, NULL); - - extension = e_source_get_extension (collection, extension_name); - settings = e_source_camel_get_settings (extension); - - g_object_unref (collection); - - return CAMEL_EWS_SETTINGS (settings); -} - -static void -ecb_ews_convert_error_to_edc_error (GError **perror) -{ - GError *error = NULL; - - if (!perror || !*perror || (*perror)->domain == E_DATA_CAL_ERROR) - return; - - if ((*perror)->domain == EWS_CONNECTION_ERROR) { - switch ((*perror)->code) { - case EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED: - error = EDC_ERROR_EX (AuthenticationFailed, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_FOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_MANAGEDFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PARENTFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PUBLICFOLDERSERVERNOTFOUND: - error = EDC_ERROR_EX (NoSuchCal, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_EVENTNOTFOUND: - case EWS_CONNECTION_ERROR_ITEMNOTFOUND: - error = EDC_ERROR_EX (ObjectNotFound, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_UNAVAILABLE: - g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND, (*perror)->message); - break; - } - - if (!error) - error = EDC_ERROR_EX (OtherError, (*perror)->message); - } - - if (error) { - g_error_free (*perror); - *perror = error; - } -} - -static void -ecb_ews_maybe_disconnect_sync (ECalBackendEws *cbews, - GError **in_perror, - GCancellable *cancellable) -{ - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cbews)); - - if (in_perror && g_error_matches (*in_perror, E_DATA_CAL_ERROR, AuthenticationFailed)) { - e_cal_meta_backend_disconnect_sync (E_CAL_META_BACKEND (cbews), cancellable, NULL); - e_backend_schedule_credentials_required (E_BACKEND (cbews), E_SOURCE_CREDENTIALS_REASON_REJECTED, NULL, 0, NULL, NULL, G_STRFUNC); - } -} - -static void -ecb_ews_server_notification_cb (ECalBackendEws *cbews, - GSList *events, - EEwsConnection *cnc) -{ - GSList *link; - gboolean update_folder = FALSE; - - g_return_if_fail (cbews != NULL); - g_return_if_fail (cbews->priv != NULL); - - for (link = events; link && !update_folder; link = g_slist_next (link)) { - EEwsNotificationEvent *event = link->data; - - switch (event->type) { - case E_EWS_NOTIFICATION_EVENT_CREATED: - case E_EWS_NOTIFICATION_EVENT_DELETED: - case E_EWS_NOTIFICATION_EVENT_MODIFIED: - g_rec_mutex_lock (&cbews->priv->cnc_lock); - if (g_strcmp0 (event->folder_id, cbews->priv->folder_id) == 0) - update_folder = TRUE; - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - break; - case E_EWS_NOTIFICATION_EVENT_MOVED: - case E_EWS_NOTIFICATION_EVENT_COPIED: - g_rec_mutex_lock (&cbews->priv->cnc_lock); - if (g_strcmp0 (event->folder_id, cbews->priv->folder_id) == 0 || - g_strcmp0 (event->old_folder_id, cbews->priv->folder_id) == 0) - update_folder = TRUE; - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - break; - default: - return; - } - } - - if (update_folder) - e_cal_meta_backend_schedule_refresh (E_CAL_META_BACKEND (cbews)); -} - -static void -ecb_ews_unset_connection (ECalBackendEws *cbews) -{ - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cbews)); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->cnc) { - e_ews_connection_set_disconnected_flag (cbews->priv->cnc, TRUE); - - g_signal_handlers_disconnect_by_func (cbews->priv->cnc, ecb_ews_server_notification_cb, cbews); - - if (cbews->priv->subscription_key != 0) { - e_ews_connection_disable_notifications_sync ( - cbews->priv->cnc, - cbews->priv->subscription_key); - cbews->priv->subscription_key = 0; - } - } - - g_clear_object (&cbews->priv->cnc); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); -} - -static icaltimezone * -ecb_ews_get_timezone (ETimezoneCache *timezone_cache, - const gchar *msdn_tzid, - const gchar *tzid, - const gchar *evo_ews_tzid) -{ - icaltimezone *zone = NULL; - const gchar *evo_ews_msdn_tzid; - - zone = e_timezone_cache_get_timezone (timezone_cache, tzid); - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (tzid); - - if (g_strcmp0 (tzid, evo_ews_tzid) == 0) - return zone; - - if (evo_ews_tzid != NULL) { - evo_ews_msdn_tzid = e_cal_backend_ews_tz_util_get_msdn_equivalent (evo_ews_tzid); - - if (g_strcmp0 (msdn_tzid, evo_ews_msdn_tzid) == 0) { - zone = e_timezone_cache_get_timezone (timezone_cache, evo_ews_tzid); - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (evo_ews_tzid); - } - } - - return zone; -} - -static icalparameter * -ecb_ews_responsetype_to_partstat (const gchar *responsetype) -{ - icalparameter *param = NULL; - - if (!responsetype) - return icalparameter_new_partstat (ICAL_PARTSTAT_NONE); - - if (g_ascii_strcasecmp (responsetype, "Organizer") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_ACCEPTED); - else if (g_ascii_strcasecmp (responsetype, "Tentative") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_TENTATIVE); - else if (g_ascii_strcasecmp (responsetype, "Accept") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_ACCEPTED); - else if (g_ascii_strcasecmp (responsetype, "Decline") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_DECLINED); - else if (g_ascii_strcasecmp (responsetype, "NoResponseReceived") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION); - else if (g_ascii_strcasecmp (responsetype, "Unknown") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NONE); - - if (!param) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NONE); - - return param; -} - -static ECalComponent * -ecb_ews_item_to_component_sync (ECalBackendEws *cbews, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *res_component = NULL; - ETimezoneCache *timezone_cache; - icalcomponent_kind kind; - EEwsItemType item_type; - icalcomponent *icalcomp, *vcomp; - icaltimezone *utc_zone = icaltimezone_get_utc_timezone (); - CamelEwsSettings *ews_settings; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), NULL); - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - timezone_cache = E_TIMEZONE_CACHE (cbews); - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - ews_settings = ecb_ews_get_collection_settings (cbews); - - item_type = e_ews_item_get_item_type (item); - if (item_type == E_EWS_ITEM_TYPE_TASK || item_type == E_EWS_ITEM_TYPE_MEMO) { - icalproperty *icalprop; - icaltimetype due_date, start_date, complete_date, created; - icalproperty_status status = ICAL_STATUS_NONE; - icalproperty_class class = ICAL_CLASS_NONE; - const gchar *ews_task_status, *sensitivity; - EwsImportance item_importance; - gint priority = 5; - gboolean has_this_date = FALSE; - - vcomp = icalcomponent_new (ICAL_VCALENDAR_COMPONENT); - /*subject*/ - icalcomp = icalcomponent_new (item_type == E_EWS_ITEM_TYPE_TASK ? ICAL_VTODO_COMPONENT : ICAL_VJOURNAL_COMPONENT); - icalprop = icalproperty_new_summary (e_ews_item_get_subject (item)); - icalcomponent_add_property (icalcomp, icalprop); - - /*date time created*/ - created = icaltime_from_timet_with_zone (e_ews_item_get_date_created (item), 0, utc_zone); - icalprop = icalproperty_new_created (created); - icalcomponent_add_property (icalcomp, icalprop); - - /*sensitivity*/ - sensitivity = e_ews_item_get_sensitivity (item); - if (g_strcmp0 (sensitivity, "Normal") == 0) - class = ICAL_CLASS_PUBLIC; - else if (g_strcmp0 (sensitivity, "Private") == 0) - class = ICAL_CLASS_PRIVATE; - else if ((g_strcmp0 (sensitivity, "Confidential") == 0) || - (g_strcmp0 (sensitivity, "Personal") == 0)) - class = ICAL_CLASS_CONFIDENTIAL; - icalprop = icalproperty_new_class (class); - icalcomponent_add_property (icalcomp, icalprop); - - /*description*/ - icalprop = icalproperty_new_description (e_ews_item_get_body (item)); - icalcomponent_add_property (icalcomp, icalprop); - - /*task assaingments*/ - if (e_ews_item_get_delegator (item) != NULL) { - const gchar *task_owner = e_ews_item_get_delegator (item); - GSList *mailboxes = NULL, *l; - gboolean includes_last_item; - gchar *mailtoname, *user_email; - icalparameter *param; - - /*The task owner according to Exchange is current user, even that the task was assigned by - *someone else. I'm making the current user attendee and task delegator will be a task organizer */ - - user_email = camel_ews_settings_dup_email (ews_settings); - mailtoname = g_strdup_printf ("mailto:%s", user_email); - icalprop = icalproperty_new_attendee (mailtoname); - g_free (mailtoname); - g_free (user_email); - - param = icalparameter_new_cn (e_ews_item_get_owner (item)); - icalproperty_add_parameter (icalprop, param); - icalcomponent_add_property (icalcomp, icalprop); - - /* get delegator mail box*/ - e_ews_connection_resolve_names_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, task_owner, - EWS_SEARCH_AD, NULL, FALSE, &mailboxes, NULL, - &includes_last_item, cancellable, error); - - for (l = mailboxes; l != NULL; l = g_slist_next (l)) { - EwsMailbox *mb = l->data; - - mailtoname = g_strdup_printf ("mailto:%s", mb->email); - icalprop = icalproperty_new_organizer (mailtoname); - param = icalparameter_new_cn (mb->name); - icalproperty_add_parameter (icalprop, param); - icalcomponent_add_property (icalcomp, icalprop); - - g_free (mailtoname); - e_ews_mailbox_free (mb); - } - g_slist_free (mailboxes); - } - - if (item_type == E_EWS_ITEM_TYPE_TASK) { - icaltimezone *user_timezone = calendar_config_get_icaltimezone (); - const gchar *percent_complete; - - /*start date*/ - has_this_date = FALSE; - e_ews_item_task_has_start_date (item, &has_this_date); - if (has_this_date) { - start_date = icaltime_from_timet_with_zone (e_ews_item_get_start_date (item), 0, user_timezone); - start_date.is_date = 1; - icalprop = icalproperty_new_dtstart (start_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*status*/ - ews_task_status = e_ews_item_get_status (item); - if (g_strcmp0 (ews_task_status, "NotStarted") != 0) { - if (g_strcmp0 (ews_task_status, "Completed") == 0) - status = ICAL_STATUS_COMPLETED; - else if (g_strcmp0 (ews_task_status, "InProgress") == 0) - status = ICAL_STATUS_INPROCESS; - else if (g_strcmp0 (ews_task_status, "WaitingOnOthers") == 0) - status = ICAL_STATUS_NEEDSACTION; - else if (g_strcmp0 (ews_task_status, "Deferred") == 0) - status = ICAL_STATUS_CANCELLED; - icalprop = icalproperty_new_status (status); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*precent complete*/ - percent_complete = e_ews_item_get_percent_complete (item); - icalprop = icalproperty_new_percentcomplete (atoi (percent_complete ? percent_complete : "0")); - icalcomponent_add_property (icalcomp, icalprop); - - /*due date*/ - e_ews_item_task_has_due_date (item, &has_this_date); - if (has_this_date) { - due_date = icaltime_from_timet_with_zone (e_ews_item_get_due_date (item), 0, user_timezone); - due_date.is_date = 1; - icalprop = icalproperty_new_due (due_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*complete date*/ - has_this_date = FALSE; - e_ews_item_task_has_complete_date (item, &has_this_date); - if (has_this_date) { - complete_date = icaltime_from_timet_with_zone (e_ews_item_get_complete_date (item), 0, user_timezone); - icalprop = icalproperty_new_completed (complete_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*priority*/ - item_importance = e_ews_item_get_importance (item); - if (item_importance == EWS_ITEM_HIGH) - priority = 3; - else if (item_importance == EWS_ITEM_LOW) - priority = 7; - icalprop = icalproperty_new_priority (priority); - icalcomponent_add_property (icalcomp, icalprop); - } - - icalcomponent_add_component (vcomp, icalcomp); - } else { - struct icaltimetype dt; - const gchar *mime_content; - const gchar *tzid; - gboolean timezone_set = FALSE; - - mime_content = e_ews_item_get_mime_content (item); - vcomp = mime_content && *mime_content ? icalparser_parse_string (mime_content) : NULL; - - if (!vcomp && mime_content && *mime_content) { - const gchar *begin_vcalendar, *end_vcalendar; - - /* Workaround Exchange 2016 error, which returns invalid iCalendar object (without 'END:VCALENDAR'), - when the event has at least one detached instance. */ - begin_vcalendar = camel_strstrcase (mime_content, "BEGIN:VCALENDAR"); - end_vcalendar = camel_strstrcase (mime_content, "END:VCALENDAR"); - - /* If it exists, then it should be alone on a separate line */ - if (!(begin_vcalendar && (begin_vcalendar == mime_content || begin_vcalendar[-1] == '\n') && - (begin_vcalendar[15 /* strlen ("BEGIN:VCALENDAR") */] == '\r' || begin_vcalendar[15] == '\n'))) - begin_vcalendar = NULL; - - /* If it exists, then it should be alone on a separate line and not at the very beginning of the mime_content */ - if (!(end_vcalendar && end_vcalendar > mime_content && end_vcalendar[-1] == '\n' && - (end_vcalendar[13 /* strlen ("END:VCALENDAR") */] == '\r' || end_vcalendar[13] == '\n' || end_vcalendar[13] == '\0'))) - end_vcalendar = NULL; - - if (begin_vcalendar && !end_vcalendar) { - gchar *str; - - str = g_strconcat (mime_content, "\r\n", "END:VCALENDAR", "\r\n", NULL); - vcomp = icalparser_parse_string (str); - g_free (str); - } - } - - if (!vcomp) { - if (mime_content) - g_warning ("%s: Failed to parse mime content:---%s---", G_STRFUNC, mime_content); - return NULL; - } - - tzid = e_ews_item_get_tzid (item); - if (tzid == NULL) { - /* - * When we are working with Exchange server 2010 or newer, we have to handle a few - * things more than we do working old servers. These things are: - * - MSDN timezone names: - * Used setting StartTimeZone and EndTimeZone. MSDN timezone names are not - * the same used in libical, so we need to have a table of equivalence to - * convert from one to another and avoid show the MSDN timezone name to the - * user and save it in the ETimezoneCache. - * - EvoEWSStartTimeZone/EvoEWSEndTimeZone - * Used to keep track if the timezone shown to the user is the same one set - * by him/her. As we have a table of equivalence, sometimes the user sets a - * timezone but without EvoEWSStartTiemZone property, another timezone name, - * in the same offset, can be shown. And we want to avoid this. - * - DTEND property: - * As we have to work with DTEND setting an event when using EWS server 2010 or - * newer, we have to care about set it properly here, instead of use the same - * as is used in DTSTART. - */ - icaltimezone *start_zone, *end_zone; - const gchar *start_tzid, *end_tzid; - const gchar *ical_start_tzid, *ical_end_tzid; - const gchar *evo_ews_start_tzid, *evo_ews_end_tzid; - - start_tzid = e_ews_item_get_start_tzid (item); - end_tzid = e_ews_item_get_end_tzid (item); - - ical_start_tzid = e_cal_backend_ews_tz_util_get_ical_equivalent (start_tzid); - ical_end_tzid = e_cal_backend_ews_tz_util_get_ical_equivalent (end_tzid); - - evo_ews_start_tzid = e_ews_item_get_iana_start_time_zone (item); - evo_ews_end_tzid = e_ews_item_get_iana_end_time_zone (item); - - /* - * We have a few timezones that don't have an equivalent MSDN timezone. - * For those, we will get ical_start_tzid being NULL and then we need to use - * start_tzid, which one has the libical's expected name. - */ - start_zone = ecb_ews_get_timezone ( - timezone_cache, - start_tzid, - ical_start_tzid != NULL ? ical_start_tzid : start_tzid, - evo_ews_start_tzid); - end_zone = ecb_ews_get_timezone ( - timezone_cache, - end_tzid, - ical_end_tzid != NULL ? ical_end_tzid : end_tzid, - evo_ews_end_tzid); - - if (start_zone != NULL) { - icalcomp = icalcomponent_get_first_component (vcomp, kind); - - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - dt = icaltime_convert_to_zone (dt, start_zone); - icalcomponent_set_dtstart (icalcomp, dt); - - timezone_set = TRUE; - e_timezone_cache_add_timezone (timezone_cache, start_zone); - - if (end_zone != NULL) { - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); - dt = icaltime_convert_to_zone (dt, end_zone); - icalcomponent_set_dtend (icalcomp, dt); - - e_timezone_cache_add_timezone (timezone_cache, end_zone); - } - } - - if (!timezone_set) - tzid = start_tzid; - } - - if (!timezone_set && tzid) { - /* - * When we are working with Exchange server older than 2010, we don't set different - * DTSTART and DTEND properties in VTIMEZONE. The reason of that is we don't use - * those properties settings/changing a meeting timezone. - * So, for older servers, here, we only set the DTSTART and DTEND properties with - * the same values. - */ - icaltimezone *zone; - gchar *new_tzid = NULL; - - icalcomp = icalcomponent_get_first_component (vcomp, kind); - - if (!icaltimezone_get_builtin_timezone (tzid) && - icalcomponent_get_uid (icalcomp)) { - icalcomponent *vtimezone; - - /* Add the timezone */ - vtimezone = icalcomponent_get_first_component (vcomp, ICAL_VTIMEZONE_COMPONENT); - if (vtimezone != NULL) { - icalproperty *prop; - - new_tzid = g_strconcat ("/evolution/ews/tzid/", icalcomponent_get_uid (icalcomp), NULL); - - zone = icaltimezone_new (); - vtimezone = icalcomponent_new_clone (vtimezone); - prop = icalcomponent_get_first_property (vtimezone, ICAL_TZID_PROPERTY); - if (prop) { - icalproperty_set_tzid (prop, new_tzid); - - prop = icalcomponent_get_first_property (vtimezone, ICAL_LOCATION_PROPERTY); - if (!prop) { - /* Use the original tzid as the timezone Location, to not expose - evolution-ews TZID. */ - prop = icalproperty_new_location (tzid); - icalcomponent_add_property (vtimezone, prop); - } - } else { - g_free (new_tzid); - new_tzid = NULL; - } - icaltimezone_set_component (zone, vtimezone); - e_timezone_cache_add_timezone (timezone_cache, zone); - icaltimezone_free (zone, TRUE); - } - } - - zone = e_timezone_cache_get_timezone (timezone_cache, new_tzid ? new_tzid : tzid); - - if (!zone && new_tzid) - zone = e_timezone_cache_get_timezone (timezone_cache, tzid); - - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (tzid); - - if (zone != NULL) { - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - dt = icaltime_convert_to_zone (dt, zone); - icalcomponent_set_dtstart (icalcomp, dt); - - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); - dt = icaltime_convert_to_zone (dt, zone); - icalcomponent_set_dtend (icalcomp, dt); - } - - g_free (new_tzid); - } - } - - /* Vevent or Vtodo */ - icalcomp = icalcomponent_get_first_component (vcomp, kind); - if (icalcomp) { - icalproperty *icalprop, *freebusy; - struct icaltimetype itt; - const EwsId *item_id; - const GSList *l = NULL; - const gchar *uid = e_ews_item_get_uid (item); - - item_id = e_ews_item_get_id (item); - - if (e_ews_item_get_is_meeting (item)) { - gboolean is_response_requested = e_ews_item_get_is_response_requested (item); - gchar *user_email; - - user_email = camel_ews_settings_dup_email (ews_settings); - - /* Attendees */ - for (l = e_ews_item_get_attendees (item); l != NULL; l = g_slist_next (l)) { - icalparameter *param, *cu_type; - gchar *mailtoname; - const gchar *email = NULL; - EwsAttendee *attendee = (EwsAttendee *) l->data; - - if (!attendee->mailbox) - continue; - - if (g_strcmp0 (attendee->mailbox->routing_type, "EX") == 0) - email = e_ews_item_util_strip_ex_address (attendee->mailbox->email); - - mailtoname = g_strdup_printf ("mailto:%s", email ? email : attendee->mailbox->email); - icalprop = icalproperty_new_attendee (mailtoname); - g_free (mailtoname); - - param = icalparameter_new_cn (attendee->mailbox->name); - icalproperty_add_parameter (icalprop, param); - - if (g_ascii_strcasecmp (attendee->attendeetype, "Required") == 0) { - param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL); - } - else if (g_ascii_strcasecmp (attendee->attendeetype, "Resource") == 0) { - param = icalparameter_new_role (ICAL_ROLE_NONPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_RESOURCE); - } - else { - param = icalparameter_new_role ( ICAL_ROLE_OPTPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL); - } - icalproperty_add_parameter (icalprop, cu_type); - icalproperty_add_parameter (icalprop, param); - - if (is_response_requested) { - param = icalparameter_new_rsvp (ICAL_RSVP_TRUE); - icalproperty_add_parameter (icalprop, param); - } - - if (user_email && (email || attendee->mailbox->email) && e_ews_item_get_my_response_type (item) && - g_ascii_strcasecmp (email ? email : attendee->mailbox->email, user_email) == 0) { - param = ecb_ews_responsetype_to_partstat (e_ews_item_get_my_response_type (item)); - } else { - param = ecb_ews_responsetype_to_partstat (attendee->responsetype); - } - icalproperty_add_parameter (icalprop, param); - - icalcomponent_add_property (icalcomp, icalprop); - } - - g_free (user_email); - } - - /* Free/Busy */ - freebusy = icalcomponent_get_first_property (icalcomp, ICAL_TRANSP_PROPERTY); - if (!freebusy && (e_ews_item_get_item_type (item) != E_EWS_ITEM_TYPE_TASK)) { - /* Busy by default */ - freebusy = icalproperty_new_transp (ICAL_TRANSP_OPAQUE); - icalcomponent_add_property (icalcomp, freebusy); - } - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); - icalprop != NULL; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) { - if (g_strcmp0 (icalproperty_get_x_name (icalprop), "X-MICROSOFT-CDO-BUSYSTATUS") == 0) { - if (g_strcmp0 (icalproperty_get_value_as_string (icalprop), "BUSY") == 0) { - icalproperty_set_transp (freebusy, ICAL_TRANSP_OPAQUE); - } else { - icalproperty_set_transp (freebusy, ICAL_TRANSP_TRANSPARENT); - } - - break; - } - } - - /* AllDayEvent */ - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); - icalprop != NULL; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) { - if (g_strcmp0 (icalproperty_get_x_name (icalprop), "X-MICROSOFT-CDO-ALLDAYEVENT") == 0) { - if (g_strcmp0 (icalproperty_get_value_as_string (icalprop), "TRUE") == 0) { - struct icaltimetype dtend, dtstart; - dtstart = icalcomponent_get_dtstart (icalcomp); - dtstart.is_date = 1; - icalcomponent_set_dtstart (icalcomp, dtstart); - - dtend = icalcomponent_get_dtend (icalcomp); - dtend.is_date = 1; - icalcomponent_set_dtend (icalcomp, dtend); - } - break; - } - } - - if (icalcomponent_get_first_property (icalcomp, ICAL_RECURRENCEID_PROPERTY)) { - /* Exchange sets RRULE even on the children, which is broken */ - icalprop = icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY); - if (icalprop) { - icalcomponent_remove_property (icalcomp, icalprop); - icalproperty_free (icalprop); - } - } - - /* The EXDATE sent by the server can be date-time format with timezone, while - the event start time can be date-only. This breaks the rules, thus correct - it and make also EXDATE date-only. */ - itt = icalcomponent_get_dtstart (icalcomp); - if (icaltime_is_valid_time (itt) && itt.is_date) { - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_EXDATE_PROPERTY); - icalprop; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_EXDATE_PROPERTY)) { - itt = icalproperty_get_exdate (icalprop); - itt.is_date = 1; - icalproperty_set_exdate (icalprop, itt); - - icalproperty_remove_parameter_by_kind (icalprop, ICAL_TZID_PARAMETER); - } - } - - /* Exchange sets an ORGANIZER on all events. RFC2445 says: - * - * This property MUST NOT be specified in an iCalendar - * object that specifies only a time zone definition or - * that defines calendar entities that are not group - * scheduled entities, but are entities only on a single - * user's calendar. - */ - if (!icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) { - if ((icalprop = icalcomponent_get_first_property (icalcomp, ICAL_ORGANIZER_PROPERTY))) { - icalcomponent_remove_property (icalcomp, icalprop); - icalproperty_free (icalprop); - } - } - - icalcomponent_set_uid (icalcomp, uid ? uid : item_id->id); - - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-ITEMID", item_id->id); - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY", item_id->change_key); - - res_component = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (icalcomp)); - - /* Categories */ - e_cal_component_set_categories_list (res_component, (GSList *) e_ews_item_get_categories (item)); - - /* - * There is no API to set/get alarm description on the server side. - * However, for some reason, the alarm description has been set to "REMINDER" - * automatically (and with no i18n). Instead of show it to the user, let's - * set the summary as the alarm description. - */ - if (e_cal_component_has_alarms (res_component)) { - GList *alarm_uids, *l; - - alarm_uids = e_cal_component_get_alarm_uids (res_component); - for (l = alarm_uids; l != NULL; l = l->next) { - ECalComponentAlarm *alarm; - ECalComponentText text; - - alarm = e_cal_component_get_alarm (res_component, l->data); - e_cal_component_get_summary (res_component, &text); - e_cal_component_alarm_set_description (alarm, &text); - - e_cal_component_alarm_free (alarm); - } - cal_obj_uid_list_free (alarm_uids); - } - } - - icalcomponent_free (vcomp); - - if (res_component) { - const GSList *attachment_ids, *aid, *l; - const gchar *uid = NULL; - GSList *info_attachments = NULL, *uris = NULL; - gboolean has_attachment = FALSE; - - e_ews_item_has_attachments (item, &has_attachment); - if (!has_attachment) - return res_component; - - e_cal_component_get_uid (res_component, &uid); - - attachment_ids = e_ews_item_get_attachments_ids (item); - - if (e_ews_connection_get_attachments_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - uid, - attachment_ids, - cbews->priv->attachments_dir, - TRUE, - &info_attachments, - NULL, NULL, - cancellable, - NULL)) { - icalcomponent *icalcomp; - icalproperty *icalprop; - icalparameter *icalparam; - - for (l = info_attachments; l; l = l->next) { - EEwsAttachmentInfo *info = l->data; - - /* ignore non-uri attachments, because it's an exception */ - if (e_ews_attachment_info_get_type (info) == E_EWS_ATTACHMENT_INFO_TYPE_URI) { - const gchar *uri = e_ews_attachment_info_get_uri (info); - - if (uri) - uris = g_slist_append (uris, g_strdup (uri)); - } - } - - e_cal_component_set_attachment_list (res_component, uris); - - icalcomp = e_cal_component_get_icalcomponent (res_component); - icalprop = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY); - for (aid = attachment_ids; aid && icalprop; aid = aid->next, icalprop = icalcomponent_get_next_property (icalcomp, ICAL_ATTACH_PROPERTY)) { - icalparam = icalparameter_new_x (aid->data); - icalparameter_set_xname (icalparam, "X-EWS-ATTACHMENTID"); - icalproperty_add_parameter (icalprop, icalparam); - } - - g_slist_free_full (uris, g_free); - g_slist_free_full (info_attachments, (GDestroyNotify) e_ews_attachment_info_free); - } - } - - return res_component; -} - -static void -ecb_ews_store_original_comp (ECalComponent *comp) -{ - gchar *comp_str; - gchar *base64; - - g_return_if_fail (E_IS_CAL_COMPONENT (comp)); - - comp_str = e_cal_component_get_as_string (comp); - g_return_if_fail (comp_str != NULL); - - /* Include NUL-terminator */ - base64 = g_base64_encode ((const guchar *) comp_str, strlen (comp_str) + 1); - - e_cal_util_set_x_property (e_cal_component_get_icalcomponent (comp), - X_EWS_ORIGINAL_COMP, base64); - - g_free (base64); - g_free (comp_str); -} - -static ECalComponent * /* free with g_object_unref(), if not NULL */ -ecb_ews_restore_original_comp (ECalComponent *from_comp) -{ - ECalComponent *comp = NULL; - const gchar *original_base64; - guchar *decoded; - gsize len = -1; - - g_return_val_if_fail (E_IS_CAL_COMPONENT (from_comp), NULL); - - original_base64 = e_cal_util_get_x_property (e_cal_component_get_icalcomponent (from_comp), X_EWS_ORIGINAL_COMP); - - if (!original_base64 || !*original_base64) - return NULL; - - decoded = g_base64_decode (original_base64, &len); - if (!decoded || !*decoded || len <= 0) { - g_free (decoded); - return NULL; - } - - if (decoded[len - 1] != '\0') { - gchar *tmp; - - tmp = g_strndup ((const gchar *) decoded, len); - - g_free (decoded); - decoded = (guchar *) tmp; - } - - if (decoded && *decoded) - comp = e_cal_component_new_from_string ((const gchar *) decoded); - - g_free (decoded); - - return comp; -} - -static gboolean -ecb_ews_get_items_sync (ECalBackendEws *cbews, - const GSList *item_ids, /* gchar * */ - const gchar *default_props, - const EEwsAdditionalProps *add_props, - GSList **out_components, /* ECalComponent * */ - GCancellable *cancellable, - GError **error) -{ - GSList *items = NULL, *link; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (out_components != NULL, FALSE); - - success = e_ews_connection_get_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - item_ids, - default_props, - add_props, - FALSE, - NULL, - E_EWS_BODY_TYPE_TEXT, - &items, - NULL, NULL, - cancellable, - error); - - if (!success) - return FALSE; - - /* fetch modified occurrences */ - for (link = items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const GSList *modified_occurrences; - - if (!item || e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - modified_occurrences = e_ews_item_get_modified_occurrences (item); - if (modified_occurrences) { - EEwsAdditionalProps *modified_add_props; - - modified_add_props = e_ews_additional_props_new (); - if (e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) { - EEwsExtendedFieldURI *ext_uri; - - modified_add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2010); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSStartTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - modified_add_props->extended_furis = g_slist_append (modified_add_props->extended_furis, ext_uri); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSEndTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - modified_add_props->extended_furis = g_slist_append (modified_add_props->extended_furis, ext_uri); - } else { - modified_add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2007); - } - - success = ecb_ews_get_items_sync (cbews, modified_occurrences, "IdOnly", modified_add_props, out_components, cancellable, error); - - e_ews_additional_props_free (modified_add_props); - - if (!success) - goto exit; - } - } - - for (link = items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - ECalComponent *comp; - GError *local_error = NULL; - - if (!item || e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - comp = ecb_ews_item_to_component_sync (cbews, item, cancellable, &local_error); - if (!comp) { - if (!local_error) - continue; - - g_propagate_error (error, local_error); - success = FALSE; - break; - } - - ecb_ews_store_original_comp (comp); - - *out_components = g_slist_prepend (*out_components, comp); - } - - exit: - g_slist_free_full (items, g_object_unref); - - return success; -} - -static gboolean -ecb_ews_fetch_items_sync (ECalBackendEws *cbews, - const GSList *items, /* EEwsItem * */ - GSList **out_components, /* ECalComponent * */ - GCancellable *cancellable, - GError **error) -{ - GSList *event_ids = NULL, *task_memo_ids = NULL, *link; - gboolean success = TRUE; - - for (link = (GSList *) items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (type == E_EWS_ITEM_TYPE_EVENT) - event_ids = g_slist_prepend (event_ids, g_strdup (id->id)); - else if (type == E_EWS_ITEM_TYPE_TASK || type == E_EWS_ITEM_TYPE_MEMO) { - task_memo_ids = g_slist_prepend (task_memo_ids, g_strdup (id->id)); - } - } - - if (event_ids) { - EEwsAdditionalProps *add_props; - - add_props = e_ews_additional_props_new (); - if (e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) { - EEwsExtendedFieldURI *ext_uri; - - add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2010); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSStartTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - add_props->extended_furis = g_slist_append (add_props->extended_furis, ext_uri); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSEndTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - add_props->extended_furis = g_slist_append (add_props->extended_furis, ext_uri); - } else { - add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2007); - } - - success = ecb_ews_get_items_sync (cbews, event_ids, "IdOnly", add_props, out_components, cancellable, error); - - e_ews_additional_props_free (add_props); - } - - if (task_memo_ids && success) - success = ecb_ews_get_items_sync (cbews, task_memo_ids, "AllProperties", NULL, out_components, cancellable, error); - - g_slist_free_full (event_ids, g_free); - g_slist_free_full (task_memo_ids, g_free); - - return success; -} - -static gboolean -ecb_ews_freebusy_ecomp_changed (ECalComponent *ecomp, - icalcomponent *vevent) -{ - icalcomponent *icomp; - gboolean changed = FALSE; - - g_return_val_if_fail (vevent != NULL, FALSE); - - if (!ecomp) - return TRUE; - - icomp = e_cal_component_get_icalcomponent (ecomp); - if (!icomp) - return TRUE; - - if (!changed) - changed = g_strcmp0 (icalcomponent_get_summary (icomp), icalcomponent_get_summary (vevent)) != 0; - if (!changed) - changed = g_strcmp0 (icalcomponent_get_location (icomp), icalcomponent_get_location (vevent)) != 0; - if (!changed) - changed = icaltime_compare (icalcomponent_get_dtstart (icomp), icalcomponent_get_dtstart (vevent)) != 0; - if (!changed) - changed = icaltime_compare (icalcomponent_get_dtend (icomp), icalcomponent_get_dtend (vevent)) != 0; - - return changed; -} - -static GSList * /* the possibly modified 'in_items' */ -ecb_ews_verify_changes (ECalCache *cal_cache, - icalcomponent_kind kind, - GSList *in_items, /* EEwsItem * */ - GCancellable *cancellable) -{ - GSList *items = NULL, *link; - - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), in_items); - - for (link = in_items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (!g_cancellable_is_cancelled (cancellable) && ( - (type == E_EWS_ITEM_TYPE_EVENT && kind == ICAL_VEVENT_COMPONENT) || - (type == E_EWS_ITEM_TYPE_MEMO && kind == ICAL_VJOURNAL_COMPONENT) || - (type == E_EWS_ITEM_TYPE_TASK && kind == ICAL_VTODO_COMPONENT) )) { - ECalComponent *existing = NULL; - - if (e_cal_cache_get_component (cal_cache, id->id, NULL, &existing, cancellable, NULL) && - existing && g_strcmp0 (e_cal_util_get_x_property (e_cal_component_get_icalcomponent (existing), - "X-EVOLUTION-CHANGEKEY"), id->change_key) == 0) { - g_object_unref (item); - } else { - items = g_slist_prepend (items, item); - } - - g_clear_object (&existing); - } else if (type == E_EWS_ITEM_TYPE_EVENT || - type == E_EWS_ITEM_TYPE_MEMO || - type == E_EWS_ITEM_TYPE_TASK) { - g_object_unref (item); - } else { - items = g_slist_prepend (items, item); - } - } - - g_slist_free (in_items); - - return items; -} - -static GSList * /* ECalMetaBackendInfo */ -ecb_ews_components_to_infos (ECalMetaBackend *meta_backend, - const GSList *components, /* ECalComponent * */ - icalcomponent_kind kind) -{ - GSList *nfos = NULL, *link; - GHashTable *sorted_by_uids; /* gchar * ~> GSList { ECalComponent * } */ - GHashTableIter iter; - gpointer key, value; - - sorted_by_uids = g_hash_table_new (g_str_hash, g_str_equal); - - for (link = (GSList *) components; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - icalcomponent *icomp; - const gchar *uid; - GSList *instances; - - if (!comp) - continue; - - icomp = e_cal_component_get_icalcomponent (comp); - uid = icalcomponent_get_uid (icomp); - - if (!uid) - continue; - - ecb_ews_store_original_comp (comp); - - instances = g_hash_table_lookup (sorted_by_uids, uid); - g_hash_table_insert (sorted_by_uids, (gpointer) uid, g_slist_prepend (instances, comp)); - } - - g_hash_table_iter_init (&iter, sorted_by_uids); - while (g_hash_table_iter_next (&iter, &key, &value)) { - const gchar *uid = key; - GSList *instances = value, *link; - icalcomponent *icomp, *merged; - ECalComponent *comp; - ECalMetaBackendInfo *nfo; - const gchar *revision, *itemid; - - if (!uid || !instances) { - g_slist_free (instances); - continue; - } - - /* Try to find master object, to have seves marter's itemid in ECalMetaBackendInfo::extra, - thus the re-load of the event is done for the whole series and not for a detached instance */ - comp = NULL; - for (link = instances; link && !comp; link = g_slist_next (link)) { - comp = link->data; - - if (!comp) - continue; - - if (e_cal_component_is_instance (comp)) - comp = NULL; - } - - if (!comp) - comp = instances->data; - - if (!comp) { - g_slist_free (instances); - continue; - } - - icomp = e_cal_component_get_icalcomponent (comp); - itemid = e_cal_util_get_x_property (icomp, "X-EVOLUTION-ITEMID"); - revision = e_cal_util_get_x_property (icomp, "X-EVOLUTION-CHANGEKEY"); - merged = e_cal_meta_backend_merge_instances (meta_backend, instances, FALSE); - - if (!merged) { - g_warn_if_fail (merged != NULL); - g_slist_free (instances); - continue; - } - - nfo = e_cal_meta_backend_info_new (uid, revision, NULL, itemid); - nfo->object = icalcomponent_as_ical_string_r (merged); - - nfos = g_slist_prepend (nfos, nfo); - - icalcomponent_free (merged); - g_slist_free (instances); - } - - g_hash_table_destroy (sorted_by_uids); - - return nfos; -} - -static void -ecb_ews_extract_item_id (ECalComponent *comp, - gchar **out_id, - gchar **out_change_key) -{ - icalcomponent *icalcomp; - - g_return_if_fail (E_IS_CAL_COMPONENT (comp)); - - icalcomp = e_cal_component_get_icalcomponent (comp); - g_return_if_fail (icalcomp != NULL); - - if (out_id) - *out_id = e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-ITEMID"); - if (out_change_key) - *out_change_key = e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY"); -} - -static gboolean -ecb_ews_is_organizer (ECalBackendEws *cbews, - ECalComponent *comp) -{ - ECalComponentOrganizer organizer; - gboolean is_organizer = FALSE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE); - - if (!e_cal_component_has_organizer (comp)) - return FALSE; - - organizer.value = NULL; - - e_cal_component_get_organizer (comp, &organizer); - if (organizer.value) { - CamelEwsSettings *ews_settings; - const gchar *email = organizer.value; - gchar *user_email; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - user_email = camel_ews_settings_dup_email (ews_settings); - - if (!g_ascii_strncasecmp (email, "mailto:", 7)) - email += 7; - - is_organizer = user_email && g_ascii_strcasecmp (email, user_email) == 0; - - g_free (user_email); - } - - return is_organizer; -} - -static gboolean -ecb_ews_connect_sync (ECalMetaBackend *meta_backend, - const ENamedParameters *credentials, - ESourceAuthenticationResult *out_auth_result, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - CamelEwsSettings *ews_settings; - gchar *hosturl; - gboolean success = FALSE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_auth_result != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->cnc) { - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - *out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED; - - return TRUE; - } - - ews_settings = ecb_ews_get_collection_settings (cbews); - hosturl = camel_ews_settings_dup_hosturl (ews_settings); - - cbews->priv->cnc = e_ews_connection_new_for_backend (E_BACKEND (cbews), e_cal_backend_get_registry (E_CAL_BACKEND (cbews)), hosturl, ews_settings); - - e_binding_bind_property ( - cbews, "proxy-resolver", - cbews->priv->cnc, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - *out_auth_result = e_ews_connection_try_credentials_sync (cbews->priv->cnc, credentials, cancellable, error); - - if (*out_auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) { - ESource *source = e_backend_get_source (E_BACKEND (cbews)); - ESourceEwsFolder *ews_folder; - - ews_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER); - - g_free (cbews->priv->folder_id); - cbews->priv->folder_id = e_source_ews_folder_dup_id (ews_folder); - cbews->priv->is_freebusy_calendar = cbews->priv->folder_id && g_str_has_prefix (cbews->priv->folder_id, "freebusy-calendar::"); - - g_signal_connect_swapped (cbews->priv->cnc, "server-notification", - G_CALLBACK (ecb_ews_server_notification_cb), cbews); - - if (!cbews->priv->is_freebusy_calendar && - camel_ews_settings_get_listen_notifications (ews_settings) && - e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010_SP1)) { - GSList *folders = NULL; - - folders = g_slist_prepend (folders, cbews->priv->folder_id); - - e_ews_connection_enable_notifications_sync (cbews->priv->cnc, - folders, &cbews->priv->subscription_key); - - g_slist_free (folders); - } - - e_cal_backend_set_writable (E_CAL_BACKEND (cbews), !cbews->priv->is_freebusy_calendar); - success = TRUE; - } else { - ecb_ews_convert_error_to_edc_error (error); - g_clear_object (&cbews->priv->cnc); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - g_free (hosturl); - - return success; -} - -static gboolean -ecb_ews_disconnect_sync (ECalMetaBackend *meta_backend, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - ecb_ews_unset_connection (cbews); - - return TRUE; -} - -static gboolean -ecb_ews_get_changes_sync (ECalMetaBackend *meta_backend, - const gchar *last_sync_tag, - gboolean is_repeat, - gchar **out_new_sync_tag, - gboolean *out_repeat, - GSList **out_created_objects, - GSList **out_modified_objects, - GSList **out_removed_objects, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - gboolean success = TRUE; - GError *local_error = NULL; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_new_sync_tag != NULL, FALSE); - g_return_val_if_fail (out_repeat != NULL, FALSE); - g_return_val_if_fail (out_created_objects != NULL, FALSE); - g_return_val_if_fail (out_modified_objects != NULL, FALSE); - g_return_val_if_fail (out_removed_objects != NULL, FALSE); - - *out_created_objects = NULL; - *out_modified_objects = NULL; - *out_removed_objects = NULL; - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->is_freebusy_calendar) { - ESourceEwsFolder *ews_folder; - EEWSFreeBusyData fbdata; - GSList *free_busy = NULL, *link; - gboolean success; - time_t today; - - ews_folder = e_source_get_extension (e_backend_get_source (E_BACKEND (cbews)), E_SOURCE_EXTENSION_EWS_FOLDER); - - today = time_day_begin (time (NULL)); - - fbdata.period_start = time_add_week (today, -e_source_ews_folder_get_freebusy_weeks_before (ews_folder)); - fbdata.period_end = time_day_end (time_add_week (today, e_source_ews_folder_get_freebusy_weeks_after (ews_folder))); - fbdata.user_mails = g_slist_prepend (NULL, e_source_ews_folder_dup_foreign_mail (ews_folder)); - - success = e_ews_connection_get_free_busy_sync (cbews->priv->cnc, G_PRIORITY_DEFAULT, - e_ews_cal_utils_prepare_free_busy_request, &fbdata, - &free_busy, cancellable, &local_error); - - if (success) { - icaltimezone *utc_zone = icaltimezone_get_utc_timezone (); - GSList *comps = NULL; - GHashTable *known; - GHashTableIter iter; - gpointer key; - - known = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - - if (e_cal_cache_search_components (cal_cache, NULL, &comps, cancellable, NULL)) { - for (link = comps; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - icalcomponent *icomp; - const gchar *uid; - - if (!comp) - continue; - - icomp = e_cal_component_get_icalcomponent (comp); - if (!icomp) - continue; - - uid = icalcomponent_get_uid (icomp); - - if (uid && *uid) - g_hash_table_insert (known, g_strdup (uid), g_object_ref (comp)); - } - - g_slist_free_full (comps, g_object_unref); - } - - for (link = free_busy; link; link = g_slist_next (link)) { - icalcomponent *fbcomp = link->data; - icalproperty *fbprop; - icalparameter *param; - struct icalperiodtype fb; - icalparameter_fbtype fbtype; - - if (!fbcomp || icalcomponent_isa (fbcomp) != ICAL_VFREEBUSY_COMPONENT) - continue; - - for (fbprop = icalcomponent_get_first_property (fbcomp, ICAL_FREEBUSY_PROPERTY); - fbprop; - fbprop = icalcomponent_get_next_property (fbcomp, ICAL_FREEBUSY_PROPERTY)) { - ECalComponent *ecomp; - icalcomponent *vevent; - const gchar *id, *summary, *location; - - param = icalproperty_get_first_parameter (fbprop, ICAL_FBTYPE_PARAMETER); - if (!param) - continue; - - fbtype = icalparameter_get_fbtype (param); - - if (fbtype != ICAL_FBTYPE_FREE && - fbtype != ICAL_FBTYPE_BUSY && - fbtype != ICAL_FBTYPE_BUSYUNAVAILABLE && - fbtype != ICAL_FBTYPE_BUSYTENTATIVE) - continue; - - fb = icalproperty_get_freebusy (fbprop); - id = icalproperty_get_parameter_as_string (fbprop, "X-EWS-ID"); - summary = icalproperty_get_parameter_as_string (fbprop, "X-SUMMARY"); - location = icalproperty_get_parameter_as_string (fbprop, "X-LOCATION"); - - vevent = icalcomponent_new_vevent (); - - if (id && *id) { - icalcomponent_set_uid (vevent, id); - } else { - gchar *uid; - - uid = g_strdup_printf ("%s-%s-%d", - icaltime_as_ical_string (fb.start), - icaltime_as_ical_string (fb.end), - (gint) fbtype); - - icalcomponent_set_uid (vevent, uid); - - g_free (uid); - } - - fb.start.zone = utc_zone; - fb.end.zone = utc_zone; - - icalcomponent_set_dtstart (vevent, fb.start); - icalcomponent_set_dtend (vevent, fb.end); - - icalcomponent_add_property (vevent, icalproperty_new_created (icaltime_current_time_with_zone (utc_zone))); - - if (fbtype == ICAL_FBTYPE_FREE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Free")); - icalcomponent_add_property (vevent, icalproperty_new_transp (ICAL_TRANSP_TRANSPARENT)); - } else if (fbtype == ICAL_FBTYPE_BUSY) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Busy")); - } else if (fbtype == ICAL_FBTYPE_BUSYUNAVAILABLE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Out of Office")); - } else if (fbtype == ICAL_FBTYPE_BUSYTENTATIVE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Tentative")); - } - - if (summary && *summary) - icalcomponent_set_summary (vevent, summary); - - if (location && *location) - icalcomponent_set_location (vevent, location); - - ecomp = g_hash_table_lookup (known, icalcomponent_get_uid (vevent)); - if (ecomp) { - g_object_ref (ecomp); - - /* This dereferences the ecomp, thus the ref() call above to keep it alive */ - g_hash_table_remove (known, icalcomponent_get_uid (vevent)); - - if (ecb_ews_freebusy_ecomp_changed (ecomp, vevent)) { - ECalMetaBackendInfo *nfo; - gchar *revision = e_util_generate_uid (); - - e_cal_util_set_x_property (vevent, "X-EVOLUTION-CHANGEKEY", revision); - - nfo = e_cal_meta_backend_info_new (icalcomponent_get_uid (vevent), NULL, NULL, NULL); - nfo->revision = revision; - nfo->object = icalcomponent_as_ical_string_r (vevent); - - *out_created_objects = g_slist_prepend (*out_created_objects, nfo); - } else { - icalcomponent_free (vevent); - } - - g_clear_object (&ecomp); - } else { - ECalMetaBackendInfo *nfo; - gchar *revision = e_util_generate_uid (); - - e_cal_util_set_x_property (vevent, "X-EVOLUTION-CHANGEKEY", revision); - - nfo = e_cal_meta_backend_info_new (icalcomponent_get_uid (vevent), NULL, NULL, NULL); - nfo->revision = revision; - nfo->object = icalcomponent_as_ical_string_r (vevent); - - *out_modified_objects = g_slist_prepend (*out_modified_objects, nfo); - } - } - } - - g_hash_table_iter_init (&iter, known); - while (g_hash_table_iter_next (&iter, &key, NULL)) { - const gchar *uid = key; - - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_cal_meta_backend_info_new (uid, NULL, NULL, NULL)); - } - - g_hash_table_destroy (known); - } else if (g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NOFREEBUSYACCESS)) { - e_cal_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL); - - e_cal_backend_notify_error (E_CAL_BACKEND (cbews), local_error->message); - g_clear_error (&local_error); - } else { - g_propagate_error (error, local_error); - } - - g_slist_free_full (free_busy, (GDestroyNotify) icalcomponent_free); - g_slist_free_full (fbdata.user_mails, g_free); - } else { - GSList *items_created = NULL, *items_modified = NULL, *items_deleted = NULL, *link; - EEwsAdditionalProps *add_props; - gboolean includes_last_item = TRUE; - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("item:ItemClass"); - - success = e_ews_connection_sync_folder_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - last_sync_tag, cbews->priv->folder_id, "IdOnly", add_props, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - - if (!success && - g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) { - g_clear_error (&local_error); - - e_cal_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL); - - success = e_ews_connection_sync_folder_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - NULL, cbews->priv->folder_id, "IdOnly", add_props, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - } - - e_ews_additional_props_free (add_props); - - if (success) { - GSList *components_created = NULL, *components_modified = NULL; - icalcomponent_kind kind; - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - - /* The sync state doesn't cover changes made by save_component_sync(), - thus verify the changes, instead of re-donwloading the component again */ - items_created = ecb_ews_verify_changes (cal_cache, kind, items_created, cancellable); - items_modified = ecb_ews_verify_changes (cal_cache, kind, items_modified, cancellable); - - if (items_created) { - success = ecb_ews_fetch_items_sync (cbews, items_created, &components_created, cancellable, error); - if (success) - *out_created_objects = ecb_ews_components_to_infos (meta_backend, components_created, kind); - } - - if (items_modified) { - success = ecb_ews_fetch_items_sync (cbews, items_modified, &components_modified, cancellable, error); - if (success) - *out_modified_objects = ecb_ews_components_to_infos (meta_backend, components_modified, kind); - } - - for (link = items_deleted; link; link = g_slist_next (link)) { - const gchar *item_id = link->data; - GSList *ids = NULL, *ilink; - - if (!e_cal_cache_get_ids_with_extra (cal_cache, item_id, &ids, cancellable, NULL)) - continue; - - for (ilink = ids; ilink; ilink = g_slist_next (ilink)) { - ECalComponentId *id = ilink->data; - - /* Use the master object */ - if (id && id->uid && *id->uid && (!id->rid || !*id->rid)) { - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_cal_meta_backend_info_new (id->uid, NULL, NULL, NULL)); - break; - } - } - - g_slist_free_full (ids, (GDestroyNotify) e_cal_component_free_id); - } - - g_slist_free_full (components_created, g_object_unref); - g_slist_free_full (components_modified, g_object_unref); - - *out_repeat = !includes_last_item; - } else if (local_error) { - g_propagate_error (error, local_error); - } - - g_slist_free_full (items_created, g_object_unref); - g_slist_free_full (items_modified, g_object_unref); - g_slist_free_full (items_deleted, g_free); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_clear_object (&cal_cache); - - return success; -} - -static gboolean -ecb_ews_load_component_sync (ECalMetaBackend *meta_backend, - const gchar *uid, - const gchar *extra, - icalcomponent **out_component, - gchar **out_extra, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - GSList *ids, *items = NULL, *components = NULL; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (uid != NULL, FALSE); - g_return_val_if_fail (out_component != NULL, FALSE); - g_return_val_if_fail (out_extra != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - ids = g_slist_prepend (NULL, (gpointer) (extra && *extra ? extra : uid)); - - success = e_ews_connection_get_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, ids, "IdOnly", - NULL, FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &items, NULL, NULL, cancellable, error); - - g_slist_free (ids); - - if (success && items) { - success = ecb_ews_fetch_items_sync (cbews, items, &components, cancellable, error); - - if (components) { - const EwsId *ews_id = e_ews_item_get_id (items->data); - - if (ews_id) - *out_extra = g_strdup (ews_id->id); - - if (components->next) { - GSList *link; - - *out_component = icalcomponent_new_vcalendar (); - - for (link = components; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - if (!comp) - continue; - - icalcomponent_add_component (*out_component, - icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp))); - } - } else { - *out_component = icalcomponent_new_clone (e_cal_component_get_icalcomponent (components->data)); - } - } else { - success = FALSE; - } - } - - if (!components && e_cal_meta_backend_refresh_sync (meta_backend, cancellable, NULL)) { - ECalCache *cal_cache; - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - if (cal_cache) { - success = e_cal_cache_get_components_by_uid (cal_cache, uid, &components, cancellable, NULL); - if (success) { - *out_component = e_cal_meta_backend_merge_instances (meta_backend, components, FALSE); - - if (!e_cal_cache_get_component_extra (cal_cache, uid, NULL, out_extra, cancellable, NULL)) - *out_extra = NULL; - - g_clear_error (error); - } - g_object_unref (cal_cache); - } - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_slist_free_full (components, g_object_unref); - g_slist_free_full (items, g_object_unref); - - return success; -} - -/* Very simple and naive component comparator, to avoid - unnecessary uploads and changes on the server. */ -static gboolean -ecb_ews_components_equal (ECalComponent *comp1, - ECalComponent *comp2) -{ - icalcomponent *icomp1, *icomp2; - icalproperty *prop1; - GHashTable *processed_props; - gboolean equal = TRUE; - - if (!comp1 && !comp2) - return TRUE; - else if (!comp1 || !comp2) - return FALSE; - - icomp1 = e_cal_component_get_icalcomponent (comp1); - icomp2 = e_cal_component_get_icalcomponent (comp2); - - if (!icomp1 || !icomp2) - return FALSE; - - if (g_strcmp0 (icalcomponent_get_uid (icomp1), icalcomponent_get_uid (icomp2)) != 0) - return FALSE; - - if (icalcomponent_count_properties (icomp1, ICAL_ANY_PROPERTY) != - icalcomponent_count_properties (icomp2, ICAL_ANY_PROPERTY)) - return FALSE; - - processed_props = g_hash_table_new (g_direct_hash, g_direct_equal); - - for (prop1 = icalcomponent_get_first_property (icomp1, ICAL_ANY_PROPERTY); - prop1 && equal; - prop1 = icalcomponent_get_next_property (icomp1, ICAL_ANY_PROPERTY)) { - icalproperty_kind kind = icalproperty_isa (prop1); - icalproperty *prop2; - - for (prop2 = icalcomponent_get_first_property (icomp2, kind); - prop2; - prop2 = icalcomponent_get_next_property (icomp2, kind)) { - gchar *str1, *str2; - gboolean same; - - if (g_hash_table_contains (processed_props, prop2)) - continue; - - if (icalproperty_count_parameters (prop1) != icalproperty_count_parameters (prop2)) - continue; - - str1 = icalproperty_as_ical_string_r (prop1); - str2 = icalproperty_as_ical_string_r (prop2); - - same = g_strcmp0 (str1, str2) == 0; - - g_free (str1); - g_free (str2); - - if (same) { - g_hash_table_insert (processed_props, prop2, NULL); - break; - } - } - - if (!prop2) - equal = FALSE; - } - - g_hash_table_destroy (processed_props); - - return equal; -} - -typedef struct _ChangeData { - ECalComponent *old_component; - ECalComponent *new_component; -} ChangeData; - -static void -change_data_free (gpointer ptr) -{ - ChangeData *cd = ptr; - - if (cd) { - g_clear_object (&cd->old_component); - g_clear_object (&cd->new_component); - g_free (cd); - } -} - -static void -ecb_ews_filter_out_unchanged_instances (const GSList *to_save_instances, - const GSList *existing_instances, - GSList **out_changed_instances, /* ChangeData * */ - GSList **out_removed_instances) /* ECalComponent * */ -{ - GSList *link = NULL; - GHashTable *existing_hash; - GHashTableIter iter; - gpointer value; - - g_return_if_fail (to_save_instances != NULL); - g_return_if_fail (existing_instances != NULL); - g_return_if_fail (out_changed_instances != NULL); - g_return_if_fail (out_removed_instances != NULL); - - *out_changed_instances = NULL; - *out_removed_instances = NULL; - - existing_hash = g_hash_table_new_full ((GHashFunc)e_cal_component_id_hash, (GEqualFunc) e_cal_component_id_equal, - (GDestroyNotify) e_cal_component_free_id, NULL); - - for (link = (GSList *) existing_instances; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id; - - id = e_cal_component_get_id (comp); - if (id) - g_hash_table_insert (existing_hash, id, comp); - } - - for (link = (GSList *) to_save_instances; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id = NULL; - - id = e_cal_component_get_id (comp); - if (id) { - ECalComponent *old_comp; - - old_comp = g_hash_table_lookup (existing_hash, id); - - if (!ecb_ews_components_equal (comp, old_comp)) { - ChangeData *cd; - - cd = g_new0 (ChangeData, 1); - cd->old_component = old_comp ? g_object_ref (old_comp) : NULL; - cd->new_component = g_object_ref (comp); - - *out_changed_instances = g_slist_prepend (*out_changed_instances, cd); - } - - g_hash_table_remove (existing_hash, id); - e_cal_component_free_id (id); - } - } - - g_hash_table_iter_init (&iter, existing_hash); - while (g_hash_table_iter_next (&iter, NULL, &value)) { - *out_removed_instances = g_slist_prepend (*out_removed_instances, g_object_ref (value)); - } - - g_hash_table_destroy (existing_hash); -} - -static gboolean -ecb_ews_extract_attachments (icalcomponent *icalcomp, - GSList **out_attachments) /* EEwsAttachmentInfo * */ -{ - icalproperty *prop; - GSList *props = NULL, *link; - - g_return_val_if_fail (icalcomp != NULL, FALSE); - g_return_val_if_fail (out_attachments != NULL, FALSE); - - *out_attachments = NULL; - - for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY); - prop; - prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTACH_PROPERTY)) { - props = g_slist_prepend (props, prop); - } - - for (link = props; link; link = g_slist_next (link)) { - EEwsAttachmentInfo *info; - icalattach *attach; - icalparameter *param; - const gchar *stored_filename; - - prop = link->data; - param = icalproperty_get_first_parameter (prop, ICAL_FILENAME_PARAMETER); - stored_filename = param ? icalparameter_get_filename (param) : NULL; - - attach = icalproperty_get_attach (prop); - if (icalattach_get_is_url (attach)) { - const gchar *uri; - - uri = icalattach_get_url (attach); - - if (!uri || !*uri) - continue; - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_URI); - - e_ews_attachment_info_set_uri (info, uri); - if (stored_filename && *stored_filename) { - e_ews_attachment_info_set_prefer_filename (info, stored_filename); - } else { - gchar *uri_filename; - - uri_filename = g_filename_from_uri (uri, NULL, NULL); - if (uri_filename && *uri_filename) { - gchar *basename; - - basename = g_path_get_basename (uri_filename); - if (basename && *basename && basename[0] != '.' && basename[0] != G_DIR_SEPARATOR) { - const gchar *uid; - - uid = icalcomponent_get_uid (icalcomp); - - if (uid && g_str_has_prefix (basename, uid) && basename[strlen (uid)] == '-') { - e_ews_attachment_info_set_prefer_filename (info, basename + strlen (uid) + 1); - } - } - - g_free (basename); - } - - g_free (uri_filename); - } - } else { - gsize len = -1; - guchar *decoded = NULL; - const gchar *content; - - content = (const gchar *) icalattach_get_data (attach); - decoded = g_base64_decode (content, &len); - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_INLINED); - e_ews_attachment_info_set_inlined_data (info, decoded, len); - - if (stored_filename && *stored_filename) - e_ews_attachment_info_set_prefer_filename (info, stored_filename); - - g_free (decoded); - } - - e_ews_attachment_info_set_id (info, icalproperty_get_parameter_as_string (prop, "X-EWS-ATTACHMENTID")); - *out_attachments = g_slist_prepend (*out_attachments, info); - } - - g_slist_free (props); - - return *out_attachments != NULL; -} - -static icaltimezone * -ecb_ews_get_timezone_from_ical_component (ECalBackendEws *cbews, - icalcomponent *icalcomp) -{ - ETimezoneCache *timezone_cache; - icalproperty *prop = NULL; - const gchar *tzid = NULL; - - timezone_cache = E_TIMEZONE_CACHE (cbews); - - prop = icalcomponent_get_first_property (icalcomp, ICAL_DTSTART_PROPERTY); - if (prop != NULL) { - icalparameter *param = NULL; - - param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER); - if (param) { - tzid = icalparameter_get_tzid (param); - } else { - struct icaltimetype dtstart; - - dtstart = icalproperty_get_dtstart (prop); - if (icaltime_is_utc (dtstart)) - tzid = "UTC"; - } - } - - if (tzid) - return e_timezone_cache_get_timezone (timezone_cache, tzid); - - return NULL; -} - -static gboolean -ecb_ews_remove_item_sync (ECalBackendEws *cbews, - ECalCache *cal_cache, - GHashTable *removed_indexes, - const gchar *uid, - const gchar *rid, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *comp = NULL, *parent = NULL; - EwsId item_id = { 0 }; - gint index = 0; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE); - g_return_val_if_fail (uid != NULL, FALSE); - - if (rid && !*rid) - rid = NULL; - - if (!e_cal_cache_get_component (cal_cache, uid, rid, &comp, cancellable, error) || - (rid && !e_cal_cache_get_component (cal_cache, uid, NULL, &parent, cancellable, error))) { - if (!parent && !comp) { - g_propagate_error (error, EDC_ERROR (ObjectNotFound)); - return FALSE; - } - } - - ecb_ews_extract_item_id (comp ? comp : parent, &item_id.id, &item_id.change_key); - - if (!item_id.id) { - g_propagate_error (error, EDC_ERROR_EX (OtherError, "Cannot determine EWS ItemId")); - success = FALSE; - } else { - if (parent) { - index = e_cal_backend_ews_rid_to_index ( - ecb_ews_get_timezone_from_ical_component (cbews, - e_cal_component_get_icalcomponent (parent)), - rid, - e_cal_component_get_icalcomponent (parent), - error); - if (index == 0) - success = comp != NULL; - } - - if (index && removed_indexes && g_hash_table_contains (removed_indexes, GINT_TO_POINTER (index))) { - /* Do nothing, it's already deleted from the server */ - } else { - if (removed_indexes && index) - g_hash_table_insert (removed_indexes, GINT_TO_POINTER (index), NULL); - - success = success && e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, index, EWS_HARD_DELETE, - ecb_ews_is_organizer (cbews, comp) ? EWS_SEND_TO_ALL_AND_SAVE_COPY : EWS_SEND_TO_NONE, - EWS_ALL_OCCURRENCES, cancellable, error); - } - } - - g_free (item_id.id); - g_free (item_id.change_key); - - g_clear_object (&comp); - g_clear_object (&parent); - - return success; -} - -static void -ecb_ews_get_attach_differences (ECalComponent *oldcomp, - ECalComponent *newcomp, - GSList **out_removed_attachment_ids, /* gchar * */ - GSList **out_added_attachments) /* EEwsAttachmentInfo * */ -{ - GSList *old_attachments = NULL, *new_attachments = NULL, *link; - - g_return_if_fail (out_removed_attachment_ids != NULL); - g_return_if_fail (out_added_attachments != NULL); - - *out_removed_attachment_ids = NULL; - *out_added_attachments = NULL; - - if (!ecb_ews_extract_attachments (e_cal_component_get_icalcomponent (oldcomp), &old_attachments)) - old_attachments = NULL; - - if (!ecb_ews_extract_attachments (e_cal_component_get_icalcomponent (newcomp), &new_attachments)) - new_attachments = NULL; - - for (link = old_attachments; link; link = g_slist_next (link)) { - EEwsAttachmentInfo *old_nfo = link->data; - GSList *nlink; - - if (!old_nfo) - continue; - - for (nlink = new_attachments; nlink; nlink = g_slist_next (nlink)) { - EEwsAttachmentInfo *new_nfo = nlink->data; - gboolean same = FALSE; - - if (!new_nfo || - e_ews_attachment_info_get_type (old_nfo) != e_ews_attachment_info_get_type (new_nfo)) - continue; - - if (e_ews_attachment_info_get_type (old_nfo) == E_EWS_ATTACHMENT_INFO_TYPE_INLINED) { - const gchar *old_data, *new_data; - gsize old_len = -1, new_len = -1; - - old_data = e_ews_attachment_info_get_inlined_data (old_nfo, &old_len); - new_data = e_ews_attachment_info_get_inlined_data (new_nfo, &new_len); - - same = old_len == new_len && (old_len == 0 || - (old_len > 0 && old_data && new_data && memcmp (old_data, new_data, old_len) == 0)); - } else if (e_ews_attachment_info_get_type (old_nfo) == E_EWS_ATTACHMENT_INFO_TYPE_URI) { - same = g_strcmp0 (e_ews_attachment_info_get_uri (old_nfo), e_ews_attachment_info_get_uri (new_nfo)) == 0; - } - - if (same) { - new_attachments = g_slist_remove (new_attachments, new_nfo); - e_ews_attachment_info_free (new_nfo); - break; - } - } - - if (!nlink) { - /* Did not find in the new_attachments, thus it's removed */ - g_warn_if_fail (e_ews_attachment_info_get_id (old_nfo) != NULL); - *out_removed_attachment_ids = g_slist_prepend (*out_removed_attachment_ids, - g_strdup (e_ews_attachment_info_get_id (old_nfo))); - } - } - - *out_added_attachments = new_attachments; - - g_slist_free_full (old_attachments, (GDestroyNotify) e_ews_attachment_info_free); -} - -struct TzidCbData { - icalcomponent *comp; - ECalBackendEws *cbews; -}; - -static void -tzid_cb (icalparameter *param, - gpointer data) -{ - struct TzidCbData *cbd = data; - const gchar *tzid; - icaltimezone *zone; - icalcomponent *new_comp; - - tzid = icalparameter_get_tzid (param); - if (!tzid) - return; - - zone = e_timezone_cache_get_timezone (E_TIMEZONE_CACHE (cbd->cbews), tzid); - if (!zone) - return; - - new_comp = icaltimezone_get_component (zone); - if (!new_comp) - return; - - icalcomponent_add_component (cbd->comp, icalcomponent_new_clone (new_comp)); -} - -static void -ecb_ews_pick_all_tzids_out (ECalBackendEws *cbews, - icalcomponent *icalcomp) -{ - - /* pick all the tzids out of the component and resolve - * them using the vtimezones in the current calendar */ - struct TzidCbData cbd; - - cbd.cbews = cbews; - cbd.comp = icalcomp; - - icalcomponent_foreach_tzid (icalcomp, tzid_cb, &cbd); -} - -static gboolean -ecb_ews_modify_item_sync (ECalBackendEws *cbews, - GHashTable *removed_indexes, - icalcomponent *old_icalcomp, - icalcomponent *new_icalcomp, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *comp = NULL, *oldcomp = NULL; - icalcomponent *icalcomp; - gchar *itemid = NULL, *changekey = NULL; - GSList *added_attachments = NULL, *removed_attachment_ids = NULL; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (new_icalcomp != NULL, FALSE); - - icalcomp = icalcomponent_new_clone (new_icalcomp); - - ecb_ews_pick_all_tzids_out (cbews, icalcomp); - - comp = e_cal_component_new_from_icalcomponent (icalcomp); - if (!comp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - ecb_ews_extract_item_id (comp, &itemid, &changekey); - if (!itemid) { - g_propagate_error (error, EDC_ERROR_EX (OtherError, "Cannot determine EWS ItemId")); - g_object_unref (comp); - return FALSE; - } - - if (old_icalcomp) { - oldcomp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (old_icalcomp)); - } else { - oldcomp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (new_icalcomp)); - } - - ecb_ews_pick_all_tzids_out (cbews, e_cal_component_get_icalcomponent (oldcomp)); - - /* In case we have updated attachments we have to run update attachments - * before update items so attendees will receive mails with already updated attachments */ - - ecb_ews_get_attach_differences (oldcomp, comp, &removed_attachment_ids, &added_attachments); - - /* preform sync delete attachemnt operation*/ - if (removed_attachment_ids) { - g_free (changekey); - changekey = NULL; - - success = e_ews_connection_delete_attachments_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - removed_attachment_ids, &changekey, cancellable, error); - - g_slist_free_full (removed_attachment_ids, g_free); - } - - /* in case we have a new attachments add them before update */ - if (added_attachments && success) { - EwsId item_id; - - item_id.id = itemid; - item_id.change_key = changekey; - - changekey = NULL; - - success = e_ews_connection_create_attachments_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - &item_id, added_attachments, - FALSE, &changekey, NULL, cancellable, error); - - g_free (item_id.change_key); - } - - if (success && old_icalcomp && - icalcomponent_get_first_property (new_icalcomp, ICAL_RRULE_PROPERTY) && - !icalcomponent_get_first_property (new_icalcomp, ICAL_RECURRENCEID_PROPERTY)) { - icalproperty *prop, *old_prop; - GSList *exceptions = NULL, *link; - EwsId item_id; - - item_id.id = itemid; - item_id.change_key = changekey; - - /* Excluded occurrences */ - for (prop = icalcomponent_get_first_property (new_icalcomp, ICAL_EXDATE_PROPERTY); - prop; - prop = icalcomponent_get_next_property (new_icalcomp, ICAL_EXDATE_PROPERTY)) { - const gchar *new_rid; - - new_rid = icalproperty_get_value_as_string (prop); - - for (old_prop = icalcomponent_get_first_property (old_icalcomp, ICAL_EXDATE_PROPERTY); - old_prop; - old_prop = icalcomponent_get_next_property (old_icalcomp, ICAL_EXDATE_PROPERTY)) { - if (g_strcmp0 (new_rid, icalproperty_get_value_as_string (old_prop)) == 0) - break; - } - - if (!old_prop) - exceptions = g_slist_prepend (exceptions, prop); - } - - exceptions = g_slist_reverse (exceptions); - - for (link = exceptions; link && success; link = g_slist_next (link)) { - guint index; - - prop = link->data; - - index = e_cal_backend_ews_rid_to_index ( - ecb_ews_get_timezone_from_ical_component (cbews, new_icalcomp), - icalproperty_get_value_as_string (prop), - new_icalcomp, - error); - - if (index == 0) { - success = FALSE; - } else if (!removed_indexes || !g_hash_table_contains (removed_indexes, GINT_TO_POINTER (index))) { - if (removed_indexes) - g_hash_table_insert (removed_indexes, GINT_TO_POINTER (index), NULL); - - success = e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, index, - EWS_HARD_DELETE, EWS_SEND_TO_NONE, EWS_ALL_OCCURRENCES, cancellable, error); - } - } - - g_slist_free (exceptions); - } - - if (success) { - EwsCalendarConvertData convert_data = { 0 }; - CamelEwsSettings *ews_settings; - const gchar *send_meeting_invitations; - const gchar *send_or_save; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.user_email = camel_ews_settings_dup_email (ews_settings); - convert_data.comp = comp; - convert_data.old_comp = oldcomp; - convert_data.item_id = itemid; - convert_data.change_key = changekey; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - if (e_cal_component_has_attendees (comp)) { - send_meeting_invitations = "SendToAllAndSaveCopy"; - send_or_save = "SendAndSaveCopy"; - } else { - /*In case of appointment we have to set SendMeetingInvites to SendToNone */ - send_meeting_invitations = "SendToNone"; - send_or_save = "SaveOnly"; - } - - success = e_ews_connection_update_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", send_or_save, send_meeting_invitations, cbews->priv->folder_id, - e_cal_backend_ews_convert_component_to_updatexml, &convert_data, - NULL, cancellable, error); - - g_free (convert_data.user_email); - } - - g_slist_free_full (added_attachments, (GDestroyNotify) e_ews_attachment_info_free); - g_clear_object (&oldcomp); - g_clear_object (&comp); - g_free (changekey); - g_free (itemid); - - return success; -} - -static gboolean -ecb_ews_save_component_sync (ECalMetaBackend *meta_backend, - gboolean overwrite_existing, - EConflictResolution conflict_resolution, - const GSList *instances, - const gchar *extra, - gchar **out_new_uid, - gchar **out_new_extra, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - ECalComponent *master = NULL; - EwsFolderId *fid; - GSList *link; - const gchar *uid = NULL; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - for (link = (GSList *) instances; link && !master; link = g_slist_next (link)) { - master = link->data; - - if (!master) - continue; - - if (e_cal_component_is_instance (master)) - master = NULL; - } - - if (!master) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - g_return_val_if_fail (cal_cache != NULL, FALSE); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - e_cal_component_get_uid (master, &uid); - fid = e_ews_folder_id_new (cbews->priv->folder_id, NULL, FALSE); - - if (overwrite_existing) { - GSList *existing = NULL, *changed_instances = NULL, *removed_instances = NULL; - - success = uid && e_cal_cache_get_components_by_uid (cal_cache, uid, &existing, cancellable, error) && existing; - - if (success) { - GSList *link; - - /* This is for offline changes, where the component in the cache - is already modified, while the original, the one on the server, - is different. Using the cached component in this case generates - empty UpdateItem request and nothing is saved. */ - for (link = existing; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - comp = ecb_ews_restore_original_comp (comp); - if (comp) { - g_object_unref (link->data); - link->data = comp; - } - } - } - - if (success) - ecb_ews_filter_out_unchanged_instances (instances, existing, &changed_instances, &removed_instances); - - if (success) { - GHashTable *removed_indexes; - - removed_indexes = g_hash_table_new (g_direct_hash, g_direct_equal); - - for (link = changed_instances; link && success; link = g_slist_next (link)) { - ChangeData *cd = link->data; - - if (!cd) - continue; - - success = ecb_ews_modify_item_sync (cbews, removed_indexes, - e_cal_component_get_icalcomponent (cd->old_component ? cd->old_component : master), - e_cal_component_get_icalcomponent (cd->new_component), - cancellable, error); - } - - for (link = removed_instances; link && success; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id = NULL; - - if (!comp) - continue; - - id = e_cal_component_get_id (comp); - - if (id) { - success = ecb_ews_remove_item_sync (cbews, cal_cache, removed_indexes, id->uid, id->rid, cancellable, error); - e_cal_component_free_id (id); - } - } - - g_hash_table_destroy (removed_indexes); - } - - if (success) - ecb_ews_extract_item_id (master, out_new_uid, NULL); - - g_slist_free_full (existing, g_object_unref); - g_slist_free_full (changed_instances, change_data_free); - g_slist_free_full (removed_instances, g_object_unref); - } else { - GHashTable *removed_indexes; - EwsCalendarConvertData convert_data = { 0 }; - EEwsItem *item = NULL; - const EwsId *ews_id = NULL; - const gchar *send_meeting_invitations; - icalcomponent *icalcomp; - icalproperty *prop; - GSList *items = NULL; - - icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (master)); - - e_ews_clean_icalcomponent (icalcomp); - - if (!e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) - ecb_ews_pick_all_tzids_out (cbews, icalcomp); - - /* - * In case we are creating a meeting with attendees and attachments. - * We have to preform 3 steps in order to allow attendees to receive attachments in their invite mails. - * 1. create meeting and do not send invites - * 2. create attachments - * 3. dummy update meeting and send invites to all - */ - if (e_cal_component_has_attendees (master)) { - if (e_cal_component_has_attachments (master)) - send_meeting_invitations = "SendToNone"; - else - send_meeting_invitations = "SendToAllAndSaveCopy"; - } else { - /* In case of appointment we have to set SendMeetingInvites to SendToNone */ - send_meeting_invitations = "SendToNone"; - } - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.icalcomp = icalcomp; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - success = e_ews_connection_create_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, "SaveOnly", send_meeting_invitations, - fid, e_cal_backend_ews_convert_calcomp_to_xml, &convert_data, - &items, cancellable, error); - - if (success && items) { - item = items->data; - if (item) { - g_object_ref (item); - - ews_id = e_ews_item_get_id (item); - } - } - - if (success && item && e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_EVENT) { - EEwsAdditionalProps *add_props; - GSList *items, *items_req = NULL; - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("calendar:UID"); - - items = g_slist_append (NULL, ews_id->id); - - /* get calender uid from server*/ - success = e_ews_connection_get_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - items, "IdOnly", add_props, FALSE, NULL, E_EWS_BODY_TYPE_TEXT, - &items_req, NULL, NULL, cancellable, error) && items_req != NULL; - - e_ews_additional_props_free (add_props); - - if (success) { - g_clear_object (&item); - - item = items_req->data; - ews_id = NULL; - - if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) { - g_propagate_error (error, g_error_copy (e_ews_item_get_error (item))); - item = NULL; - success = FALSE; - } else { - item = g_object_ref (item); - ews_id = e_ews_item_get_id (item); - } - } - - g_slist_free_full (items_req, g_object_unref); - g_slist_free (items); - } - - /* attachments */ - if (success && e_cal_component_has_attachments (master) > 0) { - GSList *info_attachments = NULL; - - g_warn_if_fail (ews_id != NULL); - - if (ews_id && ecb_ews_extract_attachments (icalcomp, &info_attachments)) { - GSList *ids = NULL; - - success = e_ews_connection_create_attachments_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - ews_id, info_attachments, FALSE, NULL, &ids, cancellable, error); - - g_slist_free_full (info_attachments, (GDestroyNotify) e_ews_attachment_info_free); - g_slist_free_full (ids, g_free); - } - } - - removed_indexes = g_hash_table_new (g_direct_hash, g_direct_equal); - - if (success && icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY)) { - GSList *exceptions = NULL; - - /* Excluded occurrences */ - for (prop = icalcomponent_get_first_property (icalcomp, ICAL_EXDATE_PROPERTY); - prop; - prop = icalcomponent_get_next_property (icalcomp, ICAL_EXDATE_PROPERTY)) { - exceptions = g_slist_prepend (exceptions, g_strdup (icalproperty_get_value_as_string (prop))); - } - - for (link = exceptions; link && success; link = g_slist_next (link)) { - success = ecb_ews_remove_item_sync (cbews, cal_cache, removed_indexes, uid, link->data, cancellable, error); - } - - g_slist_free_full (exceptions, g_free); - } - - if (success && e_cal_component_has_attendees (master) && e_cal_component_has_attachments (master)) { - if (ews_id) { - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-ITEMID", ews_id->id); - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY", ews_id->change_key); - } - - /* In case we have attendees and atachemnts we have to fake update items, - * this is the only way to pass attachments in meeting invite mail */ - success = ecb_ews_modify_item_sync (cbews, removed_indexes, NULL, icalcomp, cancellable, error); - } - - icalcomponent_free (icalcomp); - g_clear_object (&item); - - for (link = (GSList *) instances; link && success; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - if (comp == master) - continue; - - icalcomp = e_cal_component_get_icalcomponent (comp); - - success = ecb_ews_modify_item_sync (cbews, removed_indexes, NULL, icalcomp, cancellable, error); - } - - if (success && items) { - EEwsItem *item = items->data; - const EwsId *item_id; - - item_id = e_ews_item_get_id (item); - *out_new_uid = g_strdup (item_id->id); - } - - g_slist_free_full (items, g_object_unref); - g_hash_table_destroy (removed_indexes); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - g_clear_object (&cal_cache); - e_ews_folder_id_free (fid); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - - return success; -} - -static gboolean -ecb_ews_remove_component_sync (ECalMetaBackend *meta_backend, - EConflictResolution conflict_resolution, - const gchar *uid, - const gchar *extra, - const gchar *object, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalComponent *comp; - EwsId item_id; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (object != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - comp = e_cal_component_new_from_string (object); - if (!comp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - ecb_ews_extract_item_id (comp, &item_id.id, &item_id.change_key); - - success = e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, 0, EWS_HARD_DELETE, - ecb_ews_is_organizer (cbews, comp) ? EWS_SEND_TO_ALL_AND_SAVE_COPY : EWS_SEND_TO_NONE, - EWS_ALL_OCCURRENCES, cancellable, error); - - g_free (item_id.id); - g_free (item_id.change_key); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_object_unref (comp); - - return success; -} - -static void -ecb_ews_discard_alarm_sync (ECalBackendSync *cal_backend_sync, - EDataCal *cal, - GCancellable *cancellable, - const gchar *uid, - const gchar *rid, - const gchar *auid, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - ECalComponent *comp = NULL; - EwsCalendarConvertData convert_data = { 0 }; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cal_backend_sync)); - - cbews = E_CAL_BACKEND_EWS (cal_backend_sync); - - cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbews)); - g_return_if_fail (cal_cache != NULL); - - if (!e_cal_cache_get_component (cal_cache, uid, NULL, &comp, cancellable, NULL) || !comp) { - g_object_unref (cal_cache); - g_propagate_error (error, EDC_ERROR (ObjectNotFound)); - return; - } - - g_object_unref (cal_cache); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) { - g_clear_object (&comp); - return; - } - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - - if (e_cal_component_has_recurrences (comp)) { - gint *index; - - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_OCCURRENCEITEM; - e_cal_component_get_sequence (comp, &index); - - if (index != NULL) { - /*Microsoft is counting the occurrences starting from 1 - where EcalComponent is starting from zerro */ - convert_data.index = *index + 1; - e_cal_component_free_sequence (index); - } else { - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_ITEM; - convert_data.index = -1; - } - } else { - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_ITEM; - convert_data.index = -1; - } - - ecb_ews_extract_item_id (comp, &convert_data.item_id, &convert_data.change_key); - - if (e_ews_connection_update_items_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", NULL, - "SendToNone", NULL, - e_cal_backend_ews_clear_reminder_is_set, - &convert_data, - NULL, - cancellable, - error)) { - icalcomponent *icomp = e_cal_component_get_icalcomponent (comp); - GSList *modified_objects; - - modified_objects = g_slist_prepend (NULL, - e_cal_meta_backend_info_new (icalcomponent_get_uid (icomp), NULL, NULL, - e_cal_util_get_x_property (icomp, "X-EVOLUTION-ITEMID"))); - - /* Refresh the local cache, to have up-to-date ChangeKey */ - e_cal_meta_backend_process_changes_sync (E_CAL_META_BACKEND (cbews), NULL, modified_objects, NULL, cancellable, error); - - g_slist_free_full (modified_objects, e_cal_meta_backend_info_free); - } - - g_object_unref (comp); - g_free (convert_data.item_id); - g_free (convert_data.change_key); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static gboolean -ecb_ews_send_cancellation_email_sync (ECalBackendEws *cbews, - CamelAddress *from, - CamelInternetAddress *recipient, - const gchar *subject, - const gchar *body, - const gchar *calobj, - GCancellable *cancellable, - GError **error) -{ - CamelMimeMessage *message; - CamelContentType *mime_type; - CamelMultipart *multi; - CamelMimePart *text_part, *vcal_part; - gchar *ical_str; - icalcomponent *vcal, *vevent, *vtz; - icalproperty *prop; - icaltimezone *icaltz; - struct icaltimetype dt; - gboolean success; - - vcal = icalcomponent_new (ICAL_VCALENDAR_COMPONENT); - icalcomponent_add_property (vcal, icalproperty_new_version ("2.0")); - icalcomponent_add_property (vcal, icalproperty_new_prodid ("-//Evolution EWS backend//EN")); - icalcomponent_add_property (vcal, icalproperty_new_method (ICAL_METHOD_CANCEL)); - vevent = icalcomponent_new_from_string (calobj); - prop = icalcomponent_get_first_property (vevent, ICAL_STATUS_PROPERTY); - if (prop != NULL) icalcomponent_remove_property (vevent, prop); - icalcomponent_add_property (vevent, icalproperty_new_status (ICAL_STATUS_CANCELLED)); - prop = icalcomponent_get_first_property (vevent, ICAL_METHOD_PROPERTY); - if (prop != NULL) icalcomponent_remove_property (vevent, prop); - dt = e_cal_backend_ews_get_datetime_with_zone (E_TIMEZONE_CACHE (cbews), vevent, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - icaltz = (icaltimezone *) - (dt.zone ? dt.zone : ecb_ews_get_timezone_from_ical_component (cbews, vevent)); - vtz = icaltimezone_get_component (icaltz); - icalcomponent_add_component (vcal, icalcomponent_new_clone (vtz)); - icalcomponent_add_component (vcal, vevent); - text_part = camel_mime_part_new (); - camel_mime_part_set_content (text_part, body, strlen (body), "text/plain"); - - vcal_part = camel_mime_part_new (); - mime_type = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (vcal_part)); - camel_content_type_set_param (mime_type, "charset", "utf-8"); - camel_content_type_set_param (mime_type, "method", "CANCEL"); - ical_str = icalcomponent_as_ical_string_r ((icalcomponent *) vcal); - camel_mime_part_set_content (vcal_part, ical_str, strlen (ical_str), "text/calendar; method=CANCEL"); - free (ical_str); - - multi = camel_multipart_new (); - camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multi), "multipart/alternative"); - camel_multipart_add_part (multi, text_part); - camel_multipart_set_boundary (multi, NULL); - camel_multipart_add_part (multi, vcal_part); - g_object_unref (text_part); - g_object_unref (vcal_part); - - message = camel_mime_message_new (); - camel_mime_message_set_subject (message, subject); - camel_mime_message_set_from (message, CAMEL_INTERNET_ADDRESS (from)); - camel_mime_message_set_recipients (message, CAMEL_RECIPIENT_TYPE_TO, recipient); - - camel_medium_set_content ((CamelMedium *) message, (CamelDataWrapper *) multi); - g_object_unref (multi); - - success = camel_ews_utils_create_mime_message (cbews->priv->cnc, "SendOnly", NULL, message, NULL, from, NULL, NULL, NULL, cancellable, error); - - g_object_unref (message); - icalcomponent_free (vcal); - - return success; -} - -static void -ecb_ews_receive_objects_no_exchange_mail (ECalBackendEws *cbews, - icalcomponent *subcomp, - GSList **ids, - GCancellable *cancellable, - GError **error) -{ - EwsCalendarConvertData convert_data = { 0 }; - EwsFolderId *fid; - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.icalcomp = subcomp; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - fid = e_ews_folder_id_new (cbews->priv->folder_id, NULL, FALSE); - - e_ews_connection_create_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - "SaveOnly", - "SendToNone", - fid, - e_cal_backend_ews_convert_calcomp_to_xml, - &convert_data, - ids, - cancellable, - error); - - e_ews_folder_id_free (fid); -} - -static icalproperty * -find_attendee (icalcomponent *ical_comp, - const gchar *address) -{ - icalproperty *prop; - - if (address == NULL) - return NULL; - - for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY); - prop != NULL; - prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) { - gchar *attendee; - gchar *text; - - attendee = icalproperty_get_value_as_string_r (prop); - - if (!attendee) - continue; - - text = g_strdup (itip_strip_mailto (attendee)); - text = g_strstrip (text); - if (text && !g_ascii_strcasecmp (address, text)) { - g_free (text); - g_free (attendee); - break; - } - g_free (text); - g_free (attendee); - } - - return prop; -} - -static icalproperty * -find_attendee_if_sentby (icalcomponent *ical_comp, - const gchar *address) -{ - icalproperty *prop; - - if (address == NULL) - return NULL; - - for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY); - prop != NULL; - prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) { - icalparameter *param; - const gchar *attendee_sentby; - gchar *text; - - param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER); - if (!param) - continue; - - attendee_sentby = icalparameter_get_sentby (param); - - if (!attendee_sentby) - continue; - - text = g_strdup (itip_strip_mailto (attendee_sentby)); - text = g_strstrip (text); - if (text && !g_ascii_strcasecmp (address, text)) { - g_free (text); - break; - } - g_free (text); - } - - return prop; -} - -static void -ecb_ews_get_rsvp (icalproperty *attendee, - gboolean *out_rsvp_requested) -{ - if (out_rsvp_requested) - *out_rsvp_requested = FALSE; - else - return; - - if (attendee) { - icalparameter *rsvp; - - rsvp = icalproperty_get_first_parameter (attendee, ICAL_RSVP_PARAMETER); - if (rsvp) { - *out_rsvp_requested = icalparameter_get_rsvp (rsvp) == ICAL_RSVP_TRUE; - } - } -} - -static const gchar * -ecb_ews_get_current_user_meeting_reponse (ECalBackendEws *cbews, - icalcomponent *icalcomp, - const gchar *current_user_mail, - gboolean *out_rsvp_requested) -{ - icalproperty *attendee; - const gchar *attendee_str = NULL, *attendee_mail = NULL; - gint attendees_count = 0; - const gchar *response = NULL; - gboolean found = FALSE; - - if (out_rsvp_requested) - *out_rsvp_requested = FALSE; - - attendee = icalcomponent_get_first_property (icalcomp, ICAL_ORGANIZER_PROPERTY); - if (attendee) { - attendee_str = icalproperty_get_organizer (attendee); - - if (attendee_str) { - if (!strncasecmp (attendee_str, "MAILTO:", 7)) - attendee_mail = attendee_str + 7; - else - attendee_mail = attendee_str; - if (attendee_mail && current_user_mail && g_ascii_strcasecmp (attendee_mail, current_user_mail) == 0) { - /* Empty string means it's an organizer, NULL is when not found */ - return ""; - } - } - } - - for (attendee = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY); - attendee != NULL; - attendee = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY), attendees_count++) { - attendee_str = icalproperty_get_attendee (attendee); - - if (attendee_str != NULL) { - if (!strncasecmp (attendee_str, "MAILTO:", 7)) - attendee_mail = attendee_str + 7; - else - attendee_mail = attendee_str; - if (attendee_mail && current_user_mail && g_ascii_strcasecmp (attendee_mail, current_user_mail) == 0) { - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } - } - } - - /* this should not happen, but if the user's configured email does not match the one - used in the invitation, like when the invitation comes to a mailing list... */ - if (!found && attendees_count == 1) { - attendee = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY); - g_return_val_if_fail (attendee != NULL, NULL); - - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } else if (!found) { - ESourceRegistry *registry; - ECalComponent *comp; - - registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbews)); - comp = e_cal_component_new (); - if (e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp))) { - gchar *my_address; - - my_address = itip_get_comp_attendee (registry, comp, NULL); - - attendee = find_attendee (icalcomp, my_address); - if (!attendee) - attendee = find_attendee_if_sentby (icalcomp, my_address); - - if (attendee) { - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } - - g_free (my_address); - } - - g_object_unref (comp); - } - - if (found && !response) { - response = "NEEDS-ACTION"; - } - - return response; -} - -/* changekey can be NULL if you don't want it. itemid cannot. */ -static void -ecb_ews_get_item_accept_id (ECalComponent *comp, - gchar **itemid, - gchar **changekey, - gchar **mail_id) -{ - icalproperty *prop; - gchar *id_item = NULL; - gchar *id_accept = NULL; - gchar *ck = NULL; - - prop = icalcomponent_get_first_property ( - e_cal_component_get_icalcomponent (comp), - ICAL_X_PROPERTY); - while (prop) { - const gchar *x_name, *x_val; - - x_name = icalproperty_get_x_name (prop); - x_val = icalproperty_get_x (prop); - if (!id_item && g_ascii_strcasecmp (x_name, "X-EVOLUTION-ITEMID") == 0) - id_item = g_strdup (x_val); - else if (!id_accept && g_ascii_strcasecmp (x_name, "X-EVOLUTION-ACCEPT-ID") == 0) - id_accept = g_strdup (x_val); - else if (changekey && !ck && !g_ascii_strcasecmp (x_name, "X-EVOLUTION-CHANGEKEY")) - ck = g_strdup (x_val); - - prop = icalcomponent_get_next_property ( - e_cal_component_get_icalcomponent (comp), - ICAL_X_PROPERTY); - } - - if (!id_item) - id_item = g_strdup (id_accept); - - *itemid = id_item; - *mail_id = id_accept; - if (changekey) - *changekey = ck; -} - -static gboolean -ecb_ews_do_method_request_publish_reply (ECalBackendEws *cbews, - ECalComponent *comp, - icalcomponent *subcomp, - const gchar *response_type, - const gchar *user_email, - gboolean rsvp_requested, - GCancellable *cancellable, - GError **error) -{ - GError *local_error = NULL; - gchar *item_id = NULL; - gchar *change_key = NULL; - gchar *mail_id = NULL; - gint pass = 0; - GSList *ids = NULL; - - if (!response_type && - e_cal_util_component_has_organizer (subcomp) && - e_cal_util_component_has_attendee (subcomp)) { - g_set_error (error, E_DATA_CAL_ERROR, UnknownUser, _("Cannot find user “%s” between attendees"), user_email ? user_email : "NULL"); - return FALSE; - } - - if (response_type && *response_type) - ecb_ews_get_item_accept_id (comp, &item_id, &change_key, &mail_id); - else - response_type = NULL; - - while (pass < 2) { - /*in case we do not have item id we will create item with mime content only*/ - if (!item_id || (response_type && g_ascii_strcasecmp (response_type, "NEEDS-ACTION") == 0)) { - ecb_ews_receive_objects_no_exchange_mail (cbews, subcomp, &ids, cancellable, &local_error); - } else { - EwsCalendarConvertData convert_data = { 0 }; - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.response_type = (gchar *) response_type; - convert_data.item_id = item_id; - convert_data.change_key = change_key; - - e_ews_connection_create_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - rsvp_requested ? "SendAndSaveCopy" : "SaveOnly", - rsvp_requested ? NULL : "SendToNone", - NULL, - e_cal_backend_ews_prepare_accept_item_request, - &convert_data, - &ids, - cancellable, - &local_error); - } - - if (pass == 0 && mail_id != NULL && item_id != NULL && - g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_ITEMNOTFOUND)) { - /* - * maybe the associated accept calendar item changed - * on the server, thus retry with updated values - */ - GSList *my_ids = NULL; - - g_clear_error (&local_error); - - my_ids = g_slist_append (my_ids, mail_id); - - if (e_ews_connection_get_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - my_ids, - "AllProperties", - NULL, - FALSE, - NULL, - E_EWS_BODY_TYPE_ANY, - &ids, - NULL, - NULL, - cancellable, - &local_error) && - ids != NULL && - ids->data != NULL) { - EEwsItem *item = ids->data; - const EwsId *id = e_ews_item_get_id (item); - - if (id != NULL && g_strcmp0 (id->id, mail_id) == 0) { - const EwsId *cal_item_accepted_id; - - cal_item_accepted_id = e_ews_item_get_calendar_item_accept_id (item); - if (cal_item_accepted_id != NULL) { - g_clear_error (&local_error); - pass++; - - g_free (item_id); - g_free (change_key); - - item_id = g_strdup (cal_item_accepted_id->id); - change_key = g_strdup (cal_item_accepted_id->change_key); - } - } - } - - g_slist_free (my_ids); - - if (pass == 0) - break; - } else { - break; - } - - g_slist_free_full (ids, g_object_unref); - ids = NULL; - } - - if (local_error == NULL) { - icalproperty *transp; - - transp = icalcomponent_get_first_property (subcomp, ICAL_TRANSP_PROPERTY); - - if (g_strcmp0 (icalproperty_get_value_as_string (transp), "TRANSPARENT") == 0 && - g_strcmp0 (response_type, "ACCEPTED") == 0) { - EwsCalendarConvertData convert_data = { 0 }; - GSList *l; - - /* - * user can accept meeting but mark it as free in it's calendar - * the following code is updating the exchange meeting status to free - */ - for (l = ids; l != NULL; l = g_slist_next (l)) { - EEwsItem *item = l->data; - - if (item != NULL) { - const EwsId *id = e_ews_item_get_id (item); - - convert_data.item_id = id->id; - convert_data.change_key = id->change_key; - break; - } - } - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - - e_ews_connection_update_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", - NULL, - "SendToNone", - NULL, - e_cal_backend_ews_prepare_set_free_busy_status, - &convert_data, - NULL, - cancellable, - &local_error); - } - } - - if (local_error != NULL) - g_propagate_error (error, local_error); - - g_free (item_id); - g_free (change_key); - g_free (mail_id); - g_slist_free_full (ids, g_object_unref); - - return !local_error; -} - -static void -ecb_ews_receive_objects_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *calobj, - GError **error) -{ - ECalBackendEws *cbews; - ECalBackend *cal_backend; - CamelEwsSettings *ews_settings; - icalcomponent *icalcomp, *subcomp; - icalcomponent_kind kind; - gchar *user_email; - gboolean success = TRUE, do_refresh = FALSE; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - icalcomp = calobj ? icalparser_parse_string (calobj) : NULL; - - if (!icalcomp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - /* make sure ical data we parse is actually a vCalendar component */ - if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) { - icalcomponent_free (icalcomp); - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - cal_backend = E_CAL_BACKEND (cbews); - kind = e_cal_backend_get_kind (cal_backend); - - ews_settings = ecb_ews_get_collection_settings (cbews); - user_email = camel_ews_settings_dup_email (ews_settings); - - switch (icalcomponent_get_method (icalcomp)) { - case ICAL_METHOD_REQUEST: - case ICAL_METHOD_PUBLISH: - case ICAL_METHOD_REPLY: - for (subcomp = icalcomponent_get_first_component (icalcomp, kind); - subcomp && success; - subcomp = icalcomponent_get_next_component (icalcomp, kind)) { - ECalComponent *comp; - const gchar *response_type; - gboolean rsvp_requested = FALSE; - - /* getting a data for meeting request response */ - response_type = ecb_ews_get_current_user_meeting_reponse (cbews, subcomp, user_email, &rsvp_requested); - - comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (subcomp)); - - success = ecb_ews_do_method_request_publish_reply (cbews, comp, subcomp, response_type, user_email, rsvp_requested, cancellable, error); - - do_refresh = TRUE; - - g_object_unref (comp); - } - break; - case ICAL_METHOD_COUNTER: - /* - * this is a new time proposal mail from one of the attendees - * if we decline the proposal, nothing have to be done - * if we accept it we will call to modify_object - */ - for (subcomp = icalcomponent_get_first_component (icalcomp, kind); - subcomp && success; - subcomp = icalcomponent_get_next_component (icalcomp, kind)) { - const gchar *response_type; - - /* getting a data for meeting request response */ - response_type = ecb_ews_get_current_user_meeting_reponse (cbews, subcomp, user_email, NULL); - - if (g_strcmp0 (response_type, "ACCEPTED") == 0) { - gchar **split_subject; - icalproperty *summary; - - /* we have to edit the meeting subject to remove exchange header */ - summary = icalcomponent_get_first_property (subcomp, ICAL_SUMMARY_PROPERTY); - split_subject = - g_strsplit (icalproperty_get_value_as_string (summary), ":", -1); - icalproperty_set_value_from_string (summary, split_subject[1] , "NO"); - g_strfreev (split_subject); - - success = ecb_ews_modify_item_sync (cbews, NULL, NULL, subcomp, cancellable, error); - - do_refresh = TRUE; - } - } - break; - default: - break; - } - - icalcomponent_free (icalcomp); - g_free (user_email); - - if (success && do_refresh) - e_cal_meta_backend_schedule_refresh (E_CAL_META_BACKEND (cbews)); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static void -ecb_ews_send_objects_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *calobj, - GSList **users, - gchar **modified_calobj, - GError **error) -{ - ECalBackendEws *cbews; - icalcomponent_kind kind; - icalcomponent *icalcomp, *subcomp = NULL; - gchar *subcalobj; - gboolean success = TRUE; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - icalcomp = calobj ? icalparser_parse_string (calobj) : NULL; - - /* make sure data was parsed properly */ - if (!icalcomp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - /* make sure ical data we parse is actually an vcal component */ - if ((icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) && (icalcomponent_isa (icalcomp) != ICAL_VEVENT_COMPONENT)) { - icalcomponent_free (icalcomp); - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - - if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) { - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - subcomp = icalcomponent_get_first_component (icalcomp, kind); - } - if (icalcomponent_isa (icalcomp) == ICAL_VEVENT_COMPONENT) - subcomp = icalcomp; - while (subcomp && success) { - const gchar *new_body_content = NULL, *subject = NULL, *org_email = NULL; - const gchar *org = NULL, *attendee = NULL; - icalproperty *prop, *org_prop = NULL; - CamelInternetAddress *org_addr = camel_internet_address_new (); - - new_body_content = e_cal_util_get_x_property (subcomp, "X-EVOLUTION-RETRACT-COMMENT"); - subject = icalproperty_get_value_as_string (icalcomponent_get_first_property (subcomp, ICAL_SUMMARY_PROPERTY)); - - org_prop = icalcomponent_get_first_property (subcomp, ICAL_ORGANIZER_PROPERTY); - org = icalproperty_get_organizer (org_prop); - if (!g_ascii_strncasecmp (org, "MAILTO:", 7)) - org_email = (org) + 7; - else - org_email = org; - - camel_internet_address_add (org_addr, icalproperty_get_parameter_as_string (org_prop, "CN"), org_email); - - /* iterate over every attendee property */ - for (prop = icalcomponent_get_first_property (subcomp, ICAL_ATTENDEE_PROPERTY); - prop && success; - prop = icalcomponent_get_next_property (subcomp, ICAL_ATTENDEE_PROPERTY)) { - CamelInternetAddress *attendee_addr = camel_internet_address_new (); - attendee = icalproperty_get_attendee (prop); - if (g_ascii_strcasecmp (org_email, attendee) == 0) continue; - if (!g_ascii_strncasecmp (attendee, "mailto:", 7)) attendee = (attendee) + 7; - - subcalobj = icalcomponent_as_ical_string_r (subcomp); - camel_internet_address_add (attendee_addr, icalproperty_get_parameter_as_string (prop, "CN"), attendee); - success = ecb_ews_send_cancellation_email_sync (cbews, CAMEL_ADDRESS (org_addr), attendee_addr, - subject, new_body_content, subcalobj, cancellable, error); - g_object_unref (attendee_addr); - free (subcalobj); - } - - g_object_unref (org_addr); - subcomp = icalcomponent_get_next_component (icalcomp, kind); - } - - icalcomponent_free (icalcomp); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static void -ecb_ews_get_free_busy_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const GSList *users, - time_t start, - time_t end, - GSList **freebusyobjs, - GError **error) -{ - ECalBackendEws *cbews; - EEWSFreeBusyData fbdata = { 0 }; - GSList *freebusy = NULL; - gboolean success; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - g_return_if_fail (freebusyobjs != NULL); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - *freebusyobjs = NULL; - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - /* EWS can support only 100 identities, which is the maximum number of identities that the Web service method can request - see http://msdn.microsoft.com / en - us / library / aa564001 % 28v = EXCHG.140 % 29.aspx */ - if (g_slist_length ((GSList *) users) > 100) { - g_propagate_error (error, EDC_ERROR (SearchSizeLimitExceeded)); - return; - } - - fbdata.period_start = start; - fbdata.period_end = end; - fbdata.user_mails = (GSList *) users; - - success = e_ews_connection_get_free_busy_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - e_ews_cal_utils_prepare_free_busy_request, &fbdata, - &freebusy, cancellable, error); - - if (success) { - GSList *fblink, *ulink; - - for (fblink = freebusy, ulink = (GSList *) users; - fblink && ulink; - fblink = g_slist_next (fblink), ulink = g_slist_next (ulink)) { - icalcomponent *icalcomp = fblink->data; - gchar *mailto; - - /* add attendee property */ - mailto = g_strconcat ("mailto:", ulink->data, NULL); - icalcomponent_add_property (icalcomp, icalproperty_new_attendee (mailto)); - g_free (mailto); - - *freebusyobjs = g_slist_prepend (*freebusyobjs, icalcomponent_as_ical_string_r (icalcomp)); - } - - *freebusyobjs = g_slist_reverse (*freebusyobjs); - } - - g_slist_free_full (freebusy, (GDestroyNotify) icalcomponent_free); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static gchar * -ecb_ews_get_backend_property (ECalBackend *cal_backend, - const gchar *prop_name) -{ - ECalBackendEws *cbews; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cal_backend), NULL); - g_return_val_if_fail (prop_name != NULL, NULL); - - cbews = E_CAL_BACKEND_EWS (cal_backend); - - if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) { - return g_strjoin ( - ",", - CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS, - CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY, - CAL_STATIC_CAPABILITY_REMOVE_ALARMS, - CAL_STATIC_CAPABILITY_NO_THISANDPRIOR, - CAL_STATIC_CAPABILITY_NO_THISANDFUTURE, - CAL_STATIC_CAPABILITY_NO_CONV_TO_ASSIGN_TASK, - CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT, - CAL_STATIC_CAPABILITY_SAVE_SCHEDULES, - CAL_STATIC_CAPABILITY_NO_ALARM_AFTER_START, - CAL_STATIC_CAPABILITY_NO_MEMO_START_DATE, - CAL_STATIC_CAPABILITY_ALL_DAY_EVENT_AS_TIME, - CAL_STATIC_CAPABILITY_TASK_DATE_ONLY, - e_cal_meta_backend_get_capabilities (E_CAL_META_BACKEND (cbews)), - NULL); - } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS)) { - /* return email address of the person who opened the calendar */ - CamelEwsSettings *ews_settings; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - return camel_ews_settings_dup_email (ews_settings); - } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS)) { - /* ews does not support email based alarms */ - return NULL; - } - - /* Chain up to parent's method. */ - return E_CAL_BACKEND_CLASS (e_cal_backend_ews_parent_class)->get_backend_property (cal_backend, prop_name); -} - -static void -ecb_ews_get_timezone_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *tzid, - gchar **tzobject, - GError **error) -{ - GError *local_error = NULL; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - g_return_if_fail (tzid != NULL); - g_return_if_fail (tzobject != NULL); - - *tzobject = NULL; - - E_CAL_BACKEND_SYNC_CLASS (e_cal_backend_ews_parent_class)->get_timezone_sync (sync_backend, cal, cancellable, tzid, tzobject, &local_error); - - if (!*tzobject) { - /* The timezone can be sometimes the Windows zone, try to convert it to libical */ - const gchar *ical_location = e_cal_backend_ews_tz_util_get_ical_equivalent (tzid); - - if (ical_location) - E_CAL_BACKEND_SYNC_CLASS (e_cal_backend_ews_parent_class)->get_timezone_sync (sync_backend, cal, cancellable, ical_location, tzobject, NULL); - } - - if (*tzobject) - g_clear_error (&local_error); - else if (local_error) - g_propagate_error (error, local_error); -} - -static gboolean -ecb_ews_get_destination_address (EBackend *backend, - gchar **host, - guint16 *port) -{ - CamelEwsSettings *ews_settings; - SoupURI *soup_uri; - gchar *host_url; - gboolean result = FALSE; - - g_return_val_if_fail (port != NULL, FALSE); - g_return_val_if_fail (host != NULL, FALSE); - - /* Sanity checking */ - if (!e_cal_backend_get_registry (E_CAL_BACKEND (backend)) || - !e_backend_get_source (backend)) - return FALSE; - - ews_settings = ecb_ews_get_collection_settings (E_CAL_BACKEND_EWS (backend)); - g_return_val_if_fail (ews_settings != NULL, FALSE); - - host_url = camel_ews_settings_dup_hosturl (ews_settings); - g_return_val_if_fail (host_url != NULL, FALSE); - - soup_uri = soup_uri_new (host_url); - if (soup_uri) { - *host = g_strdup (soup_uri_get_host (soup_uri)); - *port = soup_uri_get_port (soup_uri); - - result = *host && **host; - if (!result) { - g_free (*host); - *host = NULL; - } - - soup_uri_free (soup_uri); - } - - g_free (host_url); - - return result; -} - -static gchar * -ecb_ews_dup_component_revision (ECalCache *cal_cache, - icalcomponent *icalcomp, - gpointer user_data) -{ - g_return_val_if_fail (icalcomp != NULL, NULL); - - return e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY"); -} - -static void -ecb_ews_constructed (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - ECalCache *cal_cache; - gchar *cache_dirname; - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->constructed (object); - - /* Reset the connectable, it steals data from Authentication extension, - where is written incorrect address */ - e_backend_set_connectable (E_BACKEND (object), NULL); - - cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbews)); - g_return_if_fail (cal_cache != NULL); - - cache_dirname = g_path_get_dirname (e_cache_get_filename (E_CACHE (cal_cache))); - g_signal_connect (cal_cache, "dup-component-revision", G_CALLBACK (ecb_ews_dup_component_revision), NULL); - - g_clear_object (&cal_cache); - - cbews->priv->attachments_dir = g_build_filename (cache_dirname, "attachments", NULL); - g_mkdir_with_parents (cbews->priv->attachments_dir, 0777); - - g_free (cache_dirname); -} - -static void -ecb_ews_dispose (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - g_clear_object (&cbews->priv->cnc); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->dispose (object); -} - -static void -ecb_ews_finalize (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - - g_free (cbews->priv->folder_id); - g_free (cbews->priv->attachments_dir); - - g_rec_mutex_clear (&cbews->priv->cnc_lock); - - e_cal_backend_ews_unref_windows_zones (); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->finalize (object); -} - -static void -e_cal_backend_ews_init (ECalBackendEws *cbews) -{ - cbews->priv = G_TYPE_INSTANCE_GET_PRIVATE (cbews, E_TYPE_CAL_BACKEND_EWS, ECalBackendEwsPrivate); - - g_rec_mutex_init (&cbews->priv->cnc_lock); - - e_cal_backend_ews_populate_windows_zones (); -} - -static void -e_cal_backend_ews_class_init (ECalBackendEwsClass *klass) -{ - GObjectClass *object_class; - EBackendClass *backend_class; - ECalBackendClass *cal_backend_class; - ECalBackendSyncClass *cal_backend_sync_class; - ECalMetaBackendClass *cal_meta_backend_class; - - g_type_class_add_private (klass, sizeof (ECalBackendEwsPrivate)); - - cal_meta_backend_class = E_CAL_META_BACKEND_CLASS (klass); - cal_meta_backend_class->connect_sync = ecb_ews_connect_sync; - cal_meta_backend_class->disconnect_sync = ecb_ews_disconnect_sync; - cal_meta_backend_class->get_changes_sync = ecb_ews_get_changes_sync; - cal_meta_backend_class->load_component_sync = ecb_ews_load_component_sync; - cal_meta_backend_class->save_component_sync = ecb_ews_save_component_sync; - cal_meta_backend_class->remove_component_sync = ecb_ews_remove_component_sync; - - cal_backend_sync_class = E_CAL_BACKEND_SYNC_CLASS (klass); - cal_backend_sync_class->discard_alarm_sync = ecb_ews_discard_alarm_sync; - cal_backend_sync_class->receive_objects_sync = ecb_ews_receive_objects_sync; - cal_backend_sync_class->send_objects_sync = ecb_ews_send_objects_sync; - cal_backend_sync_class->get_free_busy_sync = ecb_ews_get_free_busy_sync; - cal_backend_sync_class->get_timezone_sync = ecb_ews_get_timezone_sync; - - cal_backend_class = E_CAL_BACKEND_CLASS (klass); - cal_backend_class->get_backend_property = ecb_ews_get_backend_property; - - backend_class = E_BACKEND_CLASS (klass); - backend_class->get_destination_address = ecb_ews_get_destination_address; - - object_class = G_OBJECT_CLASS (klass); - object_class->constructed = ecb_ews_constructed; - object_class->dispose = ecb_ews_dispose; - object_class->finalize = ecb_ews_finalize; -} diff --git a/src/calendar/e-cal-backend-ews.c.meeting-with-attachment b/src/calendar/e-cal-backend-ews.c.meeting-with-attachment deleted file mode 100644 index cb3ca21..0000000 --- a/src/calendar/e-cal-backend-ews.c.meeting-with-attachment +++ /dev/null @@ -1,3903 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com) - * - * 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. - * - * 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#include -#include -#include -#include - -#include -#include - -#include "server/e-source-ews-folder.h" -#include "server/e-ews-calendar-utils.h" -#include "server/e-ews-connection-utils.h" -#include "server/e-ews-camel-common.h" - -#include "e-cal-backend-ews.h" -#include "e-cal-backend-ews-utils.h" - -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -#ifdef G_OS_WIN32 -#ifdef gmtime_r -#undef gmtime_r -#endif - -/* The gmtime() in Microsoft's C library is MT-safe */ -#define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0) -#endif - -/* Private part of the CalBackendEws structure */ -struct _ECalBackendEwsPrivate { - GRecMutex cnc_lock; - EEwsConnection *cnc; - gchar *folder_id; - - guint subscription_key; - gboolean is_freebusy_calendar; - - gchar *attachments_dir; -}; - -#define X_EWS_ORIGINAL_COMP "X-EWS-ORIGINAL-COMP" - -#define EWS_MAX_FETCH_COUNT 100 - -#define GET_ITEMS_SYNC_PROPERTIES \ - "item:Attachments" \ - " item:Categories" \ - " item:HasAttachments" \ - " item:MimeContent" \ - " calendar:UID" \ - " calendar:Resources" \ - " calendar:ModifiedOccurrences" \ - " calendar:IsMeeting" \ - " calendar:IsResponseRequested" \ - " calendar:MyResponseType" \ - " calendar:RequiredAttendees" \ - " calendar:OptionalAttendees" - -#define GET_ITEMS_SYNC_PROPERTIES_2007 \ - GET_ITEMS_SYNC_PROPERTIES \ - " calendar:TimeZone" - -#define GET_ITEMS_SYNC_PROPERTIES_2010 \ - GET_ITEMS_SYNC_PROPERTIES \ - " calendar:StartTimeZone" \ - " calendar:EndTimeZone" - -#define e_data_cal_error_if_fail(expr, _code) \ - G_STMT_START { \ - if (G_LIKELY (expr)) { \ - } else { \ - g_log (G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "file %s: line %d (%s): assertion `%s' failed", \ - __FILE__, __LINE__, G_STRFUNC, #expr); \ - g_set_error (&error, E_DATA_CAL_ERROR, (_code), \ - "file %s: line %d (%s): assertion `%s' failed", \ - __FILE__, __LINE__, G_STRFUNC, #expr); \ - goto exit; \ - } \ - } G_STMT_END - -G_DEFINE_TYPE (ECalBackendEws, e_cal_backend_ews, E_TYPE_CAL_META_BACKEND) - -static CamelEwsSettings * -ecb_ews_get_collection_settings (ECalBackendEws *cbews) -{ - ESource *source; - ESource *collection; - ESourceCamel *extension; - ESourceRegistry *registry; - CamelSettings *settings; - const gchar *extension_name; - - source = e_backend_get_source (E_BACKEND (cbews)); - registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbews)); - - extension_name = e_source_camel_get_extension_name ("ews"); - e_source_camel_generate_subtype ("ews", CAMEL_TYPE_EWS_SETTINGS); - - /* The collection settings live in our parent data source. */ - collection = e_source_registry_find_extension ( - registry, source, extension_name); - g_return_val_if_fail (collection != NULL, NULL); - - extension = e_source_get_extension (collection, extension_name); - settings = e_source_camel_get_settings (extension); - - g_object_unref (collection); - - return CAMEL_EWS_SETTINGS (settings); -} - -static void -ecb_ews_convert_error_to_edc_error (GError **perror) -{ - GError *error = NULL; - - if (!perror || !*perror || (*perror)->domain == E_DATA_CAL_ERROR) - return; - - if ((*perror)->domain == EWS_CONNECTION_ERROR) { - switch ((*perror)->code) { - case EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED: - error = EDC_ERROR_EX (AuthenticationFailed, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_FOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_MANAGEDFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PARENTFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PUBLICFOLDERSERVERNOTFOUND: - error = EDC_ERROR_EX (NoSuchCal, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_EVENTNOTFOUND: - case EWS_CONNECTION_ERROR_ITEMNOTFOUND: - error = EDC_ERROR_EX (ObjectNotFound, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_UNAVAILABLE: - g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND, (*perror)->message); - break; - } - - if (!error) - error = EDC_ERROR_EX (OtherError, (*perror)->message); - } - - if (error) { - g_error_free (*perror); - *perror = error; - } -} - -static void -ecb_ews_maybe_disconnect_sync (ECalBackendEws *cbews, - GError **in_perror, - GCancellable *cancellable) -{ - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cbews)); - - if (in_perror && g_error_matches (*in_perror, E_DATA_CAL_ERROR, AuthenticationFailed)) { - e_cal_meta_backend_disconnect_sync (E_CAL_META_BACKEND (cbews), cancellable, NULL); - e_backend_schedule_credentials_required (E_BACKEND (cbews), E_SOURCE_CREDENTIALS_REASON_REJECTED, NULL, 0, NULL, NULL, G_STRFUNC); - } -} - -static void -ecb_ews_server_notification_cb (ECalBackendEws *cbews, - GSList *events, - EEwsConnection *cnc) -{ - GSList *link; - gboolean update_folder = FALSE; - - g_return_if_fail (cbews != NULL); - g_return_if_fail (cbews->priv != NULL); - - for (link = events; link && !update_folder; link = g_slist_next (link)) { - EEwsNotificationEvent *event = link->data; - - switch (event->type) { - case E_EWS_NOTIFICATION_EVENT_CREATED: - case E_EWS_NOTIFICATION_EVENT_DELETED: - case E_EWS_NOTIFICATION_EVENT_MODIFIED: - g_rec_mutex_lock (&cbews->priv->cnc_lock); - if (g_strcmp0 (event->folder_id, cbews->priv->folder_id) == 0) - update_folder = TRUE; - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - break; - case E_EWS_NOTIFICATION_EVENT_MOVED: - case E_EWS_NOTIFICATION_EVENT_COPIED: - g_rec_mutex_lock (&cbews->priv->cnc_lock); - if (g_strcmp0 (event->folder_id, cbews->priv->folder_id) == 0 || - g_strcmp0 (event->old_folder_id, cbews->priv->folder_id) == 0) - update_folder = TRUE; - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - break; - default: - return; - } - } - - if (update_folder) - e_cal_meta_backend_schedule_refresh (E_CAL_META_BACKEND (cbews)); -} - -static void -ecb_ews_unset_connection (ECalBackendEws *cbews) -{ - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cbews)); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->cnc) { - e_ews_connection_set_disconnected_flag (cbews->priv->cnc, TRUE); - - g_signal_handlers_disconnect_by_func (cbews->priv->cnc, ecb_ews_server_notification_cb, cbews); - - if (cbews->priv->subscription_key != 0) { - e_ews_connection_disable_notifications_sync ( - cbews->priv->cnc, - cbews->priv->subscription_key); - cbews->priv->subscription_key = 0; - } - } - - g_clear_object (&cbews->priv->cnc); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); -} - -static icaltimezone * -ecb_ews_get_timezone (ETimezoneCache *timezone_cache, - const gchar *msdn_tzid, - const gchar *tzid, - const gchar *evo_ews_tzid) -{ - icaltimezone *zone = NULL; - const gchar *evo_ews_msdn_tzid; - - zone = e_timezone_cache_get_timezone (timezone_cache, tzid); - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (tzid); - - if (g_strcmp0 (tzid, evo_ews_tzid) == 0) - return zone; - - if (evo_ews_tzid != NULL) { - evo_ews_msdn_tzid = e_cal_backend_ews_tz_util_get_msdn_equivalent (evo_ews_tzid); - - if (g_strcmp0 (msdn_tzid, evo_ews_msdn_tzid) == 0) { - zone = e_timezone_cache_get_timezone (timezone_cache, evo_ews_tzid); - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (evo_ews_tzid); - } - } - - return zone; -} - -static icalparameter * -ecb_ews_responsetype_to_partstat (const gchar *responsetype) -{ - icalparameter *param = NULL; - - if (!responsetype) - return icalparameter_new_partstat (ICAL_PARTSTAT_NONE); - - if (g_ascii_strcasecmp (responsetype, "Organizer") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_ACCEPTED); - else if (g_ascii_strcasecmp (responsetype, "Tentative") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_TENTATIVE); - else if (g_ascii_strcasecmp (responsetype, "Accept") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_ACCEPTED); - else if (g_ascii_strcasecmp (responsetype, "Decline") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_DECLINED); - else if (g_ascii_strcasecmp (responsetype, "NoResponseReceived") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION); - else if (g_ascii_strcasecmp (responsetype, "Unknown") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION); - - if (!param) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NONE); - - return param; -} - -static ECalComponent * -ecb_ews_item_to_component_sync (ECalBackendEws *cbews, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *res_component = NULL; - ETimezoneCache *timezone_cache; - icalcomponent_kind kind; - EEwsItemType item_type; - icalcomponent *icalcomp, *vcomp; - icaltimezone *utc_zone = icaltimezone_get_utc_timezone (); - CamelEwsSettings *ews_settings; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), NULL); - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - timezone_cache = E_TIMEZONE_CACHE (cbews); - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - ews_settings = ecb_ews_get_collection_settings (cbews); - - item_type = e_ews_item_get_item_type (item); - if (item_type == E_EWS_ITEM_TYPE_TASK || item_type == E_EWS_ITEM_TYPE_MEMO) { - icalproperty *icalprop; - icaltimetype due_date, start_date, complete_date, created; - icalproperty_status status = ICAL_STATUS_NONE; - icalproperty_class class = ICAL_CLASS_NONE; - const gchar *ews_task_status, *sensitivity; - EwsImportance item_importance; - gint priority = 5; - gboolean has_this_date = FALSE; - - vcomp = icalcomponent_new (ICAL_VCALENDAR_COMPONENT); - /*subject*/ - icalcomp = icalcomponent_new (item_type == E_EWS_ITEM_TYPE_TASK ? ICAL_VTODO_COMPONENT : ICAL_VJOURNAL_COMPONENT); - icalprop = icalproperty_new_summary (e_ews_item_get_subject (item)); - icalcomponent_add_property (icalcomp, icalprop); - - /*date time created*/ - created = icaltime_from_timet_with_zone (e_ews_item_get_date_created (item), 0, utc_zone); - icalprop = icalproperty_new_created (created); - icalcomponent_add_property (icalcomp, icalprop); - - /*sensitivity*/ - sensitivity = e_ews_item_get_sensitivity (item); - if (g_strcmp0 (sensitivity, "Normal") == 0) - class = ICAL_CLASS_PUBLIC; - else if (g_strcmp0 (sensitivity, "Private") == 0) - class = ICAL_CLASS_PRIVATE; - else if ((g_strcmp0 (sensitivity, "Confidential") == 0) || - (g_strcmp0 (sensitivity, "Personal") == 0)) - class = ICAL_CLASS_CONFIDENTIAL; - icalprop = icalproperty_new_class (class); - icalcomponent_add_property (icalcomp, icalprop); - - /*description*/ - icalprop = icalproperty_new_description (e_ews_item_get_body (item)); - icalcomponent_add_property (icalcomp, icalprop); - - /*task assaingments*/ - if (e_ews_item_get_delegator (item) != NULL) { - const gchar *task_owner = e_ews_item_get_delegator (item); - GSList *mailboxes = NULL, *l; - gboolean includes_last_item; - gchar *mailtoname, *user_email; - icalparameter *param; - - /*The task owner according to Exchange is current user, even that the task was assigned by - *someone else. I'm making the current user attendee and task delegator will be a task organizer */ - - user_email = camel_ews_settings_dup_email (ews_settings); - mailtoname = g_strdup_printf ("mailto:%s", user_email); - icalprop = icalproperty_new_attendee (mailtoname); - g_free (mailtoname); - g_free (user_email); - - param = icalparameter_new_cn (e_ews_item_get_owner (item)); - icalproperty_add_parameter (icalprop, param); - icalcomponent_add_property (icalcomp, icalprop); - - /* get delegator mail box*/ - e_ews_connection_resolve_names_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, task_owner, - EWS_SEARCH_AD, NULL, FALSE, &mailboxes, NULL, - &includes_last_item, cancellable, error); - - for (l = mailboxes; l != NULL; l = g_slist_next (l)) { - EwsMailbox *mb = l->data; - - mailtoname = g_strdup_printf ("mailto:%s", mb->email); - icalprop = icalproperty_new_organizer (mailtoname); - param = icalparameter_new_cn (mb->name); - icalproperty_add_parameter (icalprop, param); - icalcomponent_add_property (icalcomp, icalprop); - - g_free (mailtoname); - e_ews_mailbox_free (mb); - } - g_slist_free (mailboxes); - } - - if (item_type == E_EWS_ITEM_TYPE_TASK) { - icaltimezone *user_timezone = calendar_config_get_icaltimezone (); - const gchar *percent_complete; - - /*start date*/ - has_this_date = FALSE; - e_ews_item_task_has_start_date (item, &has_this_date); - if (has_this_date) { - start_date = icaltime_from_timet_with_zone (e_ews_item_get_start_date (item), 0, user_timezone); - start_date.is_date = 1; - icalprop = icalproperty_new_dtstart (start_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*status*/ - ews_task_status = e_ews_item_get_status (item); - if (g_strcmp0 (ews_task_status, "NotStarted") != 0) { - if (g_strcmp0 (ews_task_status, "Completed") == 0) - status = ICAL_STATUS_COMPLETED; - else if (g_strcmp0 (ews_task_status, "InProgress") == 0) - status = ICAL_STATUS_INPROCESS; - else if (g_strcmp0 (ews_task_status, "WaitingOnOthers") == 0) - status = ICAL_STATUS_NEEDSACTION; - else if (g_strcmp0 (ews_task_status, "Deferred") == 0) - status = ICAL_STATUS_CANCELLED; - icalprop = icalproperty_new_status (status); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*precent complete*/ - percent_complete = e_ews_item_get_percent_complete (item); - icalprop = icalproperty_new_percentcomplete (atoi (percent_complete ? percent_complete : "0")); - icalcomponent_add_property (icalcomp, icalprop); - - /*due date*/ - e_ews_item_task_has_due_date (item, &has_this_date); - if (has_this_date) { - due_date = icaltime_from_timet_with_zone (e_ews_item_get_due_date (item), 0, user_timezone); - due_date.is_date = 1; - icalprop = icalproperty_new_due (due_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*complete date*/ - has_this_date = FALSE; - e_ews_item_task_has_complete_date (item, &has_this_date); - if (has_this_date) { - complete_date = icaltime_from_timet_with_zone (e_ews_item_get_complete_date (item), 0, user_timezone); - icalprop = icalproperty_new_completed (complete_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*priority*/ - item_importance = e_ews_item_get_importance (item); - if (item_importance == EWS_ITEM_HIGH) - priority = 3; - else if (item_importance == EWS_ITEM_LOW) - priority = 7; - icalprop = icalproperty_new_priority (priority); - icalcomponent_add_property (icalcomp, icalprop); - } - - icalcomponent_add_component (vcomp, icalcomp); - } else { - struct icaltimetype dt; - const gchar *mime_content; - const gchar *tzid; - gboolean timezone_set = FALSE; - - mime_content = e_ews_item_get_mime_content (item); - vcomp = mime_content && *mime_content ? icalparser_parse_string (mime_content) : NULL; - - if (!vcomp && mime_content && *mime_content) { - const gchar *begin_vcalendar, *end_vcalendar; - - /* Workaround Exchange 2016 error, which returns invalid iCalendar object (without 'END:VCALENDAR'), - when the event has at least one detached instance. */ - begin_vcalendar = camel_strstrcase (mime_content, "BEGIN:VCALENDAR"); - end_vcalendar = camel_strstrcase (mime_content, "END:VCALENDAR"); - - /* If it exists, then it should be alone on a separate line */ - if (!(begin_vcalendar && (begin_vcalendar == mime_content || begin_vcalendar[-1] == '\n') && - (begin_vcalendar[15 /* strlen ("BEGIN:VCALENDAR") */] == '\r' || begin_vcalendar[15] == '\n'))) - begin_vcalendar = NULL; - - /* If it exists, then it should be alone on a separate line and not at the very beginning of the mime_content */ - if (!(end_vcalendar && end_vcalendar > mime_content && end_vcalendar[-1] == '\n' && - (end_vcalendar[13 /* strlen ("END:VCALENDAR") */] == '\r' || end_vcalendar[13] == '\n' || end_vcalendar[13] == '\0'))) - end_vcalendar = NULL; - - if (begin_vcalendar && !end_vcalendar) { - gchar *str; - - str = g_strconcat (mime_content, "\r\n", "END:VCALENDAR", "\r\n", NULL); - vcomp = icalparser_parse_string (str); - g_free (str); - } - } - - if (!vcomp) { - if (mime_content) - g_warning ("%s: Failed to parse mime content:---%s---", G_STRFUNC, mime_content); - return NULL; - } - - tzid = e_ews_item_get_tzid (item); - if (tzid == NULL) { - /* - * When we are working with Exchange server 2010 or newer, we have to handle a few - * things more than we do working old servers. These things are: - * - MSDN timezone names: - * Used setting StartTimeZone and EndTimeZone. MSDN timezone names are not - * the same used in libical, so we need to have a table of equivalence to - * convert from one to another and avoid show the MSDN timezone name to the - * user and save it in the ETimezoneCache. - * - EvoEWSStartTimeZone/EvoEWSEndTimeZone - * Used to keep track if the timezone shown to the user is the same one set - * by him/her. As we have a table of equivalence, sometimes the user sets a - * timezone but without EvoEWSStartTiemZone property, another timezone name, - * in the same offset, can be shown. And we want to avoid this. - * - DTEND property: - * As we have to work with DTEND setting an event when using EWS server 2010 or - * newer, we have to care about set it properly here, instead of use the same - * as is used in DTSTART. - */ - icaltimezone *start_zone, *end_zone; - const gchar *start_tzid, *end_tzid; - const gchar *ical_start_tzid, *ical_end_tzid; - const gchar *evo_ews_start_tzid, *evo_ews_end_tzid; - - start_tzid = e_ews_item_get_start_tzid (item); - end_tzid = e_ews_item_get_end_tzid (item); - - ical_start_tzid = e_cal_backend_ews_tz_util_get_ical_equivalent (start_tzid); - ical_end_tzid = e_cal_backend_ews_tz_util_get_ical_equivalent (end_tzid); - - evo_ews_start_tzid = e_ews_item_get_iana_start_time_zone (item); - evo_ews_end_tzid = e_ews_item_get_iana_end_time_zone (item); - - /* - * We have a few timezones that don't have an equivalent MSDN timezone. - * For those, we will get ical_start_tzid being NULL and then we need to use - * start_tzid, which one has the libical's expected name. - */ - start_zone = ecb_ews_get_timezone ( - timezone_cache, - start_tzid, - ical_start_tzid != NULL ? ical_start_tzid : start_tzid, - evo_ews_start_tzid); - end_zone = ecb_ews_get_timezone ( - timezone_cache, - end_tzid, - ical_end_tzid != NULL ? ical_end_tzid : end_tzid, - evo_ews_end_tzid); - - if (start_zone != NULL) { - icalcomp = icalcomponent_get_first_component (vcomp, kind); - - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - dt = icaltime_convert_to_zone (dt, start_zone); - icalcomponent_set_dtstart (icalcomp, dt); - - timezone_set = TRUE; - e_timezone_cache_add_timezone (timezone_cache, start_zone); - - if (end_zone != NULL) { - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); - dt = icaltime_convert_to_zone (dt, end_zone); - icalcomponent_set_dtend (icalcomp, dt); - - e_timezone_cache_add_timezone (timezone_cache, end_zone); - } - } - - if (!timezone_set) - tzid = start_tzid; - } - - if (!timezone_set && tzid) { - /* - * When we are working with Exchange server older than 2010, we don't set different - * DTSTART and DTEND properties in VTIMEZONE. The reason of that is we don't use - * those properties settings/changing a meeting timezone. - * So, for older servers, here, we only set the DTSTART and DTEND properties with - * the same values. - */ - icaltimezone *zone; - gchar *new_tzid = NULL; - - icalcomp = icalcomponent_get_first_component (vcomp, kind); - - if (!icaltimezone_get_builtin_timezone (tzid) && - icalcomponent_get_uid (icalcomp)) { - icalcomponent *vtimezone; - - /* Add the timezone */ - vtimezone = icalcomponent_get_first_component (vcomp, ICAL_VTIMEZONE_COMPONENT); - if (vtimezone != NULL) { - icalproperty *prop; - - new_tzid = g_strconcat ("/evolution/ews/tzid/", icalcomponent_get_uid (icalcomp), NULL); - - zone = icaltimezone_new (); - vtimezone = icalcomponent_new_clone (vtimezone); - prop = icalcomponent_get_first_property (vtimezone, ICAL_TZID_PROPERTY); - if (prop) { - icalproperty_set_tzid (prop, new_tzid); - - prop = icalcomponent_get_first_property (vtimezone, ICAL_LOCATION_PROPERTY); - if (!prop) { - /* Use the original tzid as the timezone Location, to not expose - evolution-ews TZID. */ - prop = icalproperty_new_location (tzid); - icalcomponent_add_property (vtimezone, prop); - } - } else { - g_free (new_tzid); - new_tzid = NULL; - } - icaltimezone_set_component (zone, vtimezone); - e_timezone_cache_add_timezone (timezone_cache, zone); - icaltimezone_free (zone, TRUE); - } - } - - zone = e_timezone_cache_get_timezone (timezone_cache, new_tzid ? new_tzid : tzid); - - if (!zone && new_tzid) - zone = e_timezone_cache_get_timezone (timezone_cache, tzid); - - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (tzid); - - if (zone != NULL) { - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - dt = icaltime_convert_to_zone (dt, zone); - icalcomponent_set_dtstart (icalcomp, dt); - - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); - dt = icaltime_convert_to_zone (dt, zone); - icalcomponent_set_dtend (icalcomp, dt); - } - - g_free (new_tzid); - } - } - - /* Vevent or Vtodo */ - icalcomp = icalcomponent_get_first_component (vcomp, kind); - if (icalcomp) { - icalproperty *icalprop, *freebusy; - struct icaltimetype itt; - const EwsId *item_id; - const GSList *l = NULL; - const gchar *uid = e_ews_item_get_uid (item); - - item_id = e_ews_item_get_id (item); - - if (e_ews_item_get_is_meeting (item)) { - gboolean is_response_requested = e_ews_item_get_is_response_requested (item); - gchar *user_email; - - user_email = camel_ews_settings_dup_email (ews_settings); - - /* Attendees */ - for (l = e_ews_item_get_attendees (item); l != NULL; l = g_slist_next (l)) { - icalparameter *param, *cu_type; - gchar *mailtoname; - const gchar *email = NULL; - EwsAttendee *attendee = (EwsAttendee *) l->data; - - if (!attendee->mailbox) - continue; - - if (g_strcmp0 (attendee->mailbox->routing_type, "EX") == 0) - email = e_ews_item_util_strip_ex_address (attendee->mailbox->email); - - mailtoname = g_strdup_printf ("mailto:%s", email ? email : attendee->mailbox->email); - icalprop = icalproperty_new_attendee (mailtoname); - g_free (mailtoname); - - param = icalparameter_new_cn (attendee->mailbox->name); - icalproperty_add_parameter (icalprop, param); - - if (g_ascii_strcasecmp (attendee->attendeetype, "Required") == 0) { - param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL); - } - else if (g_ascii_strcasecmp (attendee->attendeetype, "Resource") == 0) { - param = icalparameter_new_role (ICAL_ROLE_NONPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_RESOURCE); - } - else { - param = icalparameter_new_role ( ICAL_ROLE_OPTPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL); - } - icalproperty_add_parameter (icalprop, cu_type); - icalproperty_add_parameter (icalprop, param); - - if (is_response_requested) { - param = icalparameter_new_rsvp (ICAL_RSVP_TRUE); - icalproperty_add_parameter (icalprop, param); - } - - if (user_email && (email || attendee->mailbox->email) && e_ews_item_get_my_response_type (item) && - g_ascii_strcasecmp (email ? email : attendee->mailbox->email, user_email) == 0) { - param = ecb_ews_responsetype_to_partstat (e_ews_item_get_my_response_type (item)); - } else { - param = ecb_ews_responsetype_to_partstat (attendee->responsetype); - } - icalproperty_add_parameter (icalprop, param); - - icalcomponent_add_property (icalcomp, icalprop); - } - - g_free (user_email); - } - - /* Free/Busy */ - freebusy = icalcomponent_get_first_property (icalcomp, ICAL_TRANSP_PROPERTY); - if (!freebusy && (e_ews_item_get_item_type (item) != E_EWS_ITEM_TYPE_TASK)) { - /* Busy by default */ - freebusy = icalproperty_new_transp (ICAL_TRANSP_OPAQUE); - icalcomponent_add_property (icalcomp, freebusy); - } - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); - icalprop != NULL; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) { - if (g_strcmp0 (icalproperty_get_x_name (icalprop), "X-MICROSOFT-CDO-BUSYSTATUS") == 0) { - if (g_strcmp0 (icalproperty_get_value_as_string (icalprop), "BUSY") == 0) { - icalproperty_set_transp (freebusy, ICAL_TRANSP_OPAQUE); - } else { - icalproperty_set_transp (freebusy, ICAL_TRANSP_TRANSPARENT); - } - - break; - } - } - - /* AllDayEvent */ - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); - icalprop != NULL; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) { - if (g_strcmp0 (icalproperty_get_x_name (icalprop), "X-MICROSOFT-CDO-ALLDAYEVENT") == 0) { - if (g_strcmp0 (icalproperty_get_value_as_string (icalprop), "TRUE") == 0) { - struct icaltimetype dtend, dtstart; - dtstart = icalcomponent_get_dtstart (icalcomp); - dtstart.is_date = 1; - icalcomponent_set_dtstart (icalcomp, dtstart); - - dtend = icalcomponent_get_dtend (icalcomp); - dtend.is_date = 1; - icalcomponent_set_dtend (icalcomp, dtend); - } - break; - } - } - - if (icalcomponent_get_first_property (icalcomp, ICAL_RECURRENCEID_PROPERTY)) { - /* Exchange sets RRULE even on the children, which is broken */ - icalprop = icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY); - if (icalprop) { - icalcomponent_remove_property (icalcomp, icalprop); - icalproperty_free (icalprop); - } - } - - /* The EXDATE sent by the server can be date-time format with timezone, while - the event start time can be date-only. This breaks the rules, thus correct - it and make also EXDATE date-only. */ - itt = icalcomponent_get_dtstart (icalcomp); - if (icaltime_is_valid_time (itt) && itt.is_date) { - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_EXDATE_PROPERTY); - icalprop; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_EXDATE_PROPERTY)) { - itt = icalproperty_get_exdate (icalprop); - itt.is_date = 1; - icalproperty_set_exdate (icalprop, itt); - - icalproperty_remove_parameter_by_kind (icalprop, ICAL_TZID_PARAMETER); - } - } - - /* Exchange sets an ORGANIZER on all events. RFC2445 says: - * - * This property MUST NOT be specified in an iCalendar - * object that specifies only a time zone definition or - * that defines calendar entities that are not group - * scheduled entities, but are entities only on a single - * user's calendar. - */ - if (!icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) { - if ((icalprop = icalcomponent_get_first_property (icalcomp, ICAL_ORGANIZER_PROPERTY))) { - icalcomponent_remove_property (icalcomp, icalprop); - icalproperty_free (icalprop); - } - } - - icalcomponent_set_uid (icalcomp, uid ? uid : item_id->id); - - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-ITEMID", item_id->id); - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY", item_id->change_key); - - res_component = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (icalcomp)); - - /* Categories */ - e_cal_component_set_categories_list (res_component, (GSList *) e_ews_item_get_categories (item)); - - /* - * There is no API to set/get alarm description on the server side. - * However, for some reason, the alarm description has been set to "REMINDER" - * automatically (and with no i18n). Instead of show it to the user, let's - * set the summary as the alarm description. - */ - if (e_cal_component_has_alarms (res_component)) { - GList *alarm_uids, *l; - - alarm_uids = e_cal_component_get_alarm_uids (res_component); - for (l = alarm_uids; l != NULL; l = l->next) { - ECalComponentAlarm *alarm; - ECalComponentText text; - - alarm = e_cal_component_get_alarm (res_component, l->data); - e_cal_component_get_summary (res_component, &text); - e_cal_component_alarm_set_description (alarm, &text); - - e_cal_component_alarm_free (alarm); - } - cal_obj_uid_list_free (alarm_uids); - } - } - - icalcomponent_free (vcomp); - - if (res_component) { - const GSList *attachment_ids, *aid, *l; - const gchar *uid = NULL; - GSList *info_attachments = NULL, *uris = NULL; - gboolean has_attachment = FALSE; - - e_ews_item_has_attachments (item, &has_attachment); - if (!has_attachment) - return res_component; - - e_cal_component_get_uid (res_component, &uid); - - attachment_ids = e_ews_item_get_attachments_ids (item); - - if (e_ews_connection_get_attachments_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - uid, - attachment_ids, - cbews->priv->attachments_dir, - TRUE, - &info_attachments, - NULL, NULL, - cancellable, - NULL)) { - icalcomponent *icalcomp; - icalproperty *icalprop; - icalparameter *icalparam; - - for (l = info_attachments; l; l = l->next) { - EEwsAttachmentInfo *info = l->data; - - /* ignore non-uri attachments, because it's an exception */ - if (e_ews_attachment_info_get_type (info) == E_EWS_ATTACHMENT_INFO_TYPE_URI) { - const gchar *uri = e_ews_attachment_info_get_uri (info); - - if (uri) - uris = g_slist_append (uris, g_strdup (uri)); - } - } - - e_cal_component_set_attachment_list (res_component, uris); - - icalcomp = e_cal_component_get_icalcomponent (res_component); - icalprop = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY); - for (aid = attachment_ids; aid && icalprop; aid = aid->next, icalprop = icalcomponent_get_next_property (icalcomp, ICAL_ATTACH_PROPERTY)) { - icalparam = icalparameter_new_x (aid->data); - icalparameter_set_xname (icalparam, "X-EWS-ATTACHMENTID"); - icalproperty_add_parameter (icalprop, icalparam); - } - - g_slist_free_full (uris, g_free); - g_slist_free_full (info_attachments, (GDestroyNotify) e_ews_attachment_info_free); - } - } - - return res_component; -} - -static void -ecb_ews_store_original_comp (ECalComponent *comp) -{ - gchar *comp_str; - gchar *base64; - - g_return_if_fail (E_IS_CAL_COMPONENT (comp)); - - comp_str = e_cal_component_get_as_string (comp); - g_return_if_fail (comp_str != NULL); - - /* Include NUL-terminator */ - base64 = g_base64_encode ((const guchar *) comp_str, strlen (comp_str) + 1); - - e_cal_util_set_x_property (e_cal_component_get_icalcomponent (comp), - X_EWS_ORIGINAL_COMP, base64); - - g_free (base64); - g_free (comp_str); -} - -static ECalComponent * /* free with g_object_unref(), if not NULL */ -ecb_ews_restore_original_comp (ECalComponent *from_comp) -{ - ECalComponent *comp = NULL; - const gchar *original_base64; - guchar *decoded; - gsize len = -1; - - g_return_val_if_fail (E_IS_CAL_COMPONENT (from_comp), NULL); - - original_base64 = e_cal_util_get_x_property (e_cal_component_get_icalcomponent (from_comp), X_EWS_ORIGINAL_COMP); - - if (!original_base64 || !*original_base64) - return NULL; - - decoded = g_base64_decode (original_base64, &len); - if (!decoded || !*decoded || len <= 0) { - g_free (decoded); - return NULL; - } - - if (decoded[len - 1] != '\0') { - gchar *tmp; - - tmp = g_strndup ((const gchar *) decoded, len); - - g_free (decoded); - decoded = (guchar *) tmp; - } - - if (decoded && *decoded) - comp = e_cal_component_new_from_string ((const gchar *) decoded); - - g_free (decoded); - - return comp; -} - -static gboolean -ecb_ews_get_items_sync (ECalBackendEws *cbews, - const GSList *item_ids, /* gchar * */ - const gchar *default_props, - const EEwsAdditionalProps *add_props, - GSList **out_components, /* ECalComponent * */ - GCancellable *cancellable, - GError **error) -{ - GSList *items = NULL, *link; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (out_components != NULL, FALSE); - - success = e_ews_connection_get_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - item_ids, - default_props, - add_props, - FALSE, - NULL, - E_EWS_BODY_TYPE_TEXT, - &items, - NULL, NULL, - cancellable, - error); - - if (!success) - return FALSE; - - /* fetch modified occurrences */ - for (link = items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const GSList *modified_occurrences; - - if (!item || e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - modified_occurrences = e_ews_item_get_modified_occurrences (item); - if (modified_occurrences) { - EEwsAdditionalProps *modified_add_props; - - modified_add_props = e_ews_additional_props_new (); - if (e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) { - EEwsExtendedFieldURI *ext_uri; - - modified_add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2010); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSStartTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - modified_add_props->extended_furis = g_slist_append (modified_add_props->extended_furis, ext_uri); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSEndTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - modified_add_props->extended_furis = g_slist_append (modified_add_props->extended_furis, ext_uri); - } else { - modified_add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2007); - } - - success = ecb_ews_get_items_sync (cbews, modified_occurrences, "IdOnly", modified_add_props, out_components, cancellable, error); - - e_ews_additional_props_free (modified_add_props); - - if (!success) - goto exit; - } - } - - for (link = items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - ECalComponent *comp; - GError *local_error = NULL; - - if (!item || e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - comp = ecb_ews_item_to_component_sync (cbews, item, cancellable, &local_error); - if (!comp) { - if (!local_error) - continue; - - g_propagate_error (error, local_error); - success = FALSE; - break; - } - - ecb_ews_store_original_comp (comp); - - *out_components = g_slist_prepend (*out_components, comp); - } - - exit: - g_slist_free_full (items, g_object_unref); - - return success; -} - -static gboolean -ecb_ews_fetch_items_sync (ECalBackendEws *cbews, - const GSList *items, /* EEwsItem * */ - GSList **out_components, /* ECalComponent * */ - GCancellable *cancellable, - GError **error) -{ - GSList *event_ids = NULL, *task_memo_ids = NULL, *link; - gboolean success = TRUE; - - for (link = (GSList *) items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (type == E_EWS_ITEM_TYPE_EVENT) - event_ids = g_slist_prepend (event_ids, g_strdup (id->id)); - else if (type == E_EWS_ITEM_TYPE_TASK || type == E_EWS_ITEM_TYPE_MEMO) { - task_memo_ids = g_slist_prepend (task_memo_ids, g_strdup (id->id)); - } - } - - if (event_ids) { - EEwsAdditionalProps *add_props; - - add_props = e_ews_additional_props_new (); - if (e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) { - EEwsExtendedFieldURI *ext_uri; - - add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2010); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSStartTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - add_props->extended_furis = g_slist_append (add_props->extended_furis, ext_uri); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSEndTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - add_props->extended_furis = g_slist_append (add_props->extended_furis, ext_uri); - } else { - add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2007); - } - - success = ecb_ews_get_items_sync (cbews, event_ids, "IdOnly", add_props, out_components, cancellable, error); - - e_ews_additional_props_free (add_props); - } - - if (task_memo_ids && success) - success = ecb_ews_get_items_sync (cbews, task_memo_ids, "AllProperties", NULL, out_components, cancellable, error); - - g_slist_free_full (event_ids, g_free); - g_slist_free_full (task_memo_ids, g_free); - - return success; -} - -static gboolean -ecb_ews_freebusy_ecomp_changed (ECalComponent *ecomp, - icalcomponent *vevent) -{ - icalcomponent *icomp; - gboolean changed = FALSE; - - g_return_val_if_fail (vevent != NULL, FALSE); - - if (!ecomp) - return TRUE; - - icomp = e_cal_component_get_icalcomponent (ecomp); - if (!icomp) - return TRUE; - - if (!changed) - changed = g_strcmp0 (icalcomponent_get_summary (icomp), icalcomponent_get_summary (vevent)) != 0; - if (!changed) - changed = g_strcmp0 (icalcomponent_get_location (icomp), icalcomponent_get_location (vevent)) != 0; - if (!changed) - changed = icaltime_compare (icalcomponent_get_dtstart (icomp), icalcomponent_get_dtstart (vevent)) != 0; - if (!changed) - changed = icaltime_compare (icalcomponent_get_dtend (icomp), icalcomponent_get_dtend (vevent)) != 0; - - return changed; -} - -static GSList * /* the possibly modified 'in_items' */ -ecb_ews_verify_changes (ECalCache *cal_cache, - icalcomponent_kind kind, - GSList *in_items, /* EEwsItem * */ - GCancellable *cancellable) -{ - GSList *items = NULL, *link; - - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), in_items); - - for (link = in_items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (!g_cancellable_is_cancelled (cancellable) && ( - (type == E_EWS_ITEM_TYPE_EVENT && kind == ICAL_VEVENT_COMPONENT) || - (type == E_EWS_ITEM_TYPE_MEMO && kind == ICAL_VJOURNAL_COMPONENT) || - (type == E_EWS_ITEM_TYPE_TASK && kind == ICAL_VTODO_COMPONENT) )) { - ECalComponent *existing = NULL; - - if (e_cal_cache_get_component (cal_cache, id->id, NULL, &existing, cancellable, NULL) && - existing && g_strcmp0 (e_cal_util_get_x_property (e_cal_component_get_icalcomponent (existing), - "X-EVOLUTION-CHANGEKEY"), id->change_key) == 0) { - g_object_unref (item); - } else { - items = g_slist_prepend (items, item); - } - - g_clear_object (&existing); - } else if (type == E_EWS_ITEM_TYPE_EVENT || - type == E_EWS_ITEM_TYPE_MEMO || - type == E_EWS_ITEM_TYPE_TASK) { - g_object_unref (item); - } else { - items = g_slist_prepend (items, item); - } - } - - g_slist_free (in_items); - - return items; -} - -static GSList * /* ECalMetaBackendInfo */ -ecb_ews_components_to_infos (ECalMetaBackend *meta_backend, - const GSList *components, /* ECalComponent * */ - icalcomponent_kind kind) -{ - GSList *nfos = NULL, *link; - GHashTable *sorted_by_uids; /* gchar * ~> GSList { ECalComponent * } */ - GHashTableIter iter; - gpointer key, value; - - sorted_by_uids = g_hash_table_new (g_str_hash, g_str_equal); - - for (link = (GSList *) components; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - icalcomponent *icomp; - const gchar *uid; - GSList *instances; - - if (!comp) - continue; - - icomp = e_cal_component_get_icalcomponent (comp); - uid = icalcomponent_get_uid (icomp); - - if (!uid) - continue; - - ecb_ews_store_original_comp (comp); - - instances = g_hash_table_lookup (sorted_by_uids, uid); - g_hash_table_insert (sorted_by_uids, (gpointer) uid, g_slist_prepend (instances, comp)); - } - - g_hash_table_iter_init (&iter, sorted_by_uids); - while (g_hash_table_iter_next (&iter, &key, &value)) { - const gchar *uid = key; - GSList *instances = value, *link; - icalcomponent *icomp, *merged; - ECalComponent *comp; - ECalMetaBackendInfo *nfo; - const gchar *revision, *itemid; - - if (!uid || !instances) { - g_slist_free (instances); - continue; - } - - /* Try to find master object, to have seves marter's itemid in ECalMetaBackendInfo::extra, - thus the re-load of the event is done for the whole series and not for a detached instance */ - comp = NULL; - for (link = instances; link && !comp; link = g_slist_next (link)) { - comp = link->data; - - if (!comp) - continue; - - if (e_cal_component_is_instance (comp)) - comp = NULL; - } - - if (!comp) - comp = instances->data; - - if (!comp) { - g_slist_free (instances); - continue; - } - - icomp = e_cal_component_get_icalcomponent (comp); - itemid = e_cal_util_get_x_property (icomp, "X-EVOLUTION-ITEMID"); - revision = e_cal_util_get_x_property (icomp, "X-EVOLUTION-CHANGEKEY"); - merged = e_cal_meta_backend_merge_instances (meta_backend, instances, FALSE); - - if (!merged) { - g_warn_if_fail (merged != NULL); - g_slist_free (instances); - continue; - } - - nfo = e_cal_meta_backend_info_new (uid, revision, NULL, itemid); - nfo->object = icalcomponent_as_ical_string_r (merged); - - nfos = g_slist_prepend (nfos, nfo); - - icalcomponent_free (merged); - g_slist_free (instances); - } - - g_hash_table_destroy (sorted_by_uids); - - return nfos; -} - -static void -ecb_ews_extract_item_id (ECalComponent *comp, - gchar **out_id, - gchar **out_change_key) -{ - icalcomponent *icalcomp; - - g_return_if_fail (E_IS_CAL_COMPONENT (comp)); - - icalcomp = e_cal_component_get_icalcomponent (comp); - g_return_if_fail (icalcomp != NULL); - - if (out_id) - *out_id = e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-ITEMID"); - if (out_change_key) - *out_change_key = e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY"); -} - -static gboolean -ecb_ews_is_organizer (ECalBackendEws *cbews, - ECalComponent *comp) -{ - ECalComponentOrganizer organizer; - gboolean is_organizer = FALSE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE); - - if (!e_cal_component_has_organizer (comp)) - return FALSE; - - organizer.value = NULL; - - e_cal_component_get_organizer (comp, &organizer); - if (organizer.value) { - CamelEwsSettings *ews_settings; - const gchar *email = organizer.value; - gchar *user_email; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - user_email = camel_ews_settings_dup_email (ews_settings); - - if (!g_ascii_strncasecmp (email, "mailto:", 7)) - email += 7; - - is_organizer = user_email && g_ascii_strcasecmp (email, user_email) == 0; - - g_free (user_email); - } - - return is_organizer; -} - -static gboolean -ecb_ews_connect_sync (ECalMetaBackend *meta_backend, - const ENamedParameters *credentials, - ESourceAuthenticationResult *out_auth_result, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - CamelEwsSettings *ews_settings; - gchar *hosturl; - gboolean success = FALSE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_auth_result != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->cnc) { - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - *out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED; - - return TRUE; - } - - ews_settings = ecb_ews_get_collection_settings (cbews); - hosturl = camel_ews_settings_dup_hosturl (ews_settings); - - cbews->priv->cnc = e_ews_connection_new_for_backend (E_BACKEND (cbews), e_cal_backend_get_registry (E_CAL_BACKEND (cbews)), hosturl, ews_settings); - - e_binding_bind_property ( - cbews, "proxy-resolver", - cbews->priv->cnc, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - *out_auth_result = e_ews_connection_try_credentials_sync (cbews->priv->cnc, credentials, cancellable, error); - - if (*out_auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) { - ESource *source = e_backend_get_source (E_BACKEND (cbews)); - ESourceEwsFolder *ews_folder; - - ews_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER); - - g_free (cbews->priv->folder_id); - cbews->priv->folder_id = e_source_ews_folder_dup_id (ews_folder); - cbews->priv->is_freebusy_calendar = cbews->priv->folder_id && g_str_has_prefix (cbews->priv->folder_id, "freebusy-calendar::"); - - g_signal_connect_swapped (cbews->priv->cnc, "server-notification", - G_CALLBACK (ecb_ews_server_notification_cb), cbews); - - if (!cbews->priv->is_freebusy_calendar && - camel_ews_settings_get_listen_notifications (ews_settings) && - e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010_SP1)) { - GSList *folders = NULL; - - folders = g_slist_prepend (folders, cbews->priv->folder_id); - - e_ews_connection_enable_notifications_sync (cbews->priv->cnc, - folders, &cbews->priv->subscription_key); - - g_slist_free (folders); - } - - e_cal_backend_set_writable (E_CAL_BACKEND (cbews), !cbews->priv->is_freebusy_calendar); - success = TRUE; - } else { - ecb_ews_convert_error_to_edc_error (error); - g_clear_object (&cbews->priv->cnc); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - g_free (hosturl); - - return success; -} - -static gboolean -ecb_ews_disconnect_sync (ECalMetaBackend *meta_backend, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - ecb_ews_unset_connection (cbews); - - return TRUE; -} - -static gboolean -ecb_ews_get_changes_sync (ECalMetaBackend *meta_backend, - const gchar *last_sync_tag, - gboolean is_repeat, - gchar **out_new_sync_tag, - gboolean *out_repeat, - GSList **out_created_objects, - GSList **out_modified_objects, - GSList **out_removed_objects, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - gboolean success = TRUE; - GError *local_error = NULL; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_new_sync_tag != NULL, FALSE); - g_return_val_if_fail (out_repeat != NULL, FALSE); - g_return_val_if_fail (out_created_objects != NULL, FALSE); - g_return_val_if_fail (out_modified_objects != NULL, FALSE); - g_return_val_if_fail (out_removed_objects != NULL, FALSE); - - *out_created_objects = NULL; - *out_modified_objects = NULL; - *out_removed_objects = NULL; - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->is_freebusy_calendar) { - ESourceEwsFolder *ews_folder; - EEWSFreeBusyData fbdata; - GSList *free_busy = NULL, *link; - gboolean success; - time_t today; - - ews_folder = e_source_get_extension (e_backend_get_source (E_BACKEND (cbews)), E_SOURCE_EXTENSION_EWS_FOLDER); - - today = time_day_begin (time (NULL)); - - fbdata.period_start = time_add_week (today, -e_source_ews_folder_get_freebusy_weeks_before (ews_folder)); - fbdata.period_end = time_day_end (time_add_week (today, e_source_ews_folder_get_freebusy_weeks_after (ews_folder))); - fbdata.user_mails = g_slist_prepend (NULL, e_source_ews_folder_dup_foreign_mail (ews_folder)); - - success = e_ews_connection_get_free_busy_sync (cbews->priv->cnc, G_PRIORITY_DEFAULT, - e_ews_cal_utils_prepare_free_busy_request, &fbdata, - &free_busy, cancellable, &local_error); - - if (success) { - icaltimezone *utc_zone = icaltimezone_get_utc_timezone (); - GSList *comps = NULL; - GHashTable *known; - GHashTableIter iter; - gpointer key; - - known = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - - if (e_cal_cache_search_components (cal_cache, NULL, &comps, cancellable, NULL)) { - for (link = comps; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - icalcomponent *icomp; - const gchar *uid; - - if (!comp) - continue; - - icomp = e_cal_component_get_icalcomponent (comp); - if (!icomp) - continue; - - uid = icalcomponent_get_uid (icomp); - - if (uid && *uid) - g_hash_table_insert (known, g_strdup (uid), g_object_ref (comp)); - } - - g_slist_free_full (comps, g_object_unref); - } - - for (link = free_busy; link; link = g_slist_next (link)) { - icalcomponent *fbcomp = link->data; - icalproperty *fbprop; - icalparameter *param; - struct icalperiodtype fb; - icalparameter_fbtype fbtype; - - if (!fbcomp || icalcomponent_isa (fbcomp) != ICAL_VFREEBUSY_COMPONENT) - continue; - - for (fbprop = icalcomponent_get_first_property (fbcomp, ICAL_FREEBUSY_PROPERTY); - fbprop; - fbprop = icalcomponent_get_next_property (fbcomp, ICAL_FREEBUSY_PROPERTY)) { - ECalComponent *ecomp; - icalcomponent *vevent; - const gchar *id, *summary, *location; - - param = icalproperty_get_first_parameter (fbprop, ICAL_FBTYPE_PARAMETER); - if (!param) - continue; - - fbtype = icalparameter_get_fbtype (param); - - if (fbtype != ICAL_FBTYPE_FREE && - fbtype != ICAL_FBTYPE_BUSY && - fbtype != ICAL_FBTYPE_BUSYUNAVAILABLE && - fbtype != ICAL_FBTYPE_BUSYTENTATIVE) - continue; - - fb = icalproperty_get_freebusy (fbprop); - id = icalproperty_get_parameter_as_string (fbprop, "X-EWS-ID"); - summary = icalproperty_get_parameter_as_string (fbprop, "X-SUMMARY"); - location = icalproperty_get_parameter_as_string (fbprop, "X-LOCATION"); - - vevent = icalcomponent_new_vevent (); - - if (id && *id) { - icalcomponent_set_uid (vevent, id); - } else { - gchar *uid; - - uid = g_strdup_printf ("%s-%s-%d", - icaltime_as_ical_string (fb.start), - icaltime_as_ical_string (fb.end), - (gint) fbtype); - - icalcomponent_set_uid (vevent, uid); - - g_free (uid); - } - - fb.start.zone = utc_zone; - fb.end.zone = utc_zone; - - icalcomponent_set_dtstart (vevent, fb.start); - icalcomponent_set_dtend (vevent, fb.end); - - icalcomponent_add_property (vevent, icalproperty_new_created (icaltime_current_time_with_zone (utc_zone))); - - if (fbtype == ICAL_FBTYPE_FREE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Free")); - icalcomponent_add_property (vevent, icalproperty_new_transp (ICAL_TRANSP_TRANSPARENT)); - } else if (fbtype == ICAL_FBTYPE_BUSY) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Busy")); - } else if (fbtype == ICAL_FBTYPE_BUSYUNAVAILABLE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Out of Office")); - } else if (fbtype == ICAL_FBTYPE_BUSYTENTATIVE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Tentative")); - } - - if (summary && *summary) - icalcomponent_set_summary (vevent, summary); - - if (location && *location) - icalcomponent_set_location (vevent, location); - - ecomp = g_hash_table_lookup (known, icalcomponent_get_uid (vevent)); - if (ecomp) { - g_object_ref (ecomp); - - /* This dereferences the ecomp, thus the ref() call above to keep it alive */ - g_hash_table_remove (known, icalcomponent_get_uid (vevent)); - - if (ecb_ews_freebusy_ecomp_changed (ecomp, vevent)) { - ECalMetaBackendInfo *nfo; - gchar *revision = e_util_generate_uid (); - - e_cal_util_set_x_property (vevent, "X-EVOLUTION-CHANGEKEY", revision); - - nfo = e_cal_meta_backend_info_new (icalcomponent_get_uid (vevent), NULL, NULL, NULL); - nfo->revision = revision; - nfo->object = icalcomponent_as_ical_string_r (vevent); - - *out_created_objects = g_slist_prepend (*out_created_objects, nfo); - } else { - icalcomponent_free (vevent); - } - - g_clear_object (&ecomp); - } else { - ECalMetaBackendInfo *nfo; - gchar *revision = e_util_generate_uid (); - - e_cal_util_set_x_property (vevent, "X-EVOLUTION-CHANGEKEY", revision); - - nfo = e_cal_meta_backend_info_new (icalcomponent_get_uid (vevent), NULL, NULL, NULL); - nfo->revision = revision; - nfo->object = icalcomponent_as_ical_string_r (vevent); - - *out_modified_objects = g_slist_prepend (*out_modified_objects, nfo); - } - } - } - - g_hash_table_iter_init (&iter, known); - while (g_hash_table_iter_next (&iter, &key, NULL)) { - const gchar *uid = key; - - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_cal_meta_backend_info_new (uid, NULL, NULL, NULL)); - } - - g_hash_table_destroy (known); - } else if (g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NOFREEBUSYACCESS)) { - e_cal_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL); - - e_cal_backend_notify_error (E_CAL_BACKEND (cbews), local_error->message); - g_clear_error (&local_error); - } else { - g_propagate_error (error, local_error); - } - - g_slist_free_full (free_busy, (GDestroyNotify) icalcomponent_free); - g_slist_free_full (fbdata.user_mails, g_free); - } else { - GSList *items_created = NULL, *items_modified = NULL, *items_deleted = NULL, *link; - EEwsAdditionalProps *add_props; - gboolean includes_last_item = TRUE; - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("item:ItemClass"); - - success = e_ews_connection_sync_folder_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - last_sync_tag, cbews->priv->folder_id, "IdOnly", add_props, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - - if (!success && - g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) { - g_clear_error (&local_error); - - e_cal_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL); - - success = e_ews_connection_sync_folder_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - NULL, cbews->priv->folder_id, "IdOnly", add_props, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - } - - e_ews_additional_props_free (add_props); - - if (success) { - GSList *components_created = NULL, *components_modified = NULL; - icalcomponent_kind kind; - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - - /* The sync state doesn't cover changes made by save_component_sync(), - thus verify the changes, instead of re-donwloading the component again */ - items_created = ecb_ews_verify_changes (cal_cache, kind, items_created, cancellable); - items_modified = ecb_ews_verify_changes (cal_cache, kind, items_modified, cancellable); - - if (items_created) { - success = ecb_ews_fetch_items_sync (cbews, items_created, &components_created, cancellable, error); - if (success) - *out_created_objects = ecb_ews_components_to_infos (meta_backend, components_created, kind); - } - - if (items_modified) { - success = ecb_ews_fetch_items_sync (cbews, items_modified, &components_modified, cancellable, error); - if (success) - *out_modified_objects = ecb_ews_components_to_infos (meta_backend, components_modified, kind); - } - - for (link = items_deleted; link; link = g_slist_next (link)) { - const gchar *item_id = link->data; - GSList *ids = NULL, *ilink; - - if (!e_cal_cache_get_ids_with_extra (cal_cache, item_id, &ids, cancellable, NULL)) - continue; - - for (ilink = ids; ilink; ilink = g_slist_next (ilink)) { - ECalComponentId *id = ilink->data; - - /* Use the master object */ - if (id && id->uid && *id->uid && (!id->rid || !*id->rid)) { - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_cal_meta_backend_info_new (id->uid, NULL, NULL, NULL)); - break; - } - } - - g_slist_free_full (ids, (GDestroyNotify) e_cal_component_free_id); - } - - g_slist_free_full (components_created, g_object_unref); - g_slist_free_full (components_modified, g_object_unref); - - *out_repeat = !includes_last_item; - } else if (local_error) { - g_propagate_error (error, local_error); - } - - g_slist_free_full (items_created, g_object_unref); - g_slist_free_full (items_modified, g_object_unref); - g_slist_free_full (items_deleted, g_free); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_clear_object (&cal_cache); - - return success; -} - -static gboolean -ecb_ews_load_component_sync (ECalMetaBackend *meta_backend, - const gchar *uid, - const gchar *extra, - icalcomponent **out_component, - gchar **out_extra, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - GSList *ids, *items = NULL, *components = NULL; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (uid != NULL, FALSE); - g_return_val_if_fail (out_component != NULL, FALSE); - g_return_val_if_fail (out_extra != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - ids = g_slist_prepend (NULL, (gpointer) (extra && *extra ? extra : uid)); - - success = e_ews_connection_get_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, ids, "IdOnly", - NULL, FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &items, NULL, NULL, cancellable, error); - - g_slist_free (ids); - - if (success && items) { - success = ecb_ews_fetch_items_sync (cbews, items, &components, cancellable, error); - - if (components) { - const EwsId *ews_id = e_ews_item_get_id (items->data); - - if (ews_id) - *out_extra = g_strdup (ews_id->id); - - if (components->next) { - GSList *link; - - *out_component = icalcomponent_new_vcalendar (); - - for (link = components; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - if (!comp) - continue; - - icalcomponent_add_component (*out_component, - icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp))); - } - } else { - *out_component = icalcomponent_new_clone (e_cal_component_get_icalcomponent (components->data)); - } - } else { - success = FALSE; - } - } - - if (!components && e_cal_meta_backend_refresh_sync (meta_backend, cancellable, NULL)) { - ECalCache *cal_cache; - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - if (cal_cache) { - success = e_cal_cache_get_components_by_uid (cal_cache, uid, &components, cancellable, NULL); - if (success) { - *out_component = e_cal_meta_backend_merge_instances (meta_backend, components, FALSE); - - if (!e_cal_cache_get_component_extra (cal_cache, uid, NULL, out_extra, cancellable, NULL)) - *out_extra = NULL; - - g_clear_error (error); - } - g_object_unref (cal_cache); - } - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_slist_free_full (components, g_object_unref); - g_slist_free_full (items, g_object_unref); - - return success; -} - -/* Very simple and naive component comparator, to avoid - unnecessary uploads and changes on the server. */ -static gboolean -ecb_ews_components_equal (ECalComponent *comp1, - ECalComponent *comp2) -{ - icalcomponent *icomp1, *icomp2; - icalproperty *prop1; - GHashTable *processed_props; - gboolean equal = TRUE; - - if (!comp1 && !comp2) - return TRUE; - else if (!comp1 || !comp2) - return FALSE; - - icomp1 = e_cal_component_get_icalcomponent (comp1); - icomp2 = e_cal_component_get_icalcomponent (comp2); - - if (!icomp1 || !icomp2) - return FALSE; - - if (g_strcmp0 (icalcomponent_get_uid (icomp1), icalcomponent_get_uid (icomp2)) != 0) - return FALSE; - - if (icalcomponent_count_properties (icomp1, ICAL_ANY_PROPERTY) != - icalcomponent_count_properties (icomp2, ICAL_ANY_PROPERTY)) - return FALSE; - - processed_props = g_hash_table_new (g_direct_hash, g_direct_equal); - - for (prop1 = icalcomponent_get_first_property (icomp1, ICAL_ANY_PROPERTY); - prop1 && equal; - prop1 = icalcomponent_get_next_property (icomp1, ICAL_ANY_PROPERTY)) { - icalproperty_kind kind = icalproperty_isa (prop1); - icalproperty *prop2; - - for (prop2 = icalcomponent_get_first_property (icomp2, kind); - prop2; - prop2 = icalcomponent_get_next_property (icomp2, kind)) { - gchar *str1, *str2; - gboolean same; - - if (g_hash_table_contains (processed_props, prop2)) - continue; - - if (icalproperty_count_parameters (prop1) != icalproperty_count_parameters (prop2)) - continue; - - str1 = icalproperty_as_ical_string_r (prop1); - str2 = icalproperty_as_ical_string_r (prop2); - - same = g_strcmp0 (str1, str2) == 0; - - g_free (str1); - g_free (str2); - - if (same) { - g_hash_table_insert (processed_props, prop2, NULL); - break; - } - } - - if (!prop2) - equal = FALSE; - } - - g_hash_table_destroy (processed_props); - - return equal; -} - -typedef struct _ChangeData { - ECalComponent *old_component; - ECalComponent *new_component; -} ChangeData; - -static void -change_data_free (gpointer ptr) -{ - ChangeData *cd = ptr; - - if (cd) { - g_clear_object (&cd->old_component); - g_clear_object (&cd->new_component); - g_free (cd); - } -} - -static void -ecb_ews_filter_out_unchanged_instances (const GSList *to_save_instances, - const GSList *existing_instances, - GSList **out_changed_instances, /* ChangeData * */ - GSList **out_removed_instances) /* ECalComponent * */ -{ - GSList *link = NULL; - GHashTable *existing_hash; - GHashTableIter iter; - gpointer value; - - g_return_if_fail (to_save_instances != NULL); - g_return_if_fail (existing_instances != NULL); - g_return_if_fail (out_changed_instances != NULL); - g_return_if_fail (out_removed_instances != NULL); - - *out_changed_instances = NULL; - *out_removed_instances = NULL; - - existing_hash = g_hash_table_new_full ((GHashFunc)e_cal_component_id_hash, (GEqualFunc) e_cal_component_id_equal, - (GDestroyNotify) e_cal_component_free_id, NULL); - - for (link = (GSList *) existing_instances; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id; - - id = e_cal_component_get_id (comp); - if (id) - g_hash_table_insert (existing_hash, id, comp); - } - - for (link = (GSList *) to_save_instances; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id = NULL; - - id = e_cal_component_get_id (comp); - if (id) { - ECalComponent *old_comp; - - old_comp = g_hash_table_lookup (existing_hash, id); - - if (!ecb_ews_components_equal (comp, old_comp)) { - ChangeData *cd; - - cd = g_new0 (ChangeData, 1); - cd->old_component = old_comp ? g_object_ref (old_comp) : NULL; - cd->new_component = g_object_ref (comp); - - *out_changed_instances = g_slist_prepend (*out_changed_instances, cd); - } - - g_hash_table_remove (existing_hash, id); - e_cal_component_free_id (id); - } - } - - g_hash_table_iter_init (&iter, existing_hash); - while (g_hash_table_iter_next (&iter, NULL, &value)) { - *out_removed_instances = g_slist_prepend (*out_removed_instances, g_object_ref (value)); - } - - g_hash_table_destroy (existing_hash); -} - -static gboolean -ecb_ews_extract_attachments (icalcomponent *icalcomp, - GSList **out_attachments) /* EEwsAttachmentInfo * */ -{ - icalproperty *prop; - GSList *props = NULL, *link; - - g_return_val_if_fail (icalcomp != NULL, FALSE); - g_return_val_if_fail (out_attachments != NULL, FALSE); - - *out_attachments = NULL; - - for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY); - prop; - prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTACH_PROPERTY)) { - props = g_slist_prepend (props, prop); - } - - for (link = props; link; link = g_slist_next (link)) { - EEwsAttachmentInfo *info; - icalattach *attach; - icalparameter *param; - const gchar *stored_filename; - - prop = link->data; - param = icalproperty_get_first_parameter (prop, ICAL_FILENAME_PARAMETER); - stored_filename = param ? icalparameter_get_filename (param) : NULL; - - attach = icalproperty_get_attach (prop); - if (icalattach_get_is_url (attach)) { - const gchar *uri; - - uri = icalattach_get_url (attach); - - if (!uri || !*uri) - continue; - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_URI); - - e_ews_attachment_info_set_uri (info, uri); - if (stored_filename && *stored_filename) { - e_ews_attachment_info_set_prefer_filename (info, stored_filename); - } else { - gchar *uri_filename; - - uri_filename = g_filename_from_uri (uri, NULL, NULL); - if (uri_filename && *uri_filename) { - gchar *basename; - - basename = g_path_get_basename (uri_filename); - if (basename && *basename && basename[0] != '.' && basename[0] != G_DIR_SEPARATOR) { - const gchar *uid; - - uid = icalcomponent_get_uid (icalcomp); - - if (uid && g_str_has_prefix (basename, uid) && basename[strlen (uid)] == '-') { - e_ews_attachment_info_set_prefer_filename (info, basename + strlen (uid) + 1); - } - } - - g_free (basename); - } - - g_free (uri_filename); - } - } else { - gsize len = -1; - guchar *decoded = NULL; - const gchar *content; - - content = (const gchar *) icalattach_get_data (attach); - decoded = g_base64_decode (content, &len); - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_INLINED); - e_ews_attachment_info_set_inlined_data (info, decoded, len); - - if (stored_filename && *stored_filename) - e_ews_attachment_info_set_prefer_filename (info, stored_filename); - - g_free (decoded); - } - - e_ews_attachment_info_set_id (info, icalproperty_get_parameter_as_string (prop, "X-EWS-ATTACHMENTID")); - *out_attachments = g_slist_prepend (*out_attachments, info); - } - - g_slist_free (props); - - return *out_attachments != NULL; -} - -static icaltimezone * -ecb_ews_get_timezone_from_ical_component (ECalBackendEws *cbews, - icalcomponent *icalcomp) -{ - ETimezoneCache *timezone_cache; - icalproperty *prop = NULL; - const gchar *tzid = NULL; - - timezone_cache = E_TIMEZONE_CACHE (cbews); - - prop = icalcomponent_get_first_property (icalcomp, ICAL_DTSTART_PROPERTY); - if (prop != NULL) { - icalparameter *param = NULL; - - param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER); - if (param) { - tzid = icalparameter_get_tzid (param); - } else { - struct icaltimetype dtstart; - - dtstart = icalproperty_get_dtstart (prop); - if (icaltime_is_utc (dtstart)) - tzid = "UTC"; - } - } - - if (tzid) - return e_timezone_cache_get_timezone (timezone_cache, tzid); - - return NULL; -} - -static gboolean -ecb_ews_remove_item_sync (ECalBackendEws *cbews, - ECalCache *cal_cache, - GHashTable *removed_indexes, - const gchar *uid, - const gchar *rid, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *comp = NULL, *parent = NULL; - EwsId item_id = { 0 }; - gint index = 0; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE); - g_return_val_if_fail (uid != NULL, FALSE); - - if (rid && !*rid) - rid = NULL; - - if (!e_cal_cache_get_component (cal_cache, uid, rid, &comp, cancellable, error) || - (rid && !e_cal_cache_get_component (cal_cache, uid, NULL, &parent, cancellable, error))) { - if (!parent && !comp) { - g_propagate_error (error, EDC_ERROR (ObjectNotFound)); - return FALSE; - } - } - - ecb_ews_extract_item_id (comp ? comp : parent, &item_id.id, &item_id.change_key); - - if (!item_id.id) { - g_propagate_error (error, EDC_ERROR_EX (OtherError, "Cannot determine EWS ItemId")); - success = FALSE; - } else { - if (parent) { - index = e_cal_backend_ews_rid_to_index ( - ecb_ews_get_timezone_from_ical_component (cbews, - e_cal_component_get_icalcomponent (parent)), - rid, - e_cal_component_get_icalcomponent (parent), - error); - if (index == 0) - success = comp != NULL; - } - - if (index && removed_indexes && g_hash_table_contains (removed_indexes, GINT_TO_POINTER (index))) { - /* Do nothing, it's already deleted from the server */ - } else { - if (removed_indexes && index) - g_hash_table_insert (removed_indexes, GINT_TO_POINTER (index), NULL); - - success = success && e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, index, EWS_HARD_DELETE, - ecb_ews_is_organizer (cbews, comp) ? EWS_SEND_TO_ALL_AND_SAVE_COPY : EWS_SEND_TO_NONE, - EWS_ALL_OCCURRENCES, cancellable, error); - } - } - - g_free (item_id.id); - g_free (item_id.change_key); - - g_clear_object (&comp); - g_clear_object (&parent); - - return success; -} - -static void -ecb_ews_get_attach_differences (ECalComponent *oldcomp, - ECalComponent *newcomp, - GSList **out_removed_attachment_ids, /* gchar * */ - GSList **out_added_attachments) /* EEwsAttachmentInfo * */ -{ - GSList *old_attachments = NULL, *new_attachments = NULL, *link; - - g_return_if_fail (out_removed_attachment_ids != NULL); - g_return_if_fail (out_added_attachments != NULL); - - *out_removed_attachment_ids = NULL; - *out_added_attachments = NULL; - - if (!ecb_ews_extract_attachments (e_cal_component_get_icalcomponent (oldcomp), &old_attachments)) - old_attachments = NULL; - - if (!ecb_ews_extract_attachments (e_cal_component_get_icalcomponent (newcomp), &new_attachments)) - new_attachments = NULL; - - for (link = old_attachments; link; link = g_slist_next (link)) { - EEwsAttachmentInfo *old_nfo = link->data; - GSList *nlink; - - if (!old_nfo) - continue; - - for (nlink = new_attachments; nlink; nlink = g_slist_next (nlink)) { - EEwsAttachmentInfo *new_nfo = nlink->data; - gboolean same = FALSE; - - if (!new_nfo || - e_ews_attachment_info_get_type (old_nfo) != e_ews_attachment_info_get_type (new_nfo)) - continue; - - if (e_ews_attachment_info_get_type (old_nfo) == E_EWS_ATTACHMENT_INFO_TYPE_INLINED) { - const gchar *old_data, *new_data; - gsize old_len = -1, new_len = -1; - - old_data = e_ews_attachment_info_get_inlined_data (old_nfo, &old_len); - new_data = e_ews_attachment_info_get_inlined_data (new_nfo, &new_len); - - same = old_len == new_len && (old_len == 0 || - (old_len > 0 && old_data && new_data && memcmp (old_data, new_data, old_len) == 0)); - } else if (e_ews_attachment_info_get_type (old_nfo) == E_EWS_ATTACHMENT_INFO_TYPE_URI) { - same = g_strcmp0 (e_ews_attachment_info_get_uri (old_nfo), e_ews_attachment_info_get_uri (new_nfo)) == 0; - } - - if (same) { - new_attachments = g_slist_remove (new_attachments, new_nfo); - e_ews_attachment_info_free (new_nfo); - break; - } - } - - if (!nlink) { - /* Did not find in the new_attachments, thus it's removed */ - g_warn_if_fail (e_ews_attachment_info_get_id (old_nfo) != NULL); - *out_removed_attachment_ids = g_slist_prepend (*out_removed_attachment_ids, - g_strdup (e_ews_attachment_info_get_id (old_nfo))); - } - } - - *out_added_attachments = new_attachments; - - g_slist_free_full (old_attachments, (GDestroyNotify) e_ews_attachment_info_free); -} - -struct TzidCbData { - icalcomponent *comp; - ECalBackendEws *cbews; -}; - -static void -tzid_cb (icalparameter *param, - gpointer data) -{ - struct TzidCbData *cbd = data; - const gchar *tzid; - icaltimezone *zone; - icalcomponent *new_comp; - - tzid = icalparameter_get_tzid (param); - if (!tzid) - return; - - zone = e_timezone_cache_get_timezone (E_TIMEZONE_CACHE (cbd->cbews), tzid); - if (!zone) - return; - - new_comp = icaltimezone_get_component (zone); - if (!new_comp) - return; - - icalcomponent_add_component (cbd->comp, icalcomponent_new_clone (new_comp)); -} - -static void -ecb_ews_pick_all_tzids_out (ECalBackendEws *cbews, - icalcomponent *icalcomp) -{ - - /* pick all the tzids out of the component and resolve - * them using the vtimezones in the current calendar */ - struct TzidCbData cbd; - - cbd.cbews = cbews; - cbd.comp = icalcomp; - - icalcomponent_foreach_tzid (icalcomp, tzid_cb, &cbd); -} - -static gboolean -ecb_ews_modify_item_sync (ECalBackendEws *cbews, - GHashTable *removed_indexes, - icalcomponent *old_icalcomp, - icalcomponent *new_icalcomp, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *comp = NULL, *oldcomp = NULL; - icalcomponent *icalcomp; - gchar *itemid = NULL, *changekey = NULL; - GSList *added_attachments = NULL, *removed_attachment_ids = NULL; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (new_icalcomp != NULL, FALSE); - - icalcomp = icalcomponent_new_clone (new_icalcomp); - - ecb_ews_pick_all_tzids_out (cbews, icalcomp); - - comp = e_cal_component_new_from_icalcomponent (icalcomp); - if (!comp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - ecb_ews_extract_item_id (comp, &itemid, &changekey); - if (!itemid) { - g_propagate_error (error, EDC_ERROR_EX (OtherError, "Cannot determine EWS ItemId")); - g_object_unref (comp); - return FALSE; - } - - if (old_icalcomp) { - oldcomp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (old_icalcomp)); - } else { - oldcomp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (new_icalcomp)); - } - - ecb_ews_pick_all_tzids_out (cbews, e_cal_component_get_icalcomponent (oldcomp)); - - /* In case we have updated attachments we have to run update attachments - * before update items so attendees will receive mails with already updated attachments */ - - ecb_ews_get_attach_differences (oldcomp, comp, &removed_attachment_ids, &added_attachments); - - /* preform sync delete attachemnt operation*/ - if (removed_attachment_ids) { - g_free (changekey); - changekey = NULL; - - success = e_ews_connection_delete_attachments_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - removed_attachment_ids, &changekey, cancellable, error); - - g_slist_free_full (removed_attachment_ids, g_free); - } - - /* in case we have a new attachments add them before update */ - if (added_attachments && success) { - EwsId item_id; - - item_id.id = itemid; - item_id.change_key = changekey; - - changekey = NULL; - - success = e_ews_connection_create_attachments_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - &item_id, added_attachments, - FALSE, &changekey, NULL, cancellable, error); - - g_free (item_id.change_key); - } - - if (success && old_icalcomp && - icalcomponent_get_first_property (new_icalcomp, ICAL_RRULE_PROPERTY) && - !icalcomponent_get_first_property (new_icalcomp, ICAL_RECURRENCEID_PROPERTY)) { - icalproperty *prop, *old_prop; - GSList *exceptions = NULL, *link; - EwsId item_id; - - item_id.id = itemid; - item_id.change_key = changekey; - - /* Excluded occurrences */ - for (prop = icalcomponent_get_first_property (new_icalcomp, ICAL_EXDATE_PROPERTY); - prop; - prop = icalcomponent_get_next_property (new_icalcomp, ICAL_EXDATE_PROPERTY)) { - const gchar *new_rid; - - new_rid = icalproperty_get_value_as_string (prop); - - for (old_prop = icalcomponent_get_first_property (old_icalcomp, ICAL_EXDATE_PROPERTY); - old_prop; - old_prop = icalcomponent_get_next_property (old_icalcomp, ICAL_EXDATE_PROPERTY)) { - if (g_strcmp0 (new_rid, icalproperty_get_value_as_string (old_prop)) == 0) - break; - } - - if (!old_prop) - exceptions = g_slist_prepend (exceptions, prop); - } - - exceptions = g_slist_reverse (exceptions); - - for (link = exceptions; link && success; link = g_slist_next (link)) { - guint index; - - prop = link->data; - - index = e_cal_backend_ews_rid_to_index ( - ecb_ews_get_timezone_from_ical_component (cbews, new_icalcomp), - icalproperty_get_value_as_string (prop), - new_icalcomp, - error); - - if (index == 0) { - success = FALSE; - } else if (!removed_indexes || !g_hash_table_contains (removed_indexes, GINT_TO_POINTER (index))) { - if (removed_indexes) - g_hash_table_insert (removed_indexes, GINT_TO_POINTER (index), NULL); - - success = e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, index, - EWS_HARD_DELETE, EWS_SEND_TO_NONE, EWS_ALL_OCCURRENCES, cancellable, error); - } - } - - g_slist_free (exceptions); - } - - if (success) { - EwsCalendarConvertData convert_data = { 0 }; - CamelEwsSettings *ews_settings; - const gchar *send_meeting_invitations; - const gchar *send_or_save; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.user_email = camel_ews_settings_dup_email (ews_settings); - convert_data.comp = comp; - convert_data.old_comp = oldcomp; - convert_data.item_id = itemid; - convert_data.change_key = changekey; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - if (e_cal_component_has_attendees (comp)) { - send_meeting_invitations = "SendToAllAndSaveCopy"; - send_or_save = "SendAndSaveCopy"; - } else { - /*In case of appointment we have to set SendMeetingInvites to SendToNone */ - send_meeting_invitations = "SendToNone"; - send_or_save = "SaveOnly"; - } - - success = e_ews_connection_update_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", send_or_save, send_meeting_invitations, cbews->priv->folder_id, - e_cal_backend_ews_convert_component_to_updatexml, &convert_data, - NULL, cancellable, error); - - g_free (convert_data.user_email); - } - - g_slist_free_full (added_attachments, (GDestroyNotify) e_ews_attachment_info_free); - g_clear_object (&oldcomp); - g_clear_object (&comp); - g_free (changekey); - g_free (itemid); - - return success; -} - -static gboolean -ecb_ews_save_component_sync (ECalMetaBackend *meta_backend, - gboolean overwrite_existing, - EConflictResolution conflict_resolution, - const GSList *instances, - const gchar *extra, - gchar **out_new_uid, - gchar **out_new_extra, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - ECalComponent *master = NULL; - EwsFolderId *fid; - GSList *link; - const gchar *uid = NULL; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - for (link = (GSList *) instances; link && !master; link = g_slist_next (link)) { - master = link->data; - - if (!master) - continue; - - if (e_cal_component_is_instance (master)) - master = NULL; - } - - if (!master) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - g_return_val_if_fail (cal_cache != NULL, FALSE); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - e_cal_component_get_uid (master, &uid); - fid = e_ews_folder_id_new (cbews->priv->folder_id, NULL, FALSE); - - if (overwrite_existing) { - GSList *existing = NULL, *changed_instances = NULL, *removed_instances = NULL; - - success = uid && e_cal_cache_get_components_by_uid (cal_cache, uid, &existing, cancellable, error) && existing; - - if (success) { - GSList *link; - - /* This is for offline changes, where the component in the cache - is already modified, while the original, the one on the server, - is different. Using the cached component in this case generates - empty UpdateItem request and nothing is saved. */ - for (link = existing; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - comp = ecb_ews_restore_original_comp (comp); - if (comp) { - g_object_unref (link->data); - link->data = comp; - } - } - } - - if (success) - ecb_ews_filter_out_unchanged_instances (instances, existing, &changed_instances, &removed_instances); - - if (success) { - GHashTable *removed_indexes; - - removed_indexes = g_hash_table_new (g_direct_hash, g_direct_equal); - - for (link = changed_instances; link && success; link = g_slist_next (link)) { - ChangeData *cd = link->data; - - if (!cd) - continue; - - success = ecb_ews_modify_item_sync (cbews, removed_indexes, - e_cal_component_get_icalcomponent (cd->old_component ? cd->old_component : master), - e_cal_component_get_icalcomponent (cd->new_component), - cancellable, error); - } - - for (link = removed_instances; link && success; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id = NULL; - - if (!comp) - continue; - - id = e_cal_component_get_id (comp); - - if (id) { - success = ecb_ews_remove_item_sync (cbews, cal_cache, removed_indexes, id->uid, id->rid, cancellable, error); - e_cal_component_free_id (id); - } - } - - g_hash_table_destroy (removed_indexes); - } - - if (success) - ecb_ews_extract_item_id (master, out_new_uid, NULL); - - g_slist_free_full (existing, g_object_unref); - g_slist_free_full (changed_instances, change_data_free); - g_slist_free_full (removed_instances, g_object_unref); - } else { - GHashTable *removed_indexes; - EwsCalendarConvertData convert_data = { 0 }; - EEwsItem *item = NULL; - const EwsId *ews_id = NULL; - const gchar *send_meeting_invitations; - icalcomponent *icalcomp; - icalproperty *prop; - GSList *items = NULL; - - icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (master)); - - e_ews_clean_icalcomponent (icalcomp); - - if (!e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) - ecb_ews_pick_all_tzids_out (cbews, icalcomp); - - /* - * In case we are creating a meeting with attendees and attachments. - * We have to preform 3 steps in order to allow attendees to receive attachments in their invite mails. - * 1. create meeting and do not send invites - * 2. create attachments - * 3. dummy update meeting and send invites to all - */ - if (e_cal_component_has_attendees (master)) { - if (e_cal_component_has_attachments (master)) - send_meeting_invitations = "SendToNone"; - else - send_meeting_invitations = "SendToAllAndSaveCopy"; - } else { - /* In case of appointment we have to set SendMeetingInvites to SendToNone */ - send_meeting_invitations = "SendToNone"; - } - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.icalcomp = icalcomp; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - success = e_ews_connection_create_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, "SaveOnly", send_meeting_invitations, - fid, e_cal_backend_ews_convert_calcomp_to_xml, &convert_data, - &items, cancellable, error); - - if (success && items) { - item = items->data; - if (item) { - g_object_ref (item); - - ews_id = e_ews_item_get_id (item); - } - } - - if (success && item && e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_EVENT) { - EEwsAdditionalProps *add_props; - GSList *items, *items_req = NULL; - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("calendar:UID"); - - items = g_slist_append (NULL, ews_id->id); - - /* get calender uid from server*/ - success = e_ews_connection_get_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - items, "IdOnly", add_props, FALSE, NULL, E_EWS_BODY_TYPE_TEXT, - &items_req, NULL, NULL, cancellable, error) && items_req != NULL; - - e_ews_additional_props_free (add_props); - - if (success) { - g_clear_object (&item); - - item = items_req->data; - ews_id = NULL; - - if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) { - g_propagate_error (error, g_error_copy (e_ews_item_get_error (item))); - item = NULL; - success = FALSE; - } else { - item = g_object_ref (item); - ews_id = e_ews_item_get_id (item); - } - } - - g_slist_free_full (items_req, g_object_unref); - g_slist_free (items); - } - - /* attachments */ - if (success && e_cal_component_has_attachments (master) > 0) { - GSList *info_attachments = NULL; - - g_warn_if_fail (ews_id != NULL); - - if (ews_id && ecb_ews_extract_attachments (icalcomp, &info_attachments)) { - GSList *ids = NULL; - - success = e_ews_connection_create_attachments_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - ews_id, info_attachments, FALSE, NULL, &ids, cancellable, error); - - g_slist_free_full (info_attachments, (GDestroyNotify) e_ews_attachment_info_free); - g_slist_free_full (ids, g_free); - } - } - - removed_indexes = g_hash_table_new (g_direct_hash, g_direct_equal); - - if (success && icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY)) { - GSList *exceptions = NULL; - - /* Excluded occurrences */ - for (prop = icalcomponent_get_first_property (icalcomp, ICAL_EXDATE_PROPERTY); - prop; - prop = icalcomponent_get_next_property (icalcomp, ICAL_EXDATE_PROPERTY)) { - exceptions = g_slist_prepend (exceptions, g_strdup (icalproperty_get_value_as_string (prop))); - } - - for (link = exceptions; link && success; link = g_slist_next (link)) { - success = ecb_ews_remove_item_sync (cbews, cal_cache, removed_indexes, uid, link->data, cancellable, error); - } - - g_slist_free_full (exceptions, g_free); - } - - if (success && e_cal_component_has_attendees (master) && e_cal_component_has_attachments (master)) { - if (ews_id) { - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-ITEMID", ews_id->id); - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY", ews_id->change_key); - } - - /* In case we have attendees and atachemnts we have to fake update items, - * this is the only way to pass attachments in meeting invite mail */ - success = ecb_ews_modify_item_sync (cbews, removed_indexes, NULL, icalcomp, cancellable, error); - } - - icalcomponent_free (icalcomp); - g_clear_object (&item); - - for (link = (GSList *) instances; link && success; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - if (comp == master) - continue; - - icalcomp = e_cal_component_get_icalcomponent (comp); - - success = ecb_ews_modify_item_sync (cbews, removed_indexes, NULL, icalcomp, cancellable, error); - } - - if (success && items) { - EEwsItem *item = items->data; - const EwsId *item_id; - - item_id = e_ews_item_get_id (item); - *out_new_uid = g_strdup (item_id->id); - } - - g_slist_free_full (items, g_object_unref); - g_hash_table_destroy (removed_indexes); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - g_clear_object (&cal_cache); - e_ews_folder_id_free (fid); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - - return success; -} - -static gboolean -ecb_ews_remove_component_sync (ECalMetaBackend *meta_backend, - EConflictResolution conflict_resolution, - const gchar *uid, - const gchar *extra, - const gchar *object, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalComponent *comp; - EwsId item_id; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (object != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - comp = e_cal_component_new_from_string (object); - if (!comp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - ecb_ews_extract_item_id (comp, &item_id.id, &item_id.change_key); - - success = e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, 0, EWS_HARD_DELETE, - ecb_ews_is_organizer (cbews, comp) ? EWS_SEND_TO_ALL_AND_SAVE_COPY : EWS_SEND_TO_NONE, - EWS_ALL_OCCURRENCES, cancellable, error); - - g_free (item_id.id); - g_free (item_id.change_key); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_object_unref (comp); - - return success; -} - -static void -ecb_ews_discard_alarm_sync (ECalBackendSync *cal_backend_sync, - EDataCal *cal, - GCancellable *cancellable, - const gchar *uid, - const gchar *rid, - const gchar *auid, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - ECalComponent *comp = NULL; - EwsCalendarConvertData convert_data = { 0 }; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cal_backend_sync)); - - cbews = E_CAL_BACKEND_EWS (cal_backend_sync); - - cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbews)); - g_return_if_fail (cal_cache != NULL); - - if (!e_cal_cache_get_component (cal_cache, uid, NULL, &comp, cancellable, NULL) || !comp) { - g_object_unref (cal_cache); - g_propagate_error (error, EDC_ERROR (ObjectNotFound)); - return; - } - - g_object_unref (cal_cache); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) { - g_clear_object (&comp); - return; - } - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - - if (e_cal_component_has_recurrences (comp)) { - gint *index; - - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_OCCURRENCEITEM; - e_cal_component_get_sequence (comp, &index); - - if (index != NULL) { - /*Microsoft is counting the occurrences starting from 1 - where EcalComponent is starting from zerro */ - convert_data.index = *index + 1; - e_cal_component_free_sequence (index); - } else { - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_ITEM; - convert_data.index = -1; - } - } else { - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_ITEM; - convert_data.index = -1; - } - - ecb_ews_extract_item_id (comp, &convert_data.item_id, &convert_data.change_key); - - if (e_ews_connection_update_items_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", NULL, - "SendToNone", NULL, - e_cal_backend_ews_clear_reminder_is_set, - &convert_data, - NULL, - cancellable, - error)) { - icalcomponent *icomp = e_cal_component_get_icalcomponent (comp); - GSList *modified_objects; - - modified_objects = g_slist_prepend (NULL, - e_cal_meta_backend_info_new (icalcomponent_get_uid (icomp), NULL, NULL, - e_cal_util_get_x_property (icomp, "X-EVOLUTION-ITEMID"))); - - /* Refresh the local cache, to have up-to-date ChangeKey */ - e_cal_meta_backend_process_changes_sync (E_CAL_META_BACKEND (cbews), NULL, modified_objects, NULL, cancellable, error); - - g_slist_free_full (modified_objects, e_cal_meta_backend_info_free); - } - - g_object_unref (comp); - g_free (convert_data.item_id); - g_free (convert_data.change_key); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static gboolean -ecb_ews_send_cancellation_email_sync (ECalBackendEws *cbews, - CamelAddress *from, - CamelInternetAddress *recipient, - const gchar *subject, - const gchar *body, - const gchar *calobj, - GCancellable *cancellable, - GError **error) -{ - CamelMimeMessage *message; - CamelContentType *mime_type; - CamelMultipart *multi; - CamelMimePart *text_part, *vcal_part; - gchar *ical_str; - icalcomponent *vcal, *vevent, *vtz; - icalproperty *prop; - icaltimezone *icaltz; - struct icaltimetype dt; - gboolean success; - - vcal = icalcomponent_new (ICAL_VCALENDAR_COMPONENT); - icalcomponent_add_property (vcal, icalproperty_new_version ("2.0")); - icalcomponent_add_property (vcal, icalproperty_new_prodid ("-//Evolution EWS backend//EN")); - icalcomponent_add_property (vcal, icalproperty_new_method (ICAL_METHOD_CANCEL)); - vevent = icalcomponent_new_from_string (calobj); - prop = icalcomponent_get_first_property (vevent, ICAL_STATUS_PROPERTY); - if (prop != NULL) icalcomponent_remove_property (vevent, prop); - icalcomponent_add_property (vevent, icalproperty_new_status (ICAL_STATUS_CANCELLED)); - prop = icalcomponent_get_first_property (vevent, ICAL_METHOD_PROPERTY); - if (prop != NULL) icalcomponent_remove_property (vevent, prop); - dt = e_cal_backend_ews_get_datetime_with_zone (E_TIMEZONE_CACHE (cbews), NULL, vevent, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - icaltz = (icaltimezone *) - (dt.zone ? dt.zone : ecb_ews_get_timezone_from_ical_component (cbews, vevent)); - vtz = icaltimezone_get_component (icaltz); - icalcomponent_add_component (vcal, icalcomponent_new_clone (vtz)); - icalcomponent_add_component (vcal, vevent); - text_part = camel_mime_part_new (); - camel_mime_part_set_content (text_part, body, strlen (body), "text/plain"); - - vcal_part = camel_mime_part_new (); - mime_type = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (vcal_part)); - camel_content_type_set_param (mime_type, "charset", "utf-8"); - camel_content_type_set_param (mime_type, "method", "CANCEL"); - ical_str = icalcomponent_as_ical_string_r ((icalcomponent *) vcal); - camel_mime_part_set_content (vcal_part, ical_str, strlen (ical_str), "text/calendar; method=CANCEL"); - free (ical_str); - - multi = camel_multipart_new (); - camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multi), "multipart/alternative"); - camel_multipart_add_part (multi, text_part); - camel_multipart_set_boundary (multi, NULL); - camel_multipart_add_part (multi, vcal_part); - g_object_unref (text_part); - g_object_unref (vcal_part); - - message = camel_mime_message_new (); - camel_mime_message_set_subject (message, subject); - camel_mime_message_set_from (message, CAMEL_INTERNET_ADDRESS (from)); - camel_mime_message_set_recipients (message, CAMEL_RECIPIENT_TYPE_TO, recipient); - - camel_medium_set_content ((CamelMedium *) message, (CamelDataWrapper *) multi); - g_object_unref (multi); - - success = camel_ews_utils_create_mime_message (cbews->priv->cnc, "SendOnly", NULL, message, NULL, from, NULL, NULL, NULL, cancellable, error); - - g_object_unref (message); - icalcomponent_free (vcal); - - return success; -} - -static void -ecb_ews_receive_objects_no_exchange_mail (ECalBackendEws *cbews, - icalcomponent *vcalendar, - icalcomponent *subcomp, - GSList **ids, - GCancellable *cancellable, - GError **error) -{ - EwsCalendarConvertData convert_data = { 0 }; - EwsFolderId *fid; - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.icalcomp = subcomp; - convert_data.vcalendar = vcalendar; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - fid = e_ews_folder_id_new (cbews->priv->folder_id, NULL, FALSE); - - e_ews_connection_create_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - "SaveOnly", - "SendToNone", - fid, - e_cal_backend_ews_convert_calcomp_to_xml, - &convert_data, - ids, - cancellable, - error); - - e_ews_folder_id_free (fid); -} - -static icalproperty * -find_attendee (icalcomponent *ical_comp, - const gchar *address) -{ - icalproperty *prop; - - if (address == NULL) - return NULL; - - for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY); - prop != NULL; - prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) { - gchar *attendee; - gchar *text; - - attendee = icalproperty_get_value_as_string_r (prop); - - if (!attendee) - continue; - - text = g_strdup (itip_strip_mailto (attendee)); - text = g_strstrip (text); - if (text && !g_ascii_strcasecmp (address, text)) { - g_free (text); - g_free (attendee); - break; - } - g_free (text); - g_free (attendee); - } - - return prop; -} - -static icalproperty * -find_attendee_if_sentby (icalcomponent *ical_comp, - const gchar *address) -{ - icalproperty *prop; - - if (address == NULL) - return NULL; - - for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY); - prop != NULL; - prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) { - icalparameter *param; - const gchar *attendee_sentby; - gchar *text; - - param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER); - if (!param) - continue; - - attendee_sentby = icalparameter_get_sentby (param); - - if (!attendee_sentby) - continue; - - text = g_strdup (itip_strip_mailto (attendee_sentby)); - text = g_strstrip (text); - if (text && !g_ascii_strcasecmp (address, text)) { - g_free (text); - break; - } - g_free (text); - } - - return prop; -} - -static void -ecb_ews_get_rsvp (icalproperty *attendee, - gboolean *out_rsvp_requested) -{ - if (out_rsvp_requested) - *out_rsvp_requested = FALSE; - else - return; - - if (attendee) { - icalparameter *rsvp; - - rsvp = icalproperty_get_first_parameter (attendee, ICAL_RSVP_PARAMETER); - if (rsvp) { - *out_rsvp_requested = icalparameter_get_rsvp (rsvp) == ICAL_RSVP_TRUE; - } - } -} - -static const gchar * -ecb_ews_get_current_user_meeting_reponse (ECalBackendEws *cbews, - icalcomponent *icalcomp, - const gchar *current_user_mail, - gboolean *out_rsvp_requested) -{ - icalproperty *attendee; - const gchar *attendee_str = NULL, *attendee_mail = NULL; - gint attendees_count = 0; - const gchar *response = NULL; - gboolean found = FALSE; - - if (out_rsvp_requested) - *out_rsvp_requested = FALSE; - - attendee = icalcomponent_get_first_property (icalcomp, ICAL_ORGANIZER_PROPERTY); - if (attendee) { - attendee_str = icalproperty_get_organizer (attendee); - - if (attendee_str) { - if (!strncasecmp (attendee_str, "MAILTO:", 7)) - attendee_mail = attendee_str + 7; - else - attendee_mail = attendee_str; - if (attendee_mail && current_user_mail && g_ascii_strcasecmp (attendee_mail, current_user_mail) == 0) { - /* Empty string means it's an organizer, NULL is when not found */ - return ""; - } - } - } - - for (attendee = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY); - attendee != NULL; - attendee = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY), attendees_count++) { - attendee_str = icalproperty_get_attendee (attendee); - - if (attendee_str != NULL) { - if (!strncasecmp (attendee_str, "MAILTO:", 7)) - attendee_mail = attendee_str + 7; - else - attendee_mail = attendee_str; - if (attendee_mail && current_user_mail && g_ascii_strcasecmp (attendee_mail, current_user_mail) == 0) { - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } - } - } - - /* this should not happen, but if the user's configured email does not match the one - used in the invitation, like when the invitation comes to a mailing list... */ - if (!found && attendees_count == 1) { - attendee = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY); - g_return_val_if_fail (attendee != NULL, NULL); - - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } else if (!found) { - ESourceRegistry *registry; - ECalComponent *comp; - - registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbews)); - comp = e_cal_component_new (); - if (e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp))) { - gchar *my_address; - - my_address = itip_get_comp_attendee (registry, comp, NULL); - - attendee = find_attendee (icalcomp, my_address); - if (!attendee) - attendee = find_attendee_if_sentby (icalcomp, my_address); - - if (attendee) { - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } - - g_free (my_address); - } - - g_object_unref (comp); - } - - if (found && !response) { - response = "NEEDS-ACTION"; - } - - return response; -} - -/* changekey can be NULL if you don't want it. itemid cannot. */ -static void -ecb_ews_get_item_accept_id (ECalComponent *comp, - gchar **itemid, - gchar **changekey, - gchar **mail_id) -{ - icalproperty *prop; - gchar *id_item = NULL; - gchar *id_accept = NULL; - gchar *ck = NULL; - - prop = icalcomponent_get_first_property ( - e_cal_component_get_icalcomponent (comp), - ICAL_X_PROPERTY); - while (prop) { - const gchar *x_name, *x_val; - - x_name = icalproperty_get_x_name (prop); - x_val = icalproperty_get_x (prop); - if (!id_item && g_ascii_strcasecmp (x_name, "X-EVOLUTION-ITEMID") == 0) - id_item = g_strdup (x_val); - else if (!id_accept && g_ascii_strcasecmp (x_name, "X-EVOLUTION-ACCEPT-ID") == 0) - id_accept = g_strdup (x_val); - else if (changekey && !ck && !g_ascii_strcasecmp (x_name, "X-EVOLUTION-CHANGEKEY")) - ck = g_strdup (x_val); - - prop = icalcomponent_get_next_property ( - e_cal_component_get_icalcomponent (comp), - ICAL_X_PROPERTY); - } - - if (!id_item) - id_item = g_strdup (id_accept); - - *itemid = id_item; - *mail_id = id_accept; - if (changekey) - *changekey = ck; -} - -static gboolean -ecb_ews_do_method_request_publish_reply (ECalBackendEws *cbews, - icalcomponent *vcalendar, - ECalComponent *comp, - icalcomponent *subcomp, - const gchar *response_type, - const gchar *user_email, - gboolean rsvp_requested, - GCancellable *cancellable, - GError **error) -{ - GError *local_error = NULL; - gchar *item_id = NULL; - gchar *change_key = NULL; - gchar *mail_id = NULL; - gint pass = 0; - GSList *ids = NULL; - - if (!response_type && - e_cal_util_component_has_organizer (subcomp) && - e_cal_util_component_has_attendee (subcomp)) { - g_set_error (error, E_DATA_CAL_ERROR, UnknownUser, _("Cannot find user “%s” between attendees"), user_email ? user_email : "NULL"); - return FALSE; - } - - if (response_type && *response_type) - ecb_ews_get_item_accept_id (comp, &item_id, &change_key, &mail_id); - else - response_type = NULL; - - while (pass < 2) { - /*in case we do not have item id we will create item with mime content only*/ - if (!item_id || (response_type && g_ascii_strcasecmp (response_type, "NEEDS-ACTION") == 0)) { - ecb_ews_receive_objects_no_exchange_mail (cbews, vcalendar, subcomp, &ids, cancellable, &local_error); - } else { - EwsCalendarConvertData convert_data = { 0 }; - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.response_type = (gchar *) response_type; - convert_data.item_id = item_id; - convert_data.change_key = change_key; - - e_ews_connection_create_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - rsvp_requested ? "SendAndSaveCopy" : "SaveOnly", - rsvp_requested ? NULL : "SendToNone", - NULL, - e_cal_backend_ews_prepare_accept_item_request, - &convert_data, - &ids, - cancellable, - &local_error); - } - - if (pass == 0 && mail_id != NULL && item_id != NULL && - g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_ITEMNOTFOUND)) { - /* - * maybe the associated accept calendar item changed - * on the server, thus retry with updated values - */ - GSList *my_ids = NULL; - - g_clear_error (&local_error); - - my_ids = g_slist_append (my_ids, mail_id); - - if (e_ews_connection_get_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - my_ids, - "AllProperties", - NULL, - FALSE, - NULL, - E_EWS_BODY_TYPE_ANY, - &ids, - NULL, - NULL, - cancellable, - &local_error) && - ids != NULL && - ids->data != NULL) { - EEwsItem *item = ids->data; - const EwsId *id = e_ews_item_get_id (item); - - if (id != NULL && g_strcmp0 (id->id, mail_id) == 0) { - const EwsId *cal_item_accepted_id; - - cal_item_accepted_id = e_ews_item_get_calendar_item_accept_id (item); - if (cal_item_accepted_id != NULL) { - g_clear_error (&local_error); - pass++; - - g_free (item_id); - g_free (change_key); - - item_id = g_strdup (cal_item_accepted_id->id); - change_key = g_strdup (cal_item_accepted_id->change_key); - } - } - } - - g_slist_free (my_ids); - - if (pass == 0) - break; - } else { - break; - } - - g_slist_free_full (ids, g_object_unref); - ids = NULL; - } - - if (local_error == NULL) { - icalproperty *transp; - - transp = icalcomponent_get_first_property (subcomp, ICAL_TRANSP_PROPERTY); - - if (g_strcmp0 (icalproperty_get_value_as_string (transp), "TRANSPARENT") == 0 && - g_strcmp0 (response_type, "ACCEPTED") == 0) { - EwsCalendarConvertData convert_data = { 0 }; - GSList *l; - - /* - * user can accept meeting but mark it as free in it's calendar - * the following code is updating the exchange meeting status to free - */ - for (l = ids; l != NULL; l = g_slist_next (l)) { - EEwsItem *item = l->data; - - if (item != NULL) { - const EwsId *id = e_ews_item_get_id (item); - - convert_data.item_id = id->id; - convert_data.change_key = id->change_key; - break; - } - } - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.vcalendar = vcalendar; - - e_ews_connection_update_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", - NULL, - "SendToNone", - NULL, - e_cal_backend_ews_prepare_set_free_busy_status, - &convert_data, - NULL, - cancellable, - &local_error); - } - } - - if (local_error != NULL) - g_propagate_error (error, local_error); - - g_free (item_id); - g_free (change_key); - g_free (mail_id); - g_slist_free_full (ids, g_object_unref); - - return !local_error; -} - -static void -ecb_ews_receive_objects_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *calobj, - GError **error) -{ - ECalBackendEws *cbews; - ECalBackend *cal_backend; - CamelEwsSettings *ews_settings; - icalcomponent *icalcomp, *subcomp; - icalcomponent_kind kind; - gchar *user_email; - gboolean success = TRUE, do_refresh = FALSE; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - icalcomp = calobj ? icalparser_parse_string (calobj) : NULL; - - if (!icalcomp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - /* make sure ical data we parse is actually a vCalendar component */ - if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) { - icalcomponent_free (icalcomp); - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - cal_backend = E_CAL_BACKEND (cbews); - kind = e_cal_backend_get_kind (cal_backend); - - ews_settings = ecb_ews_get_collection_settings (cbews); - user_email = camel_ews_settings_dup_email (ews_settings); - - switch (icalcomponent_get_method (icalcomp)) { - case ICAL_METHOD_REQUEST: - case ICAL_METHOD_PUBLISH: - case ICAL_METHOD_REPLY: - for (subcomp = icalcomponent_get_first_component (icalcomp, kind); - subcomp && success; - subcomp = icalcomponent_get_next_component (icalcomp, kind)) { - ECalComponent *comp; - const gchar *response_type; - gboolean rsvp_requested = FALSE; - - /* getting a data for meeting request response */ - response_type = ecb_ews_get_current_user_meeting_reponse (cbews, subcomp, user_email, &rsvp_requested); - - comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (subcomp)); - - success = ecb_ews_do_method_request_publish_reply (cbews, icalcomp, comp, subcomp, response_type, user_email, rsvp_requested, cancellable, error); - - do_refresh = TRUE; - - g_object_unref (comp); - } - break; - case ICAL_METHOD_COUNTER: - /* - * this is a new time proposal mail from one of the attendees - * if we decline the proposal, nothing have to be done - * if we accept it we will call to modify_object - */ - for (subcomp = icalcomponent_get_first_component (icalcomp, kind); - subcomp && success; - subcomp = icalcomponent_get_next_component (icalcomp, kind)) { - const gchar *response_type; - - /* getting a data for meeting request response */ - response_type = ecb_ews_get_current_user_meeting_reponse (cbews, subcomp, user_email, NULL); - - if (g_strcmp0 (response_type, "ACCEPTED") == 0) { - gchar **split_subject; - icalproperty *summary; - - /* we have to edit the meeting subject to remove exchange header */ - summary = icalcomponent_get_first_property (subcomp, ICAL_SUMMARY_PROPERTY); - split_subject = - g_strsplit (icalproperty_get_value_as_string (summary), ":", -1); - icalproperty_set_value_from_string (summary, split_subject[1] , "NO"); - g_strfreev (split_subject); - - success = ecb_ews_modify_item_sync (cbews, NULL, NULL, subcomp, cancellable, error); - - do_refresh = TRUE; - } - } - break; - default: - break; - } - - icalcomponent_free (icalcomp); - g_free (user_email); - - if (success && do_refresh) - e_cal_meta_backend_schedule_refresh (E_CAL_META_BACKEND (cbews)); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static void -ecb_ews_send_objects_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *calobj, - GSList **users, - gchar **modified_calobj, - GError **error) -{ - ECalBackendEws *cbews; - icalcomponent_kind kind; - icalcomponent *icalcomp, *subcomp = NULL; - gchar *subcalobj; - gboolean success = TRUE; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - icalcomp = calobj ? icalparser_parse_string (calobj) : NULL; - - /* make sure data was parsed properly */ - if (!icalcomp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - /* make sure ical data we parse is actually an vcal component */ - if ((icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) && (icalcomponent_isa (icalcomp) != ICAL_VEVENT_COMPONENT)) { - icalcomponent_free (icalcomp); - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - - if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) { - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - subcomp = icalcomponent_get_first_component (icalcomp, kind); - } - if (icalcomponent_isa (icalcomp) == ICAL_VEVENT_COMPONENT) - subcomp = icalcomp; - while (subcomp && success) { - const gchar *new_body_content = NULL, *subject = NULL, *org_email = NULL; - const gchar *org = NULL, *attendee = NULL; - icalproperty *prop, *org_prop = NULL; - CamelInternetAddress *org_addr = camel_internet_address_new (); - - new_body_content = e_cal_util_get_x_property (subcomp, "X-EVOLUTION-RETRACT-COMMENT"); - subject = icalproperty_get_value_as_string (icalcomponent_get_first_property (subcomp, ICAL_SUMMARY_PROPERTY)); - - org_prop = icalcomponent_get_first_property (subcomp, ICAL_ORGANIZER_PROPERTY); - org = icalproperty_get_organizer (org_prop); - if (!g_ascii_strncasecmp (org, "MAILTO:", 7)) - org_email = (org) + 7; - else - org_email = org; - - camel_internet_address_add (org_addr, icalproperty_get_parameter_as_string (org_prop, "CN"), org_email); - - /* iterate over every attendee property */ - for (prop = icalcomponent_get_first_property (subcomp, ICAL_ATTENDEE_PROPERTY); - prop && success; - prop = icalcomponent_get_next_property (subcomp, ICAL_ATTENDEE_PROPERTY)) { - CamelInternetAddress *attendee_addr = camel_internet_address_new (); - attendee = icalproperty_get_attendee (prop); - if (g_ascii_strcasecmp (org_email, attendee) == 0) continue; - if (!g_ascii_strncasecmp (attendee, "mailto:", 7)) attendee = (attendee) + 7; - - subcalobj = icalcomponent_as_ical_string_r (subcomp); - camel_internet_address_add (attendee_addr, icalproperty_get_parameter_as_string (prop, "CN"), attendee); - success = ecb_ews_send_cancellation_email_sync (cbews, CAMEL_ADDRESS (org_addr), attendee_addr, - subject, new_body_content, subcalobj, cancellable, error); - g_object_unref (attendee_addr); - free (subcalobj); - } - - g_object_unref (org_addr); - subcomp = icalcomponent_get_next_component (icalcomp, kind); - } - - icalcomponent_free (icalcomp); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static void -ecb_ews_get_free_busy_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const GSList *users, - time_t start, - time_t end, - GSList **freebusyobjs, - GError **error) -{ - ECalBackendEws *cbews; - EEWSFreeBusyData fbdata = { 0 }; - GSList *freebusy = NULL; - gboolean success; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - g_return_if_fail (freebusyobjs != NULL); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - *freebusyobjs = NULL; - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - /* EWS can support only 100 identities, which is the maximum number of identities that the Web service method can request - see http://msdn.microsoft.com / en - us / library / aa564001 % 28v = EXCHG.140 % 29.aspx */ - if (g_slist_length ((GSList *) users) > 100) { - g_propagate_error (error, EDC_ERROR (SearchSizeLimitExceeded)); - return; - } - - fbdata.period_start = start; - fbdata.period_end = end; - fbdata.user_mails = (GSList *) users; - - success = e_ews_connection_get_free_busy_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - e_ews_cal_utils_prepare_free_busy_request, &fbdata, - &freebusy, cancellable, error); - - if (success) { - GSList *fblink, *ulink; - - for (fblink = freebusy, ulink = (GSList *) users; - fblink && ulink; - fblink = g_slist_next (fblink), ulink = g_slist_next (ulink)) { - icalcomponent *icalcomp = fblink->data; - gchar *mailto; - - /* add attendee property */ - mailto = g_strconcat ("mailto:", ulink->data, NULL); - icalcomponent_add_property (icalcomp, icalproperty_new_attendee (mailto)); - g_free (mailto); - - *freebusyobjs = g_slist_prepend (*freebusyobjs, icalcomponent_as_ical_string_r (icalcomp)); - } - - *freebusyobjs = g_slist_reverse (*freebusyobjs); - } - - g_slist_free_full (freebusy, (GDestroyNotify) icalcomponent_free); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static gchar * -ecb_ews_get_backend_property (ECalBackend *cal_backend, - const gchar *prop_name) -{ - ECalBackendEws *cbews; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cal_backend), NULL); - g_return_val_if_fail (prop_name != NULL, NULL); - - cbews = E_CAL_BACKEND_EWS (cal_backend); - - if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) { - return g_strjoin ( - ",", - CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS, - CAL_STATIC_CAPABILITY_NO_AUDIO_ALARMS, - CAL_STATIC_CAPABILITY_NO_PROCEDURE_ALARMS, - CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY, - CAL_STATIC_CAPABILITY_REMOVE_ALARMS, - CAL_STATIC_CAPABILITY_NO_THISANDPRIOR, - CAL_STATIC_CAPABILITY_NO_THISANDFUTURE, - CAL_STATIC_CAPABILITY_NO_CONV_TO_ASSIGN_TASK, - CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT, - CAL_STATIC_CAPABILITY_SAVE_SCHEDULES, - CAL_STATIC_CAPABILITY_NO_ALARM_AFTER_START, - CAL_STATIC_CAPABILITY_NO_MEMO_START_DATE, - CAL_STATIC_CAPABILITY_ALL_DAY_EVENT_AS_TIME, - CAL_STATIC_CAPABILITY_TASK_DATE_ONLY, - e_cal_meta_backend_get_capabilities (E_CAL_META_BACKEND (cbews)), - NULL); - } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS)) { - /* return email address of the person who opened the calendar */ - CamelEwsSettings *ews_settings; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - return camel_ews_settings_dup_email (ews_settings); - } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS)) { - /* ews does not support email based alarms */ - return NULL; - } - - /* Chain up to parent's method. */ - return E_CAL_BACKEND_CLASS (e_cal_backend_ews_parent_class)->get_backend_property (cal_backend, prop_name); -} - -static void -ecb_ews_get_timezone_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *tzid, - gchar **tzobject, - GError **error) -{ - GError *local_error = NULL; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - g_return_if_fail (tzid != NULL); - g_return_if_fail (tzobject != NULL); - - *tzobject = NULL; - - E_CAL_BACKEND_SYNC_CLASS (e_cal_backend_ews_parent_class)->get_timezone_sync (sync_backend, cal, cancellable, tzid, tzobject, &local_error); - - if (!*tzobject) { - /* The timezone can be sometimes the Windows zone, try to convert it to libical */ - const gchar *ical_location = e_cal_backend_ews_tz_util_get_ical_equivalent (tzid); - - if (ical_location) - E_CAL_BACKEND_SYNC_CLASS (e_cal_backend_ews_parent_class)->get_timezone_sync (sync_backend, cal, cancellable, ical_location, tzobject, NULL); - } - - if (*tzobject) - g_clear_error (&local_error); - else if (local_error) - g_propagate_error (error, local_error); -} - -static gboolean -ecb_ews_get_destination_address (EBackend *backend, - gchar **host, - guint16 *port) -{ - CamelEwsSettings *ews_settings; - SoupURI *soup_uri; - gchar *host_url; - gboolean result = FALSE; - - g_return_val_if_fail (port != NULL, FALSE); - g_return_val_if_fail (host != NULL, FALSE); - - /* Sanity checking */ - if (!e_cal_backend_get_registry (E_CAL_BACKEND (backend)) || - !e_backend_get_source (backend)) - return FALSE; - - ews_settings = ecb_ews_get_collection_settings (E_CAL_BACKEND_EWS (backend)); - g_return_val_if_fail (ews_settings != NULL, FALSE); - - host_url = camel_ews_settings_dup_hosturl (ews_settings); - g_return_val_if_fail (host_url != NULL, FALSE); - - soup_uri = soup_uri_new (host_url); - if (soup_uri) { - *host = g_strdup (soup_uri_get_host (soup_uri)); - *port = soup_uri_get_port (soup_uri); - - result = *host && **host; - if (!result) { - g_free (*host); - *host = NULL; - } - - soup_uri_free (soup_uri); - } - - g_free (host_url); - - return result; -} - -static gchar * -ecb_ews_dup_component_revision (ECalCache *cal_cache, - icalcomponent *icalcomp, - gpointer user_data) -{ - g_return_val_if_fail (icalcomp != NULL, NULL); - - return e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY"); -} - -static void -ecb_ews_constructed (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - ECalCache *cal_cache; - gchar *cache_dirname; - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->constructed (object); - - /* Reset the connectable, it steals data from Authentication extension, - where is written incorrect address */ - e_backend_set_connectable (E_BACKEND (object), NULL); - - cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbews)); - g_return_if_fail (cal_cache != NULL); - - cache_dirname = g_path_get_dirname (e_cache_get_filename (E_CACHE (cal_cache))); - g_signal_connect (cal_cache, "dup-component-revision", G_CALLBACK (ecb_ews_dup_component_revision), NULL); - - g_clear_object (&cal_cache); - - cbews->priv->attachments_dir = g_build_filename (cache_dirname, "attachments", NULL); - g_mkdir_with_parents (cbews->priv->attachments_dir, 0777); - - g_free (cache_dirname); -} - -static void -ecb_ews_dispose (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - g_clear_object (&cbews->priv->cnc); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->dispose (object); -} - -static void -ecb_ews_finalize (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - - g_free (cbews->priv->folder_id); - g_free (cbews->priv->attachments_dir); - - g_rec_mutex_clear (&cbews->priv->cnc_lock); - - e_cal_backend_ews_unref_windows_zones (); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->finalize (object); -} - -static void -e_cal_backend_ews_init (ECalBackendEws *cbews) -{ - cbews->priv = G_TYPE_INSTANCE_GET_PRIVATE (cbews, E_TYPE_CAL_BACKEND_EWS, ECalBackendEwsPrivate); - - g_rec_mutex_init (&cbews->priv->cnc_lock); - - e_cal_backend_ews_populate_windows_zones (); -} - -static void -e_cal_backend_ews_class_init (ECalBackendEwsClass *klass) -{ - GObjectClass *object_class; - EBackendClass *backend_class; - ECalBackendClass *cal_backend_class; - ECalBackendSyncClass *cal_backend_sync_class; - ECalMetaBackendClass *cal_meta_backend_class; - - g_type_class_add_private (klass, sizeof (ECalBackendEwsPrivate)); - - cal_meta_backend_class = E_CAL_META_BACKEND_CLASS (klass); - cal_meta_backend_class->connect_sync = ecb_ews_connect_sync; - cal_meta_backend_class->disconnect_sync = ecb_ews_disconnect_sync; - cal_meta_backend_class->get_changes_sync = ecb_ews_get_changes_sync; - cal_meta_backend_class->load_component_sync = ecb_ews_load_component_sync; - cal_meta_backend_class->save_component_sync = ecb_ews_save_component_sync; - cal_meta_backend_class->remove_component_sync = ecb_ews_remove_component_sync; - - cal_backend_sync_class = E_CAL_BACKEND_SYNC_CLASS (klass); - cal_backend_sync_class->discard_alarm_sync = ecb_ews_discard_alarm_sync; - cal_backend_sync_class->receive_objects_sync = ecb_ews_receive_objects_sync; - cal_backend_sync_class->send_objects_sync = ecb_ews_send_objects_sync; - cal_backend_sync_class->get_free_busy_sync = ecb_ews_get_free_busy_sync; - cal_backend_sync_class->get_timezone_sync = ecb_ews_get_timezone_sync; - - cal_backend_class = E_CAL_BACKEND_CLASS (klass); - cal_backend_class->get_backend_property = ecb_ews_get_backend_property; - - backend_class = E_BACKEND_CLASS (klass); - backend_class->get_destination_address = ecb_ews_get_destination_address; - - object_class = G_OBJECT_CLASS (klass); - object_class->constructed = ecb_ews_constructed; - object_class->dispose = ecb_ews_dispose; - object_class->finalize = ecb_ews_finalize; -} diff --git a/src/calendar/e-cal-backend-ews.c.save-only-if-organizer b/src/calendar/e-cal-backend-ews.c.save-only-if-organizer deleted file mode 100644 index c1742c3..0000000 --- a/src/calendar/e-cal-backend-ews.c.save-only-if-organizer +++ /dev/null @@ -1,3915 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com) - * - * 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. - * - * 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#include -#include -#include -#include - -#include -#include - -#include "server/e-source-ews-folder.h" -#include "server/e-ews-calendar-utils.h" -#include "server/e-ews-connection-utils.h" -#include "server/e-ews-camel-common.h" - -#include "e-cal-backend-ews.h" -#include "e-cal-backend-ews-utils.h" - -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -#ifdef G_OS_WIN32 -#ifdef gmtime_r -#undef gmtime_r -#endif - -/* The gmtime() in Microsoft's C library is MT-safe */ -#define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0) -#endif - -/* Private part of the CalBackendEws structure */ -struct _ECalBackendEwsPrivate { - GRecMutex cnc_lock; - EEwsConnection *cnc; - gchar *folder_id; - - guint subscription_key; - gboolean is_freebusy_calendar; - - gchar *attachments_dir; -}; - -#define X_EWS_ORIGINAL_COMP "X-EWS-ORIGINAL-COMP" - -#define EWS_MAX_FETCH_COUNT 100 - -#define GET_ITEMS_SYNC_PROPERTIES \ - "item:Attachments" \ - " item:Categories" \ - " item:HasAttachments" \ - " item:MimeContent" \ - " calendar:UID" \ - " calendar:Resources" \ - " calendar:ModifiedOccurrences" \ - " calendar:IsMeeting" \ - " calendar:IsResponseRequested" \ - " calendar:MyResponseType" \ - " calendar:RequiredAttendees" \ - " calendar:OptionalAttendees" - -#define GET_ITEMS_SYNC_PROPERTIES_2007 \ - GET_ITEMS_SYNC_PROPERTIES \ - " calendar:TimeZone" - -#define GET_ITEMS_SYNC_PROPERTIES_2010 \ - GET_ITEMS_SYNC_PROPERTIES \ - " calendar:StartTimeZone" \ - " calendar:EndTimeZone" - -#define e_data_cal_error_if_fail(expr, _code) \ - G_STMT_START { \ - if (G_LIKELY (expr)) { \ - } else { \ - g_log (G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "file %s: line %d (%s): assertion `%s' failed", \ - __FILE__, __LINE__, G_STRFUNC, #expr); \ - g_set_error (&error, E_DATA_CAL_ERROR, (_code), \ - "file %s: line %d (%s): assertion `%s' failed", \ - __FILE__, __LINE__, G_STRFUNC, #expr); \ - goto exit; \ - } \ - } G_STMT_END - -G_DEFINE_TYPE (ECalBackendEws, e_cal_backend_ews, E_TYPE_CAL_META_BACKEND) - -static CamelEwsSettings * -ecb_ews_get_collection_settings (ECalBackendEws *cbews) -{ - ESource *source; - ESource *collection; - ESourceCamel *extension; - ESourceRegistry *registry; - CamelSettings *settings; - const gchar *extension_name; - - source = e_backend_get_source (E_BACKEND (cbews)); - registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbews)); - - extension_name = e_source_camel_get_extension_name ("ews"); - e_source_camel_generate_subtype ("ews", CAMEL_TYPE_EWS_SETTINGS); - - /* The collection settings live in our parent data source. */ - collection = e_source_registry_find_extension ( - registry, source, extension_name); - g_return_val_if_fail (collection != NULL, NULL); - - extension = e_source_get_extension (collection, extension_name); - settings = e_source_camel_get_settings (extension); - - g_object_unref (collection); - - return CAMEL_EWS_SETTINGS (settings); -} - -static void -ecb_ews_convert_error_to_edc_error (GError **perror) -{ - GError *error = NULL; - - if (!perror || !*perror || (*perror)->domain == E_DATA_CAL_ERROR) - return; - - if ((*perror)->domain == EWS_CONNECTION_ERROR) { - switch ((*perror)->code) { - case EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED: - error = EDC_ERROR_EX (AuthenticationFailed, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_FOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_MANAGEDFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PARENTFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PUBLICFOLDERSERVERNOTFOUND: - error = EDC_ERROR_EX (NoSuchCal, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_EVENTNOTFOUND: - case EWS_CONNECTION_ERROR_ITEMNOTFOUND: - error = EDC_ERROR_EX (ObjectNotFound, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_UNAVAILABLE: - g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND, (*perror)->message); - break; - } - - if (!error) - error = EDC_ERROR_EX (OtherError, (*perror)->message); - } - - if (error) { - g_error_free (*perror); - *perror = error; - } -} - -static void -ecb_ews_maybe_disconnect_sync (ECalBackendEws *cbews, - GError **in_perror, - GCancellable *cancellable) -{ - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cbews)); - - if (in_perror && g_error_matches (*in_perror, E_DATA_CAL_ERROR, AuthenticationFailed)) { - e_cal_meta_backend_disconnect_sync (E_CAL_META_BACKEND (cbews), cancellable, NULL); - e_backend_schedule_credentials_required (E_BACKEND (cbews), E_SOURCE_CREDENTIALS_REASON_REJECTED, NULL, 0, NULL, NULL, G_STRFUNC); - } -} - -static void -ecb_ews_server_notification_cb (ECalBackendEws *cbews, - GSList *events, - EEwsConnection *cnc) -{ - GSList *link; - gboolean update_folder = FALSE; - - g_return_if_fail (cbews != NULL); - g_return_if_fail (cbews->priv != NULL); - - for (link = events; link && !update_folder; link = g_slist_next (link)) { - EEwsNotificationEvent *event = link->data; - - switch (event->type) { - case E_EWS_NOTIFICATION_EVENT_CREATED: - case E_EWS_NOTIFICATION_EVENT_DELETED: - case E_EWS_NOTIFICATION_EVENT_MODIFIED: - g_rec_mutex_lock (&cbews->priv->cnc_lock); - if (g_strcmp0 (event->folder_id, cbews->priv->folder_id) == 0) - update_folder = TRUE; - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - break; - case E_EWS_NOTIFICATION_EVENT_MOVED: - case E_EWS_NOTIFICATION_EVENT_COPIED: - g_rec_mutex_lock (&cbews->priv->cnc_lock); - if (g_strcmp0 (event->folder_id, cbews->priv->folder_id) == 0 || - g_strcmp0 (event->old_folder_id, cbews->priv->folder_id) == 0) - update_folder = TRUE; - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - break; - default: - return; - } - } - - if (update_folder) - e_cal_meta_backend_schedule_refresh (E_CAL_META_BACKEND (cbews)); -} - -static void -ecb_ews_unset_connection (ECalBackendEws *cbews) -{ - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cbews)); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->cnc) { - e_ews_connection_set_disconnected_flag (cbews->priv->cnc, TRUE); - - g_signal_handlers_disconnect_by_func (cbews->priv->cnc, ecb_ews_server_notification_cb, cbews); - - if (cbews->priv->subscription_key != 0) { - e_ews_connection_disable_notifications_sync ( - cbews->priv->cnc, - cbews->priv->subscription_key); - cbews->priv->subscription_key = 0; - } - } - - g_clear_object (&cbews->priv->cnc); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); -} - -static icaltimezone * -ecb_ews_get_timezone (ETimezoneCache *timezone_cache, - const gchar *msdn_tzid, - const gchar *tzid, - const gchar *evo_ews_tzid) -{ - icaltimezone *zone = NULL; - const gchar *evo_ews_msdn_tzid; - - zone = e_timezone_cache_get_timezone (timezone_cache, tzid); - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (tzid); - - if (g_strcmp0 (tzid, evo_ews_tzid) == 0) - return zone; - - if (evo_ews_tzid != NULL) { - evo_ews_msdn_tzid = e_cal_backend_ews_tz_util_get_msdn_equivalent (evo_ews_tzid); - - if (g_strcmp0 (msdn_tzid, evo_ews_msdn_tzid) == 0) { - zone = e_timezone_cache_get_timezone (timezone_cache, evo_ews_tzid); - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (evo_ews_tzid); - } - } - - return zone; -} - -static icalparameter * -ecb_ews_responsetype_to_partstat (const gchar *responsetype) -{ - icalparameter *param = NULL; - - if (!responsetype) - return icalparameter_new_partstat (ICAL_PARTSTAT_NONE); - - if (g_ascii_strcasecmp (responsetype, "Organizer") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_ACCEPTED); - else if (g_ascii_strcasecmp (responsetype, "Tentative") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_TENTATIVE); - else if (g_ascii_strcasecmp (responsetype, "Accept") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_ACCEPTED); - else if (g_ascii_strcasecmp (responsetype, "Decline") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_DECLINED); - else if (g_ascii_strcasecmp (responsetype, "NoResponseReceived") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION); - else if (g_ascii_strcasecmp (responsetype, "Unknown") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION); - - if (!param) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NONE); - - return param; -} - -static ECalComponent * -ecb_ews_item_to_component_sync (ECalBackendEws *cbews, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *res_component = NULL; - ETimezoneCache *timezone_cache; - icalcomponent_kind kind; - EEwsItemType item_type; - icalcomponent *icalcomp, *vcomp; - icaltimezone *utc_zone = icaltimezone_get_utc_timezone (); - CamelEwsSettings *ews_settings; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), NULL); - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - timezone_cache = E_TIMEZONE_CACHE (cbews); - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - ews_settings = ecb_ews_get_collection_settings (cbews); - - item_type = e_ews_item_get_item_type (item); - if (item_type == E_EWS_ITEM_TYPE_TASK || item_type == E_EWS_ITEM_TYPE_MEMO) { - icalproperty *icalprop; - icaltimetype due_date, start_date, complete_date, created; - icalproperty_status status = ICAL_STATUS_NONE; - icalproperty_class class = ICAL_CLASS_NONE; - const gchar *ews_task_status, *sensitivity; - EwsImportance item_importance; - gint priority = 5; - gboolean has_this_date = FALSE; - - vcomp = icalcomponent_new (ICAL_VCALENDAR_COMPONENT); - /*subject*/ - icalcomp = icalcomponent_new (item_type == E_EWS_ITEM_TYPE_TASK ? ICAL_VTODO_COMPONENT : ICAL_VJOURNAL_COMPONENT); - icalprop = icalproperty_new_summary (e_ews_item_get_subject (item)); - icalcomponent_add_property (icalcomp, icalprop); - - /*date time created*/ - created = icaltime_from_timet_with_zone (e_ews_item_get_date_created (item), 0, utc_zone); - icalprop = icalproperty_new_created (created); - icalcomponent_add_property (icalcomp, icalprop); - - /*sensitivity*/ - sensitivity = e_ews_item_get_sensitivity (item); - if (g_strcmp0 (sensitivity, "Normal") == 0) - class = ICAL_CLASS_PUBLIC; - else if (g_strcmp0 (sensitivity, "Private") == 0) - class = ICAL_CLASS_PRIVATE; - else if ((g_strcmp0 (sensitivity, "Confidential") == 0) || - (g_strcmp0 (sensitivity, "Personal") == 0)) - class = ICAL_CLASS_CONFIDENTIAL; - icalprop = icalproperty_new_class (class); - icalcomponent_add_property (icalcomp, icalprop); - - /*description*/ - icalprop = icalproperty_new_description (e_ews_item_get_body (item)); - icalcomponent_add_property (icalcomp, icalprop); - - /*task assaingments*/ - if (e_ews_item_get_delegator (item) != NULL) { - const gchar *task_owner = e_ews_item_get_delegator (item); - GSList *mailboxes = NULL, *l; - gboolean includes_last_item; - gchar *mailtoname, *user_email; - icalparameter *param; - - /*The task owner according to Exchange is current user, even that the task was assigned by - *someone else. I'm making the current user attendee and task delegator will be a task organizer */ - - user_email = camel_ews_settings_dup_email (ews_settings); - mailtoname = g_strdup_printf ("mailto:%s", user_email); - icalprop = icalproperty_new_attendee (mailtoname); - g_free (mailtoname); - g_free (user_email); - - param = icalparameter_new_cn (e_ews_item_get_owner (item)); - icalproperty_add_parameter (icalprop, param); - icalcomponent_add_property (icalcomp, icalprop); - - /* get delegator mail box*/ - e_ews_connection_resolve_names_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, task_owner, - EWS_SEARCH_AD, NULL, FALSE, &mailboxes, NULL, - &includes_last_item, cancellable, error); - - for (l = mailboxes; l != NULL; l = g_slist_next (l)) { - EwsMailbox *mb = l->data; - - mailtoname = g_strdup_printf ("mailto:%s", mb->email); - icalprop = icalproperty_new_organizer (mailtoname); - param = icalparameter_new_cn (mb->name); - icalproperty_add_parameter (icalprop, param); - icalcomponent_add_property (icalcomp, icalprop); - - g_free (mailtoname); - e_ews_mailbox_free (mb); - } - g_slist_free (mailboxes); - } - - if (item_type == E_EWS_ITEM_TYPE_TASK) { - icaltimezone *user_timezone = calendar_config_get_icaltimezone (); - const gchar *percent_complete; - - /*start date*/ - has_this_date = FALSE; - e_ews_item_task_has_start_date (item, &has_this_date); - if (has_this_date) { - start_date = icaltime_from_timet_with_zone (e_ews_item_get_start_date (item), 0, user_timezone); - start_date.is_date = 1; - icalprop = icalproperty_new_dtstart (start_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*status*/ - ews_task_status = e_ews_item_get_status (item); - if (g_strcmp0 (ews_task_status, "NotStarted") != 0) { - if (g_strcmp0 (ews_task_status, "Completed") == 0) - status = ICAL_STATUS_COMPLETED; - else if (g_strcmp0 (ews_task_status, "InProgress") == 0) - status = ICAL_STATUS_INPROCESS; - else if (g_strcmp0 (ews_task_status, "WaitingOnOthers") == 0) - status = ICAL_STATUS_NEEDSACTION; - else if (g_strcmp0 (ews_task_status, "Deferred") == 0) - status = ICAL_STATUS_CANCELLED; - icalprop = icalproperty_new_status (status); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*precent complete*/ - percent_complete = e_ews_item_get_percent_complete (item); - icalprop = icalproperty_new_percentcomplete (atoi (percent_complete ? percent_complete : "0")); - icalcomponent_add_property (icalcomp, icalprop); - - /*due date*/ - e_ews_item_task_has_due_date (item, &has_this_date); - if (has_this_date) { - due_date = icaltime_from_timet_with_zone (e_ews_item_get_due_date (item), 0, user_timezone); - due_date.is_date = 1; - icalprop = icalproperty_new_due (due_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*complete date*/ - has_this_date = FALSE; - e_ews_item_task_has_complete_date (item, &has_this_date); - if (has_this_date) { - complete_date = icaltime_from_timet_with_zone (e_ews_item_get_complete_date (item), 0, user_timezone); - icalprop = icalproperty_new_completed (complete_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*priority*/ - item_importance = e_ews_item_get_importance (item); - if (item_importance == EWS_ITEM_HIGH) - priority = 3; - else if (item_importance == EWS_ITEM_LOW) - priority = 7; - icalprop = icalproperty_new_priority (priority); - icalcomponent_add_property (icalcomp, icalprop); - } - - icalcomponent_add_component (vcomp, icalcomp); - } else { - struct icaltimetype dt; - const gchar *mime_content; - const gchar *tzid; - gboolean timezone_set = FALSE; - - mime_content = e_ews_item_get_mime_content (item); - vcomp = mime_content && *mime_content ? icalparser_parse_string (mime_content) : NULL; - - if (!vcomp && mime_content && *mime_content) { - const gchar *begin_vcalendar, *end_vcalendar; - - /* Workaround Exchange 2016 error, which returns invalid iCalendar object (without 'END:VCALENDAR'), - when the event has at least one detached instance. */ - begin_vcalendar = camel_strstrcase (mime_content, "BEGIN:VCALENDAR"); - end_vcalendar = camel_strstrcase (mime_content, "END:VCALENDAR"); - - /* If it exists, then it should be alone on a separate line */ - if (!(begin_vcalendar && (begin_vcalendar == mime_content || begin_vcalendar[-1] == '\n') && - (begin_vcalendar[15 /* strlen ("BEGIN:VCALENDAR") */] == '\r' || begin_vcalendar[15] == '\n'))) - begin_vcalendar = NULL; - - /* If it exists, then it should be alone on a separate line and not at the very beginning of the mime_content */ - if (!(end_vcalendar && end_vcalendar > mime_content && end_vcalendar[-1] == '\n' && - (end_vcalendar[13 /* strlen ("END:VCALENDAR") */] == '\r' || end_vcalendar[13] == '\n' || end_vcalendar[13] == '\0'))) - end_vcalendar = NULL; - - if (begin_vcalendar && !end_vcalendar) { - gchar *str; - - str = g_strconcat (mime_content, "\r\n", "END:VCALENDAR", "\r\n", NULL); - vcomp = icalparser_parse_string (str); - g_free (str); - } - } - - if (!vcomp) { - if (mime_content) - g_warning ("%s: Failed to parse mime content:---%s---", G_STRFUNC, mime_content); - return NULL; - } - - tzid = e_ews_item_get_tzid (item); - if (tzid == NULL) { - /* - * When we are working with Exchange server 2010 or newer, we have to handle a few - * things more than we do working old servers. These things are: - * - MSDN timezone names: - * Used setting StartTimeZone and EndTimeZone. MSDN timezone names are not - * the same used in libical, so we need to have a table of equivalence to - * convert from one to another and avoid show the MSDN timezone name to the - * user and save it in the ETimezoneCache. - * - EvoEWSStartTimeZone/EvoEWSEndTimeZone - * Used to keep track if the timezone shown to the user is the same one set - * by him/her. As we have a table of equivalence, sometimes the user sets a - * timezone but without EvoEWSStartTiemZone property, another timezone name, - * in the same offset, can be shown. And we want to avoid this. - * - DTEND property: - * As we have to work with DTEND setting an event when using EWS server 2010 or - * newer, we have to care about set it properly here, instead of use the same - * as is used in DTSTART. - */ - icaltimezone *start_zone, *end_zone; - const gchar *start_tzid, *end_tzid; - const gchar *ical_start_tzid, *ical_end_tzid; - const gchar *evo_ews_start_tzid, *evo_ews_end_tzid; - - start_tzid = e_ews_item_get_start_tzid (item); - end_tzid = e_ews_item_get_end_tzid (item); - - ical_start_tzid = e_cal_backend_ews_tz_util_get_ical_equivalent (start_tzid); - ical_end_tzid = e_cal_backend_ews_tz_util_get_ical_equivalent (end_tzid); - - evo_ews_start_tzid = e_ews_item_get_iana_start_time_zone (item); - evo_ews_end_tzid = e_ews_item_get_iana_end_time_zone (item); - - /* - * We have a few timezones that don't have an equivalent MSDN timezone. - * For those, we will get ical_start_tzid being NULL and then we need to use - * start_tzid, which one has the libical's expected name. - */ - start_zone = ecb_ews_get_timezone ( - timezone_cache, - start_tzid, - ical_start_tzid != NULL ? ical_start_tzid : start_tzid, - evo_ews_start_tzid); - end_zone = ecb_ews_get_timezone ( - timezone_cache, - end_tzid, - ical_end_tzid != NULL ? ical_end_tzid : end_tzid, - evo_ews_end_tzid); - - if (start_zone != NULL) { - icalcomp = icalcomponent_get_first_component (vcomp, kind); - - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - dt = icaltime_convert_to_zone (dt, start_zone); - icalcomponent_set_dtstart (icalcomp, dt); - - timezone_set = TRUE; - e_timezone_cache_add_timezone (timezone_cache, start_zone); - - if (end_zone != NULL) { - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); - dt = icaltime_convert_to_zone (dt, end_zone); - icalcomponent_set_dtend (icalcomp, dt); - - e_timezone_cache_add_timezone (timezone_cache, end_zone); - } - } - - if (!timezone_set) - tzid = start_tzid; - } - - if (!timezone_set && tzid) { - /* - * When we are working with Exchange server older than 2010, we don't set different - * DTSTART and DTEND properties in VTIMEZONE. The reason of that is we don't use - * those properties settings/changing a meeting timezone. - * So, for older servers, here, we only set the DTSTART and DTEND properties with - * the same values. - */ - icaltimezone *zone; - gchar *new_tzid = NULL; - - icalcomp = icalcomponent_get_first_component (vcomp, kind); - - if (!icaltimezone_get_builtin_timezone (tzid) && - icalcomponent_get_uid (icalcomp)) { - icalcomponent *vtimezone; - - /* Add the timezone */ - vtimezone = icalcomponent_get_first_component (vcomp, ICAL_VTIMEZONE_COMPONENT); - if (vtimezone != NULL) { - icalproperty *prop; - - new_tzid = g_strconcat ("/evolution/ews/tzid/", icalcomponent_get_uid (icalcomp), NULL); - - zone = icaltimezone_new (); - vtimezone = icalcomponent_new_clone (vtimezone); - prop = icalcomponent_get_first_property (vtimezone, ICAL_TZID_PROPERTY); - if (prop) { - icalproperty_set_tzid (prop, new_tzid); - - prop = icalcomponent_get_first_property (vtimezone, ICAL_LOCATION_PROPERTY); - if (!prop) { - /* Use the original tzid as the timezone Location, to not expose - evolution-ews TZID. */ - prop = icalproperty_new_location (tzid); - icalcomponent_add_property (vtimezone, prop); - } - } else { - g_free (new_tzid); - new_tzid = NULL; - } - icaltimezone_set_component (zone, vtimezone); - e_timezone_cache_add_timezone (timezone_cache, zone); - icaltimezone_free (zone, TRUE); - } - } - - zone = e_timezone_cache_get_timezone (timezone_cache, new_tzid ? new_tzid : tzid); - - if (!zone && new_tzid) - zone = e_timezone_cache_get_timezone (timezone_cache, tzid); - - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (tzid); - - if (zone != NULL) { - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - dt = icaltime_convert_to_zone (dt, zone); - icalcomponent_set_dtstart (icalcomp, dt); - - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); - dt = icaltime_convert_to_zone (dt, zone); - icalcomponent_set_dtend (icalcomp, dt); - } - - g_free (new_tzid); - } - } - - /* Vevent or Vtodo */ - icalcomp = icalcomponent_get_first_component (vcomp, kind); - if (icalcomp) { - icalproperty *icalprop, *freebusy; - struct icaltimetype itt; - const EwsId *item_id; - const GSList *l = NULL; - const gchar *uid = e_ews_item_get_uid (item); - - item_id = e_ews_item_get_id (item); - - if (e_ews_item_get_is_meeting (item)) { - gboolean is_response_requested = e_ews_item_get_is_response_requested (item); - gchar *user_email; - - user_email = camel_ews_settings_dup_email (ews_settings); - - /* Attendees */ - for (l = e_ews_item_get_attendees (item); l != NULL; l = g_slist_next (l)) { - icalparameter *param, *cu_type; - gchar *mailtoname; - const gchar *email = NULL; - EwsAttendee *attendee = (EwsAttendee *) l->data; - - if (!attendee->mailbox) - continue; - - if (g_strcmp0 (attendee->mailbox->routing_type, "EX") == 0) - email = e_ews_item_util_strip_ex_address (attendee->mailbox->email); - - mailtoname = g_strdup_printf ("mailto:%s", email ? email : attendee->mailbox->email); - icalprop = icalproperty_new_attendee (mailtoname); - g_free (mailtoname); - - param = icalparameter_new_cn (attendee->mailbox->name); - icalproperty_add_parameter (icalprop, param); - - if (g_ascii_strcasecmp (attendee->attendeetype, "Required") == 0) { - param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL); - } - else if (g_ascii_strcasecmp (attendee->attendeetype, "Resource") == 0) { - param = icalparameter_new_role (ICAL_ROLE_NONPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_RESOURCE); - } - else { - param = icalparameter_new_role ( ICAL_ROLE_OPTPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL); - } - icalproperty_add_parameter (icalprop, cu_type); - icalproperty_add_parameter (icalprop, param); - - if (is_response_requested) { - param = icalparameter_new_rsvp (ICAL_RSVP_TRUE); - icalproperty_add_parameter (icalprop, param); - } - - if (user_email && (email || attendee->mailbox->email) && e_ews_item_get_my_response_type (item) && - g_ascii_strcasecmp (email ? email : attendee->mailbox->email, user_email) == 0) { - param = ecb_ews_responsetype_to_partstat (e_ews_item_get_my_response_type (item)); - } else { - param = ecb_ews_responsetype_to_partstat (attendee->responsetype); - } - icalproperty_add_parameter (icalprop, param); - - icalcomponent_add_property (icalcomp, icalprop); - } - - g_free (user_email); - } - - /* Free/Busy */ - freebusy = icalcomponent_get_first_property (icalcomp, ICAL_TRANSP_PROPERTY); - if (!freebusy && (e_ews_item_get_item_type (item) != E_EWS_ITEM_TYPE_TASK)) { - /* Busy by default */ - freebusy = icalproperty_new_transp (ICAL_TRANSP_OPAQUE); - icalcomponent_add_property (icalcomp, freebusy); - } - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); - icalprop != NULL; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) { - if (g_strcmp0 (icalproperty_get_x_name (icalprop), "X-MICROSOFT-CDO-BUSYSTATUS") == 0) { - if (g_strcmp0 (icalproperty_get_value_as_string (icalprop), "BUSY") == 0) { - icalproperty_set_transp (freebusy, ICAL_TRANSP_OPAQUE); - } else { - icalproperty_set_transp (freebusy, ICAL_TRANSP_TRANSPARENT); - } - - break; - } - } - - /* AllDayEvent */ - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); - icalprop != NULL; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) { - if (g_strcmp0 (icalproperty_get_x_name (icalprop), "X-MICROSOFT-CDO-ALLDAYEVENT") == 0) { - if (g_strcmp0 (icalproperty_get_value_as_string (icalprop), "TRUE") == 0) { - struct icaltimetype dtend, dtstart; - dtstart = icalcomponent_get_dtstart (icalcomp); - dtstart.is_date = 1; - icalcomponent_set_dtstart (icalcomp, dtstart); - - dtend = icalcomponent_get_dtend (icalcomp); - dtend.is_date = 1; - icalcomponent_set_dtend (icalcomp, dtend); - } - break; - } - } - - if (icalcomponent_get_first_property (icalcomp, ICAL_RECURRENCEID_PROPERTY)) { - /* Exchange sets RRULE even on the children, which is broken */ - icalprop = icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY); - if (icalprop) { - icalcomponent_remove_property (icalcomp, icalprop); - icalproperty_free (icalprop); - } - } - - /* The EXDATE sent by the server can be date-time format with timezone, while - the event start time can be date-only. This breaks the rules, thus correct - it and make also EXDATE date-only. */ - itt = icalcomponent_get_dtstart (icalcomp); - if (icaltime_is_valid_time (itt) && itt.is_date) { - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_EXDATE_PROPERTY); - icalprop; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_EXDATE_PROPERTY)) { - itt = icalproperty_get_exdate (icalprop); - itt.is_date = 1; - icalproperty_set_exdate (icalprop, itt); - - icalproperty_remove_parameter_by_kind (icalprop, ICAL_TZID_PARAMETER); - } - } - - /* Exchange sets an ORGANIZER on all events. RFC2445 says: - * - * This property MUST NOT be specified in an iCalendar - * object that specifies only a time zone definition or - * that defines calendar entities that are not group - * scheduled entities, but are entities only on a single - * user's calendar. - */ - if (!icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) { - if ((icalprop = icalcomponent_get_first_property (icalcomp, ICAL_ORGANIZER_PROPERTY))) { - icalcomponent_remove_property (icalcomp, icalprop); - icalproperty_free (icalprop); - } - } - - icalcomponent_set_uid (icalcomp, uid ? uid : item_id->id); - - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-ITEMID", item_id->id); - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY", item_id->change_key); - - res_component = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (icalcomp)); - - /* Categories */ - e_cal_component_set_categories_list (res_component, (GSList *) e_ews_item_get_categories (item)); - - /* - * There is no API to set/get alarm description on the server side. - * However, for some reason, the alarm description has been set to "REMINDER" - * automatically (and with no i18n). Instead of show it to the user, let's - * set the summary as the alarm description. - */ - if (e_cal_component_has_alarms (res_component)) { - GList *alarm_uids, *l; - - alarm_uids = e_cal_component_get_alarm_uids (res_component); - for (l = alarm_uids; l != NULL; l = l->next) { - ECalComponentAlarm *alarm; - ECalComponentText text; - - alarm = e_cal_component_get_alarm (res_component, l->data); - e_cal_component_get_summary (res_component, &text); - e_cal_component_alarm_set_description (alarm, &text); - - e_cal_component_alarm_free (alarm); - } - cal_obj_uid_list_free (alarm_uids); - } - } - - icalcomponent_free (vcomp); - - if (res_component) { - const GSList *attachment_ids, *aid, *l; - const gchar *uid = NULL; - GSList *info_attachments = NULL, *uris = NULL; - gboolean has_attachment = FALSE; - - e_ews_item_has_attachments (item, &has_attachment); - if (!has_attachment) - return res_component; - - e_cal_component_get_uid (res_component, &uid); - - attachment_ids = e_ews_item_get_attachments_ids (item); - - if (e_ews_connection_get_attachments_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - uid, - attachment_ids, - cbews->priv->attachments_dir, - TRUE, - &info_attachments, - NULL, NULL, - cancellable, - NULL)) { - icalcomponent *icalcomp; - icalproperty *icalprop; - icalparameter *icalparam; - - for (l = info_attachments; l; l = l->next) { - EEwsAttachmentInfo *info = l->data; - - /* ignore non-uri attachments, because it's an exception */ - if (e_ews_attachment_info_get_type (info) == E_EWS_ATTACHMENT_INFO_TYPE_URI) { - const gchar *uri = e_ews_attachment_info_get_uri (info); - - if (uri) - uris = g_slist_append (uris, g_strdup (uri)); - } - } - - e_cal_component_set_attachment_list (res_component, uris); - - icalcomp = e_cal_component_get_icalcomponent (res_component); - icalprop = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY); - for (aid = attachment_ids; aid && icalprop; aid = aid->next, icalprop = icalcomponent_get_next_property (icalcomp, ICAL_ATTACH_PROPERTY)) { - icalparam = icalparameter_new_x (aid->data); - icalparameter_set_xname (icalparam, "X-EWS-ATTACHMENTID"); - icalproperty_add_parameter (icalprop, icalparam); - } - - g_slist_free_full (uris, g_free); - g_slist_free_full (info_attachments, (GDestroyNotify) e_ews_attachment_info_free); - } - } - - return res_component; -} - -static void -ecb_ews_store_original_comp (ECalComponent *comp) -{ - gchar *comp_str; - gchar *base64; - - g_return_if_fail (E_IS_CAL_COMPONENT (comp)); - - comp_str = e_cal_component_get_as_string (comp); - g_return_if_fail (comp_str != NULL); - - /* Include NUL-terminator */ - base64 = g_base64_encode ((const guchar *) comp_str, strlen (comp_str) + 1); - - e_cal_util_set_x_property (e_cal_component_get_icalcomponent (comp), - X_EWS_ORIGINAL_COMP, base64); - - g_free (base64); - g_free (comp_str); -} - -static ECalComponent * /* free with g_object_unref(), if not NULL */ -ecb_ews_restore_original_comp (ECalComponent *from_comp) -{ - ECalComponent *comp = NULL; - const gchar *original_base64; - guchar *decoded; - gsize len = -1; - - g_return_val_if_fail (E_IS_CAL_COMPONENT (from_comp), NULL); - - original_base64 = e_cal_util_get_x_property (e_cal_component_get_icalcomponent (from_comp), X_EWS_ORIGINAL_COMP); - - if (!original_base64 || !*original_base64) - return NULL; - - decoded = g_base64_decode (original_base64, &len); - if (!decoded || !*decoded || len <= 0) { - g_free (decoded); - return NULL; - } - - if (decoded[len - 1] != '\0') { - gchar *tmp; - - tmp = g_strndup ((const gchar *) decoded, len); - - g_free (decoded); - decoded = (guchar *) tmp; - } - - if (decoded && *decoded) - comp = e_cal_component_new_from_string ((const gchar *) decoded); - - g_free (decoded); - - return comp; -} - -static gboolean -ecb_ews_get_items_sync (ECalBackendEws *cbews, - const GSList *item_ids, /* gchar * */ - const gchar *default_props, - const EEwsAdditionalProps *add_props, - GSList **out_components, /* ECalComponent * */ - GCancellable *cancellable, - GError **error) -{ - GSList *items = NULL, *link; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (out_components != NULL, FALSE); - - success = e_ews_connection_get_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - item_ids, - default_props, - add_props, - FALSE, - NULL, - E_EWS_BODY_TYPE_TEXT, - &items, - NULL, NULL, - cancellable, - error); - - if (!success) - return FALSE; - - /* fetch modified occurrences */ - for (link = items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const GSList *modified_occurrences; - - if (!item || e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - modified_occurrences = e_ews_item_get_modified_occurrences (item); - if (modified_occurrences) { - EEwsAdditionalProps *modified_add_props; - - modified_add_props = e_ews_additional_props_new (); - if (e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) { - EEwsExtendedFieldURI *ext_uri; - - modified_add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2010); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSStartTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - modified_add_props->extended_furis = g_slist_append (modified_add_props->extended_furis, ext_uri); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSEndTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - modified_add_props->extended_furis = g_slist_append (modified_add_props->extended_furis, ext_uri); - } else { - modified_add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2007); - } - - success = ecb_ews_get_items_sync (cbews, modified_occurrences, "IdOnly", modified_add_props, out_components, cancellable, error); - - e_ews_additional_props_free (modified_add_props); - - if (!success) - goto exit; - } - } - - for (link = items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - ECalComponent *comp; - GError *local_error = NULL; - - if (!item || e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - comp = ecb_ews_item_to_component_sync (cbews, item, cancellable, &local_error); - if (!comp) { - if (!local_error) - continue; - - g_propagate_error (error, local_error); - success = FALSE; - break; - } - - ecb_ews_store_original_comp (comp); - - *out_components = g_slist_prepend (*out_components, comp); - } - - exit: - g_slist_free_full (items, g_object_unref); - - return success; -} - -static gboolean -ecb_ews_fetch_items_sync (ECalBackendEws *cbews, - const GSList *items, /* EEwsItem * */ - GSList **out_components, /* ECalComponent * */ - GCancellable *cancellable, - GError **error) -{ - GSList *event_ids = NULL, *task_memo_ids = NULL, *link; - gboolean success = TRUE; - - for (link = (GSList *) items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (type == E_EWS_ITEM_TYPE_EVENT) - event_ids = g_slist_prepend (event_ids, g_strdup (id->id)); - else if (type == E_EWS_ITEM_TYPE_TASK || type == E_EWS_ITEM_TYPE_MEMO) { - task_memo_ids = g_slist_prepend (task_memo_ids, g_strdup (id->id)); - } - } - - if (event_ids) { - EEwsAdditionalProps *add_props; - - add_props = e_ews_additional_props_new (); - if (e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) { - EEwsExtendedFieldURI *ext_uri; - - add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2010); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSStartTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - add_props->extended_furis = g_slist_append (add_props->extended_furis, ext_uri); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSEndTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - add_props->extended_furis = g_slist_append (add_props->extended_furis, ext_uri); - } else { - add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2007); - } - - success = ecb_ews_get_items_sync (cbews, event_ids, "IdOnly", add_props, out_components, cancellable, error); - - e_ews_additional_props_free (add_props); - } - - if (task_memo_ids && success) - success = ecb_ews_get_items_sync (cbews, task_memo_ids, "AllProperties", NULL, out_components, cancellable, error); - - g_slist_free_full (event_ids, g_free); - g_slist_free_full (task_memo_ids, g_free); - - return success; -} - -static gboolean -ecb_ews_freebusy_ecomp_changed (ECalComponent *ecomp, - icalcomponent *vevent) -{ - icalcomponent *icomp; - gboolean changed = FALSE; - - g_return_val_if_fail (vevent != NULL, FALSE); - - if (!ecomp) - return TRUE; - - icomp = e_cal_component_get_icalcomponent (ecomp); - if (!icomp) - return TRUE; - - if (!changed) - changed = g_strcmp0 (icalcomponent_get_summary (icomp), icalcomponent_get_summary (vevent)) != 0; - if (!changed) - changed = g_strcmp0 (icalcomponent_get_location (icomp), icalcomponent_get_location (vevent)) != 0; - if (!changed) - changed = icaltime_compare (icalcomponent_get_dtstart (icomp), icalcomponent_get_dtstart (vevent)) != 0; - if (!changed) - changed = icaltime_compare (icalcomponent_get_dtend (icomp), icalcomponent_get_dtend (vevent)) != 0; - - return changed; -} - -static GSList * /* the possibly modified 'in_items' */ -ecb_ews_verify_changes (ECalCache *cal_cache, - icalcomponent_kind kind, - GSList *in_items, /* EEwsItem * */ - GCancellable *cancellable) -{ - GSList *items = NULL, *link; - - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), in_items); - - for (link = in_items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (!g_cancellable_is_cancelled (cancellable) && ( - (type == E_EWS_ITEM_TYPE_EVENT && kind == ICAL_VEVENT_COMPONENT) || - (type == E_EWS_ITEM_TYPE_MEMO && kind == ICAL_VJOURNAL_COMPONENT) || - (type == E_EWS_ITEM_TYPE_TASK && kind == ICAL_VTODO_COMPONENT) )) { - ECalComponent *existing = NULL; - - if (e_cal_cache_get_component (cal_cache, id->id, NULL, &existing, cancellable, NULL) && - existing && g_strcmp0 (e_cal_util_get_x_property (e_cal_component_get_icalcomponent (existing), - "X-EVOLUTION-CHANGEKEY"), id->change_key) == 0) { - g_object_unref (item); - } else { - items = g_slist_prepend (items, item); - } - - g_clear_object (&existing); - } else if (type == E_EWS_ITEM_TYPE_EVENT || - type == E_EWS_ITEM_TYPE_MEMO || - type == E_EWS_ITEM_TYPE_TASK) { - g_object_unref (item); - } else { - items = g_slist_prepend (items, item); - } - } - - g_slist_free (in_items); - - return items; -} - -static GSList * /* ECalMetaBackendInfo */ -ecb_ews_components_to_infos (ECalMetaBackend *meta_backend, - const GSList *components, /* ECalComponent * */ - icalcomponent_kind kind) -{ - GSList *nfos = NULL, *link; - GHashTable *sorted_by_uids; /* gchar * ~> GSList { ECalComponent * } */ - GHashTableIter iter; - gpointer key, value; - - sorted_by_uids = g_hash_table_new (g_str_hash, g_str_equal); - - for (link = (GSList *) components; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - icalcomponent *icomp; - const gchar *uid; - GSList *instances; - - if (!comp) - continue; - - icomp = e_cal_component_get_icalcomponent (comp); - uid = icalcomponent_get_uid (icomp); - - if (!uid) - continue; - - ecb_ews_store_original_comp (comp); - - instances = g_hash_table_lookup (sorted_by_uids, uid); - g_hash_table_insert (sorted_by_uids, (gpointer) uid, g_slist_prepend (instances, comp)); - } - - g_hash_table_iter_init (&iter, sorted_by_uids); - while (g_hash_table_iter_next (&iter, &key, &value)) { - const gchar *uid = key; - GSList *instances = value, *link; - icalcomponent *icomp, *merged; - ECalComponent *comp; - ECalMetaBackendInfo *nfo; - const gchar *revision, *itemid; - - if (!uid || !instances) { - g_slist_free (instances); - continue; - } - - /* Try to find master object, to have seves marter's itemid in ECalMetaBackendInfo::extra, - thus the re-load of the event is done for the whole series and not for a detached instance */ - comp = NULL; - for (link = instances; link && !comp; link = g_slist_next (link)) { - comp = link->data; - - if (!comp) - continue; - - if (e_cal_component_is_instance (comp)) - comp = NULL; - } - - if (!comp) - comp = instances->data; - - if (!comp) { - g_slist_free (instances); - continue; - } - - icomp = e_cal_component_get_icalcomponent (comp); - itemid = e_cal_util_get_x_property (icomp, "X-EVOLUTION-ITEMID"); - revision = e_cal_util_get_x_property (icomp, "X-EVOLUTION-CHANGEKEY"); - merged = e_cal_meta_backend_merge_instances (meta_backend, instances, FALSE); - - if (!merged) { - g_warn_if_fail (merged != NULL); - g_slist_free (instances); - continue; - } - - nfo = e_cal_meta_backend_info_new (uid, revision, NULL, itemid); - nfo->object = icalcomponent_as_ical_string_r (merged); - - nfos = g_slist_prepend (nfos, nfo); - - icalcomponent_free (merged); - g_slist_free (instances); - } - - g_hash_table_destroy (sorted_by_uids); - - return nfos; -} - -static void -ecb_ews_extract_item_id (ECalComponent *comp, - gchar **out_id, - gchar **out_change_key) -{ - icalcomponent *icalcomp; - - g_return_if_fail (E_IS_CAL_COMPONENT (comp)); - - icalcomp = e_cal_component_get_icalcomponent (comp); - g_return_if_fail (icalcomp != NULL); - - if (out_id) - *out_id = e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-ITEMID"); - if (out_change_key) - *out_change_key = e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY"); -} - -static gboolean -ecb_ews_is_organizer (ECalBackendEws *cbews, - ECalComponent *comp) -{ - ECalComponentOrganizer organizer; - gboolean is_organizer = FALSE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE); - - if (!e_cal_component_has_organizer (comp)) - return FALSE; - - organizer.value = NULL; - - e_cal_component_get_organizer (comp, &organizer); - if (organizer.value) { - CamelEwsSettings *ews_settings; - const gchar *email = organizer.value; - gchar *user_email; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - user_email = camel_ews_settings_dup_email (ews_settings); - - if (!g_ascii_strncasecmp (email, "mailto:", 7)) - email += 7; - - is_organizer = user_email && g_ascii_strcasecmp (email, user_email) == 0; - - g_free (user_email); - } - - return is_organizer; -} - -static gboolean -ecb_ews_connect_sync (ECalMetaBackend *meta_backend, - const ENamedParameters *credentials, - ESourceAuthenticationResult *out_auth_result, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - CamelEwsSettings *ews_settings; - gchar *hosturl; - gboolean success = FALSE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_auth_result != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->cnc) { - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - *out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED; - - return TRUE; - } - - ews_settings = ecb_ews_get_collection_settings (cbews); - hosturl = camel_ews_settings_dup_hosturl (ews_settings); - - cbews->priv->cnc = e_ews_connection_new_for_backend (E_BACKEND (cbews), e_cal_backend_get_registry (E_CAL_BACKEND (cbews)), hosturl, ews_settings); - - e_binding_bind_property ( - cbews, "proxy-resolver", - cbews->priv->cnc, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - *out_auth_result = e_ews_connection_try_credentials_sync (cbews->priv->cnc, credentials, NULL, - out_certificate_pem, out_certificate_errors, cancellable, error); - - if (*out_auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) { - ESource *source = e_backend_get_source (E_BACKEND (cbews)); - ESourceEwsFolder *ews_folder; - - ews_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER); - - g_free (cbews->priv->folder_id); - cbews->priv->folder_id = e_source_ews_folder_dup_id (ews_folder); - cbews->priv->is_freebusy_calendar = cbews->priv->folder_id && g_str_has_prefix (cbews->priv->folder_id, "freebusy-calendar::"); - - g_signal_connect_swapped (cbews->priv->cnc, "server-notification", - G_CALLBACK (ecb_ews_server_notification_cb), cbews); - - if (!cbews->priv->is_freebusy_calendar && - camel_ews_settings_get_listen_notifications (ews_settings) && - e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010_SP1)) { - GSList *folders = NULL; - - folders = g_slist_prepend (folders, cbews->priv->folder_id); - - e_ews_connection_enable_notifications_sync (cbews->priv->cnc, - folders, &cbews->priv->subscription_key); - - g_slist_free (folders); - } - - e_cal_backend_set_writable (E_CAL_BACKEND (cbews), !cbews->priv->is_freebusy_calendar); - success = TRUE; - } else { - ecb_ews_convert_error_to_edc_error (error); - g_clear_object (&cbews->priv->cnc); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - g_free (hosturl); - - return success; -} - -static gboolean -ecb_ews_disconnect_sync (ECalMetaBackend *meta_backend, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - ecb_ews_unset_connection (cbews); - - return TRUE; -} - -static gboolean -ecb_ews_get_changes_sync (ECalMetaBackend *meta_backend, - const gchar *last_sync_tag, - gboolean is_repeat, - gchar **out_new_sync_tag, - gboolean *out_repeat, - GSList **out_created_objects, - GSList **out_modified_objects, - GSList **out_removed_objects, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - gboolean success = TRUE; - GError *local_error = NULL; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_new_sync_tag != NULL, FALSE); - g_return_val_if_fail (out_repeat != NULL, FALSE); - g_return_val_if_fail (out_created_objects != NULL, FALSE); - g_return_val_if_fail (out_modified_objects != NULL, FALSE); - g_return_val_if_fail (out_removed_objects != NULL, FALSE); - - *out_created_objects = NULL; - *out_modified_objects = NULL; - *out_removed_objects = NULL; - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->is_freebusy_calendar) { - ESourceEwsFolder *ews_folder; - EEWSFreeBusyData fbdata; - GSList *free_busy = NULL, *link; - gboolean success; - time_t today; - - ews_folder = e_source_get_extension (e_backend_get_source (E_BACKEND (cbews)), E_SOURCE_EXTENSION_EWS_FOLDER); - - today = time_day_begin (time (NULL)); - - fbdata.period_start = time_add_week (today, -e_source_ews_folder_get_freebusy_weeks_before (ews_folder)); - fbdata.period_end = time_day_end (time_add_week (today, e_source_ews_folder_get_freebusy_weeks_after (ews_folder))); - fbdata.user_mails = g_slist_prepend (NULL, e_source_ews_folder_dup_foreign_mail (ews_folder)); - - success = e_ews_connection_get_free_busy_sync (cbews->priv->cnc, G_PRIORITY_DEFAULT, - e_ews_cal_utils_prepare_free_busy_request, &fbdata, - &free_busy, cancellable, &local_error); - - if (success) { - icaltimezone *utc_zone = icaltimezone_get_utc_timezone (); - GSList *comps = NULL; - GHashTable *known; - GHashTableIter iter; - gpointer key; - - known = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - - if (e_cal_cache_search_components (cal_cache, NULL, &comps, cancellable, NULL)) { - for (link = comps; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - icalcomponent *icomp; - const gchar *uid; - - if (!comp) - continue; - - icomp = e_cal_component_get_icalcomponent (comp); - if (!icomp) - continue; - - uid = icalcomponent_get_uid (icomp); - - if (uid && *uid) - g_hash_table_insert (known, g_strdup (uid), g_object_ref (comp)); - } - - g_slist_free_full (comps, g_object_unref); - } - - for (link = free_busy; link; link = g_slist_next (link)) { - icalcomponent *fbcomp = link->data; - icalproperty *fbprop; - icalparameter *param; - struct icalperiodtype fb; - icalparameter_fbtype fbtype; - - if (!fbcomp || icalcomponent_isa (fbcomp) != ICAL_VFREEBUSY_COMPONENT) - continue; - - for (fbprop = icalcomponent_get_first_property (fbcomp, ICAL_FREEBUSY_PROPERTY); - fbprop; - fbprop = icalcomponent_get_next_property (fbcomp, ICAL_FREEBUSY_PROPERTY)) { - ECalComponent *ecomp; - icalcomponent *vevent; - const gchar *id, *summary, *location; - - param = icalproperty_get_first_parameter (fbprop, ICAL_FBTYPE_PARAMETER); - if (!param) - continue; - - fbtype = icalparameter_get_fbtype (param); - - if (fbtype != ICAL_FBTYPE_FREE && - fbtype != ICAL_FBTYPE_BUSY && - fbtype != ICAL_FBTYPE_BUSYUNAVAILABLE && - fbtype != ICAL_FBTYPE_BUSYTENTATIVE) - continue; - - fb = icalproperty_get_freebusy (fbprop); - id = icalproperty_get_parameter_as_string (fbprop, "X-EWS-ID"); - summary = icalproperty_get_parameter_as_string (fbprop, "X-SUMMARY"); - location = icalproperty_get_parameter_as_string (fbprop, "X-LOCATION"); - - vevent = icalcomponent_new_vevent (); - - if (id && *id) { - icalcomponent_set_uid (vevent, id); - } else { - gchar *uid; - - uid = g_strdup_printf ("%s-%s-%d", - icaltime_as_ical_string (fb.start), - icaltime_as_ical_string (fb.end), - (gint) fbtype); - - icalcomponent_set_uid (vevent, uid); - - g_free (uid); - } - - fb.start.zone = utc_zone; - fb.end.zone = utc_zone; - - icalcomponent_set_dtstart (vevent, fb.start); - icalcomponent_set_dtend (vevent, fb.end); - - icalcomponent_add_property (vevent, icalproperty_new_created (icaltime_current_time_with_zone (utc_zone))); - - if (fbtype == ICAL_FBTYPE_FREE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Free")); - icalcomponent_add_property (vevent, icalproperty_new_transp (ICAL_TRANSP_TRANSPARENT)); - } else if (fbtype == ICAL_FBTYPE_BUSY) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Busy")); - } else if (fbtype == ICAL_FBTYPE_BUSYUNAVAILABLE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Out of Office")); - } else if (fbtype == ICAL_FBTYPE_BUSYTENTATIVE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Tentative")); - } - - if (summary && *summary) - icalcomponent_set_summary (vevent, summary); - - if (location && *location) - icalcomponent_set_location (vevent, location); - - ecomp = g_hash_table_lookup (known, icalcomponent_get_uid (vevent)); - if (ecomp) { - g_object_ref (ecomp); - - /* This dereferences the ecomp, thus the ref() call above to keep it alive */ - g_hash_table_remove (known, icalcomponent_get_uid (vevent)); - - if (ecb_ews_freebusy_ecomp_changed (ecomp, vevent)) { - ECalMetaBackendInfo *nfo; - gchar *revision = e_util_generate_uid (); - - e_cal_util_set_x_property (vevent, "X-EVOLUTION-CHANGEKEY", revision); - - nfo = e_cal_meta_backend_info_new (icalcomponent_get_uid (vevent), NULL, NULL, NULL); - nfo->revision = revision; - nfo->object = icalcomponent_as_ical_string_r (vevent); - - *out_created_objects = g_slist_prepend (*out_created_objects, nfo); - } else { - icalcomponent_free (vevent); - } - - g_clear_object (&ecomp); - } else { - ECalMetaBackendInfo *nfo; - gchar *revision = e_util_generate_uid (); - - e_cal_util_set_x_property (vevent, "X-EVOLUTION-CHANGEKEY", revision); - - nfo = e_cal_meta_backend_info_new (icalcomponent_get_uid (vevent), NULL, NULL, NULL); - nfo->revision = revision; - nfo->object = icalcomponent_as_ical_string_r (vevent); - - *out_modified_objects = g_slist_prepend (*out_modified_objects, nfo); - } - } - } - - g_hash_table_iter_init (&iter, known); - while (g_hash_table_iter_next (&iter, &key, NULL)) { - const gchar *uid = key; - - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_cal_meta_backend_info_new (uid, NULL, NULL, NULL)); - } - - g_hash_table_destroy (known); - } else if (g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NOFREEBUSYACCESS)) { - e_cal_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL); - - e_cal_backend_notify_error (E_CAL_BACKEND (cbews), local_error->message); - g_clear_error (&local_error); - } else { - g_propagate_error (error, local_error); - } - - g_slist_free_full (free_busy, (GDestroyNotify) icalcomponent_free); - g_slist_free_full (fbdata.user_mails, g_free); - } else { - GSList *items_created = NULL, *items_modified = NULL, *items_deleted = NULL, *link; - EEwsAdditionalProps *add_props; - gboolean includes_last_item = TRUE; - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("item:ItemClass"); - - success = e_ews_connection_sync_folder_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - last_sync_tag, cbews->priv->folder_id, "IdOnly", add_props, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - - if (!success && - g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) { - g_clear_error (&local_error); - - e_cal_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL); - - success = e_ews_connection_sync_folder_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - NULL, cbews->priv->folder_id, "IdOnly", add_props, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - } - - e_ews_additional_props_free (add_props); - - if (success) { - GSList *components_created = NULL, *components_modified = NULL; - icalcomponent_kind kind; - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - - /* The sync state doesn't cover changes made by save_component_sync(), - thus verify the changes, instead of re-donwloading the component again */ - items_created = ecb_ews_verify_changes (cal_cache, kind, items_created, cancellable); - items_modified = ecb_ews_verify_changes (cal_cache, kind, items_modified, cancellable); - - if (items_created) { - success = ecb_ews_fetch_items_sync (cbews, items_created, &components_created, cancellable, error); - if (success) - *out_created_objects = ecb_ews_components_to_infos (meta_backend, components_created, kind); - } - - if (items_modified) { - success = ecb_ews_fetch_items_sync (cbews, items_modified, &components_modified, cancellable, error); - if (success) - *out_modified_objects = ecb_ews_components_to_infos (meta_backend, components_modified, kind); - } - - for (link = items_deleted; link; link = g_slist_next (link)) { - const gchar *item_id = link->data; - GSList *ids = NULL, *ilink; - - if (!e_cal_cache_get_ids_with_extra (cal_cache, item_id, &ids, cancellable, NULL)) - continue; - - for (ilink = ids; ilink; ilink = g_slist_next (ilink)) { - ECalComponentId *id = ilink->data; - - /* Use the master object */ - if (id && id->uid && *id->uid && (!id->rid || !*id->rid)) { - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_cal_meta_backend_info_new (id->uid, NULL, NULL, NULL)); - break; - } - } - - g_slist_free_full (ids, (GDestroyNotify) e_cal_component_free_id); - } - - g_slist_free_full (components_created, g_object_unref); - g_slist_free_full (components_modified, g_object_unref); - - *out_repeat = !includes_last_item; - } else if (local_error) { - g_propagate_error (error, local_error); - } - - g_slist_free_full (items_created, g_object_unref); - g_slist_free_full (items_modified, g_object_unref); - g_slist_free_full (items_deleted, g_free); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_clear_object (&cal_cache); - - return success; -} - -static gboolean -ecb_ews_load_component_sync (ECalMetaBackend *meta_backend, - const gchar *uid, - const gchar *extra, - icalcomponent **out_component, - gchar **out_extra, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - GSList *ids, *items = NULL, *components = NULL; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (uid != NULL, FALSE); - g_return_val_if_fail (out_component != NULL, FALSE); - g_return_val_if_fail (out_extra != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - ids = g_slist_prepend (NULL, (gpointer) (extra && *extra ? extra : uid)); - - success = e_ews_connection_get_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, ids, "IdOnly", - NULL, FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &items, NULL, NULL, cancellable, error); - - g_slist_free (ids); - - if (success && items) { - success = ecb_ews_fetch_items_sync (cbews, items, &components, cancellable, error); - - if (components) { - const EwsId *ews_id = e_ews_item_get_id (items->data); - - if (ews_id) - *out_extra = g_strdup (ews_id->id); - - if (components->next) { - GSList *link; - - *out_component = icalcomponent_new_vcalendar (); - - for (link = components; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - if (!comp) - continue; - - icalcomponent_add_component (*out_component, - icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp))); - } - } else { - *out_component = icalcomponent_new_clone (e_cal_component_get_icalcomponent (components->data)); - } - } else { - success = FALSE; - } - } - - if (!components && e_cal_meta_backend_refresh_sync (meta_backend, cancellable, NULL)) { - ECalCache *cal_cache; - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - if (cal_cache) { - success = e_cal_cache_get_components_by_uid (cal_cache, uid, &components, cancellable, NULL); - if (success) { - *out_component = e_cal_meta_backend_merge_instances (meta_backend, components, FALSE); - - if (!e_cal_cache_get_component_extra (cal_cache, uid, NULL, out_extra, cancellable, NULL)) - *out_extra = NULL; - - g_clear_error (error); - } - g_object_unref (cal_cache); - } - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_slist_free_full (components, g_object_unref); - g_slist_free_full (items, g_object_unref); - - return success; -} - -/* Very simple and naive component comparator, to avoid - unnecessary uploads and changes on the server. */ -static gboolean -ecb_ews_components_equal (ECalComponent *comp1, - ECalComponent *comp2) -{ - icalcomponent *icomp1, *icomp2; - icalproperty *prop1; - GHashTable *processed_props; - gboolean equal = TRUE; - - if (!comp1 && !comp2) - return TRUE; - else if (!comp1 || !comp2) - return FALSE; - - icomp1 = e_cal_component_get_icalcomponent (comp1); - icomp2 = e_cal_component_get_icalcomponent (comp2); - - if (!icomp1 || !icomp2) - return FALSE; - - if (g_strcmp0 (icalcomponent_get_uid (icomp1), icalcomponent_get_uid (icomp2)) != 0) - return FALSE; - - if (icalcomponent_count_properties (icomp1, ICAL_ANY_PROPERTY) != - icalcomponent_count_properties (icomp2, ICAL_ANY_PROPERTY)) - return FALSE; - - processed_props = g_hash_table_new (g_direct_hash, g_direct_equal); - - for (prop1 = icalcomponent_get_first_property (icomp1, ICAL_ANY_PROPERTY); - prop1 && equal; - prop1 = icalcomponent_get_next_property (icomp1, ICAL_ANY_PROPERTY)) { - icalproperty_kind kind = icalproperty_isa (prop1); - icalproperty *prop2; - - for (prop2 = icalcomponent_get_first_property (icomp2, kind); - prop2; - prop2 = icalcomponent_get_next_property (icomp2, kind)) { - gchar *str1, *str2; - gboolean same; - - if (g_hash_table_contains (processed_props, prop2)) - continue; - - if (icalproperty_count_parameters (prop1) != icalproperty_count_parameters (prop2)) - continue; - - str1 = icalproperty_as_ical_string_r (prop1); - str2 = icalproperty_as_ical_string_r (prop2); - - same = g_strcmp0 (str1, str2) == 0; - - g_free (str1); - g_free (str2); - - if (same) { - g_hash_table_insert (processed_props, prop2, NULL); - break; - } - } - - if (!prop2) - equal = FALSE; - } - - g_hash_table_destroy (processed_props); - - return equal; -} - -typedef struct _ChangeData { - ECalComponent *old_component; - ECalComponent *new_component; -} ChangeData; - -static void -change_data_free (gpointer ptr) -{ - ChangeData *cd = ptr; - - if (cd) { - g_clear_object (&cd->old_component); - g_clear_object (&cd->new_component); - g_free (cd); - } -} - -static void -ecb_ews_filter_out_unchanged_instances (const GSList *to_save_instances, - const GSList *existing_instances, - GSList **out_changed_instances, /* ChangeData * */ - GSList **out_removed_instances) /* ECalComponent * */ -{ - GSList *link = NULL; - GHashTable *existing_hash; - GHashTableIter iter; - gpointer value; - - g_return_if_fail (to_save_instances != NULL); - g_return_if_fail (existing_instances != NULL); - g_return_if_fail (out_changed_instances != NULL); - g_return_if_fail (out_removed_instances != NULL); - - *out_changed_instances = NULL; - *out_removed_instances = NULL; - - existing_hash = g_hash_table_new_full ((GHashFunc)e_cal_component_id_hash, (GEqualFunc) e_cal_component_id_equal, - (GDestroyNotify) e_cal_component_free_id, NULL); - - for (link = (GSList *) existing_instances; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id; - - id = e_cal_component_get_id (comp); - if (id) - g_hash_table_insert (existing_hash, id, comp); - } - - for (link = (GSList *) to_save_instances; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id = NULL; - - id = e_cal_component_get_id (comp); - if (id) { - ECalComponent *old_comp; - - old_comp = g_hash_table_lookup (existing_hash, id); - - if (!ecb_ews_components_equal (comp, old_comp)) { - ChangeData *cd; - - cd = g_new0 (ChangeData, 1); - cd->old_component = old_comp ? g_object_ref (old_comp) : NULL; - cd->new_component = g_object_ref (comp); - - *out_changed_instances = g_slist_prepend (*out_changed_instances, cd); - } - - g_hash_table_remove (existing_hash, id); - e_cal_component_free_id (id); - } - } - - g_hash_table_iter_init (&iter, existing_hash); - while (g_hash_table_iter_next (&iter, NULL, &value)) { - *out_removed_instances = g_slist_prepend (*out_removed_instances, g_object_ref (value)); - } - - g_hash_table_destroy (existing_hash); -} - -static gboolean -ecb_ews_extract_attachments (icalcomponent *icalcomp, - GSList **out_attachments) /* EEwsAttachmentInfo * */ -{ - icalproperty *prop; - GSList *props = NULL, *link; - - g_return_val_if_fail (icalcomp != NULL, FALSE); - g_return_val_if_fail (out_attachments != NULL, FALSE); - - *out_attachments = NULL; - - for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY); - prop; - prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTACH_PROPERTY)) { - props = g_slist_prepend (props, prop); - } - - for (link = props; link; link = g_slist_next (link)) { - EEwsAttachmentInfo *info; - icalattach *attach; - icalparameter *param; - const gchar *stored_filename; - - prop = link->data; - param = icalproperty_get_first_parameter (prop, ICAL_FILENAME_PARAMETER); - stored_filename = param ? icalparameter_get_filename (param) : NULL; - - attach = icalproperty_get_attach (prop); - if (icalattach_get_is_url (attach)) { - const gchar *uri; - - uri = icalattach_get_url (attach); - - if (!uri || !*uri) - continue; - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_URI); - - e_ews_attachment_info_set_uri (info, uri); - if (stored_filename && *stored_filename) { - e_ews_attachment_info_set_prefer_filename (info, stored_filename); - } else { - gchar *uri_filename; - - uri_filename = g_filename_from_uri (uri, NULL, NULL); - if (uri_filename && *uri_filename) { - gchar *basename; - - basename = g_path_get_basename (uri_filename); - if (basename && *basename && basename[0] != '.' && basename[0] != G_DIR_SEPARATOR) { - const gchar *uid; - - uid = icalcomponent_get_uid (icalcomp); - - if (uid && g_str_has_prefix (basename, uid) && basename[strlen (uid)] == '-') { - e_ews_attachment_info_set_prefer_filename (info, basename + strlen (uid) + 1); - } - } - - g_free (basename); - } - - g_free (uri_filename); - } - } else { - gsize len = -1; - guchar *decoded = NULL; - const gchar *content; - - content = (const gchar *) icalattach_get_data (attach); - decoded = g_base64_decode (content, &len); - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_INLINED); - e_ews_attachment_info_set_inlined_data (info, decoded, len); - - if (stored_filename && *stored_filename) - e_ews_attachment_info_set_prefer_filename (info, stored_filename); - - g_free (decoded); - } - - e_ews_attachment_info_set_id (info, icalproperty_get_parameter_as_string (prop, "X-EWS-ATTACHMENTID")); - *out_attachments = g_slist_prepend (*out_attachments, info); - } - - g_slist_free (props); - - return *out_attachments != NULL; -} - -static icaltimezone * -ecb_ews_get_timezone_from_ical_component (ECalBackendEws *cbews, - icalcomponent *icalcomp) -{ - ETimezoneCache *timezone_cache; - icalproperty *prop = NULL; - const gchar *tzid = NULL; - - timezone_cache = E_TIMEZONE_CACHE (cbews); - - prop = icalcomponent_get_first_property (icalcomp, ICAL_DTSTART_PROPERTY); - if (prop != NULL) { - icalparameter *param = NULL; - - param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER); - if (param) { - tzid = icalparameter_get_tzid (param); - } else { - struct icaltimetype dtstart; - - dtstart = icalproperty_get_dtstart (prop); - if (icaltime_is_utc (dtstart)) - tzid = "UTC"; - } - } - - if (tzid) - return e_timezone_cache_get_timezone (timezone_cache, tzid); - - return NULL; -} - -static gboolean -ecb_ews_remove_item_sync (ECalBackendEws *cbews, - ECalCache *cal_cache, - GHashTable *removed_indexes, - const gchar *uid, - const gchar *rid, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *comp = NULL, *parent = NULL; - EwsId item_id = { 0 }; - gint index = 0; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE); - g_return_val_if_fail (uid != NULL, FALSE); - - if (rid && !*rid) - rid = NULL; - - if (!e_cal_cache_get_component (cal_cache, uid, rid, &comp, cancellable, error) || - (rid && !e_cal_cache_get_component (cal_cache, uid, NULL, &parent, cancellable, error))) { - if (!parent && !comp) { - g_propagate_error (error, EDC_ERROR (ObjectNotFound)); - return FALSE; - } - } - - ecb_ews_extract_item_id (comp ? comp : parent, &item_id.id, &item_id.change_key); - - if (!item_id.id) { - g_propagate_error (error, EDC_ERROR_EX (OtherError, "Cannot determine EWS ItemId")); - success = FALSE; - } else { - if (parent) { - index = e_cal_backend_ews_rid_to_index ( - ecb_ews_get_timezone_from_ical_component (cbews, - e_cal_component_get_icalcomponent (parent)), - rid, - e_cal_component_get_icalcomponent (parent), - error); - if (index == 0) - success = comp != NULL; - } - - if (index && removed_indexes && g_hash_table_contains (removed_indexes, GINT_TO_POINTER (index))) { - /* Do nothing, it's already deleted from the server */ - } else { - if (removed_indexes && index) - g_hash_table_insert (removed_indexes, GINT_TO_POINTER (index), NULL); - - success = success && e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, index, EWS_HARD_DELETE, - ecb_ews_is_organizer (cbews, comp) ? EWS_SEND_TO_ALL_AND_SAVE_COPY : EWS_SEND_TO_NONE, - EWS_ALL_OCCURRENCES, cancellable, error); - } - } - - g_free (item_id.id); - g_free (item_id.change_key); - - g_clear_object (&comp); - g_clear_object (&parent); - - return success; -} - -static void -ecb_ews_get_attach_differences (ECalComponent *oldcomp, - ECalComponent *newcomp, - GSList **out_removed_attachment_ids, /* gchar * */ - GSList **out_added_attachments) /* EEwsAttachmentInfo * */ -{ - GSList *old_attachments = NULL, *new_attachments = NULL, *link; - - g_return_if_fail (out_removed_attachment_ids != NULL); - g_return_if_fail (out_added_attachments != NULL); - - *out_removed_attachment_ids = NULL; - *out_added_attachments = NULL; - - if (!ecb_ews_extract_attachments (e_cal_component_get_icalcomponent (oldcomp), &old_attachments)) - old_attachments = NULL; - - if (!ecb_ews_extract_attachments (e_cal_component_get_icalcomponent (newcomp), &new_attachments)) - new_attachments = NULL; - - for (link = old_attachments; link; link = g_slist_next (link)) { - EEwsAttachmentInfo *old_nfo = link->data; - GSList *nlink; - - if (!old_nfo) - continue; - - for (nlink = new_attachments; nlink; nlink = g_slist_next (nlink)) { - EEwsAttachmentInfo *new_nfo = nlink->data; - gboolean same = FALSE; - - if (!new_nfo || - e_ews_attachment_info_get_type (old_nfo) != e_ews_attachment_info_get_type (new_nfo)) - continue; - - if (e_ews_attachment_info_get_type (old_nfo) == E_EWS_ATTACHMENT_INFO_TYPE_INLINED) { - const gchar *old_data, *new_data; - gsize old_len = -1, new_len = -1; - - old_data = e_ews_attachment_info_get_inlined_data (old_nfo, &old_len); - new_data = e_ews_attachment_info_get_inlined_data (new_nfo, &new_len); - - same = old_len == new_len && (old_len == 0 || - (old_len > 0 && old_data && new_data && memcmp (old_data, new_data, old_len) == 0)); - } else if (e_ews_attachment_info_get_type (old_nfo) == E_EWS_ATTACHMENT_INFO_TYPE_URI) { - same = g_strcmp0 (e_ews_attachment_info_get_uri (old_nfo), e_ews_attachment_info_get_uri (new_nfo)) == 0; - } - - if (same) { - new_attachments = g_slist_remove (new_attachments, new_nfo); - e_ews_attachment_info_free (new_nfo); - break; - } - } - - if (!nlink) { - /* Did not find in the new_attachments, thus it's removed */ - g_warn_if_fail (e_ews_attachment_info_get_id (old_nfo) != NULL); - *out_removed_attachment_ids = g_slist_prepend (*out_removed_attachment_ids, - g_strdup (e_ews_attachment_info_get_id (old_nfo))); - } - } - - *out_added_attachments = new_attachments; - - g_slist_free_full (old_attachments, (GDestroyNotify) e_ews_attachment_info_free); -} - -struct TzidCbData { - icalcomponent *comp; - ECalBackendEws *cbews; -}; - -static void -tzid_cb (icalparameter *param, - gpointer data) -{ - struct TzidCbData *cbd = data; - const gchar *tzid; - icaltimezone *zone; - icalcomponent *new_comp; - - tzid = icalparameter_get_tzid (param); - if (!tzid) - return; - - zone = e_timezone_cache_get_timezone (E_TIMEZONE_CACHE (cbd->cbews), tzid); - if (!zone) - return; - - new_comp = icaltimezone_get_component (zone); - if (!new_comp) - return; - - icalcomponent_add_component (cbd->comp, icalcomponent_new_clone (new_comp)); -} - -static void -ecb_ews_pick_all_tzids_out (ECalBackendEws *cbews, - icalcomponent *icalcomp) -{ - - /* pick all the tzids out of the component and resolve - * them using the vtimezones in the current calendar */ - struct TzidCbData cbd; - - cbd.cbews = cbews; - cbd.comp = icalcomp; - - icalcomponent_foreach_tzid (icalcomp, tzid_cb, &cbd); -} - -static gboolean -ecb_ews_modify_item_sync (ECalBackendEws *cbews, - GHashTable *removed_indexes, - icalcomponent *old_icalcomp, - icalcomponent *new_icalcomp, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *comp = NULL, *oldcomp = NULL; - icalcomponent *icalcomp; - gchar *itemid = NULL, *changekey = NULL; - GSList *added_attachments = NULL, *removed_attachment_ids = NULL; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (new_icalcomp != NULL, FALSE); - - icalcomp = icalcomponent_new_clone (new_icalcomp); - - ecb_ews_pick_all_tzids_out (cbews, icalcomp); - - comp = e_cal_component_new_from_icalcomponent (icalcomp); - if (!comp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - ecb_ews_extract_item_id (comp, &itemid, &changekey); - if (!itemid) { - g_propagate_error (error, EDC_ERROR_EX (OtherError, "Cannot determine EWS ItemId")); - g_object_unref (comp); - return FALSE; - } - - if (old_icalcomp) { - oldcomp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (old_icalcomp)); - } else { - oldcomp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (new_icalcomp)); - } - - ecb_ews_pick_all_tzids_out (cbews, e_cal_component_get_icalcomponent (oldcomp)); - - /* In case we have updated attachments we have to run update attachments - * before update items so attendees will receive mails with already updated attachments */ - - ecb_ews_get_attach_differences (oldcomp, comp, &removed_attachment_ids, &added_attachments); - - /* preform sync delete attachemnt operation*/ - if (removed_attachment_ids) { - g_free (changekey); - changekey = NULL; - - success = e_ews_connection_delete_attachments_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - removed_attachment_ids, &changekey, cancellable, error); - - g_slist_free_full (removed_attachment_ids, g_free); - } - - /* in case we have a new attachments add them before update */ - if (added_attachments && success) { - EwsId item_id; - - item_id.id = itemid; - item_id.change_key = changekey; - - changekey = NULL; - - success = e_ews_connection_create_attachments_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - &item_id, added_attachments, - FALSE, &changekey, NULL, cancellable, error); - - g_free (item_id.change_key); - } - - if (success && old_icalcomp && - icalcomponent_get_first_property (new_icalcomp, ICAL_RRULE_PROPERTY) && - !icalcomponent_get_first_property (new_icalcomp, ICAL_RECURRENCEID_PROPERTY)) { - icalproperty *prop, *old_prop; - GSList *exceptions = NULL, *link; - EwsId item_id; - - item_id.id = itemid; - item_id.change_key = changekey; - - /* Excluded occurrences */ - for (prop = icalcomponent_get_first_property (new_icalcomp, ICAL_EXDATE_PROPERTY); - prop; - prop = icalcomponent_get_next_property (new_icalcomp, ICAL_EXDATE_PROPERTY)) { - const gchar *new_rid; - - new_rid = icalproperty_get_value_as_string (prop); - - for (old_prop = icalcomponent_get_first_property (old_icalcomp, ICAL_EXDATE_PROPERTY); - old_prop; - old_prop = icalcomponent_get_next_property (old_icalcomp, ICAL_EXDATE_PROPERTY)) { - if (g_strcmp0 (new_rid, icalproperty_get_value_as_string (old_prop)) == 0) - break; - } - - if (!old_prop) - exceptions = g_slist_prepend (exceptions, prop); - } - - exceptions = g_slist_reverse (exceptions); - - for (link = exceptions; link && success; link = g_slist_next (link)) { - guint index; - - prop = link->data; - - index = e_cal_backend_ews_rid_to_index ( - ecb_ews_get_timezone_from_ical_component (cbews, new_icalcomp), - icalproperty_get_value_as_string (prop), - new_icalcomp, - error); - - if (index == 0) { - success = FALSE; - } else if (!removed_indexes || !g_hash_table_contains (removed_indexes, GINT_TO_POINTER (index))) { - if (removed_indexes) - g_hash_table_insert (removed_indexes, GINT_TO_POINTER (index), NULL); - - success = e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, index, - EWS_HARD_DELETE, EWS_SEND_TO_NONE, EWS_ALL_OCCURRENCES, cancellable, error); - } - } - - g_slist_free (exceptions); - } - - if (success) { - EwsCalendarConvertData convert_data = { 0 }; - CamelEwsSettings *ews_settings; - const gchar *send_meeting_invitations; - const gchar *send_or_save; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.user_email = camel_ews_settings_dup_email (ews_settings); - convert_data.comp = comp; - convert_data.old_comp = oldcomp; - convert_data.item_id = itemid; - convert_data.change_key = changekey; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - if (e_cal_component_has_attendees (comp)) { - send_meeting_invitations = "SendToAllAndSaveCopy"; - send_or_save = "SendAndSaveCopy"; - } else { - /*In case of appointment we have to set SendMeetingInvites to SendToNone */ - send_meeting_invitations = "SendToNone"; - send_or_save = "SaveOnly"; - } - - success = e_ews_connection_update_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", send_or_save, send_meeting_invitations, cbews->priv->folder_id, - e_cal_backend_ews_convert_component_to_updatexml, &convert_data, - NULL, cancellable, error); - - g_free (convert_data.user_email); - } - - g_slist_free_full (added_attachments, (GDestroyNotify) e_ews_attachment_info_free); - g_clear_object (&oldcomp); - g_clear_object (&comp); - g_free (changekey); - g_free (itemid); - - return success; -} - -static gboolean -ecb_ews_save_component_sync (ECalMetaBackend *meta_backend, - gboolean overwrite_existing, - EConflictResolution conflict_resolution, - const GSList *instances, - const gchar *extra, - gchar **out_new_uid, - gchar **out_new_extra, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - ECalComponent *master = NULL; - EwsFolderId *fid; - GSList *link; - const gchar *uid = NULL; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - for (link = (GSList *) instances; link && !master; link = g_slist_next (link)) { - master = link->data; - - if (!master) - continue; - - if (e_cal_component_is_instance (master)) - master = NULL; - } - - if (!master) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - g_return_val_if_fail (cal_cache != NULL, FALSE); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - e_cal_component_get_uid (master, &uid); - fid = e_ews_folder_id_new (cbews->priv->folder_id, NULL, FALSE); - - if (overwrite_existing) { - GSList *existing = NULL, *changed_instances = NULL, *removed_instances = NULL; - - success = uid && e_cal_cache_get_components_by_uid (cal_cache, uid, &existing, cancellable, error) && existing; - - if (success) { - GSList *link; - - /* This is for offline changes, where the component in the cache - is already modified, while the original, the one on the server, - is different. Using the cached component in this case generates - empty UpdateItem request and nothing is saved. */ - for (link = existing; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - comp = ecb_ews_restore_original_comp (comp); - if (comp) { - g_object_unref (link->data); - link->data = comp; - } - } - } - - if (success) - ecb_ews_filter_out_unchanged_instances (instances, existing, &changed_instances, &removed_instances); - - if (success) { - GHashTable *removed_indexes; - - removed_indexes = g_hash_table_new (g_direct_hash, g_direct_equal); - - for (link = changed_instances; link && success; link = g_slist_next (link)) { - ChangeData *cd = link->data; - - if (!cd) - continue; - - success = ecb_ews_modify_item_sync (cbews, removed_indexes, - e_cal_component_get_icalcomponent (cd->old_component ? cd->old_component : master), - e_cal_component_get_icalcomponent (cd->new_component), - cancellable, error); - } - - for (link = removed_instances; link && success; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id = NULL; - - if (!comp) - continue; - - id = e_cal_component_get_id (comp); - - if (id) { - success = ecb_ews_remove_item_sync (cbews, cal_cache, removed_indexes, id->uid, id->rid, cancellable, error); - e_cal_component_free_id (id); - } - } - - g_hash_table_destroy (removed_indexes); - } - - if (success) - ecb_ews_extract_item_id (master, out_new_uid, NULL); - - g_slist_free_full (existing, g_object_unref); - g_slist_free_full (changed_instances, change_data_free); - g_slist_free_full (removed_instances, g_object_unref); - } else { - GHashTable *removed_indexes; - EwsCalendarConvertData convert_data = { 0 }; - EEwsItem *item = NULL; - EwsId *ews_id = NULL; - const gchar *send_meeting_invitations; - icalcomponent *icalcomp; - icalproperty *prop; - GSList *items = NULL; - - icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (master)); - - e_ews_clean_icalcomponent (icalcomp); - - if (!e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) - ecb_ews_pick_all_tzids_out (cbews, icalcomp); - - /* - * In case we are creating a meeting with attendees and attachments. - * We have to preform 3 steps in order to allow attendees to receive attachments in their invite mails. - * 1. create meeting and do not send invites - * 2. create attachments - * 3. dummy update meeting and send invites to all - */ - if (e_cal_component_has_attendees (master)) { - if (e_cal_component_has_attachments (master)) - send_meeting_invitations = "SendToNone"; - else - send_meeting_invitations = "SendToAllAndSaveCopy"; - } else { - /* In case of appointment we have to set SendMeetingInvites to SendToNone */ - send_meeting_invitations = "SendToNone"; - } - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.icalcomp = icalcomp; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - success = e_ews_connection_create_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, "SaveOnly", send_meeting_invitations, - fid, e_cal_backend_ews_convert_calcomp_to_xml, &convert_data, - &items, cancellable, error); - - if (success && items) { - item = items->data; - if (item) { - g_object_ref (item); - - ews_id = e_ews_id_copy (e_ews_item_get_id (item)); - } - } - - if (success && item && e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_EVENT) { - EEwsAdditionalProps *add_props; - GSList *items, *items_req = NULL; - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("calendar:UID"); - - items = g_slist_append (NULL, ews_id->id); - - /* get calender uid from server*/ - success = e_ews_connection_get_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - items, "IdOnly", add_props, FALSE, NULL, E_EWS_BODY_TYPE_TEXT, - &items_req, NULL, NULL, cancellable, error) && items_req != NULL; - - e_ews_additional_props_free (add_props); - - if (success) { - g_clear_object (&item); - - item = items_req->data; - - e_ews_id_free (ews_id); - ews_id = NULL; - - if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) { - g_propagate_error (error, g_error_copy (e_ews_item_get_error (item))); - item = NULL; - success = FALSE; - } else { - item = g_object_ref (item); - ews_id = e_ews_id_copy (e_ews_item_get_id (item)); - } - } - - g_slist_free_full (items_req, g_object_unref); - g_slist_free (items); - } - - /* attachments */ - if (success && e_cal_component_has_attachments (master) > 0) { - GSList *info_attachments = NULL; - - g_warn_if_fail (ews_id != NULL); - - if (ews_id && ecb_ews_extract_attachments (icalcomp, &info_attachments)) { - gchar *changekey = NULL; - GSList *ids = NULL; - - success = e_ews_connection_create_attachments_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - ews_id, info_attachments, FALSE, &changekey, &ids, cancellable, error); - - g_slist_free_full (info_attachments, (GDestroyNotify) e_ews_attachment_info_free); - g_slist_free_full (ids, g_free); - - if (success && changekey) { - g_free (ews_id->change_key); - ews_id->change_key = changekey; - } else { - g_free (changekey); - } - } - } - - removed_indexes = g_hash_table_new (g_direct_hash, g_direct_equal); - - if (success && icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY)) { - GSList *exceptions = NULL; - - /* Excluded occurrences */ - for (prop = icalcomponent_get_first_property (icalcomp, ICAL_EXDATE_PROPERTY); - prop; - prop = icalcomponent_get_next_property (icalcomp, ICAL_EXDATE_PROPERTY)) { - exceptions = g_slist_prepend (exceptions, g_strdup (icalproperty_get_value_as_string (prop))); - } - - for (link = exceptions; link && success; link = g_slist_next (link)) { - success = ecb_ews_remove_item_sync (cbews, cal_cache, removed_indexes, uid, link->data, cancellable, error); - } - - g_slist_free_full (exceptions, g_free); - } - - if (success && e_cal_component_has_attendees (master) && e_cal_component_has_attachments (master)) { - if (ews_id) { - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-ITEMID", ews_id->id); - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY", ews_id->change_key); - } - - /* In case we have attendees and atachemnts we have to fake update items, - * this is the only way to pass attachments in meeting invite mail */ - success = ecb_ews_modify_item_sync (cbews, removed_indexes, NULL, icalcomp, cancellable, error); - } - - icalcomponent_free (icalcomp); - e_ews_id_free (ews_id); - g_clear_object (&item); - - for (link = (GSList *) instances; link && success; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - if (comp == master) - continue; - - icalcomp = e_cal_component_get_icalcomponent (comp); - - success = ecb_ews_modify_item_sync (cbews, removed_indexes, NULL, icalcomp, cancellable, error); - } - - if (success && items) { - EEwsItem *item = items->data; - const EwsId *item_id; - - item_id = e_ews_item_get_id (item); - *out_new_uid = g_strdup (item_id->id); - } - - g_slist_free_full (items, g_object_unref); - g_hash_table_destroy (removed_indexes); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - g_clear_object (&cal_cache); - e_ews_folder_id_free (fid); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - - return success; -} - -static gboolean -ecb_ews_remove_component_sync (ECalMetaBackend *meta_backend, - EConflictResolution conflict_resolution, - const gchar *uid, - const gchar *extra, - const gchar *object, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalComponent *comp; - EwsId item_id; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (object != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - comp = e_cal_component_new_from_string (object); - if (!comp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - ecb_ews_extract_item_id (comp, &item_id.id, &item_id.change_key); - - success = e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, 0, EWS_HARD_DELETE, - ecb_ews_is_organizer (cbews, comp) ? EWS_SEND_TO_ALL_AND_SAVE_COPY : EWS_SEND_TO_NONE, - EWS_ALL_OCCURRENCES, cancellable, error); - - g_free (item_id.id); - g_free (item_id.change_key); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_object_unref (comp); - - return success; -} - -static void -ecb_ews_discard_alarm_sync (ECalBackendSync *cal_backend_sync, - EDataCal *cal, - GCancellable *cancellable, - const gchar *uid, - const gchar *rid, - const gchar *auid, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - ECalComponent *comp = NULL; - EwsCalendarConvertData convert_data = { 0 }; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cal_backend_sync)); - - cbews = E_CAL_BACKEND_EWS (cal_backend_sync); - - cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbews)); - g_return_if_fail (cal_cache != NULL); - - if (!e_cal_cache_get_component (cal_cache, uid, NULL, &comp, cancellable, NULL) || !comp) { - g_object_unref (cal_cache); - g_propagate_error (error, EDC_ERROR (ObjectNotFound)); - return; - } - - g_object_unref (cal_cache); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) { - g_clear_object (&comp); - return; - } - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - - if (e_cal_component_has_recurrences (comp)) { - gint *index; - - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_OCCURRENCEITEM; - e_cal_component_get_sequence (comp, &index); - - if (index != NULL) { - /*Microsoft is counting the occurrences starting from 1 - where EcalComponent is starting from zerro */ - convert_data.index = *index + 1; - e_cal_component_free_sequence (index); - } else { - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_ITEM; - convert_data.index = -1; - } - } else { - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_ITEM; - convert_data.index = -1; - } - - ecb_ews_extract_item_id (comp, &convert_data.item_id, &convert_data.change_key); - - if (e_ews_connection_update_items_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", NULL, - "SendToNone", NULL, - e_cal_backend_ews_clear_reminder_is_set, - &convert_data, - NULL, - cancellable, - error)) { - icalcomponent *icomp = e_cal_component_get_icalcomponent (comp); - GSList *modified_objects; - - modified_objects = g_slist_prepend (NULL, - e_cal_meta_backend_info_new (icalcomponent_get_uid (icomp), NULL, NULL, - e_cal_util_get_x_property (icomp, "X-EVOLUTION-ITEMID"))); - - /* Refresh the local cache, to have up-to-date ChangeKey */ - e_cal_meta_backend_process_changes_sync (E_CAL_META_BACKEND (cbews), NULL, modified_objects, NULL, cancellable, error); - - g_slist_free_full (modified_objects, e_cal_meta_backend_info_free); - } - - g_object_unref (comp); - g_free (convert_data.item_id); - g_free (convert_data.change_key); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static gboolean -ecb_ews_send_cancellation_email_sync (ECalBackendEws *cbews, - CamelAddress *from, - CamelInternetAddress *recipient, - const gchar *subject, - const gchar *body, - const gchar *calobj, - GCancellable *cancellable, - GError **error) -{ - CamelMimeMessage *message; - CamelContentType *mime_type; - CamelMultipart *multi; - CamelMimePart *text_part, *vcal_part; - gchar *ical_str; - icalcomponent *vcal, *vevent, *vtz; - icalproperty *prop; - icaltimezone *icaltz; - struct icaltimetype dt; - gboolean success; - - vcal = icalcomponent_new (ICAL_VCALENDAR_COMPONENT); - icalcomponent_add_property (vcal, icalproperty_new_version ("2.0")); - icalcomponent_add_property (vcal, icalproperty_new_prodid ("-//Evolution EWS backend//EN")); - icalcomponent_add_property (vcal, icalproperty_new_method (ICAL_METHOD_CANCEL)); - vevent = icalcomponent_new_from_string (calobj); - prop = icalcomponent_get_first_property (vevent, ICAL_STATUS_PROPERTY); - if (prop != NULL) icalcomponent_remove_property (vevent, prop); - icalcomponent_add_property (vevent, icalproperty_new_status (ICAL_STATUS_CANCELLED)); - prop = icalcomponent_get_first_property (vevent, ICAL_METHOD_PROPERTY); - if (prop != NULL) icalcomponent_remove_property (vevent, prop); - dt = e_cal_backend_ews_get_datetime_with_zone (E_TIMEZONE_CACHE (cbews), NULL, vevent, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - icaltz = (icaltimezone *) - (dt.zone ? dt.zone : ecb_ews_get_timezone_from_ical_component (cbews, vevent)); - vtz = icaltimezone_get_component (icaltz); - icalcomponent_add_component (vcal, icalcomponent_new_clone (vtz)); - icalcomponent_add_component (vcal, vevent); - text_part = camel_mime_part_new (); - camel_mime_part_set_content (text_part, body, strlen (body), "text/plain"); - - vcal_part = camel_mime_part_new (); - mime_type = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (vcal_part)); - camel_content_type_set_param (mime_type, "charset", "utf-8"); - camel_content_type_set_param (mime_type, "method", "CANCEL"); - ical_str = icalcomponent_as_ical_string_r ((icalcomponent *) vcal); - camel_mime_part_set_content (vcal_part, ical_str, strlen (ical_str), "text/calendar; method=CANCEL"); - free (ical_str); - - multi = camel_multipart_new (); - camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multi), "multipart/alternative"); - camel_multipart_add_part (multi, text_part); - camel_multipart_set_boundary (multi, NULL); - camel_multipart_add_part (multi, vcal_part); - g_object_unref (text_part); - g_object_unref (vcal_part); - - message = camel_mime_message_new (); - camel_mime_message_set_subject (message, subject); - camel_mime_message_set_from (message, CAMEL_INTERNET_ADDRESS (from)); - camel_mime_message_set_recipients (message, CAMEL_RECIPIENT_TYPE_TO, recipient); - - camel_medium_set_content ((CamelMedium *) message, (CamelDataWrapper *) multi); - g_object_unref (multi); - - success = camel_ews_utils_create_mime_message (cbews->priv->cnc, "SendOnly", NULL, message, NULL, from, NULL, NULL, NULL, cancellable, error); - - g_object_unref (message); - icalcomponent_free (vcal); - - return success; -} - -static void -ecb_ews_receive_objects_no_exchange_mail (ECalBackendEws *cbews, - icalcomponent *vcalendar, - icalcomponent *subcomp, - GSList **ids, - GCancellable *cancellable, - GError **error) -{ - EwsCalendarConvertData convert_data = { 0 }; - EwsFolderId *fid; - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.icalcomp = subcomp; - convert_data.vcalendar = vcalendar; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - fid = e_ews_folder_id_new (cbews->priv->folder_id, NULL, FALSE); - - e_ews_connection_create_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - "SaveOnly", - "SendToNone", - fid, - e_cal_backend_ews_convert_calcomp_to_xml, - &convert_data, - ids, - cancellable, - error); - - e_ews_folder_id_free (fid); -} - -static icalproperty * -find_attendee (icalcomponent *ical_comp, - const gchar *address) -{ - icalproperty *prop; - - if (address == NULL) - return NULL; - - for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY); - prop != NULL; - prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) { - gchar *attendee; - gchar *text; - - attendee = icalproperty_get_value_as_string_r (prop); - - if (!attendee) - continue; - - text = g_strdup (itip_strip_mailto (attendee)); - text = g_strstrip (text); - if (text && !g_ascii_strcasecmp (address, text)) { - g_free (text); - g_free (attendee); - break; - } - g_free (text); - g_free (attendee); - } - - return prop; -} - -static icalproperty * -find_attendee_if_sentby (icalcomponent *ical_comp, - const gchar *address) -{ - icalproperty *prop; - - if (address == NULL) - return NULL; - - for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY); - prop != NULL; - prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) { - icalparameter *param; - const gchar *attendee_sentby; - gchar *text; - - param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER); - if (!param) - continue; - - attendee_sentby = icalparameter_get_sentby (param); - - if (!attendee_sentby) - continue; - - text = g_strdup (itip_strip_mailto (attendee_sentby)); - text = g_strstrip (text); - if (text && !g_ascii_strcasecmp (address, text)) { - g_free (text); - break; - } - g_free (text); - } - - return prop; -} - -static void -ecb_ews_get_rsvp (icalproperty *attendee, - gboolean *out_rsvp_requested) -{ - if (out_rsvp_requested) - *out_rsvp_requested = FALSE; - else - return; - - if (attendee) { - icalparameter *rsvp; - - rsvp = icalproperty_get_first_parameter (attendee, ICAL_RSVP_PARAMETER); - if (rsvp) { - *out_rsvp_requested = icalparameter_get_rsvp (rsvp) == ICAL_RSVP_TRUE; - } - } -} - -static const gchar * -ecb_ews_get_current_user_meeting_reponse (ECalBackendEws *cbews, - icalcomponent *icalcomp, - const gchar *current_user_mail, - gboolean *out_rsvp_requested) -{ - icalproperty *attendee; - const gchar *attendee_str = NULL, *attendee_mail = NULL; - gint attendees_count = 0; - const gchar *response = NULL; - gboolean found = FALSE; - - if (out_rsvp_requested) - *out_rsvp_requested = FALSE; - - attendee = icalcomponent_get_first_property (icalcomp, ICAL_ORGANIZER_PROPERTY); - if (attendee) { - attendee_str = icalproperty_get_organizer (attendee); - - if (attendee_str) { - if (!strncasecmp (attendee_str, "MAILTO:", 7)) - attendee_mail = attendee_str + 7; - else - attendee_mail = attendee_str; - if (attendee_mail && current_user_mail && g_ascii_strcasecmp (attendee_mail, current_user_mail) == 0) { - /* Empty string means it's an organizer, NULL is when not found */ - return ""; - } - } - } - - for (attendee = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY); - attendee != NULL; - attendee = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY), attendees_count++) { - attendee_str = icalproperty_get_attendee (attendee); - - if (attendee_str != NULL) { - if (!strncasecmp (attendee_str, "MAILTO:", 7)) - attendee_mail = attendee_str + 7; - else - attendee_mail = attendee_str; - if (attendee_mail && current_user_mail && g_ascii_strcasecmp (attendee_mail, current_user_mail) == 0) { - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } - } - } - - /* this should not happen, but if the user's configured email does not match the one - used in the invitation, like when the invitation comes to a mailing list... */ - if (!found && attendees_count == 1) { - attendee = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY); - g_return_val_if_fail (attendee != NULL, NULL); - - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } else if (!found) { - ESourceRegistry *registry; - ECalComponent *comp; - - registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbews)); - comp = e_cal_component_new (); - if (e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp))) { - gchar *my_address; - - my_address = itip_get_comp_attendee (registry, comp, NULL); - - attendee = find_attendee (icalcomp, my_address); - if (!attendee) - attendee = find_attendee_if_sentby (icalcomp, my_address); - - if (attendee) { - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } - - g_free (my_address); - } - - g_object_unref (comp); - } - - if (found && !response) { - response = "NEEDS-ACTION"; - } - - return response; -} - -/* changekey can be NULL if you don't want it. itemid cannot. */ -static void -ecb_ews_get_item_accept_id (ECalComponent *comp, - gchar **itemid, - gchar **changekey, - gchar **mail_id) -{ - icalproperty *prop; - gchar *id_item = NULL; - gchar *id_accept = NULL; - gchar *ck = NULL; - - prop = icalcomponent_get_first_property ( - e_cal_component_get_icalcomponent (comp), - ICAL_X_PROPERTY); - while (prop) { - const gchar *x_name, *x_val; - - x_name = icalproperty_get_x_name (prop); - x_val = icalproperty_get_x (prop); - if (!id_item && g_ascii_strcasecmp (x_name, "X-EVOLUTION-ITEMID") == 0) - id_item = g_strdup (x_val); - else if (!id_accept && g_ascii_strcasecmp (x_name, "X-EVOLUTION-ACCEPT-ID") == 0) - id_accept = g_strdup (x_val); - else if (changekey && !ck && !g_ascii_strcasecmp (x_name, "X-EVOLUTION-CHANGEKEY")) - ck = g_strdup (x_val); - - prop = icalcomponent_get_next_property ( - e_cal_component_get_icalcomponent (comp), - ICAL_X_PROPERTY); - } - - if (!id_item) - id_item = g_strdup (id_accept); - - *itemid = id_item; - *mail_id = id_accept; - if (changekey) - *changekey = ck; -} - -static gboolean -ecb_ews_do_method_request_publish_reply (ECalBackendEws *cbews, - icalcomponent *vcalendar, - ECalComponent *comp, - icalcomponent *subcomp, - const gchar *response_type, - const gchar *user_email, - gboolean rsvp_requested, - GCancellable *cancellable, - GError **error) -{ - GError *local_error = NULL; - gchar *item_id = NULL; - gchar *change_key = NULL; - gchar *mail_id = NULL; - gint pass = 0; - GSList *ids = NULL; - - if (!response_type && - e_cal_util_component_has_organizer (subcomp) && - e_cal_util_component_has_attendee (subcomp)) { - g_set_error (error, E_DATA_CAL_ERROR, UnknownUser, _("Cannot find user “%s” between attendees"), user_email ? user_email : "NULL"); - return FALSE; - } - - if (response_type && *response_type) - ecb_ews_get_item_accept_id (comp, &item_id, &change_key, &mail_id); - else - response_type = NULL; - - while (pass < 2) { - /*in case we do not have item id we will create item with mime content only*/ - if (!item_id || (response_type && g_ascii_strcasecmp (response_type, "NEEDS-ACTION") == 0)) { - ecb_ews_receive_objects_no_exchange_mail (cbews, vcalendar, subcomp, &ids, cancellable, &local_error); - } else { - EwsCalendarConvertData convert_data = { 0 }; - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.response_type = (gchar *) response_type; - convert_data.item_id = item_id; - convert_data.change_key = change_key; - - e_ews_connection_create_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - rsvp_requested ? "SendAndSaveCopy" : "SaveOnly", - rsvp_requested ? NULL : "SendToNone", - NULL, - e_cal_backend_ews_prepare_accept_item_request, - &convert_data, - &ids, - cancellable, - &local_error); - } - - if (pass == 0 && mail_id != NULL && item_id != NULL && - g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_ITEMNOTFOUND)) { - /* - * maybe the associated accept calendar item changed - * on the server, thus retry with updated values - */ - GSList *my_ids = NULL; - - g_clear_error (&local_error); - - my_ids = g_slist_append (my_ids, mail_id); - - if (e_ews_connection_get_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - my_ids, - "AllProperties", - NULL, - FALSE, - NULL, - E_EWS_BODY_TYPE_ANY, - &ids, - NULL, - NULL, - cancellable, - &local_error) && - ids != NULL && - ids->data != NULL) { - EEwsItem *item = ids->data; - const EwsId *id = e_ews_item_get_id (item); - - if (id != NULL && g_strcmp0 (id->id, mail_id) == 0) { - const EwsId *cal_item_accepted_id; - - cal_item_accepted_id = e_ews_item_get_calendar_item_accept_id (item); - if (cal_item_accepted_id != NULL) { - g_clear_error (&local_error); - pass++; - - g_free (item_id); - g_free (change_key); - - item_id = g_strdup (cal_item_accepted_id->id); - change_key = g_strdup (cal_item_accepted_id->change_key); - } - } - } - - g_slist_free (my_ids); - - if (pass == 0) - break; - } else { - break; - } - - g_slist_free_full (ids, g_object_unref); - ids = NULL; - } - - if (local_error == NULL) { - icalproperty *transp; - - transp = icalcomponent_get_first_property (subcomp, ICAL_TRANSP_PROPERTY); - - if (g_strcmp0 (icalproperty_get_value_as_string (transp), "TRANSPARENT") == 0 && - g_strcmp0 (response_type, "ACCEPTED") == 0) { - EwsCalendarConvertData convert_data = { 0 }; - GSList *l; - - /* - * user can accept meeting but mark it as free in it's calendar - * the following code is updating the exchange meeting status to free - */ - for (l = ids; l != NULL; l = g_slist_next (l)) { - EEwsItem *item = l->data; - - if (item != NULL) { - const EwsId *id = e_ews_item_get_id (item); - - convert_data.item_id = id->id; - convert_data.change_key = id->change_key; - break; - } - } - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.vcalendar = vcalendar; - - e_ews_connection_update_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", - NULL, - "SendToNone", - NULL, - e_cal_backend_ews_prepare_set_free_busy_status, - &convert_data, - NULL, - cancellable, - &local_error); - } - } - - if (local_error != NULL) - g_propagate_error (error, local_error); - - g_free (item_id); - g_free (change_key); - g_free (mail_id); - g_slist_free_full (ids, g_object_unref); - - return !local_error; -} - -static void -ecb_ews_receive_objects_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *calobj, - GError **error) -{ - ECalBackendEws *cbews; - ECalBackend *cal_backend; - CamelEwsSettings *ews_settings; - icalcomponent *icalcomp, *subcomp; - icalcomponent_kind kind; - gchar *user_email; - gboolean success = TRUE, do_refresh = FALSE; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - icalcomp = calobj ? icalparser_parse_string (calobj) : NULL; - - if (!icalcomp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - /* make sure ical data we parse is actually a vCalendar component */ - if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) { - icalcomponent_free (icalcomp); - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - cal_backend = E_CAL_BACKEND (cbews); - kind = e_cal_backend_get_kind (cal_backend); - - ews_settings = ecb_ews_get_collection_settings (cbews); - user_email = camel_ews_settings_dup_email (ews_settings); - - switch (icalcomponent_get_method (icalcomp)) { - case ICAL_METHOD_REQUEST: - case ICAL_METHOD_PUBLISH: - case ICAL_METHOD_REPLY: - for (subcomp = icalcomponent_get_first_component (icalcomp, kind); - subcomp && success; - subcomp = icalcomponent_get_next_component (icalcomp, kind)) { - ECalComponent *comp; - const gchar *response_type; - gboolean rsvp_requested = FALSE; - - /* getting a data for meeting request response */ - response_type = ecb_ews_get_current_user_meeting_reponse (cbews, subcomp, user_email, &rsvp_requested); - - comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (subcomp)); - - success = ecb_ews_do_method_request_publish_reply (cbews, icalcomp, comp, subcomp, response_type, user_email, rsvp_requested, cancellable, error); - - do_refresh = TRUE; - - g_object_unref (comp); - } - break; - case ICAL_METHOD_COUNTER: - /* - * this is a new time proposal mail from one of the attendees - * if we decline the proposal, nothing have to be done - * if we accept it we will call to modify_object - */ - for (subcomp = icalcomponent_get_first_component (icalcomp, kind); - subcomp && success; - subcomp = icalcomponent_get_next_component (icalcomp, kind)) { - const gchar *response_type; - - /* getting a data for meeting request response */ - response_type = ecb_ews_get_current_user_meeting_reponse (cbews, subcomp, user_email, NULL); - - if (g_strcmp0 (response_type, "ACCEPTED") == 0) { - gchar **split_subject; - icalproperty *summary; - - /* we have to edit the meeting subject to remove exchange header */ - summary = icalcomponent_get_first_property (subcomp, ICAL_SUMMARY_PROPERTY); - split_subject = - g_strsplit (icalproperty_get_value_as_string (summary), ":", -1); - icalproperty_set_value_from_string (summary, split_subject[1] , "NO"); - g_strfreev (split_subject); - - success = ecb_ews_modify_item_sync (cbews, NULL, NULL, subcomp, cancellable, error); - - do_refresh = TRUE; - } - } - break; - default: - break; - } - - icalcomponent_free (icalcomp); - g_free (user_email); - - if (success && do_refresh) - e_cal_meta_backend_schedule_refresh (E_CAL_META_BACKEND (cbews)); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static void -ecb_ews_send_objects_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *calobj, - GSList **users, - gchar **modified_calobj, - GError **error) -{ - ECalBackendEws *cbews; - icalcomponent_kind kind; - icalcomponent *icalcomp, *subcomp = NULL; - gchar *subcalobj; - gboolean success = TRUE; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - icalcomp = calobj ? icalparser_parse_string (calobj) : NULL; - - /* make sure data was parsed properly */ - if (!icalcomp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - /* make sure ical data we parse is actually an vcal component */ - if ((icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) && (icalcomponent_isa (icalcomp) != ICAL_VEVENT_COMPONENT)) { - icalcomponent_free (icalcomp); - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - - if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) { - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - subcomp = icalcomponent_get_first_component (icalcomp, kind); - } - if (icalcomponent_isa (icalcomp) == ICAL_VEVENT_COMPONENT) - subcomp = icalcomp; - while (subcomp && success) { - const gchar *new_body_content = NULL, *subject = NULL, *org_email = NULL; - const gchar *org = NULL, *attendee = NULL; - icalproperty *prop, *org_prop = NULL; - CamelInternetAddress *org_addr = camel_internet_address_new (); - - new_body_content = e_cal_util_get_x_property (subcomp, "X-EVOLUTION-RETRACT-COMMENT"); - subject = icalproperty_get_value_as_string (icalcomponent_get_first_property (subcomp, ICAL_SUMMARY_PROPERTY)); - - org_prop = icalcomponent_get_first_property (subcomp, ICAL_ORGANIZER_PROPERTY); - org = icalproperty_get_organizer (org_prop); - if (!g_ascii_strncasecmp (org, "MAILTO:", 7)) - org_email = (org) + 7; - else - org_email = org; - - camel_internet_address_add (org_addr, icalproperty_get_parameter_as_string (org_prop, "CN"), org_email); - - /* iterate over every attendee property */ - for (prop = icalcomponent_get_first_property (subcomp, ICAL_ATTENDEE_PROPERTY); - prop && success; - prop = icalcomponent_get_next_property (subcomp, ICAL_ATTENDEE_PROPERTY)) { - CamelInternetAddress *attendee_addr = camel_internet_address_new (); - attendee = icalproperty_get_attendee (prop); - if (g_ascii_strcasecmp (org_email, attendee) == 0) continue; - if (!g_ascii_strncasecmp (attendee, "mailto:", 7)) attendee = (attendee) + 7; - - subcalobj = icalcomponent_as_ical_string_r (subcomp); - camel_internet_address_add (attendee_addr, icalproperty_get_parameter_as_string (prop, "CN"), attendee); - success = ecb_ews_send_cancellation_email_sync (cbews, CAMEL_ADDRESS (org_addr), attendee_addr, - subject, new_body_content, subcalobj, cancellable, error); - g_object_unref (attendee_addr); - free (subcalobj); - } - - g_object_unref (org_addr); - subcomp = icalcomponent_get_next_component (icalcomp, kind); - } - - icalcomponent_free (icalcomp); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static void -ecb_ews_get_free_busy_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const GSList *users, - time_t start, - time_t end, - GSList **freebusyobjs, - GError **error) -{ - ECalBackendEws *cbews; - EEWSFreeBusyData fbdata = { 0 }; - GSList *freebusy = NULL; - gboolean success; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - g_return_if_fail (freebusyobjs != NULL); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - *freebusyobjs = NULL; - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - /* EWS can support only 100 identities, which is the maximum number of identities that the Web service method can request - see http://msdn.microsoft.com / en - us / library / aa564001 % 28v = EXCHG.140 % 29.aspx */ - if (g_slist_length ((GSList *) users) > 100) { - g_propagate_error (error, EDC_ERROR (SearchSizeLimitExceeded)); - return; - } - - fbdata.period_start = start; - fbdata.period_end = end; - fbdata.user_mails = (GSList *) users; - - success = e_ews_connection_get_free_busy_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - e_ews_cal_utils_prepare_free_busy_request, &fbdata, - &freebusy, cancellable, error); - - if (success) { - GSList *fblink, *ulink; - - for (fblink = freebusy, ulink = (GSList *) users; - fblink && ulink; - fblink = g_slist_next (fblink), ulink = g_slist_next (ulink)) { - icalcomponent *icalcomp = fblink->data; - gchar *mailto; - - /* add attendee property */ - mailto = g_strconcat ("mailto:", ulink->data, NULL); - icalcomponent_add_property (icalcomp, icalproperty_new_attendee (mailto)); - g_free (mailto); - - *freebusyobjs = g_slist_prepend (*freebusyobjs, icalcomponent_as_ical_string_r (icalcomp)); - } - - *freebusyobjs = g_slist_reverse (*freebusyobjs); - } - - g_slist_free_full (freebusy, (GDestroyNotify) icalcomponent_free); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static gchar * -ecb_ews_get_backend_property (ECalBackend *cal_backend, - const gchar *prop_name) -{ - ECalBackendEws *cbews; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cal_backend), NULL); - g_return_val_if_fail (prop_name != NULL, NULL); - - cbews = E_CAL_BACKEND_EWS (cal_backend); - - if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) { - return g_strjoin ( - ",", - CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS, - CAL_STATIC_CAPABILITY_NO_AUDIO_ALARMS, - CAL_STATIC_CAPABILITY_NO_PROCEDURE_ALARMS, - CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY, - CAL_STATIC_CAPABILITY_REMOVE_ALARMS, - CAL_STATIC_CAPABILITY_NO_THISANDPRIOR, - CAL_STATIC_CAPABILITY_NO_THISANDFUTURE, - CAL_STATIC_CAPABILITY_NO_CONV_TO_ASSIGN_TASK, - CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT, - CAL_STATIC_CAPABILITY_SAVE_SCHEDULES, - CAL_STATIC_CAPABILITY_NO_ALARM_AFTER_START, - CAL_STATIC_CAPABILITY_NO_MEMO_START_DATE, - CAL_STATIC_CAPABILITY_ALL_DAY_EVENT_AS_TIME, - CAL_STATIC_CAPABILITY_TASK_DATE_ONLY, - e_cal_meta_backend_get_capabilities (E_CAL_META_BACKEND (cbews)), - NULL); - } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS)) { - /* return email address of the person who opened the calendar */ - CamelEwsSettings *ews_settings; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - return camel_ews_settings_dup_email (ews_settings); - } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS)) { - /* ews does not support email based alarms */ - return NULL; - } - - /* Chain up to parent's method. */ - return E_CAL_BACKEND_CLASS (e_cal_backend_ews_parent_class)->get_backend_property (cal_backend, prop_name); -} - -static void -ecb_ews_get_timezone_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *tzid, - gchar **tzobject, - GError **error) -{ - GError *local_error = NULL; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - g_return_if_fail (tzid != NULL); - g_return_if_fail (tzobject != NULL); - - *tzobject = NULL; - - E_CAL_BACKEND_SYNC_CLASS (e_cal_backend_ews_parent_class)->get_timezone_sync (sync_backend, cal, cancellable, tzid, tzobject, &local_error); - - if (!*tzobject) { - /* The timezone can be sometimes the Windows zone, try to convert it to libical */ - const gchar *ical_location = e_cal_backend_ews_tz_util_get_ical_equivalent (tzid); - - if (ical_location) - E_CAL_BACKEND_SYNC_CLASS (e_cal_backend_ews_parent_class)->get_timezone_sync (sync_backend, cal, cancellable, ical_location, tzobject, NULL); - } - - if (*tzobject) - g_clear_error (&local_error); - else if (local_error) - g_propagate_error (error, local_error); -} - -static gboolean -ecb_ews_get_destination_address (EBackend *backend, - gchar **host, - guint16 *port) -{ - CamelEwsSettings *ews_settings; - SoupURI *soup_uri; - gchar *host_url; - gboolean result = FALSE; - - g_return_val_if_fail (port != NULL, FALSE); - g_return_val_if_fail (host != NULL, FALSE); - - /* Sanity checking */ - if (!e_cal_backend_get_registry (E_CAL_BACKEND (backend)) || - !e_backend_get_source (backend)) - return FALSE; - - ews_settings = ecb_ews_get_collection_settings (E_CAL_BACKEND_EWS (backend)); - g_return_val_if_fail (ews_settings != NULL, FALSE); - - host_url = camel_ews_settings_dup_hosturl (ews_settings); - g_return_val_if_fail (host_url != NULL, FALSE); - - soup_uri = soup_uri_new (host_url); - if (soup_uri) { - *host = g_strdup (soup_uri_get_host (soup_uri)); - *port = soup_uri_get_port (soup_uri); - - result = *host && **host; - if (!result) { - g_free (*host); - *host = NULL; - } - - soup_uri_free (soup_uri); - } - - g_free (host_url); - - return result; -} - -static gchar * -ecb_ews_dup_component_revision (ECalCache *cal_cache, - icalcomponent *icalcomp, - gpointer user_data) -{ - g_return_val_if_fail (icalcomp != NULL, NULL); - - return e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY"); -} - -static void -ecb_ews_constructed (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - ECalCache *cal_cache; - gchar *cache_dirname; - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->constructed (object); - - /* Reset the connectable, it steals data from Authentication extension, - where is written incorrect address */ - e_backend_set_connectable (E_BACKEND (object), NULL); - - cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbews)); - g_return_if_fail (cal_cache != NULL); - - cache_dirname = g_path_get_dirname (e_cache_get_filename (E_CACHE (cal_cache))); - g_signal_connect (cal_cache, "dup-component-revision", G_CALLBACK (ecb_ews_dup_component_revision), NULL); - - g_clear_object (&cal_cache); - - cbews->priv->attachments_dir = g_build_filename (cache_dirname, "attachments", NULL); - g_mkdir_with_parents (cbews->priv->attachments_dir, 0777); - - g_free (cache_dirname); -} - -static void -ecb_ews_dispose (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - g_clear_object (&cbews->priv->cnc); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->dispose (object); -} - -static void -ecb_ews_finalize (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - - g_free (cbews->priv->folder_id); - g_free (cbews->priv->attachments_dir); - - g_rec_mutex_clear (&cbews->priv->cnc_lock); - - e_cal_backend_ews_unref_windows_zones (); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->finalize (object); -} - -static void -e_cal_backend_ews_init (ECalBackendEws *cbews) -{ - cbews->priv = G_TYPE_INSTANCE_GET_PRIVATE (cbews, E_TYPE_CAL_BACKEND_EWS, ECalBackendEwsPrivate); - - g_rec_mutex_init (&cbews->priv->cnc_lock); - - e_cal_backend_ews_populate_windows_zones (); -} - -static void -e_cal_backend_ews_class_init (ECalBackendEwsClass *klass) -{ - GObjectClass *object_class; - EBackendClass *backend_class; - ECalBackendClass *cal_backend_class; - ECalBackendSyncClass *cal_backend_sync_class; - ECalMetaBackendClass *cal_meta_backend_class; - - g_type_class_add_private (klass, sizeof (ECalBackendEwsPrivate)); - - cal_meta_backend_class = E_CAL_META_BACKEND_CLASS (klass); - cal_meta_backend_class->connect_sync = ecb_ews_connect_sync; - cal_meta_backend_class->disconnect_sync = ecb_ews_disconnect_sync; - cal_meta_backend_class->get_changes_sync = ecb_ews_get_changes_sync; - cal_meta_backend_class->load_component_sync = ecb_ews_load_component_sync; - cal_meta_backend_class->save_component_sync = ecb_ews_save_component_sync; - cal_meta_backend_class->remove_component_sync = ecb_ews_remove_component_sync; - - cal_backend_sync_class = E_CAL_BACKEND_SYNC_CLASS (klass); - cal_backend_sync_class->discard_alarm_sync = ecb_ews_discard_alarm_sync; - cal_backend_sync_class->receive_objects_sync = ecb_ews_receive_objects_sync; - cal_backend_sync_class->send_objects_sync = ecb_ews_send_objects_sync; - cal_backend_sync_class->get_free_busy_sync = ecb_ews_get_free_busy_sync; - cal_backend_sync_class->get_timezone_sync = ecb_ews_get_timezone_sync; - - cal_backend_class = E_CAL_BACKEND_CLASS (klass); - cal_backend_class->get_backend_property = ecb_ews_get_backend_property; - - backend_class = E_BACKEND_CLASS (klass); - backend_class->get_destination_address = ecb_ews_get_destination_address; - - object_class = G_OBJECT_CLASS (klass); - object_class->constructed = ecb_ews_constructed; - object_class->dispose = ecb_ews_dispose; - object_class->finalize = ecb_ews_finalize; -} diff --git a/src/calendar/e-cal-backend-ews.c.unknown-responsetype-as-needs-action b/src/calendar/e-cal-backend-ews.c.unknown-responsetype-as-needs-action deleted file mode 100644 index 1890a54..0000000 --- a/src/calendar/e-cal-backend-ews.c.unknown-responsetype-as-needs-action +++ /dev/null @@ -1,3903 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com) - * - * 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. - * - * 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -#include -#include -#include -#include - -#include -#include - -#include "server/e-source-ews-folder.h" -#include "server/e-ews-calendar-utils.h" -#include "server/e-ews-connection-utils.h" -#include "server/e-ews-camel-common.h" - -#include "e-cal-backend-ews.h" -#include "e-cal-backend-ews-utils.h" - -#ifndef O_BINARY -#define O_BINARY 0 -#endif - -#ifdef G_OS_WIN32 -#ifdef gmtime_r -#undef gmtime_r -#endif - -/* The gmtime() in Microsoft's C library is MT-safe */ -#define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0) -#endif - -/* Private part of the CalBackendEws structure */ -struct _ECalBackendEwsPrivate { - GRecMutex cnc_lock; - EEwsConnection *cnc; - gchar *folder_id; - - guint subscription_key; - gboolean is_freebusy_calendar; - - gchar *attachments_dir; -}; - -#define X_EWS_ORIGINAL_COMP "X-EWS-ORIGINAL-COMP" - -#define EWS_MAX_FETCH_COUNT 100 - -#define GET_ITEMS_SYNC_PROPERTIES \ - "item:Attachments" \ - " item:Categories" \ - " item:HasAttachments" \ - " item:MimeContent" \ - " calendar:UID" \ - " calendar:Resources" \ - " calendar:ModifiedOccurrences" \ - " calendar:IsMeeting" \ - " calendar:IsResponseRequested" \ - " calendar:MyResponseType" \ - " calendar:RequiredAttendees" \ - " calendar:OptionalAttendees" - -#define GET_ITEMS_SYNC_PROPERTIES_2007 \ - GET_ITEMS_SYNC_PROPERTIES \ - " calendar:TimeZone" - -#define GET_ITEMS_SYNC_PROPERTIES_2010 \ - GET_ITEMS_SYNC_PROPERTIES \ - " calendar:StartTimeZone" \ - " calendar:EndTimeZone" - -#define e_data_cal_error_if_fail(expr, _code) \ - G_STMT_START { \ - if (G_LIKELY (expr)) { \ - } else { \ - g_log (G_LOG_DOMAIN, \ - G_LOG_LEVEL_CRITICAL, \ - "file %s: line %d (%s): assertion `%s' failed", \ - __FILE__, __LINE__, G_STRFUNC, #expr); \ - g_set_error (&error, E_DATA_CAL_ERROR, (_code), \ - "file %s: line %d (%s): assertion `%s' failed", \ - __FILE__, __LINE__, G_STRFUNC, #expr); \ - goto exit; \ - } \ - } G_STMT_END - -G_DEFINE_TYPE (ECalBackendEws, e_cal_backend_ews, E_TYPE_CAL_META_BACKEND) - -static CamelEwsSettings * -ecb_ews_get_collection_settings (ECalBackendEws *cbews) -{ - ESource *source; - ESource *collection; - ESourceCamel *extension; - ESourceRegistry *registry; - CamelSettings *settings; - const gchar *extension_name; - - source = e_backend_get_source (E_BACKEND (cbews)); - registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbews)); - - extension_name = e_source_camel_get_extension_name ("ews"); - e_source_camel_generate_subtype ("ews", CAMEL_TYPE_EWS_SETTINGS); - - /* The collection settings live in our parent data source. */ - collection = e_source_registry_find_extension ( - registry, source, extension_name); - g_return_val_if_fail (collection != NULL, NULL); - - extension = e_source_get_extension (collection, extension_name); - settings = e_source_camel_get_settings (extension); - - g_object_unref (collection); - - return CAMEL_EWS_SETTINGS (settings); -} - -static void -ecb_ews_convert_error_to_edc_error (GError **perror) -{ - GError *error = NULL; - - if (!perror || !*perror || (*perror)->domain == E_DATA_CAL_ERROR) - return; - - if ((*perror)->domain == EWS_CONNECTION_ERROR) { - switch ((*perror)->code) { - case EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED: - error = EDC_ERROR_EX (AuthenticationFailed, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_FOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_MANAGEDFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PARENTFOLDERNOTFOUND: - case EWS_CONNECTION_ERROR_PUBLICFOLDERSERVERNOTFOUND: - error = EDC_ERROR_EX (NoSuchCal, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_EVENTNOTFOUND: - case EWS_CONNECTION_ERROR_ITEMNOTFOUND: - error = EDC_ERROR_EX (ObjectNotFound, (*perror)->message); - break; - case EWS_CONNECTION_ERROR_UNAVAILABLE: - g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND, (*perror)->message); - break; - } - - if (!error) - error = EDC_ERROR_EX (OtherError, (*perror)->message); - } - - if (error) { - g_error_free (*perror); - *perror = error; - } -} - -static void -ecb_ews_maybe_disconnect_sync (ECalBackendEws *cbews, - GError **in_perror, - GCancellable *cancellable) -{ - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cbews)); - - if (in_perror && g_error_matches (*in_perror, E_DATA_CAL_ERROR, AuthenticationFailed)) { - e_cal_meta_backend_disconnect_sync (E_CAL_META_BACKEND (cbews), cancellable, NULL); - e_backend_schedule_credentials_required (E_BACKEND (cbews), E_SOURCE_CREDENTIALS_REASON_REJECTED, NULL, 0, NULL, NULL, G_STRFUNC); - } -} - -static void -ecb_ews_server_notification_cb (ECalBackendEws *cbews, - GSList *events, - EEwsConnection *cnc) -{ - GSList *link; - gboolean update_folder = FALSE; - - g_return_if_fail (cbews != NULL); - g_return_if_fail (cbews->priv != NULL); - - for (link = events; link && !update_folder; link = g_slist_next (link)) { - EEwsNotificationEvent *event = link->data; - - switch (event->type) { - case E_EWS_NOTIFICATION_EVENT_CREATED: - case E_EWS_NOTIFICATION_EVENT_DELETED: - case E_EWS_NOTIFICATION_EVENT_MODIFIED: - g_rec_mutex_lock (&cbews->priv->cnc_lock); - if (g_strcmp0 (event->folder_id, cbews->priv->folder_id) == 0) - update_folder = TRUE; - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - break; - case E_EWS_NOTIFICATION_EVENT_MOVED: - case E_EWS_NOTIFICATION_EVENT_COPIED: - g_rec_mutex_lock (&cbews->priv->cnc_lock); - if (g_strcmp0 (event->folder_id, cbews->priv->folder_id) == 0 || - g_strcmp0 (event->old_folder_id, cbews->priv->folder_id) == 0) - update_folder = TRUE; - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - break; - default: - return; - } - } - - if (update_folder) - e_cal_meta_backend_schedule_refresh (E_CAL_META_BACKEND (cbews)); -} - -static void -ecb_ews_unset_connection (ECalBackendEws *cbews) -{ - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cbews)); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->cnc) { - e_ews_connection_set_disconnected_flag (cbews->priv->cnc, TRUE); - - g_signal_handlers_disconnect_by_func (cbews->priv->cnc, ecb_ews_server_notification_cb, cbews); - - if (cbews->priv->subscription_key != 0) { - e_ews_connection_disable_notifications_sync ( - cbews->priv->cnc, - cbews->priv->subscription_key); - cbews->priv->subscription_key = 0; - } - } - - g_clear_object (&cbews->priv->cnc); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); -} - -static icaltimezone * -ecb_ews_get_timezone (ETimezoneCache *timezone_cache, - const gchar *msdn_tzid, - const gchar *tzid, - const gchar *evo_ews_tzid) -{ - icaltimezone *zone = NULL; - const gchar *evo_ews_msdn_tzid; - - zone = e_timezone_cache_get_timezone (timezone_cache, tzid); - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (tzid); - - if (g_strcmp0 (tzid, evo_ews_tzid) == 0) - return zone; - - if (evo_ews_tzid != NULL) { - evo_ews_msdn_tzid = e_cal_backend_ews_tz_util_get_msdn_equivalent (evo_ews_tzid); - - if (g_strcmp0 (msdn_tzid, evo_ews_msdn_tzid) == 0) { - zone = e_timezone_cache_get_timezone (timezone_cache, evo_ews_tzid); - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (evo_ews_tzid); - } - } - - return zone; -} - -static icalparameter * -ecb_ews_responsetype_to_partstat (const gchar *responsetype) -{ - icalparameter *param = NULL; - - if (!responsetype) - return icalparameter_new_partstat (ICAL_PARTSTAT_NONE); - - if (g_ascii_strcasecmp (responsetype, "Organizer") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_ACCEPTED); - else if (g_ascii_strcasecmp (responsetype, "Tentative") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_TENTATIVE); - else if (g_ascii_strcasecmp (responsetype, "Accept") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_ACCEPTED); - else if (g_ascii_strcasecmp (responsetype, "Decline") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_DECLINED); - else if (g_ascii_strcasecmp (responsetype, "NoResponseReceived") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NEEDSACTION); - else if (g_ascii_strcasecmp (responsetype, "Unknown") == 0) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NONE); - - if (!param) - param = icalparameter_new_partstat (ICAL_PARTSTAT_NONE); - - return param; -} - -static ECalComponent * -ecb_ews_item_to_component_sync (ECalBackendEws *cbews, - EEwsItem *item, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *res_component = NULL; - ETimezoneCache *timezone_cache; - icalcomponent_kind kind; - EEwsItemType item_type; - icalcomponent *icalcomp, *vcomp; - icaltimezone *utc_zone = icaltimezone_get_utc_timezone (); - CamelEwsSettings *ews_settings; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), NULL); - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - timezone_cache = E_TIMEZONE_CACHE (cbews); - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - ews_settings = ecb_ews_get_collection_settings (cbews); - - item_type = e_ews_item_get_item_type (item); - if (item_type == E_EWS_ITEM_TYPE_TASK || item_type == E_EWS_ITEM_TYPE_MEMO) { - icalproperty *icalprop; - icaltimetype due_date, start_date, complete_date, created; - icalproperty_status status = ICAL_STATUS_NONE; - icalproperty_class class = ICAL_CLASS_NONE; - const gchar *ews_task_status, *sensitivity; - EwsImportance item_importance; - gint priority = 5; - gboolean has_this_date = FALSE; - - vcomp = icalcomponent_new (ICAL_VCALENDAR_COMPONENT); - /*subject*/ - icalcomp = icalcomponent_new (item_type == E_EWS_ITEM_TYPE_TASK ? ICAL_VTODO_COMPONENT : ICAL_VJOURNAL_COMPONENT); - icalprop = icalproperty_new_summary (e_ews_item_get_subject (item)); - icalcomponent_add_property (icalcomp, icalprop); - - /*date time created*/ - created = icaltime_from_timet_with_zone (e_ews_item_get_date_created (item), 0, utc_zone); - icalprop = icalproperty_new_created (created); - icalcomponent_add_property (icalcomp, icalprop); - - /*sensitivity*/ - sensitivity = e_ews_item_get_sensitivity (item); - if (g_strcmp0 (sensitivity, "Normal") == 0) - class = ICAL_CLASS_PUBLIC; - else if (g_strcmp0 (sensitivity, "Private") == 0) - class = ICAL_CLASS_PRIVATE; - else if ((g_strcmp0 (sensitivity, "Confidential") == 0) || - (g_strcmp0 (sensitivity, "Personal") == 0)) - class = ICAL_CLASS_CONFIDENTIAL; - icalprop = icalproperty_new_class (class); - icalcomponent_add_property (icalcomp, icalprop); - - /*description*/ - icalprop = icalproperty_new_description (e_ews_item_get_body (item)); - icalcomponent_add_property (icalcomp, icalprop); - - /*task assaingments*/ - if (e_ews_item_get_delegator (item) != NULL) { - const gchar *task_owner = e_ews_item_get_delegator (item); - GSList *mailboxes = NULL, *l; - gboolean includes_last_item; - gchar *mailtoname, *user_email; - icalparameter *param; - - /*The task owner according to Exchange is current user, even that the task was assigned by - *someone else. I'm making the current user attendee and task delegator will be a task organizer */ - - user_email = camel_ews_settings_dup_email (ews_settings); - mailtoname = g_strdup_printf ("mailto:%s", user_email); - icalprop = icalproperty_new_attendee (mailtoname); - g_free (mailtoname); - g_free (user_email); - - param = icalparameter_new_cn (e_ews_item_get_owner (item)); - icalproperty_add_parameter (icalprop, param); - icalcomponent_add_property (icalcomp, icalprop); - - /* get delegator mail box*/ - e_ews_connection_resolve_names_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, task_owner, - EWS_SEARCH_AD, NULL, FALSE, &mailboxes, NULL, - &includes_last_item, cancellable, error); - - for (l = mailboxes; l != NULL; l = g_slist_next (l)) { - EwsMailbox *mb = l->data; - - mailtoname = g_strdup_printf ("mailto:%s", mb->email); - icalprop = icalproperty_new_organizer (mailtoname); - param = icalparameter_new_cn (mb->name); - icalproperty_add_parameter (icalprop, param); - icalcomponent_add_property (icalcomp, icalprop); - - g_free (mailtoname); - e_ews_mailbox_free (mb); - } - g_slist_free (mailboxes); - } - - if (item_type == E_EWS_ITEM_TYPE_TASK) { - icaltimezone *user_timezone = calendar_config_get_icaltimezone (); - const gchar *percent_complete; - - /*start date*/ - has_this_date = FALSE; - e_ews_item_task_has_start_date (item, &has_this_date); - if (has_this_date) { - start_date = icaltime_from_timet_with_zone (e_ews_item_get_start_date (item), 0, user_timezone); - start_date.is_date = 1; - icalprop = icalproperty_new_dtstart (start_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*status*/ - ews_task_status = e_ews_item_get_status (item); - if (g_strcmp0 (ews_task_status, "NotStarted") != 0) { - if (g_strcmp0 (ews_task_status, "Completed") == 0) - status = ICAL_STATUS_COMPLETED; - else if (g_strcmp0 (ews_task_status, "InProgress") == 0) - status = ICAL_STATUS_INPROCESS; - else if (g_strcmp0 (ews_task_status, "WaitingOnOthers") == 0) - status = ICAL_STATUS_NEEDSACTION; - else if (g_strcmp0 (ews_task_status, "Deferred") == 0) - status = ICAL_STATUS_CANCELLED; - icalprop = icalproperty_new_status (status); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*precent complete*/ - percent_complete = e_ews_item_get_percent_complete (item); - icalprop = icalproperty_new_percentcomplete (atoi (percent_complete ? percent_complete : "0")); - icalcomponent_add_property (icalcomp, icalprop); - - /*due date*/ - e_ews_item_task_has_due_date (item, &has_this_date); - if (has_this_date) { - due_date = icaltime_from_timet_with_zone (e_ews_item_get_due_date (item), 0, user_timezone); - due_date.is_date = 1; - icalprop = icalproperty_new_due (due_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*complete date*/ - has_this_date = FALSE; - e_ews_item_task_has_complete_date (item, &has_this_date); - if (has_this_date) { - complete_date = icaltime_from_timet_with_zone (e_ews_item_get_complete_date (item), 0, user_timezone); - icalprop = icalproperty_new_completed (complete_date); - icalcomponent_add_property (icalcomp, icalprop); - } - - /*priority*/ - item_importance = e_ews_item_get_importance (item); - if (item_importance == EWS_ITEM_HIGH) - priority = 3; - else if (item_importance == EWS_ITEM_LOW) - priority = 7; - icalprop = icalproperty_new_priority (priority); - icalcomponent_add_property (icalcomp, icalprop); - } - - icalcomponent_add_component (vcomp, icalcomp); - } else { - struct icaltimetype dt; - const gchar *mime_content; - const gchar *tzid; - gboolean timezone_set = FALSE; - - mime_content = e_ews_item_get_mime_content (item); - vcomp = mime_content && *mime_content ? icalparser_parse_string (mime_content) : NULL; - - if (!vcomp && mime_content && *mime_content) { - const gchar *begin_vcalendar, *end_vcalendar; - - /* Workaround Exchange 2016 error, which returns invalid iCalendar object (without 'END:VCALENDAR'), - when the event has at least one detached instance. */ - begin_vcalendar = camel_strstrcase (mime_content, "BEGIN:VCALENDAR"); - end_vcalendar = camel_strstrcase (mime_content, "END:VCALENDAR"); - - /* If it exists, then it should be alone on a separate line */ - if (!(begin_vcalendar && (begin_vcalendar == mime_content || begin_vcalendar[-1] == '\n') && - (begin_vcalendar[15 /* strlen ("BEGIN:VCALENDAR") */] == '\r' || begin_vcalendar[15] == '\n'))) - begin_vcalendar = NULL; - - /* If it exists, then it should be alone on a separate line and not at the very beginning of the mime_content */ - if (!(end_vcalendar && end_vcalendar > mime_content && end_vcalendar[-1] == '\n' && - (end_vcalendar[13 /* strlen ("END:VCALENDAR") */] == '\r' || end_vcalendar[13] == '\n' || end_vcalendar[13] == '\0'))) - end_vcalendar = NULL; - - if (begin_vcalendar && !end_vcalendar) { - gchar *str; - - str = g_strconcat (mime_content, "\r\n", "END:VCALENDAR", "\r\n", NULL); - vcomp = icalparser_parse_string (str); - g_free (str); - } - } - - if (!vcomp) { - if (mime_content) - g_warning ("%s: Failed to parse mime content:---%s---", G_STRFUNC, mime_content); - return NULL; - } - - tzid = e_ews_item_get_tzid (item); - if (tzid == NULL) { - /* - * When we are working with Exchange server 2010 or newer, we have to handle a few - * things more than we do working old servers. These things are: - * - MSDN timezone names: - * Used setting StartTimeZone and EndTimeZone. MSDN timezone names are not - * the same used in libical, so we need to have a table of equivalence to - * convert from one to another and avoid show the MSDN timezone name to the - * user and save it in the ETimezoneCache. - * - EvoEWSStartTimeZone/EvoEWSEndTimeZone - * Used to keep track if the timezone shown to the user is the same one set - * by him/her. As we have a table of equivalence, sometimes the user sets a - * timezone but without EvoEWSStartTiemZone property, another timezone name, - * in the same offset, can be shown. And we want to avoid this. - * - DTEND property: - * As we have to work with DTEND setting an event when using EWS server 2010 or - * newer, we have to care about set it properly here, instead of use the same - * as is used in DTSTART. - */ - icaltimezone *start_zone, *end_zone; - const gchar *start_tzid, *end_tzid; - const gchar *ical_start_tzid, *ical_end_tzid; - const gchar *evo_ews_start_tzid, *evo_ews_end_tzid; - - start_tzid = e_ews_item_get_start_tzid (item); - end_tzid = e_ews_item_get_end_tzid (item); - - ical_start_tzid = e_cal_backend_ews_tz_util_get_ical_equivalent (start_tzid); - ical_end_tzid = e_cal_backend_ews_tz_util_get_ical_equivalent (end_tzid); - - evo_ews_start_tzid = e_ews_item_get_iana_start_time_zone (item); - evo_ews_end_tzid = e_ews_item_get_iana_end_time_zone (item); - - /* - * We have a few timezones that don't have an equivalent MSDN timezone. - * For those, we will get ical_start_tzid being NULL and then we need to use - * start_tzid, which one has the libical's expected name. - */ - start_zone = ecb_ews_get_timezone ( - timezone_cache, - start_tzid, - ical_start_tzid != NULL ? ical_start_tzid : start_tzid, - evo_ews_start_tzid); - end_zone = ecb_ews_get_timezone ( - timezone_cache, - end_tzid, - ical_end_tzid != NULL ? ical_end_tzid : end_tzid, - evo_ews_end_tzid); - - if (start_zone != NULL) { - icalcomp = icalcomponent_get_first_component (vcomp, kind); - - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - dt = icaltime_convert_to_zone (dt, start_zone); - icalcomponent_set_dtstart (icalcomp, dt); - - timezone_set = TRUE; - e_timezone_cache_add_timezone (timezone_cache, start_zone); - - if (end_zone != NULL) { - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); - dt = icaltime_convert_to_zone (dt, end_zone); - icalcomponent_set_dtend (icalcomp, dt); - - e_timezone_cache_add_timezone (timezone_cache, end_zone); - } - } - - if (!timezone_set) - tzid = start_tzid; - } - - if (!timezone_set && tzid) { - /* - * When we are working with Exchange server older than 2010, we don't set different - * DTSTART and DTEND properties in VTIMEZONE. The reason of that is we don't use - * those properties settings/changing a meeting timezone. - * So, for older servers, here, we only set the DTSTART and DTEND properties with - * the same values. - */ - icaltimezone *zone; - gchar *new_tzid = NULL; - - icalcomp = icalcomponent_get_first_component (vcomp, kind); - - if (!icaltimezone_get_builtin_timezone (tzid) && - icalcomponent_get_uid (icalcomp)) { - icalcomponent *vtimezone; - - /* Add the timezone */ - vtimezone = icalcomponent_get_first_component (vcomp, ICAL_VTIMEZONE_COMPONENT); - if (vtimezone != NULL) { - icalproperty *prop; - - new_tzid = g_strconcat ("/evolution/ews/tzid/", icalcomponent_get_uid (icalcomp), NULL); - - zone = icaltimezone_new (); - vtimezone = icalcomponent_new_clone (vtimezone); - prop = icalcomponent_get_first_property (vtimezone, ICAL_TZID_PROPERTY); - if (prop) { - icalproperty_set_tzid (prop, new_tzid); - - prop = icalcomponent_get_first_property (vtimezone, ICAL_LOCATION_PROPERTY); - if (!prop) { - /* Use the original tzid as the timezone Location, to not expose - evolution-ews TZID. */ - prop = icalproperty_new_location (tzid); - icalcomponent_add_property (vtimezone, prop); - } - } else { - g_free (new_tzid); - new_tzid = NULL; - } - icaltimezone_set_component (zone, vtimezone); - e_timezone_cache_add_timezone (timezone_cache, zone); - icaltimezone_free (zone, TRUE); - } - } - - zone = e_timezone_cache_get_timezone (timezone_cache, new_tzid ? new_tzid : tzid); - - if (!zone && new_tzid) - zone = e_timezone_cache_get_timezone (timezone_cache, tzid); - - if (zone == NULL) - zone = icaltimezone_get_builtin_timezone (tzid); - - if (zone != NULL) { - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - dt = icaltime_convert_to_zone (dt, zone); - icalcomponent_set_dtstart (icalcomp, dt); - - dt = e_cal_backend_ews_get_datetime_with_zone (timezone_cache, vcomp, icalcomp, ICAL_DTEND_PROPERTY, icalproperty_get_dtend); - dt = icaltime_convert_to_zone (dt, zone); - icalcomponent_set_dtend (icalcomp, dt); - } - - g_free (new_tzid); - } - } - - /* Vevent or Vtodo */ - icalcomp = icalcomponent_get_first_component (vcomp, kind); - if (icalcomp) { - icalproperty *icalprop, *freebusy; - struct icaltimetype itt; - const EwsId *item_id; - const GSList *l = NULL; - const gchar *uid = e_ews_item_get_uid (item); - - item_id = e_ews_item_get_id (item); - - if (e_ews_item_get_is_meeting (item)) { - gboolean is_response_requested = e_ews_item_get_is_response_requested (item); - gchar *user_email; - - user_email = camel_ews_settings_dup_email (ews_settings); - - /* Attendees */ - for (l = e_ews_item_get_attendees (item); l != NULL; l = g_slist_next (l)) { - icalparameter *param, *cu_type; - gchar *mailtoname; - const gchar *email = NULL; - EwsAttendee *attendee = (EwsAttendee *) l->data; - - if (!attendee->mailbox) - continue; - - if (g_strcmp0 (attendee->mailbox->routing_type, "EX") == 0) - email = e_ews_item_util_strip_ex_address (attendee->mailbox->email); - - mailtoname = g_strdup_printf ("mailto:%s", email ? email : attendee->mailbox->email); - icalprop = icalproperty_new_attendee (mailtoname); - g_free (mailtoname); - - param = icalparameter_new_cn (attendee->mailbox->name); - icalproperty_add_parameter (icalprop, param); - - if (g_ascii_strcasecmp (attendee->attendeetype, "Required") == 0) { - param = icalparameter_new_role (ICAL_ROLE_REQPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL); - } - else if (g_ascii_strcasecmp (attendee->attendeetype, "Resource") == 0) { - param = icalparameter_new_role (ICAL_ROLE_NONPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_RESOURCE); - } - else { - param = icalparameter_new_role ( ICAL_ROLE_OPTPARTICIPANT); - cu_type = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL); - } - icalproperty_add_parameter (icalprop, cu_type); - icalproperty_add_parameter (icalprop, param); - - if (is_response_requested) { - param = icalparameter_new_rsvp (ICAL_RSVP_TRUE); - icalproperty_add_parameter (icalprop, param); - } - - if (user_email && (email || attendee->mailbox->email) && e_ews_item_get_my_response_type (item) && - g_ascii_strcasecmp (email ? email : attendee->mailbox->email, user_email) == 0) { - param = ecb_ews_responsetype_to_partstat (e_ews_item_get_my_response_type (item)); - } else { - param = ecb_ews_responsetype_to_partstat (attendee->responsetype); - } - icalproperty_add_parameter (icalprop, param); - - icalcomponent_add_property (icalcomp, icalprop); - } - - g_free (user_email); - } - - /* Free/Busy */ - freebusy = icalcomponent_get_first_property (icalcomp, ICAL_TRANSP_PROPERTY); - if (!freebusy && (e_ews_item_get_item_type (item) != E_EWS_ITEM_TYPE_TASK)) { - /* Busy by default */ - freebusy = icalproperty_new_transp (ICAL_TRANSP_OPAQUE); - icalcomponent_add_property (icalcomp, freebusy); - } - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); - icalprop != NULL; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) { - if (g_strcmp0 (icalproperty_get_x_name (icalprop), "X-MICROSOFT-CDO-BUSYSTATUS") == 0) { - if (g_strcmp0 (icalproperty_get_value_as_string (icalprop), "BUSY") == 0) { - icalproperty_set_transp (freebusy, ICAL_TRANSP_OPAQUE); - } else { - icalproperty_set_transp (freebusy, ICAL_TRANSP_TRANSPARENT); - } - - break; - } - } - - /* AllDayEvent */ - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY); - icalprop != NULL; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY)) { - if (g_strcmp0 (icalproperty_get_x_name (icalprop), "X-MICROSOFT-CDO-ALLDAYEVENT") == 0) { - if (g_strcmp0 (icalproperty_get_value_as_string (icalprop), "TRUE") == 0) { - struct icaltimetype dtend, dtstart; - dtstart = icalcomponent_get_dtstart (icalcomp); - dtstart.is_date = 1; - icalcomponent_set_dtstart (icalcomp, dtstart); - - dtend = icalcomponent_get_dtend (icalcomp); - dtend.is_date = 1; - icalcomponent_set_dtend (icalcomp, dtend); - } - break; - } - } - - if (icalcomponent_get_first_property (icalcomp, ICAL_RECURRENCEID_PROPERTY)) { - /* Exchange sets RRULE even on the children, which is broken */ - icalprop = icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY); - if (icalprop) { - icalcomponent_remove_property (icalcomp, icalprop); - icalproperty_free (icalprop); - } - } - - /* The EXDATE sent by the server can be date-time format with timezone, while - the event start time can be date-only. This breaks the rules, thus correct - it and make also EXDATE date-only. */ - itt = icalcomponent_get_dtstart (icalcomp); - if (icaltime_is_valid_time (itt) && itt.is_date) { - for (icalprop = icalcomponent_get_first_property (icalcomp, ICAL_EXDATE_PROPERTY); - icalprop; - icalprop = icalcomponent_get_next_property (icalcomp, ICAL_EXDATE_PROPERTY)) { - itt = icalproperty_get_exdate (icalprop); - itt.is_date = 1; - icalproperty_set_exdate (icalprop, itt); - - icalproperty_remove_parameter_by_kind (icalprop, ICAL_TZID_PARAMETER); - } - } - - /* Exchange sets an ORGANIZER on all events. RFC2445 says: - * - * This property MUST NOT be specified in an iCalendar - * object that specifies only a time zone definition or - * that defines calendar entities that are not group - * scheduled entities, but are entities only on a single - * user's calendar. - */ - if (!icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY)) { - if ((icalprop = icalcomponent_get_first_property (icalcomp, ICAL_ORGANIZER_PROPERTY))) { - icalcomponent_remove_property (icalcomp, icalprop); - icalproperty_free (icalprop); - } - } - - icalcomponent_set_uid (icalcomp, uid ? uid : item_id->id); - - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-ITEMID", item_id->id); - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY", item_id->change_key); - - res_component = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (icalcomp)); - - /* Categories */ - e_cal_component_set_categories_list (res_component, (GSList *) e_ews_item_get_categories (item)); - - /* - * There is no API to set/get alarm description on the server side. - * However, for some reason, the alarm description has been set to "REMINDER" - * automatically (and with no i18n). Instead of show it to the user, let's - * set the summary as the alarm description. - */ - if (e_cal_component_has_alarms (res_component)) { - GList *alarm_uids, *l; - - alarm_uids = e_cal_component_get_alarm_uids (res_component); - for (l = alarm_uids; l != NULL; l = l->next) { - ECalComponentAlarm *alarm; - ECalComponentText text; - - alarm = e_cal_component_get_alarm (res_component, l->data); - e_cal_component_get_summary (res_component, &text); - e_cal_component_alarm_set_description (alarm, &text); - - e_cal_component_alarm_free (alarm); - } - cal_obj_uid_list_free (alarm_uids); - } - } - - icalcomponent_free (vcomp); - - if (res_component) { - const GSList *attachment_ids, *aid, *l; - const gchar *uid = NULL; - GSList *info_attachments = NULL, *uris = NULL; - gboolean has_attachment = FALSE; - - e_ews_item_has_attachments (item, &has_attachment); - if (!has_attachment) - return res_component; - - e_cal_component_get_uid (res_component, &uid); - - attachment_ids = e_ews_item_get_attachments_ids (item); - - if (e_ews_connection_get_attachments_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - uid, - attachment_ids, - cbews->priv->attachments_dir, - TRUE, - &info_attachments, - NULL, NULL, - cancellable, - NULL)) { - icalcomponent *icalcomp; - icalproperty *icalprop; - icalparameter *icalparam; - - for (l = info_attachments; l; l = l->next) { - EEwsAttachmentInfo *info = l->data; - - /* ignore non-uri attachments, because it's an exception */ - if (e_ews_attachment_info_get_type (info) == E_EWS_ATTACHMENT_INFO_TYPE_URI) { - const gchar *uri = e_ews_attachment_info_get_uri (info); - - if (uri) - uris = g_slist_append (uris, g_strdup (uri)); - } - } - - e_cal_component_set_attachment_list (res_component, uris); - - icalcomp = e_cal_component_get_icalcomponent (res_component); - icalprop = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY); - for (aid = attachment_ids; aid && icalprop; aid = aid->next, icalprop = icalcomponent_get_next_property (icalcomp, ICAL_ATTACH_PROPERTY)) { - icalparam = icalparameter_new_x (aid->data); - icalparameter_set_xname (icalparam, "X-EWS-ATTACHMENTID"); - icalproperty_add_parameter (icalprop, icalparam); - } - - g_slist_free_full (uris, g_free); - g_slist_free_full (info_attachments, (GDestroyNotify) e_ews_attachment_info_free); - } - } - - return res_component; -} - -static void -ecb_ews_store_original_comp (ECalComponent *comp) -{ - gchar *comp_str; - gchar *base64; - - g_return_if_fail (E_IS_CAL_COMPONENT (comp)); - - comp_str = e_cal_component_get_as_string (comp); - g_return_if_fail (comp_str != NULL); - - /* Include NUL-terminator */ - base64 = g_base64_encode ((const guchar *) comp_str, strlen (comp_str) + 1); - - e_cal_util_set_x_property (e_cal_component_get_icalcomponent (comp), - X_EWS_ORIGINAL_COMP, base64); - - g_free (base64); - g_free (comp_str); -} - -static ECalComponent * /* free with g_object_unref(), if not NULL */ -ecb_ews_restore_original_comp (ECalComponent *from_comp) -{ - ECalComponent *comp = NULL; - const gchar *original_base64; - guchar *decoded; - gsize len = -1; - - g_return_val_if_fail (E_IS_CAL_COMPONENT (from_comp), NULL); - - original_base64 = e_cal_util_get_x_property (e_cal_component_get_icalcomponent (from_comp), X_EWS_ORIGINAL_COMP); - - if (!original_base64 || !*original_base64) - return NULL; - - decoded = g_base64_decode (original_base64, &len); - if (!decoded || !*decoded || len <= 0) { - g_free (decoded); - return NULL; - } - - if (decoded[len - 1] != '\0') { - gchar *tmp; - - tmp = g_strndup ((const gchar *) decoded, len); - - g_free (decoded); - decoded = (guchar *) tmp; - } - - if (decoded && *decoded) - comp = e_cal_component_new_from_string ((const gchar *) decoded); - - g_free (decoded); - - return comp; -} - -static gboolean -ecb_ews_get_items_sync (ECalBackendEws *cbews, - const GSList *item_ids, /* gchar * */ - const gchar *default_props, - const EEwsAdditionalProps *add_props, - GSList **out_components, /* ECalComponent * */ - GCancellable *cancellable, - GError **error) -{ - GSList *items = NULL, *link; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (out_components != NULL, FALSE); - - success = e_ews_connection_get_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - item_ids, - default_props, - add_props, - FALSE, - NULL, - E_EWS_BODY_TYPE_TEXT, - &items, - NULL, NULL, - cancellable, - error); - - if (!success) - return FALSE; - - /* fetch modified occurrences */ - for (link = items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const GSList *modified_occurrences; - - if (!item || e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - modified_occurrences = e_ews_item_get_modified_occurrences (item); - if (modified_occurrences) { - EEwsAdditionalProps *modified_add_props; - - modified_add_props = e_ews_additional_props_new (); - if (e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) { - EEwsExtendedFieldURI *ext_uri; - - modified_add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2010); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSStartTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - modified_add_props->extended_furis = g_slist_append (modified_add_props->extended_furis, ext_uri); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSEndTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - modified_add_props->extended_furis = g_slist_append (modified_add_props->extended_furis, ext_uri); - } else { - modified_add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2007); - } - - success = ecb_ews_get_items_sync (cbews, modified_occurrences, "IdOnly", modified_add_props, out_components, cancellable, error); - - e_ews_additional_props_free (modified_add_props); - - if (!success) - goto exit; - } - } - - for (link = items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - ECalComponent *comp; - GError *local_error = NULL; - - if (!item || e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - continue; - - comp = ecb_ews_item_to_component_sync (cbews, item, cancellable, &local_error); - if (!comp) { - if (!local_error) - continue; - - g_propagate_error (error, local_error); - success = FALSE; - break; - } - - ecb_ews_store_original_comp (comp); - - *out_components = g_slist_prepend (*out_components, comp); - } - - exit: - g_slist_free_full (items, g_object_unref); - - return success; -} - -static gboolean -ecb_ews_fetch_items_sync (ECalBackendEws *cbews, - const GSList *items, /* EEwsItem * */ - GSList **out_components, /* ECalComponent * */ - GCancellable *cancellable, - GError **error) -{ - GSList *event_ids = NULL, *task_memo_ids = NULL, *link; - gboolean success = TRUE; - - for (link = (GSList *) items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (type == E_EWS_ITEM_TYPE_EVENT) - event_ids = g_slist_prepend (event_ids, g_strdup (id->id)); - else if (type == E_EWS_ITEM_TYPE_TASK || type == E_EWS_ITEM_TYPE_MEMO) { - task_memo_ids = g_slist_prepend (task_memo_ids, g_strdup (id->id)); - } - } - - if (event_ids) { - EEwsAdditionalProps *add_props; - - add_props = e_ews_additional_props_new (); - if (e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) { - EEwsExtendedFieldURI *ext_uri; - - add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2010); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSStartTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - add_props->extended_furis = g_slist_append (add_props->extended_furis, ext_uri); - - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->distinguished_prop_set_id = g_strdup ("PublicStrings"); - ext_uri->prop_name = g_strdup ("EvolutionEWSEndTimeZone"); - ext_uri->prop_type = g_strdup ("String"); - add_props->extended_furis = g_slist_append (add_props->extended_furis, ext_uri); - } else { - add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2007); - } - - success = ecb_ews_get_items_sync (cbews, event_ids, "IdOnly", add_props, out_components, cancellable, error); - - e_ews_additional_props_free (add_props); - } - - if (task_memo_ids && success) - success = ecb_ews_get_items_sync (cbews, task_memo_ids, "AllProperties", NULL, out_components, cancellable, error); - - g_slist_free_full (event_ids, g_free); - g_slist_free_full (task_memo_ids, g_free); - - return success; -} - -static gboolean -ecb_ews_freebusy_ecomp_changed (ECalComponent *ecomp, - icalcomponent *vevent) -{ - icalcomponent *icomp; - gboolean changed = FALSE; - - g_return_val_if_fail (vevent != NULL, FALSE); - - if (!ecomp) - return TRUE; - - icomp = e_cal_component_get_icalcomponent (ecomp); - if (!icomp) - return TRUE; - - if (!changed) - changed = g_strcmp0 (icalcomponent_get_summary (icomp), icalcomponent_get_summary (vevent)) != 0; - if (!changed) - changed = g_strcmp0 (icalcomponent_get_location (icomp), icalcomponent_get_location (vevent)) != 0; - if (!changed) - changed = icaltime_compare (icalcomponent_get_dtstart (icomp), icalcomponent_get_dtstart (vevent)) != 0; - if (!changed) - changed = icaltime_compare (icalcomponent_get_dtend (icomp), icalcomponent_get_dtend (vevent)) != 0; - - return changed; -} - -static GSList * /* the possibly modified 'in_items' */ -ecb_ews_verify_changes (ECalCache *cal_cache, - icalcomponent_kind kind, - GSList *in_items, /* EEwsItem * */ - GCancellable *cancellable) -{ - GSList *items = NULL, *link; - - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), in_items); - - for (link = in_items; link; link = g_slist_next (link)) { - EEwsItem *item = link->data; - const EwsId *id = e_ews_item_get_id (item); - EEwsItemType type = e_ews_item_get_item_type (item); - - if (!g_cancellable_is_cancelled (cancellable) && ( - (type == E_EWS_ITEM_TYPE_EVENT && kind == ICAL_VEVENT_COMPONENT) || - (type == E_EWS_ITEM_TYPE_MEMO && kind == ICAL_VJOURNAL_COMPONENT) || - (type == E_EWS_ITEM_TYPE_TASK && kind == ICAL_VTODO_COMPONENT) )) { - ECalComponent *existing = NULL; - - if (e_cal_cache_get_component (cal_cache, id->id, NULL, &existing, cancellable, NULL) && - existing && g_strcmp0 (e_cal_util_get_x_property (e_cal_component_get_icalcomponent (existing), - "X-EVOLUTION-CHANGEKEY"), id->change_key) == 0) { - g_object_unref (item); - } else { - items = g_slist_prepend (items, item); - } - - g_clear_object (&existing); - } else if (type == E_EWS_ITEM_TYPE_EVENT || - type == E_EWS_ITEM_TYPE_MEMO || - type == E_EWS_ITEM_TYPE_TASK) { - g_object_unref (item); - } else { - items = g_slist_prepend (items, item); - } - } - - g_slist_free (in_items); - - return items; -} - -static GSList * /* ECalMetaBackendInfo */ -ecb_ews_components_to_infos (ECalMetaBackend *meta_backend, - const GSList *components, /* ECalComponent * */ - icalcomponent_kind kind) -{ - GSList *nfos = NULL, *link; - GHashTable *sorted_by_uids; /* gchar * ~> GSList { ECalComponent * } */ - GHashTableIter iter; - gpointer key, value; - - sorted_by_uids = g_hash_table_new (g_str_hash, g_str_equal); - - for (link = (GSList *) components; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - icalcomponent *icomp; - const gchar *uid; - GSList *instances; - - if (!comp) - continue; - - icomp = e_cal_component_get_icalcomponent (comp); - uid = icalcomponent_get_uid (icomp); - - if (!uid) - continue; - - ecb_ews_store_original_comp (comp); - - instances = g_hash_table_lookup (sorted_by_uids, uid); - g_hash_table_insert (sorted_by_uids, (gpointer) uid, g_slist_prepend (instances, comp)); - } - - g_hash_table_iter_init (&iter, sorted_by_uids); - while (g_hash_table_iter_next (&iter, &key, &value)) { - const gchar *uid = key; - GSList *instances = value, *link; - icalcomponent *icomp, *merged; - ECalComponent *comp; - ECalMetaBackendInfo *nfo; - const gchar *revision, *itemid; - - if (!uid || !instances) { - g_slist_free (instances); - continue; - } - - /* Try to find master object, to have seves marter's itemid in ECalMetaBackendInfo::extra, - thus the re-load of the event is done for the whole series and not for a detached instance */ - comp = NULL; - for (link = instances; link && !comp; link = g_slist_next (link)) { - comp = link->data; - - if (!comp) - continue; - - if (e_cal_component_is_instance (comp)) - comp = NULL; - } - - if (!comp) - comp = instances->data; - - if (!comp) { - g_slist_free (instances); - continue; - } - - icomp = e_cal_component_get_icalcomponent (comp); - itemid = e_cal_util_get_x_property (icomp, "X-EVOLUTION-ITEMID"); - revision = e_cal_util_get_x_property (icomp, "X-EVOLUTION-CHANGEKEY"); - merged = e_cal_meta_backend_merge_instances (meta_backend, instances, FALSE); - - if (!merged) { - g_warn_if_fail (merged != NULL); - g_slist_free (instances); - continue; - } - - nfo = e_cal_meta_backend_info_new (uid, revision, NULL, itemid); - nfo->object = icalcomponent_as_ical_string_r (merged); - - nfos = g_slist_prepend (nfos, nfo); - - icalcomponent_free (merged); - g_slist_free (instances); - } - - g_hash_table_destroy (sorted_by_uids); - - return nfos; -} - -static void -ecb_ews_extract_item_id (ECalComponent *comp, - gchar **out_id, - gchar **out_change_key) -{ - icalcomponent *icalcomp; - - g_return_if_fail (E_IS_CAL_COMPONENT (comp)); - - icalcomp = e_cal_component_get_icalcomponent (comp); - g_return_if_fail (icalcomp != NULL); - - if (out_id) - *out_id = e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-ITEMID"); - if (out_change_key) - *out_change_key = e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY"); -} - -static gboolean -ecb_ews_is_organizer (ECalBackendEws *cbews, - ECalComponent *comp) -{ - ECalComponentOrganizer organizer; - gboolean is_organizer = FALSE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE); - - if (!e_cal_component_has_organizer (comp)) - return FALSE; - - organizer.value = NULL; - - e_cal_component_get_organizer (comp, &organizer); - if (organizer.value) { - CamelEwsSettings *ews_settings; - const gchar *email = organizer.value; - gchar *user_email; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - user_email = camel_ews_settings_dup_email (ews_settings); - - if (!g_ascii_strncasecmp (email, "mailto:", 7)) - email += 7; - - is_organizer = user_email && g_ascii_strcasecmp (email, user_email) == 0; - - g_free (user_email); - } - - return is_organizer; -} - -static gboolean -ecb_ews_connect_sync (ECalMetaBackend *meta_backend, - const ENamedParameters *credentials, - ESourceAuthenticationResult *out_auth_result, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - CamelEwsSettings *ews_settings; - gchar *hosturl; - gboolean success = FALSE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_auth_result != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->cnc) { - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - *out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED; - - return TRUE; - } - - ews_settings = ecb_ews_get_collection_settings (cbews); - hosturl = camel_ews_settings_dup_hosturl (ews_settings); - - cbews->priv->cnc = e_ews_connection_new_for_backend (E_BACKEND (cbews), e_cal_backend_get_registry (E_CAL_BACKEND (cbews)), hosturl, ews_settings); - - e_binding_bind_property ( - cbews, "proxy-resolver", - cbews->priv->cnc, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - *out_auth_result = e_ews_connection_try_credentials_sync (cbews->priv->cnc, credentials, cancellable, error); - - if (*out_auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) { - ESource *source = e_backend_get_source (E_BACKEND (cbews)); - ESourceEwsFolder *ews_folder; - - ews_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER); - - g_free (cbews->priv->folder_id); - cbews->priv->folder_id = e_source_ews_folder_dup_id (ews_folder); - cbews->priv->is_freebusy_calendar = cbews->priv->folder_id && g_str_has_prefix (cbews->priv->folder_id, "freebusy-calendar::"); - - g_signal_connect_swapped (cbews->priv->cnc, "server-notification", - G_CALLBACK (ecb_ews_server_notification_cb), cbews); - - if (!cbews->priv->is_freebusy_calendar && - camel_ews_settings_get_listen_notifications (ews_settings) && - e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010_SP1)) { - GSList *folders = NULL; - - folders = g_slist_prepend (folders, cbews->priv->folder_id); - - e_ews_connection_enable_notifications_sync (cbews->priv->cnc, - folders, &cbews->priv->subscription_key); - - g_slist_free (folders); - } - - e_cal_backend_set_writable (E_CAL_BACKEND (cbews), !cbews->priv->is_freebusy_calendar); - success = TRUE; - } else { - ecb_ews_convert_error_to_edc_error (error); - g_clear_object (&cbews->priv->cnc); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - g_free (hosturl); - - return success; -} - -static gboolean -ecb_ews_disconnect_sync (ECalMetaBackend *meta_backend, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - ecb_ews_unset_connection (cbews); - - return TRUE; -} - -static gboolean -ecb_ews_get_changes_sync (ECalMetaBackend *meta_backend, - const gchar *last_sync_tag, - gboolean is_repeat, - gchar **out_new_sync_tag, - gboolean *out_repeat, - GSList **out_created_objects, - GSList **out_modified_objects, - GSList **out_removed_objects, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - gboolean success = TRUE; - GError *local_error = NULL; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (out_new_sync_tag != NULL, FALSE); - g_return_val_if_fail (out_repeat != NULL, FALSE); - g_return_val_if_fail (out_created_objects != NULL, FALSE); - g_return_val_if_fail (out_modified_objects != NULL, FALSE); - g_return_val_if_fail (out_removed_objects != NULL, FALSE); - - *out_created_objects = NULL; - *out_modified_objects = NULL; - *out_removed_objects = NULL; - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - if (cbews->priv->is_freebusy_calendar) { - ESourceEwsFolder *ews_folder; - EEWSFreeBusyData fbdata; - GSList *free_busy = NULL, *link; - gboolean success; - time_t today; - - ews_folder = e_source_get_extension (e_backend_get_source (E_BACKEND (cbews)), E_SOURCE_EXTENSION_EWS_FOLDER); - - today = time_day_begin (time (NULL)); - - fbdata.period_start = time_add_week (today, -e_source_ews_folder_get_freebusy_weeks_before (ews_folder)); - fbdata.period_end = time_day_end (time_add_week (today, e_source_ews_folder_get_freebusy_weeks_after (ews_folder))); - fbdata.user_mails = g_slist_prepend (NULL, e_source_ews_folder_dup_foreign_mail (ews_folder)); - - success = e_ews_connection_get_free_busy_sync (cbews->priv->cnc, G_PRIORITY_DEFAULT, - e_ews_cal_utils_prepare_free_busy_request, &fbdata, - &free_busy, cancellable, &local_error); - - if (success) { - icaltimezone *utc_zone = icaltimezone_get_utc_timezone (); - GSList *comps = NULL; - GHashTable *known; - GHashTableIter iter; - gpointer key; - - known = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - - if (e_cal_cache_search_components (cal_cache, NULL, &comps, cancellable, NULL)) { - for (link = comps; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - icalcomponent *icomp; - const gchar *uid; - - if (!comp) - continue; - - icomp = e_cal_component_get_icalcomponent (comp); - if (!icomp) - continue; - - uid = icalcomponent_get_uid (icomp); - - if (uid && *uid) - g_hash_table_insert (known, g_strdup (uid), g_object_ref (comp)); - } - - g_slist_free_full (comps, g_object_unref); - } - - for (link = free_busy; link; link = g_slist_next (link)) { - icalcomponent *fbcomp = link->data; - icalproperty *fbprop; - icalparameter *param; - struct icalperiodtype fb; - icalparameter_fbtype fbtype; - - if (!fbcomp || icalcomponent_isa (fbcomp) != ICAL_VFREEBUSY_COMPONENT) - continue; - - for (fbprop = icalcomponent_get_first_property (fbcomp, ICAL_FREEBUSY_PROPERTY); - fbprop; - fbprop = icalcomponent_get_next_property (fbcomp, ICAL_FREEBUSY_PROPERTY)) { - ECalComponent *ecomp; - icalcomponent *vevent; - const gchar *id, *summary, *location; - - param = icalproperty_get_first_parameter (fbprop, ICAL_FBTYPE_PARAMETER); - if (!param) - continue; - - fbtype = icalparameter_get_fbtype (param); - - if (fbtype != ICAL_FBTYPE_FREE && - fbtype != ICAL_FBTYPE_BUSY && - fbtype != ICAL_FBTYPE_BUSYUNAVAILABLE && - fbtype != ICAL_FBTYPE_BUSYTENTATIVE) - continue; - - fb = icalproperty_get_freebusy (fbprop); - id = icalproperty_get_parameter_as_string (fbprop, "X-EWS-ID"); - summary = icalproperty_get_parameter_as_string (fbprop, "X-SUMMARY"); - location = icalproperty_get_parameter_as_string (fbprop, "X-LOCATION"); - - vevent = icalcomponent_new_vevent (); - - if (id && *id) { - icalcomponent_set_uid (vevent, id); - } else { - gchar *uid; - - uid = g_strdup_printf ("%s-%s-%d", - icaltime_as_ical_string (fb.start), - icaltime_as_ical_string (fb.end), - (gint) fbtype); - - icalcomponent_set_uid (vevent, uid); - - g_free (uid); - } - - fb.start.zone = utc_zone; - fb.end.zone = utc_zone; - - icalcomponent_set_dtstart (vevent, fb.start); - icalcomponent_set_dtend (vevent, fb.end); - - icalcomponent_add_property (vevent, icalproperty_new_created (icaltime_current_time_with_zone (utc_zone))); - - if (fbtype == ICAL_FBTYPE_FREE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Free")); - icalcomponent_add_property (vevent, icalproperty_new_transp (ICAL_TRANSP_TRANSPARENT)); - } else if (fbtype == ICAL_FBTYPE_BUSY) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Busy")); - } else if (fbtype == ICAL_FBTYPE_BUSYUNAVAILABLE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Out of Office")); - } else if (fbtype == ICAL_FBTYPE_BUSYTENTATIVE) { - icalcomponent_set_summary (vevent, C_("FreeBusyType", "Tentative")); - } - - if (summary && *summary) - icalcomponent_set_summary (vevent, summary); - - if (location && *location) - icalcomponent_set_location (vevent, location); - - ecomp = g_hash_table_lookup (known, icalcomponent_get_uid (vevent)); - if (ecomp) { - g_object_ref (ecomp); - - /* This dereferences the ecomp, thus the ref() call above to keep it alive */ - g_hash_table_remove (known, icalcomponent_get_uid (vevent)); - - if (ecb_ews_freebusy_ecomp_changed (ecomp, vevent)) { - ECalMetaBackendInfo *nfo; - gchar *revision = e_util_generate_uid (); - - e_cal_util_set_x_property (vevent, "X-EVOLUTION-CHANGEKEY", revision); - - nfo = e_cal_meta_backend_info_new (icalcomponent_get_uid (vevent), NULL, NULL, NULL); - nfo->revision = revision; - nfo->object = icalcomponent_as_ical_string_r (vevent); - - *out_created_objects = g_slist_prepend (*out_created_objects, nfo); - } else { - icalcomponent_free (vevent); - } - - g_clear_object (&ecomp); - } else { - ECalMetaBackendInfo *nfo; - gchar *revision = e_util_generate_uid (); - - e_cal_util_set_x_property (vevent, "X-EVOLUTION-CHANGEKEY", revision); - - nfo = e_cal_meta_backend_info_new (icalcomponent_get_uid (vevent), NULL, NULL, NULL); - nfo->revision = revision; - nfo->object = icalcomponent_as_ical_string_r (vevent); - - *out_modified_objects = g_slist_prepend (*out_modified_objects, nfo); - } - } - } - - g_hash_table_iter_init (&iter, known); - while (g_hash_table_iter_next (&iter, &key, NULL)) { - const gchar *uid = key; - - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_cal_meta_backend_info_new (uid, NULL, NULL, NULL)); - } - - g_hash_table_destroy (known); - } else if (g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NOFREEBUSYACCESS)) { - e_cal_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL); - - e_cal_backend_notify_error (E_CAL_BACKEND (cbews), local_error->message); - g_clear_error (&local_error); - } else { - g_propagate_error (error, local_error); - } - - g_slist_free_full (free_busy, (GDestroyNotify) icalcomponent_free); - g_slist_free_full (fbdata.user_mails, g_free); - } else { - GSList *items_created = NULL, *items_modified = NULL, *items_deleted = NULL, *link; - EEwsAdditionalProps *add_props; - gboolean includes_last_item = TRUE; - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("item:ItemClass"); - - success = e_ews_connection_sync_folder_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - last_sync_tag, cbews->priv->folder_id, "IdOnly", add_props, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - - if (!success && - g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) { - g_clear_error (&local_error); - - e_cal_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL); - - success = e_ews_connection_sync_folder_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - NULL, cbews->priv->folder_id, "IdOnly", add_props, EWS_MAX_FETCH_COUNT, - out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted, - cancellable, &local_error); - } - - e_ews_additional_props_free (add_props); - - if (success) { - GSList *components_created = NULL, *components_modified = NULL; - icalcomponent_kind kind; - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - - /* The sync state doesn't cover changes made by save_component_sync(), - thus verify the changes, instead of re-donwloading the component again */ - items_created = ecb_ews_verify_changes (cal_cache, kind, items_created, cancellable); - items_modified = ecb_ews_verify_changes (cal_cache, kind, items_modified, cancellable); - - if (items_created) { - success = ecb_ews_fetch_items_sync (cbews, items_created, &components_created, cancellable, error); - if (success) - *out_created_objects = ecb_ews_components_to_infos (meta_backend, components_created, kind); - } - - if (items_modified) { - success = ecb_ews_fetch_items_sync (cbews, items_modified, &components_modified, cancellable, error); - if (success) - *out_modified_objects = ecb_ews_components_to_infos (meta_backend, components_modified, kind); - } - - for (link = items_deleted; link; link = g_slist_next (link)) { - const gchar *item_id = link->data; - GSList *ids = NULL, *ilink; - - if (!e_cal_cache_get_ids_with_extra (cal_cache, item_id, &ids, cancellable, NULL)) - continue; - - for (ilink = ids; ilink; ilink = g_slist_next (ilink)) { - ECalComponentId *id = ilink->data; - - /* Use the master object */ - if (id && id->uid && *id->uid && (!id->rid || !*id->rid)) { - *out_removed_objects = g_slist_prepend (*out_removed_objects, - e_cal_meta_backend_info_new (id->uid, NULL, NULL, NULL)); - break; - } - } - - g_slist_free_full (ids, (GDestroyNotify) e_cal_component_free_id); - } - - g_slist_free_full (components_created, g_object_unref); - g_slist_free_full (components_modified, g_object_unref); - - *out_repeat = !includes_last_item; - } else if (local_error) { - g_propagate_error (error, local_error); - } - - g_slist_free_full (items_created, g_object_unref); - g_slist_free_full (items_modified, g_object_unref); - g_slist_free_full (items_deleted, g_free); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_clear_object (&cal_cache); - - return success; -} - -static gboolean -ecb_ews_load_component_sync (ECalMetaBackend *meta_backend, - const gchar *uid, - const gchar *extra, - icalcomponent **out_component, - gchar **out_extra, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - GSList *ids, *items = NULL, *components = NULL; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (uid != NULL, FALSE); - g_return_val_if_fail (out_component != NULL, FALSE); - g_return_val_if_fail (out_extra != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - ids = g_slist_prepend (NULL, (gpointer) (extra && *extra ? extra : uid)); - - success = e_ews_connection_get_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, ids, "IdOnly", - NULL, FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &items, NULL, NULL, cancellable, error); - - g_slist_free (ids); - - if (success && items) { - success = ecb_ews_fetch_items_sync (cbews, items, &components, cancellable, error); - - if (components) { - const EwsId *ews_id = e_ews_item_get_id (items->data); - - if (ews_id) - *out_extra = g_strdup (ews_id->id); - - if (components->next) { - GSList *link; - - *out_component = icalcomponent_new_vcalendar (); - - for (link = components; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - if (!comp) - continue; - - icalcomponent_add_component (*out_component, - icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp))); - } - } else { - *out_component = icalcomponent_new_clone (e_cal_component_get_icalcomponent (components->data)); - } - } else { - success = FALSE; - } - } - - if (!components && e_cal_meta_backend_refresh_sync (meta_backend, cancellable, NULL)) { - ECalCache *cal_cache; - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - if (cal_cache) { - success = e_cal_cache_get_components_by_uid (cal_cache, uid, &components, cancellable, NULL); - if (success) { - *out_component = e_cal_meta_backend_merge_instances (meta_backend, components, FALSE); - - if (!e_cal_cache_get_component_extra (cal_cache, uid, NULL, out_extra, cancellable, NULL)) - *out_extra = NULL; - - g_clear_error (error); - } - g_object_unref (cal_cache); - } - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_slist_free_full (components, g_object_unref); - g_slist_free_full (items, g_object_unref); - - return success; -} - -/* Very simple and naive component comparator, to avoid - unnecessary uploads and changes on the server. */ -static gboolean -ecb_ews_components_equal (ECalComponent *comp1, - ECalComponent *comp2) -{ - icalcomponent *icomp1, *icomp2; - icalproperty *prop1; - GHashTable *processed_props; - gboolean equal = TRUE; - - if (!comp1 && !comp2) - return TRUE; - else if (!comp1 || !comp2) - return FALSE; - - icomp1 = e_cal_component_get_icalcomponent (comp1); - icomp2 = e_cal_component_get_icalcomponent (comp2); - - if (!icomp1 || !icomp2) - return FALSE; - - if (g_strcmp0 (icalcomponent_get_uid (icomp1), icalcomponent_get_uid (icomp2)) != 0) - return FALSE; - - if (icalcomponent_count_properties (icomp1, ICAL_ANY_PROPERTY) != - icalcomponent_count_properties (icomp2, ICAL_ANY_PROPERTY)) - return FALSE; - - processed_props = g_hash_table_new (g_direct_hash, g_direct_equal); - - for (prop1 = icalcomponent_get_first_property (icomp1, ICAL_ANY_PROPERTY); - prop1 && equal; - prop1 = icalcomponent_get_next_property (icomp1, ICAL_ANY_PROPERTY)) { - icalproperty_kind kind = icalproperty_isa (prop1); - icalproperty *prop2; - - for (prop2 = icalcomponent_get_first_property (icomp2, kind); - prop2; - prop2 = icalcomponent_get_next_property (icomp2, kind)) { - gchar *str1, *str2; - gboolean same; - - if (g_hash_table_contains (processed_props, prop2)) - continue; - - if (icalproperty_count_parameters (prop1) != icalproperty_count_parameters (prop2)) - continue; - - str1 = icalproperty_as_ical_string_r (prop1); - str2 = icalproperty_as_ical_string_r (prop2); - - same = g_strcmp0 (str1, str2) == 0; - - g_free (str1); - g_free (str2); - - if (same) { - g_hash_table_insert (processed_props, prop2, NULL); - break; - } - } - - if (!prop2) - equal = FALSE; - } - - g_hash_table_destroy (processed_props); - - return equal; -} - -typedef struct _ChangeData { - ECalComponent *old_component; - ECalComponent *new_component; -} ChangeData; - -static void -change_data_free (gpointer ptr) -{ - ChangeData *cd = ptr; - - if (cd) { - g_clear_object (&cd->old_component); - g_clear_object (&cd->new_component); - g_free (cd); - } -} - -static void -ecb_ews_filter_out_unchanged_instances (const GSList *to_save_instances, - const GSList *existing_instances, - GSList **out_changed_instances, /* ChangeData * */ - GSList **out_removed_instances) /* ECalComponent * */ -{ - GSList *link = NULL; - GHashTable *existing_hash; - GHashTableIter iter; - gpointer value; - - g_return_if_fail (to_save_instances != NULL); - g_return_if_fail (existing_instances != NULL); - g_return_if_fail (out_changed_instances != NULL); - g_return_if_fail (out_removed_instances != NULL); - - *out_changed_instances = NULL; - *out_removed_instances = NULL; - - existing_hash = g_hash_table_new_full ((GHashFunc)e_cal_component_id_hash, (GEqualFunc) e_cal_component_id_equal, - (GDestroyNotify) e_cal_component_free_id, NULL); - - for (link = (GSList *) existing_instances; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id; - - id = e_cal_component_get_id (comp); - if (id) - g_hash_table_insert (existing_hash, id, comp); - } - - for (link = (GSList *) to_save_instances; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id = NULL; - - id = e_cal_component_get_id (comp); - if (id) { - ECalComponent *old_comp; - - old_comp = g_hash_table_lookup (existing_hash, id); - - if (!ecb_ews_components_equal (comp, old_comp)) { - ChangeData *cd; - - cd = g_new0 (ChangeData, 1); - cd->old_component = old_comp ? g_object_ref (old_comp) : NULL; - cd->new_component = g_object_ref (comp); - - *out_changed_instances = g_slist_prepend (*out_changed_instances, cd); - } - - g_hash_table_remove (existing_hash, id); - e_cal_component_free_id (id); - } - } - - g_hash_table_iter_init (&iter, existing_hash); - while (g_hash_table_iter_next (&iter, NULL, &value)) { - *out_removed_instances = g_slist_prepend (*out_removed_instances, g_object_ref (value)); - } - - g_hash_table_destroy (existing_hash); -} - -static gboolean -ecb_ews_extract_attachments (icalcomponent *icalcomp, - GSList **out_attachments) /* EEwsAttachmentInfo * */ -{ - icalproperty *prop; - GSList *props = NULL, *link; - - g_return_val_if_fail (icalcomp != NULL, FALSE); - g_return_val_if_fail (out_attachments != NULL, FALSE); - - *out_attachments = NULL; - - for (prop = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY); - prop; - prop = icalcomponent_get_next_property (icalcomp, ICAL_ATTACH_PROPERTY)) { - props = g_slist_prepend (props, prop); - } - - for (link = props; link; link = g_slist_next (link)) { - EEwsAttachmentInfo *info; - icalattach *attach; - icalparameter *param; - const gchar *stored_filename; - - prop = link->data; - param = icalproperty_get_first_parameter (prop, ICAL_FILENAME_PARAMETER); - stored_filename = param ? icalparameter_get_filename (param) : NULL; - - attach = icalproperty_get_attach (prop); - if (icalattach_get_is_url (attach)) { - const gchar *uri; - - uri = icalattach_get_url (attach); - - if (!uri || !*uri) - continue; - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_URI); - - e_ews_attachment_info_set_uri (info, uri); - if (stored_filename && *stored_filename) { - e_ews_attachment_info_set_prefer_filename (info, stored_filename); - } else { - gchar *uri_filename; - - uri_filename = g_filename_from_uri (uri, NULL, NULL); - if (uri_filename && *uri_filename) { - gchar *basename; - - basename = g_path_get_basename (uri_filename); - if (basename && *basename && basename[0] != '.' && basename[0] != G_DIR_SEPARATOR) { - const gchar *uid; - - uid = icalcomponent_get_uid (icalcomp); - - if (uid && g_str_has_prefix (basename, uid) && basename[strlen (uid)] == '-') { - e_ews_attachment_info_set_prefer_filename (info, basename + strlen (uid) + 1); - } - } - - g_free (basename); - } - - g_free (uri_filename); - } - } else { - gsize len = -1; - guchar *decoded = NULL; - const gchar *content; - - content = (const gchar *) icalattach_get_data (attach); - decoded = g_base64_decode (content, &len); - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_INLINED); - e_ews_attachment_info_set_inlined_data (info, decoded, len); - - if (stored_filename && *stored_filename) - e_ews_attachment_info_set_prefer_filename (info, stored_filename); - - g_free (decoded); - } - - e_ews_attachment_info_set_id (info, icalproperty_get_parameter_as_string (prop, "X-EWS-ATTACHMENTID")); - *out_attachments = g_slist_prepend (*out_attachments, info); - } - - g_slist_free (props); - - return *out_attachments != NULL; -} - -static icaltimezone * -ecb_ews_get_timezone_from_ical_component (ECalBackendEws *cbews, - icalcomponent *icalcomp) -{ - ETimezoneCache *timezone_cache; - icalproperty *prop = NULL; - const gchar *tzid = NULL; - - timezone_cache = E_TIMEZONE_CACHE (cbews); - - prop = icalcomponent_get_first_property (icalcomp, ICAL_DTSTART_PROPERTY); - if (prop != NULL) { - icalparameter *param = NULL; - - param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER); - if (param) { - tzid = icalparameter_get_tzid (param); - } else { - struct icaltimetype dtstart; - - dtstart = icalproperty_get_dtstart (prop); - if (icaltime_is_utc (dtstart)) - tzid = "UTC"; - } - } - - if (tzid) - return e_timezone_cache_get_timezone (timezone_cache, tzid); - - return NULL; -} - -static gboolean -ecb_ews_remove_item_sync (ECalBackendEws *cbews, - ECalCache *cal_cache, - GHashTable *removed_indexes, - const gchar *uid, - const gchar *rid, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *comp = NULL, *parent = NULL; - EwsId item_id = { 0 }; - gint index = 0; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE); - g_return_val_if_fail (uid != NULL, FALSE); - - if (rid && !*rid) - rid = NULL; - - if (!e_cal_cache_get_component (cal_cache, uid, rid, &comp, cancellable, error) || - (rid && !e_cal_cache_get_component (cal_cache, uid, NULL, &parent, cancellable, error))) { - if (!parent && !comp) { - g_propagate_error (error, EDC_ERROR (ObjectNotFound)); - return FALSE; - } - } - - ecb_ews_extract_item_id (comp ? comp : parent, &item_id.id, &item_id.change_key); - - if (!item_id.id) { - g_propagate_error (error, EDC_ERROR_EX (OtherError, "Cannot determine EWS ItemId")); - success = FALSE; - } else { - if (parent) { - index = e_cal_backend_ews_rid_to_index ( - ecb_ews_get_timezone_from_ical_component (cbews, - e_cal_component_get_icalcomponent (parent)), - rid, - e_cal_component_get_icalcomponent (parent), - error); - if (index == 0) - success = comp != NULL; - } - - if (index && removed_indexes && g_hash_table_contains (removed_indexes, GINT_TO_POINTER (index))) { - /* Do nothing, it's already deleted from the server */ - } else { - if (removed_indexes && index) - g_hash_table_insert (removed_indexes, GINT_TO_POINTER (index), NULL); - - success = success && e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, index, EWS_HARD_DELETE, - ecb_ews_is_organizer (cbews, comp) ? EWS_SEND_TO_ALL_AND_SAVE_COPY : EWS_SEND_TO_NONE, - EWS_ALL_OCCURRENCES, cancellable, error); - } - } - - g_free (item_id.id); - g_free (item_id.change_key); - - g_clear_object (&comp); - g_clear_object (&parent); - - return success; -} - -static void -ecb_ews_get_attach_differences (ECalComponent *oldcomp, - ECalComponent *newcomp, - GSList **out_removed_attachment_ids, /* gchar * */ - GSList **out_added_attachments) /* EEwsAttachmentInfo * */ -{ - GSList *old_attachments = NULL, *new_attachments = NULL, *link; - - g_return_if_fail (out_removed_attachment_ids != NULL); - g_return_if_fail (out_added_attachments != NULL); - - *out_removed_attachment_ids = NULL; - *out_added_attachments = NULL; - - if (!ecb_ews_extract_attachments (e_cal_component_get_icalcomponent (oldcomp), &old_attachments)) - old_attachments = NULL; - - if (!ecb_ews_extract_attachments (e_cal_component_get_icalcomponent (newcomp), &new_attachments)) - new_attachments = NULL; - - for (link = old_attachments; link; link = g_slist_next (link)) { - EEwsAttachmentInfo *old_nfo = link->data; - GSList *nlink; - - if (!old_nfo) - continue; - - for (nlink = new_attachments; nlink; nlink = g_slist_next (nlink)) { - EEwsAttachmentInfo *new_nfo = nlink->data; - gboolean same = FALSE; - - if (!new_nfo || - e_ews_attachment_info_get_type (old_nfo) != e_ews_attachment_info_get_type (new_nfo)) - continue; - - if (e_ews_attachment_info_get_type (old_nfo) == E_EWS_ATTACHMENT_INFO_TYPE_INLINED) { - const gchar *old_data, *new_data; - gsize old_len = -1, new_len = -1; - - old_data = e_ews_attachment_info_get_inlined_data (old_nfo, &old_len); - new_data = e_ews_attachment_info_get_inlined_data (new_nfo, &new_len); - - same = old_len == new_len && (old_len == 0 || - (old_len > 0 && old_data && new_data && memcmp (old_data, new_data, old_len) == 0)); - } else if (e_ews_attachment_info_get_type (old_nfo) == E_EWS_ATTACHMENT_INFO_TYPE_URI) { - same = g_strcmp0 (e_ews_attachment_info_get_uri (old_nfo), e_ews_attachment_info_get_uri (new_nfo)) == 0; - } - - if (same) { - new_attachments = g_slist_remove (new_attachments, new_nfo); - e_ews_attachment_info_free (new_nfo); - break; - } - } - - if (!nlink) { - /* Did not find in the new_attachments, thus it's removed */ - g_warn_if_fail (e_ews_attachment_info_get_id (old_nfo) != NULL); - *out_removed_attachment_ids = g_slist_prepend (*out_removed_attachment_ids, - g_strdup (e_ews_attachment_info_get_id (old_nfo))); - } - } - - *out_added_attachments = new_attachments; - - g_slist_free_full (old_attachments, (GDestroyNotify) e_ews_attachment_info_free); -} - -struct TzidCbData { - icalcomponent *comp; - ECalBackendEws *cbews; -}; - -static void -tzid_cb (icalparameter *param, - gpointer data) -{ - struct TzidCbData *cbd = data; - const gchar *tzid; - icaltimezone *zone; - icalcomponent *new_comp; - - tzid = icalparameter_get_tzid (param); - if (!tzid) - return; - - zone = e_timezone_cache_get_timezone (E_TIMEZONE_CACHE (cbd->cbews), tzid); - if (!zone) - return; - - new_comp = icaltimezone_get_component (zone); - if (!new_comp) - return; - - icalcomponent_add_component (cbd->comp, icalcomponent_new_clone (new_comp)); -} - -static void -ecb_ews_pick_all_tzids_out (ECalBackendEws *cbews, - icalcomponent *icalcomp) -{ - - /* pick all the tzids out of the component and resolve - * them using the vtimezones in the current calendar */ - struct TzidCbData cbd; - - cbd.cbews = cbews; - cbd.comp = icalcomp; - - icalcomponent_foreach_tzid (icalcomp, tzid_cb, &cbd); -} - -static gboolean -ecb_ews_modify_item_sync (ECalBackendEws *cbews, - GHashTable *removed_indexes, - icalcomponent *old_icalcomp, - icalcomponent *new_icalcomp, - GCancellable *cancellable, - GError **error) -{ - ECalComponent *comp = NULL, *oldcomp = NULL; - icalcomponent *icalcomp; - gchar *itemid = NULL, *changekey = NULL; - GSList *added_attachments = NULL, *removed_attachment_ids = NULL; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE); - g_return_val_if_fail (new_icalcomp != NULL, FALSE); - - icalcomp = icalcomponent_new_clone (new_icalcomp); - - ecb_ews_pick_all_tzids_out (cbews, icalcomp); - - comp = e_cal_component_new_from_icalcomponent (icalcomp); - if (!comp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - ecb_ews_extract_item_id (comp, &itemid, &changekey); - if (!itemid) { - g_propagate_error (error, EDC_ERROR_EX (OtherError, "Cannot determine EWS ItemId")); - g_object_unref (comp); - return FALSE; - } - - if (old_icalcomp) { - oldcomp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (old_icalcomp)); - } else { - oldcomp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (new_icalcomp)); - } - - ecb_ews_pick_all_tzids_out (cbews, e_cal_component_get_icalcomponent (oldcomp)); - - /* In case we have updated attachments we have to run update attachments - * before update items so attendees will receive mails with already updated attachments */ - - ecb_ews_get_attach_differences (oldcomp, comp, &removed_attachment_ids, &added_attachments); - - /* preform sync delete attachemnt operation*/ - if (removed_attachment_ids) { - g_free (changekey); - changekey = NULL; - - success = e_ews_connection_delete_attachments_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - removed_attachment_ids, &changekey, cancellable, error); - - g_slist_free_full (removed_attachment_ids, g_free); - } - - /* in case we have a new attachments add them before update */ - if (added_attachments && success) { - EwsId item_id; - - item_id.id = itemid; - item_id.change_key = changekey; - - changekey = NULL; - - success = e_ews_connection_create_attachments_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - &item_id, added_attachments, - FALSE, &changekey, NULL, cancellable, error); - - g_free (item_id.change_key); - } - - if (success && old_icalcomp && - icalcomponent_get_first_property (new_icalcomp, ICAL_RRULE_PROPERTY) && - !icalcomponent_get_first_property (new_icalcomp, ICAL_RECURRENCEID_PROPERTY)) { - icalproperty *prop, *old_prop; - GSList *exceptions = NULL, *link; - EwsId item_id; - - item_id.id = itemid; - item_id.change_key = changekey; - - /* Excluded occurrences */ - for (prop = icalcomponent_get_first_property (new_icalcomp, ICAL_EXDATE_PROPERTY); - prop; - prop = icalcomponent_get_next_property (new_icalcomp, ICAL_EXDATE_PROPERTY)) { - const gchar *new_rid; - - new_rid = icalproperty_get_value_as_string (prop); - - for (old_prop = icalcomponent_get_first_property (old_icalcomp, ICAL_EXDATE_PROPERTY); - old_prop; - old_prop = icalcomponent_get_next_property (old_icalcomp, ICAL_EXDATE_PROPERTY)) { - if (g_strcmp0 (new_rid, icalproperty_get_value_as_string (old_prop)) == 0) - break; - } - - if (!old_prop) - exceptions = g_slist_prepend (exceptions, prop); - } - - exceptions = g_slist_reverse (exceptions); - - for (link = exceptions; link && success; link = g_slist_next (link)) { - guint index; - - prop = link->data; - - index = e_cal_backend_ews_rid_to_index ( - ecb_ews_get_timezone_from_ical_component (cbews, new_icalcomp), - icalproperty_get_value_as_string (prop), - new_icalcomp, - error); - - if (index == 0) { - success = FALSE; - } else if (!removed_indexes || !g_hash_table_contains (removed_indexes, GINT_TO_POINTER (index))) { - if (removed_indexes) - g_hash_table_insert (removed_indexes, GINT_TO_POINTER (index), NULL); - - success = e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, index, - EWS_HARD_DELETE, EWS_SEND_TO_NONE, EWS_ALL_OCCURRENCES, cancellable, error); - } - } - - g_slist_free (exceptions); - } - - if (success) { - EwsCalendarConvertData convert_data = { 0 }; - CamelEwsSettings *ews_settings; - const gchar *send_meeting_invitations; - const gchar *send_or_save; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.user_email = camel_ews_settings_dup_email (ews_settings); - convert_data.comp = comp; - convert_data.old_comp = oldcomp; - convert_data.item_id = itemid; - convert_data.change_key = changekey; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - if (e_cal_component_has_attendees (comp)) { - send_meeting_invitations = "SendToAllAndSaveCopy"; - send_or_save = "SendAndSaveCopy"; - } else { - /*In case of appointment we have to set SendMeetingInvites to SendToNone */ - send_meeting_invitations = "SendToNone"; - send_or_save = "SaveOnly"; - } - - success = e_ews_connection_update_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", send_or_save, send_meeting_invitations, cbews->priv->folder_id, - e_cal_backend_ews_convert_component_to_updatexml, &convert_data, - NULL, cancellable, error); - - g_free (convert_data.user_email); - } - - g_slist_free_full (added_attachments, (GDestroyNotify) e_ews_attachment_info_free); - g_clear_object (&oldcomp); - g_clear_object (&comp); - g_free (changekey); - g_free (itemid); - - return success; -} - -static gboolean -ecb_ews_save_component_sync (ECalMetaBackend *meta_backend, - gboolean overwrite_existing, - EConflictResolution conflict_resolution, - const GSList *instances, - const gchar *extra, - gchar **out_new_uid, - gchar **out_new_extra, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - ECalComponent *master = NULL; - EwsFolderId *fid; - GSList *link; - const gchar *uid = NULL; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - for (link = (GSList *) instances; link && !master; link = g_slist_next (link)) { - master = link->data; - - if (!master) - continue; - - if (e_cal_component_is_instance (master)) - master = NULL; - } - - if (!master) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - cal_cache = e_cal_meta_backend_ref_cache (meta_backend); - g_return_val_if_fail (cal_cache != NULL, FALSE); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - e_cal_component_get_uid (master, &uid); - fid = e_ews_folder_id_new (cbews->priv->folder_id, NULL, FALSE); - - if (overwrite_existing) { - GSList *existing = NULL, *changed_instances = NULL, *removed_instances = NULL; - - success = uid && e_cal_cache_get_components_by_uid (cal_cache, uid, &existing, cancellable, error) && existing; - - if (success) { - GSList *link; - - /* This is for offline changes, where the component in the cache - is already modified, while the original, the one on the server, - is different. Using the cached component in this case generates - empty UpdateItem request and nothing is saved. */ - for (link = existing; link; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - comp = ecb_ews_restore_original_comp (comp); - if (comp) { - g_object_unref (link->data); - link->data = comp; - } - } - } - - if (success) - ecb_ews_filter_out_unchanged_instances (instances, existing, &changed_instances, &removed_instances); - - if (success) { - GHashTable *removed_indexes; - - removed_indexes = g_hash_table_new (g_direct_hash, g_direct_equal); - - for (link = changed_instances; link && success; link = g_slist_next (link)) { - ChangeData *cd = link->data; - - if (!cd) - continue; - - success = ecb_ews_modify_item_sync (cbews, removed_indexes, - e_cal_component_get_icalcomponent (cd->old_component ? cd->old_component : master), - e_cal_component_get_icalcomponent (cd->new_component), - cancellable, error); - } - - for (link = removed_instances; link && success; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - ECalComponentId *id = NULL; - - if (!comp) - continue; - - id = e_cal_component_get_id (comp); - - if (id) { - success = ecb_ews_remove_item_sync (cbews, cal_cache, removed_indexes, id->uid, id->rid, cancellable, error); - e_cal_component_free_id (id); - } - } - - g_hash_table_destroy (removed_indexes); - } - - if (success) - ecb_ews_extract_item_id (master, out_new_uid, NULL); - - g_slist_free_full (existing, g_object_unref); - g_slist_free_full (changed_instances, change_data_free); - g_slist_free_full (removed_instances, g_object_unref); - } else { - GHashTable *removed_indexes; - EwsCalendarConvertData convert_data = { 0 }; - EEwsItem *item = NULL; - const EwsId *ews_id = NULL; - const gchar *send_meeting_invitations; - icalcomponent *icalcomp; - icalproperty *prop; - GSList *items = NULL; - - icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (master)); - - e_ews_clean_icalcomponent (icalcomp); - - if (!e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) - ecb_ews_pick_all_tzids_out (cbews, icalcomp); - - /* - * In case we are creating a meeting with attendees and attachments. - * We have to preform 3 steps in order to allow attendees to receive attachments in their invite mails. - * 1. create meeting and do not send invites - * 2. create attachments - * 3. dummy update meeting and send invites to all - */ - if (e_cal_component_has_attendees (master)) { - if (e_cal_component_has_attachments (master)) - send_meeting_invitations = "SendToNone"; - else - send_meeting_invitations = "SendToAllAndSaveCopy"; - } else { - /* In case of appointment we have to set SendMeetingInvites to SendToNone */ - send_meeting_invitations = "SendToNone"; - } - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.icalcomp = icalcomp; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - success = e_ews_connection_create_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, "SaveOnly", send_meeting_invitations, - fid, e_cal_backend_ews_convert_calcomp_to_xml, &convert_data, - &items, cancellable, error); - - if (success && items) { - item = items->data; - if (item) { - g_object_ref (item); - - ews_id = e_ews_item_get_id (item); - } - } - - if (success && item && e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_EVENT) { - EEwsAdditionalProps *add_props; - GSList *items, *items_req = NULL; - - add_props = e_ews_additional_props_new (); - add_props->field_uri = g_strdup ("calendar:UID"); - - items = g_slist_append (NULL, ews_id->id); - - /* get calender uid from server*/ - success = e_ews_connection_get_items_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - items, "IdOnly", add_props, FALSE, NULL, E_EWS_BODY_TYPE_TEXT, - &items_req, NULL, NULL, cancellable, error) && items_req != NULL; - - e_ews_additional_props_free (add_props); - - if (success) { - g_clear_object (&item); - - item = items_req->data; - ews_id = NULL; - - if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) { - g_propagate_error (error, g_error_copy (e_ews_item_get_error (item))); - item = NULL; - success = FALSE; - } else { - item = g_object_ref (item); - ews_id = e_ews_item_get_id (item); - } - } - - g_slist_free_full (items_req, g_object_unref); - g_slist_free (items); - } - - /* attachments */ - if (success && e_cal_component_has_attachments (master) > 0) { - GSList *info_attachments = NULL; - - g_warn_if_fail (ews_id != NULL); - - if (ews_id && ecb_ews_extract_attachments (icalcomp, &info_attachments)) { - GSList *ids = NULL; - - success = e_ews_connection_create_attachments_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - ews_id, info_attachments, FALSE, NULL, &ids, cancellable, error); - - g_slist_free_full (info_attachments, (GDestroyNotify) e_ews_attachment_info_free); - g_slist_free_full (ids, g_free); - } - } - - removed_indexes = g_hash_table_new (g_direct_hash, g_direct_equal); - - if (success && icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY)) { - GSList *exceptions = NULL; - - /* Excluded occurrences */ - for (prop = icalcomponent_get_first_property (icalcomp, ICAL_EXDATE_PROPERTY); - prop; - prop = icalcomponent_get_next_property (icalcomp, ICAL_EXDATE_PROPERTY)) { - exceptions = g_slist_prepend (exceptions, g_strdup (icalproperty_get_value_as_string (prop))); - } - - for (link = exceptions; link && success; link = g_slist_next (link)) { - success = ecb_ews_remove_item_sync (cbews, cal_cache, removed_indexes, uid, link->data, cancellable, error); - } - - g_slist_free_full (exceptions, g_free); - } - - if (success && e_cal_component_has_attendees (master) && e_cal_component_has_attachments (master)) { - if (ews_id) { - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-ITEMID", ews_id->id); - e_cal_util_set_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY", ews_id->change_key); - } - - /* In case we have attendees and atachemnts we have to fake update items, - * this is the only way to pass attachments in meeting invite mail */ - success = ecb_ews_modify_item_sync (cbews, removed_indexes, NULL, icalcomp, cancellable, error); - } - - icalcomponent_free (icalcomp); - g_clear_object (&item); - - for (link = (GSList *) instances; link && success; link = g_slist_next (link)) { - ECalComponent *comp = link->data; - - if (comp == master) - continue; - - icalcomp = e_cal_component_get_icalcomponent (comp); - - success = ecb_ews_modify_item_sync (cbews, removed_indexes, NULL, icalcomp, cancellable, error); - } - - if (success && items) { - EEwsItem *item = items->data; - const EwsId *item_id; - - item_id = e_ews_item_get_id (item); - *out_new_uid = g_strdup (item_id->id); - } - - g_slist_free_full (items, g_object_unref); - g_hash_table_destroy (removed_indexes); - } - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - g_clear_object (&cal_cache); - e_ews_folder_id_free (fid); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - - return success; -} - -static gboolean -ecb_ews_remove_component_sync (ECalMetaBackend *meta_backend, - EConflictResolution conflict_resolution, - const gchar *uid, - const gchar *extra, - const gchar *object, - GCancellable *cancellable, - GError **error) -{ - ECalBackendEws *cbews; - ECalComponent *comp; - EwsId item_id; - gboolean success; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE); - g_return_val_if_fail (object != NULL, FALSE); - - cbews = E_CAL_BACKEND_EWS (meta_backend); - - comp = e_cal_component_new_from_string (object); - if (!comp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return FALSE; - } - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - ecb_ews_extract_item_id (comp, &item_id.id, &item_id.change_key); - - success = e_ews_connection_delete_item_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, 0, EWS_HARD_DELETE, - ecb_ews_is_organizer (cbews, comp) ? EWS_SEND_TO_ALL_AND_SAVE_COPY : EWS_SEND_TO_NONE, - EWS_ALL_OCCURRENCES, cancellable, error); - - g_free (item_id.id); - g_free (item_id.change_key); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); - g_object_unref (comp); - - return success; -} - -static void -ecb_ews_discard_alarm_sync (ECalBackendSync *cal_backend_sync, - EDataCal *cal, - GCancellable *cancellable, - const gchar *uid, - const gchar *rid, - const gchar *auid, - GError **error) -{ - ECalBackendEws *cbews; - ECalCache *cal_cache; - ECalComponent *comp = NULL; - EwsCalendarConvertData convert_data = { 0 }; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (cal_backend_sync)); - - cbews = E_CAL_BACKEND_EWS (cal_backend_sync); - - cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbews)); - g_return_if_fail (cal_cache != NULL); - - if (!e_cal_cache_get_component (cal_cache, uid, NULL, &comp, cancellable, NULL) || !comp) { - g_object_unref (cal_cache); - g_propagate_error (error, EDC_ERROR (ObjectNotFound)); - return; - } - - g_object_unref (cal_cache); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) { - g_clear_object (&comp); - return; - } - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - - if (e_cal_component_has_recurrences (comp)) { - gint *index; - - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_OCCURRENCEITEM; - e_cal_component_get_sequence (comp, &index); - - if (index != NULL) { - /*Microsoft is counting the occurrences starting from 1 - where EcalComponent is starting from zerro */ - convert_data.index = *index + 1; - e_cal_component_free_sequence (index); - } else { - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_ITEM; - convert_data.index = -1; - } - } else { - convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_ITEM; - convert_data.index = -1; - } - - ecb_ews_extract_item_id (comp, &convert_data.item_id, &convert_data.change_key); - - if (e_ews_connection_update_items_sync ( - cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", NULL, - "SendToNone", NULL, - e_cal_backend_ews_clear_reminder_is_set, - &convert_data, - NULL, - cancellable, - error)) { - icalcomponent *icomp = e_cal_component_get_icalcomponent (comp); - GSList *modified_objects; - - modified_objects = g_slist_prepend (NULL, - e_cal_meta_backend_info_new (icalcomponent_get_uid (icomp), NULL, NULL, - e_cal_util_get_x_property (icomp, "X-EVOLUTION-ITEMID"))); - - /* Refresh the local cache, to have up-to-date ChangeKey */ - e_cal_meta_backend_process_changes_sync (E_CAL_META_BACKEND (cbews), NULL, modified_objects, NULL, cancellable, error); - - g_slist_free_full (modified_objects, e_cal_meta_backend_info_free); - } - - g_object_unref (comp); - g_free (convert_data.item_id); - g_free (convert_data.change_key); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static gboolean -ecb_ews_send_cancellation_email_sync (ECalBackendEws *cbews, - CamelAddress *from, - CamelInternetAddress *recipient, - const gchar *subject, - const gchar *body, - const gchar *calobj, - GCancellable *cancellable, - GError **error) -{ - CamelMimeMessage *message; - CamelContentType *mime_type; - CamelMultipart *multi; - CamelMimePart *text_part, *vcal_part; - gchar *ical_str; - icalcomponent *vcal, *vevent, *vtz; - icalproperty *prop; - icaltimezone *icaltz; - struct icaltimetype dt; - gboolean success; - - vcal = icalcomponent_new (ICAL_VCALENDAR_COMPONENT); - icalcomponent_add_property (vcal, icalproperty_new_version ("2.0")); - icalcomponent_add_property (vcal, icalproperty_new_prodid ("-//Evolution EWS backend//EN")); - icalcomponent_add_property (vcal, icalproperty_new_method (ICAL_METHOD_CANCEL)); - vevent = icalcomponent_new_from_string (calobj); - prop = icalcomponent_get_first_property (vevent, ICAL_STATUS_PROPERTY); - if (prop != NULL) icalcomponent_remove_property (vevent, prop); - icalcomponent_add_property (vevent, icalproperty_new_status (ICAL_STATUS_CANCELLED)); - prop = icalcomponent_get_first_property (vevent, ICAL_METHOD_PROPERTY); - if (prop != NULL) icalcomponent_remove_property (vevent, prop); - dt = e_cal_backend_ews_get_datetime_with_zone (E_TIMEZONE_CACHE (cbews), NULL, vevent, ICAL_DTSTART_PROPERTY, icalproperty_get_dtstart); - icaltz = (icaltimezone *) - (dt.zone ? dt.zone : ecb_ews_get_timezone_from_ical_component (cbews, vevent)); - vtz = icaltimezone_get_component (icaltz); - icalcomponent_add_component (vcal, icalcomponent_new_clone (vtz)); - icalcomponent_add_component (vcal, vevent); - text_part = camel_mime_part_new (); - camel_mime_part_set_content (text_part, body, strlen (body), "text/plain"); - - vcal_part = camel_mime_part_new (); - mime_type = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (vcal_part)); - camel_content_type_set_param (mime_type, "charset", "utf-8"); - camel_content_type_set_param (mime_type, "method", "CANCEL"); - ical_str = icalcomponent_as_ical_string_r ((icalcomponent *) vcal); - camel_mime_part_set_content (vcal_part, ical_str, strlen (ical_str), "text/calendar; method=CANCEL"); - free (ical_str); - - multi = camel_multipart_new (); - camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multi), "multipart/alternative"); - camel_multipart_add_part (multi, text_part); - camel_multipart_set_boundary (multi, NULL); - camel_multipart_add_part (multi, vcal_part); - g_object_unref (text_part); - g_object_unref (vcal_part); - - message = camel_mime_message_new (); - camel_mime_message_set_subject (message, subject); - camel_mime_message_set_from (message, CAMEL_INTERNET_ADDRESS (from)); - camel_mime_message_set_recipients (message, CAMEL_RECIPIENT_TYPE_TO, recipient); - - camel_medium_set_content ((CamelMedium *) message, (CamelDataWrapper *) multi); - g_object_unref (multi); - - success = camel_ews_utils_create_mime_message (cbews->priv->cnc, "SendOnly", NULL, message, NULL, from, NULL, NULL, NULL, cancellable, error); - - g_object_unref (message); - icalcomponent_free (vcal); - - return success; -} - -static void -ecb_ews_receive_objects_no_exchange_mail (ECalBackendEws *cbews, - icalcomponent *vcalendar, - icalcomponent *subcomp, - GSList **ids, - GCancellable *cancellable, - GError **error) -{ - EwsCalendarConvertData convert_data = { 0 }; - EwsFolderId *fid; - - convert_data.connection = cbews->priv->cnc; - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.icalcomp = subcomp; - convert_data.vcalendar = vcalendar; - convert_data.default_zone = icaltimezone_get_utc_timezone (); - - fid = e_ews_folder_id_new (cbews->priv->folder_id, NULL, FALSE); - - e_ews_connection_create_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - "SaveOnly", - "SendToNone", - fid, - e_cal_backend_ews_convert_calcomp_to_xml, - &convert_data, - ids, - cancellable, - error); - - e_ews_folder_id_free (fid); -} - -static icalproperty * -find_attendee (icalcomponent *ical_comp, - const gchar *address) -{ - icalproperty *prop; - - if (address == NULL) - return NULL; - - for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY); - prop != NULL; - prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) { - gchar *attendee; - gchar *text; - - attendee = icalproperty_get_value_as_string_r (prop); - - if (!attendee) - continue; - - text = g_strdup (itip_strip_mailto (attendee)); - text = g_strstrip (text); - if (text && !g_ascii_strcasecmp (address, text)) { - g_free (text); - g_free (attendee); - break; - } - g_free (text); - g_free (attendee); - } - - return prop; -} - -static icalproperty * -find_attendee_if_sentby (icalcomponent *ical_comp, - const gchar *address) -{ - icalproperty *prop; - - if (address == NULL) - return NULL; - - for (prop = icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY); - prop != NULL; - prop = icalcomponent_get_next_property (ical_comp, ICAL_ATTENDEE_PROPERTY)) { - icalparameter *param; - const gchar *attendee_sentby; - gchar *text; - - param = icalproperty_get_first_parameter (prop, ICAL_SENTBY_PARAMETER); - if (!param) - continue; - - attendee_sentby = icalparameter_get_sentby (param); - - if (!attendee_sentby) - continue; - - text = g_strdup (itip_strip_mailto (attendee_sentby)); - text = g_strstrip (text); - if (text && !g_ascii_strcasecmp (address, text)) { - g_free (text); - break; - } - g_free (text); - } - - return prop; -} - -static void -ecb_ews_get_rsvp (icalproperty *attendee, - gboolean *out_rsvp_requested) -{ - if (out_rsvp_requested) - *out_rsvp_requested = FALSE; - else - return; - - if (attendee) { - icalparameter *rsvp; - - rsvp = icalproperty_get_first_parameter (attendee, ICAL_RSVP_PARAMETER); - if (rsvp) { - *out_rsvp_requested = icalparameter_get_rsvp (rsvp) == ICAL_RSVP_TRUE; - } - } -} - -static const gchar * -ecb_ews_get_current_user_meeting_reponse (ECalBackendEws *cbews, - icalcomponent *icalcomp, - const gchar *current_user_mail, - gboolean *out_rsvp_requested) -{ - icalproperty *attendee; - const gchar *attendee_str = NULL, *attendee_mail = NULL; - gint attendees_count = 0; - const gchar *response = NULL; - gboolean found = FALSE; - - if (out_rsvp_requested) - *out_rsvp_requested = FALSE; - - attendee = icalcomponent_get_first_property (icalcomp, ICAL_ORGANIZER_PROPERTY); - if (attendee) { - attendee_str = icalproperty_get_organizer (attendee); - - if (attendee_str) { - if (!strncasecmp (attendee_str, "MAILTO:", 7)) - attendee_mail = attendee_str + 7; - else - attendee_mail = attendee_str; - if (attendee_mail && current_user_mail && g_ascii_strcasecmp (attendee_mail, current_user_mail) == 0) { - /* Empty string means it's an organizer, NULL is when not found */ - return ""; - } - } - } - - for (attendee = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY); - attendee != NULL; - attendee = icalcomponent_get_next_property (icalcomp, ICAL_ATTENDEE_PROPERTY), attendees_count++) { - attendee_str = icalproperty_get_attendee (attendee); - - if (attendee_str != NULL) { - if (!strncasecmp (attendee_str, "MAILTO:", 7)) - attendee_mail = attendee_str + 7; - else - attendee_mail = attendee_str; - if (attendee_mail && current_user_mail && g_ascii_strcasecmp (attendee_mail, current_user_mail) == 0) { - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } - } - } - - /* this should not happen, but if the user's configured email does not match the one - used in the invitation, like when the invitation comes to a mailing list... */ - if (!found && attendees_count == 1) { - attendee = icalcomponent_get_first_property (icalcomp, ICAL_ATTENDEE_PROPERTY); - g_return_val_if_fail (attendee != NULL, NULL); - - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } else if (!found) { - ESourceRegistry *registry; - ECalComponent *comp; - - registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbews)); - comp = e_cal_component_new (); - if (e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp))) { - gchar *my_address; - - my_address = itip_get_comp_attendee (registry, comp, NULL); - - attendee = find_attendee (icalcomp, my_address); - if (!attendee) - attendee = find_attendee_if_sentby (icalcomp, my_address); - - if (attendee) { - response = icalproperty_get_parameter_as_string (attendee, "PARTSTAT"); - ecb_ews_get_rsvp (attendee, out_rsvp_requested); - found = TRUE; - } - - g_free (my_address); - } - - g_object_unref (comp); - } - - if (found && !response) { - response = "NEEDS-ACTION"; - } - - return response; -} - -/* changekey can be NULL if you don't want it. itemid cannot. */ -static void -ecb_ews_get_item_accept_id (ECalComponent *comp, - gchar **itemid, - gchar **changekey, - gchar **mail_id) -{ - icalproperty *prop; - gchar *id_item = NULL; - gchar *id_accept = NULL; - gchar *ck = NULL; - - prop = icalcomponent_get_first_property ( - e_cal_component_get_icalcomponent (comp), - ICAL_X_PROPERTY); - while (prop) { - const gchar *x_name, *x_val; - - x_name = icalproperty_get_x_name (prop); - x_val = icalproperty_get_x (prop); - if (!id_item && g_ascii_strcasecmp (x_name, "X-EVOLUTION-ITEMID") == 0) - id_item = g_strdup (x_val); - else if (!id_accept && g_ascii_strcasecmp (x_name, "X-EVOLUTION-ACCEPT-ID") == 0) - id_accept = g_strdup (x_val); - else if (changekey && !ck && !g_ascii_strcasecmp (x_name, "X-EVOLUTION-CHANGEKEY")) - ck = g_strdup (x_val); - - prop = icalcomponent_get_next_property ( - e_cal_component_get_icalcomponent (comp), - ICAL_X_PROPERTY); - } - - if (!id_item) - id_item = g_strdup (id_accept); - - *itemid = id_item; - *mail_id = id_accept; - if (changekey) - *changekey = ck; -} - -static gboolean -ecb_ews_do_method_request_publish_reply (ECalBackendEws *cbews, - icalcomponent *vcalendar, - ECalComponent *comp, - icalcomponent *subcomp, - const gchar *response_type, - const gchar *user_email, - gboolean rsvp_requested, - GCancellable *cancellable, - GError **error) -{ - GError *local_error = NULL; - gchar *item_id = NULL; - gchar *change_key = NULL; - gchar *mail_id = NULL; - gint pass = 0; - GSList *ids = NULL; - - if (!response_type && - e_cal_util_component_has_organizer (subcomp) && - e_cal_util_component_has_attendee (subcomp)) { - g_set_error (error, E_DATA_CAL_ERROR, UnknownUser, _("Cannot find user “%s” between attendees"), user_email ? user_email : "NULL"); - return FALSE; - } - - if (response_type && *response_type) - ecb_ews_get_item_accept_id (comp, &item_id, &change_key, &mail_id); - else - response_type = NULL; - - while (pass < 2) { - /*in case we do not have item id we will create item with mime content only*/ - if (!item_id || (response_type && g_ascii_strcasecmp (response_type, "NEEDS-ACTION") == 0)) { - ecb_ews_receive_objects_no_exchange_mail (cbews, vcalendar, subcomp, &ids, cancellable, &local_error); - } else { - EwsCalendarConvertData convert_data = { 0 }; - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.response_type = (gchar *) response_type; - convert_data.item_id = item_id; - convert_data.change_key = change_key; - - e_ews_connection_create_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - rsvp_requested ? "SendAndSaveCopy" : "SaveOnly", - rsvp_requested ? NULL : "SendToNone", - NULL, - e_cal_backend_ews_prepare_accept_item_request, - &convert_data, - &ids, - cancellable, - &local_error); - } - - if (pass == 0 && mail_id != NULL && item_id != NULL && - g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_ITEMNOTFOUND)) { - /* - * maybe the associated accept calendar item changed - * on the server, thus retry with updated values - */ - GSList *my_ids = NULL; - - g_clear_error (&local_error); - - my_ids = g_slist_append (my_ids, mail_id); - - if (e_ews_connection_get_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - my_ids, - "AllProperties", - NULL, - FALSE, - NULL, - E_EWS_BODY_TYPE_ANY, - &ids, - NULL, - NULL, - cancellable, - &local_error) && - ids != NULL && - ids->data != NULL) { - EEwsItem *item = ids->data; - const EwsId *id = e_ews_item_get_id (item); - - if (id != NULL && g_strcmp0 (id->id, mail_id) == 0) { - const EwsId *cal_item_accepted_id; - - cal_item_accepted_id = e_ews_item_get_calendar_item_accept_id (item); - if (cal_item_accepted_id != NULL) { - g_clear_error (&local_error); - pass++; - - g_free (item_id); - g_free (change_key); - - item_id = g_strdup (cal_item_accepted_id->id); - change_key = g_strdup (cal_item_accepted_id->change_key); - } - } - } - - g_slist_free (my_ids); - - if (pass == 0) - break; - } else { - break; - } - - g_slist_free_full (ids, g_object_unref); - ids = NULL; - } - - if (local_error == NULL) { - icalproperty *transp; - - transp = icalcomponent_get_first_property (subcomp, ICAL_TRANSP_PROPERTY); - - if (g_strcmp0 (icalproperty_get_value_as_string (transp), "TRANSPARENT") == 0 && - g_strcmp0 (response_type, "ACCEPTED") == 0) { - EwsCalendarConvertData convert_data = { 0 }; - GSList *l; - - /* - * user can accept meeting but mark it as free in it's calendar - * the following code is updating the exchange meeting status to free - */ - for (l = ids; l != NULL; l = g_slist_next (l)) { - EEwsItem *item = l->data; - - if (item != NULL) { - const EwsId *id = e_ews_item_get_id (item); - - convert_data.item_id = id->id; - convert_data.change_key = id->change_key; - break; - } - } - - convert_data.timezone_cache = E_TIMEZONE_CACHE (cbews); - convert_data.vcalendar = vcalendar; - - e_ews_connection_update_items_sync ( - cbews->priv->cnc, - EWS_PRIORITY_MEDIUM, - "AlwaysOverwrite", - NULL, - "SendToNone", - NULL, - e_cal_backend_ews_prepare_set_free_busy_status, - &convert_data, - NULL, - cancellable, - &local_error); - } - } - - if (local_error != NULL) - g_propagate_error (error, local_error); - - g_free (item_id); - g_free (change_key); - g_free (mail_id); - g_slist_free_full (ids, g_object_unref); - - return !local_error; -} - -static void -ecb_ews_receive_objects_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *calobj, - GError **error) -{ - ECalBackendEws *cbews; - ECalBackend *cal_backend; - CamelEwsSettings *ews_settings; - icalcomponent *icalcomp, *subcomp; - icalcomponent_kind kind; - gchar *user_email; - gboolean success = TRUE, do_refresh = FALSE; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - icalcomp = calobj ? icalparser_parse_string (calobj) : NULL; - - if (!icalcomp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - /* make sure ical data we parse is actually a vCalendar component */ - if (icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) { - icalcomponent_free (icalcomp); - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - cal_backend = E_CAL_BACKEND (cbews); - kind = e_cal_backend_get_kind (cal_backend); - - ews_settings = ecb_ews_get_collection_settings (cbews); - user_email = camel_ews_settings_dup_email (ews_settings); - - switch (icalcomponent_get_method (icalcomp)) { - case ICAL_METHOD_REQUEST: - case ICAL_METHOD_PUBLISH: - case ICAL_METHOD_REPLY: - for (subcomp = icalcomponent_get_first_component (icalcomp, kind); - subcomp && success; - subcomp = icalcomponent_get_next_component (icalcomp, kind)) { - ECalComponent *comp; - const gchar *response_type; - gboolean rsvp_requested = FALSE; - - /* getting a data for meeting request response */ - response_type = ecb_ews_get_current_user_meeting_reponse (cbews, subcomp, user_email, &rsvp_requested); - - comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (subcomp)); - - success = ecb_ews_do_method_request_publish_reply (cbews, icalcomp, comp, subcomp, response_type, user_email, rsvp_requested, cancellable, error); - - do_refresh = TRUE; - - g_object_unref (comp); - } - break; - case ICAL_METHOD_COUNTER: - /* - * this is a new time proposal mail from one of the attendees - * if we decline the proposal, nothing have to be done - * if we accept it we will call to modify_object - */ - for (subcomp = icalcomponent_get_first_component (icalcomp, kind); - subcomp && success; - subcomp = icalcomponent_get_next_component (icalcomp, kind)) { - const gchar *response_type; - - /* getting a data for meeting request response */ - response_type = ecb_ews_get_current_user_meeting_reponse (cbews, subcomp, user_email, NULL); - - if (g_strcmp0 (response_type, "ACCEPTED") == 0) { - gchar **split_subject; - icalproperty *summary; - - /* we have to edit the meeting subject to remove exchange header */ - summary = icalcomponent_get_first_property (subcomp, ICAL_SUMMARY_PROPERTY); - split_subject = - g_strsplit (icalproperty_get_value_as_string (summary), ":", -1); - icalproperty_set_value_from_string (summary, split_subject[1] , "NO"); - g_strfreev (split_subject); - - success = ecb_ews_modify_item_sync (cbews, NULL, NULL, subcomp, cancellable, error); - - do_refresh = TRUE; - } - } - break; - default: - break; - } - - icalcomponent_free (icalcomp); - g_free (user_email); - - if (success && do_refresh) - e_cal_meta_backend_schedule_refresh (E_CAL_META_BACKEND (cbews)); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static void -ecb_ews_send_objects_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *calobj, - GSList **users, - gchar **modified_calobj, - GError **error) -{ - ECalBackendEws *cbews; - icalcomponent_kind kind; - icalcomponent *icalcomp, *subcomp = NULL; - gchar *subcalobj; - gboolean success = TRUE; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - icalcomp = calobj ? icalparser_parse_string (calobj) : NULL; - - /* make sure data was parsed properly */ - if (!icalcomp) { - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - /* make sure ical data we parse is actually an vcal component */ - if ((icalcomponent_isa (icalcomp) != ICAL_VCALENDAR_COMPONENT) && (icalcomponent_isa (icalcomp) != ICAL_VEVENT_COMPONENT)) { - icalcomponent_free (icalcomp); - g_propagate_error (error, EDC_ERROR (InvalidObject)); - return; - } - - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - - if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) { - kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbews)); - subcomp = icalcomponent_get_first_component (icalcomp, kind); - } - if (icalcomponent_isa (icalcomp) == ICAL_VEVENT_COMPONENT) - subcomp = icalcomp; - while (subcomp && success) { - const gchar *new_body_content = NULL, *subject = NULL, *org_email = NULL; - const gchar *org = NULL, *attendee = NULL; - icalproperty *prop, *org_prop = NULL; - CamelInternetAddress *org_addr = camel_internet_address_new (); - - new_body_content = e_cal_util_get_x_property (subcomp, "X-EVOLUTION-RETRACT-COMMENT"); - subject = icalproperty_get_value_as_string (icalcomponent_get_first_property (subcomp, ICAL_SUMMARY_PROPERTY)); - - org_prop = icalcomponent_get_first_property (subcomp, ICAL_ORGANIZER_PROPERTY); - org = icalproperty_get_organizer (org_prop); - if (!g_ascii_strncasecmp (org, "MAILTO:", 7)) - org_email = (org) + 7; - else - org_email = org; - - camel_internet_address_add (org_addr, icalproperty_get_parameter_as_string (org_prop, "CN"), org_email); - - /* iterate over every attendee property */ - for (prop = icalcomponent_get_first_property (subcomp, ICAL_ATTENDEE_PROPERTY); - prop && success; - prop = icalcomponent_get_next_property (subcomp, ICAL_ATTENDEE_PROPERTY)) { - CamelInternetAddress *attendee_addr = camel_internet_address_new (); - attendee = icalproperty_get_attendee (prop); - if (g_ascii_strcasecmp (org_email, attendee) == 0) continue; - if (!g_ascii_strncasecmp (attendee, "mailto:", 7)) attendee = (attendee) + 7; - - subcalobj = icalcomponent_as_ical_string_r (subcomp); - camel_internet_address_add (attendee_addr, icalproperty_get_parameter_as_string (prop, "CN"), attendee); - success = ecb_ews_send_cancellation_email_sync (cbews, CAMEL_ADDRESS (org_addr), attendee_addr, - subject, new_body_content, subcalobj, cancellable, error); - g_object_unref (attendee_addr); - free (subcalobj); - } - - g_object_unref (org_addr); - subcomp = icalcomponent_get_next_component (icalcomp, kind); - } - - icalcomponent_free (icalcomp); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static void -ecb_ews_get_free_busy_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const GSList *users, - time_t start, - time_t end, - GSList **freebusyobjs, - GError **error) -{ - ECalBackendEws *cbews; - EEWSFreeBusyData fbdata = { 0 }; - GSList *freebusy = NULL; - gboolean success; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - g_return_if_fail (freebusyobjs != NULL); - - cbews = E_CAL_BACKEND_EWS (sync_backend); - - *freebusyobjs = NULL; - - if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error)) - return; - - /* EWS can support only 100 identities, which is the maximum number of identities that the Web service method can request - see http://msdn.microsoft.com / en - us / library / aa564001 % 28v = EXCHG.140 % 29.aspx */ - if (g_slist_length ((GSList *) users) > 100) { - g_propagate_error (error, EDC_ERROR (SearchSizeLimitExceeded)); - return; - } - - fbdata.period_start = start; - fbdata.period_end = end; - fbdata.user_mails = (GSList *) users; - - success = e_ews_connection_get_free_busy_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM, - e_ews_cal_utils_prepare_free_busy_request, &fbdata, - &freebusy, cancellable, error); - - if (success) { - GSList *fblink, *ulink; - - for (fblink = freebusy, ulink = (GSList *) users; - fblink && ulink; - fblink = g_slist_next (fblink), ulink = g_slist_next (ulink)) { - icalcomponent *icalcomp = fblink->data; - gchar *mailto; - - /* add attendee property */ - mailto = g_strconcat ("mailto:", ulink->data, NULL); - icalcomponent_add_property (icalcomp, icalproperty_new_attendee (mailto)); - g_free (mailto); - - *freebusyobjs = g_slist_prepend (*freebusyobjs, icalcomponent_as_ical_string_r (icalcomp)); - } - - *freebusyobjs = g_slist_reverse (*freebusyobjs); - } - - g_slist_free_full (freebusy, (GDestroyNotify) icalcomponent_free); - - ecb_ews_convert_error_to_edc_error (error); - ecb_ews_maybe_disconnect_sync (cbews, error, cancellable); -} - -static gchar * -ecb_ews_get_backend_property (ECalBackend *cal_backend, - const gchar *prop_name) -{ - ECalBackendEws *cbews; - - g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cal_backend), NULL); - g_return_val_if_fail (prop_name != NULL, NULL); - - cbews = E_CAL_BACKEND_EWS (cal_backend); - - if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) { - return g_strjoin ( - ",", - CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS, - CAL_STATIC_CAPABILITY_NO_AUDIO_ALARMS, - CAL_STATIC_CAPABILITY_NO_PROCEDURE_ALARMS, - CAL_STATIC_CAPABILITY_ONE_ALARM_ONLY, - CAL_STATIC_CAPABILITY_REMOVE_ALARMS, - CAL_STATIC_CAPABILITY_NO_THISANDPRIOR, - CAL_STATIC_CAPABILITY_NO_THISANDFUTURE, - CAL_STATIC_CAPABILITY_NO_CONV_TO_ASSIGN_TASK, - CAL_STATIC_CAPABILITY_NO_TASK_ASSIGNMENT, - CAL_STATIC_CAPABILITY_SAVE_SCHEDULES, - CAL_STATIC_CAPABILITY_NO_ALARM_AFTER_START, - CAL_STATIC_CAPABILITY_NO_MEMO_START_DATE, - CAL_STATIC_CAPABILITY_ALL_DAY_EVENT_AS_TIME, - CAL_STATIC_CAPABILITY_TASK_DATE_ONLY, - e_cal_meta_backend_get_capabilities (E_CAL_META_BACKEND (cbews)), - NULL); - } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS)) { - /* return email address of the person who opened the calendar */ - CamelEwsSettings *ews_settings; - - ews_settings = ecb_ews_get_collection_settings (cbews); - - return camel_ews_settings_dup_email (ews_settings); - } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS)) { - /* ews does not support email based alarms */ - return NULL; - } - - /* Chain up to parent's method. */ - return E_CAL_BACKEND_CLASS (e_cal_backend_ews_parent_class)->get_backend_property (cal_backend, prop_name); -} - -static void -ecb_ews_get_timezone_sync (ECalBackendSync *sync_backend, - EDataCal *cal, - GCancellable *cancellable, - const gchar *tzid, - gchar **tzobject, - GError **error) -{ - GError *local_error = NULL; - - g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend)); - g_return_if_fail (tzid != NULL); - g_return_if_fail (tzobject != NULL); - - *tzobject = NULL; - - E_CAL_BACKEND_SYNC_CLASS (e_cal_backend_ews_parent_class)->get_timezone_sync (sync_backend, cal, cancellable, tzid, tzobject, &local_error); - - if (!*tzobject) { - /* The timezone can be sometimes the Windows zone, try to convert it to libical */ - const gchar *ical_location = e_cal_backend_ews_tz_util_get_ical_equivalent (tzid); - - if (ical_location) - E_CAL_BACKEND_SYNC_CLASS (e_cal_backend_ews_parent_class)->get_timezone_sync (sync_backend, cal, cancellable, ical_location, tzobject, NULL); - } - - if (*tzobject) - g_clear_error (&local_error); - else if (local_error) - g_propagate_error (error, local_error); -} - -static gboolean -ecb_ews_get_destination_address (EBackend *backend, - gchar **host, - guint16 *port) -{ - CamelEwsSettings *ews_settings; - SoupURI *soup_uri; - gchar *host_url; - gboolean result = FALSE; - - g_return_val_if_fail (port != NULL, FALSE); - g_return_val_if_fail (host != NULL, FALSE); - - /* Sanity checking */ - if (!e_cal_backend_get_registry (E_CAL_BACKEND (backend)) || - !e_backend_get_source (backend)) - return FALSE; - - ews_settings = ecb_ews_get_collection_settings (E_CAL_BACKEND_EWS (backend)); - g_return_val_if_fail (ews_settings != NULL, FALSE); - - host_url = camel_ews_settings_dup_hosturl (ews_settings); - g_return_val_if_fail (host_url != NULL, FALSE); - - soup_uri = soup_uri_new (host_url); - if (soup_uri) { - *host = g_strdup (soup_uri_get_host (soup_uri)); - *port = soup_uri_get_port (soup_uri); - - result = *host && **host; - if (!result) { - g_free (*host); - *host = NULL; - } - - soup_uri_free (soup_uri); - } - - g_free (host_url); - - return result; -} - -static gchar * -ecb_ews_dup_component_revision (ECalCache *cal_cache, - icalcomponent *icalcomp, - gpointer user_data) -{ - g_return_val_if_fail (icalcomp != NULL, NULL); - - return e_cal_util_dup_x_property (icalcomp, "X-EVOLUTION-CHANGEKEY"); -} - -static void -ecb_ews_constructed (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - ECalCache *cal_cache; - gchar *cache_dirname; - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->constructed (object); - - /* Reset the connectable, it steals data from Authentication extension, - where is written incorrect address */ - e_backend_set_connectable (E_BACKEND (object), NULL); - - cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbews)); - g_return_if_fail (cal_cache != NULL); - - cache_dirname = g_path_get_dirname (e_cache_get_filename (E_CACHE (cal_cache))); - g_signal_connect (cal_cache, "dup-component-revision", G_CALLBACK (ecb_ews_dup_component_revision), NULL); - - g_clear_object (&cal_cache); - - cbews->priv->attachments_dir = g_build_filename (cache_dirname, "attachments", NULL); - g_mkdir_with_parents (cbews->priv->attachments_dir, 0777); - - g_free (cache_dirname); -} - -static void -ecb_ews_dispose (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - - g_rec_mutex_lock (&cbews->priv->cnc_lock); - - g_clear_object (&cbews->priv->cnc); - - g_rec_mutex_unlock (&cbews->priv->cnc_lock); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->dispose (object); -} - -static void -ecb_ews_finalize (GObject *object) -{ - ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object); - - g_free (cbews->priv->folder_id); - g_free (cbews->priv->attachments_dir); - - g_rec_mutex_clear (&cbews->priv->cnc_lock); - - e_cal_backend_ews_unref_windows_zones (); - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->finalize (object); -} - -static void -e_cal_backend_ews_init (ECalBackendEws *cbews) -{ - cbews->priv = G_TYPE_INSTANCE_GET_PRIVATE (cbews, E_TYPE_CAL_BACKEND_EWS, ECalBackendEwsPrivate); - - g_rec_mutex_init (&cbews->priv->cnc_lock); - - e_cal_backend_ews_populate_windows_zones (); -} - -static void -e_cal_backend_ews_class_init (ECalBackendEwsClass *klass) -{ - GObjectClass *object_class; - EBackendClass *backend_class; - ECalBackendClass *cal_backend_class; - ECalBackendSyncClass *cal_backend_sync_class; - ECalMetaBackendClass *cal_meta_backend_class; - - g_type_class_add_private (klass, sizeof (ECalBackendEwsPrivate)); - - cal_meta_backend_class = E_CAL_META_BACKEND_CLASS (klass); - cal_meta_backend_class->connect_sync = ecb_ews_connect_sync; - cal_meta_backend_class->disconnect_sync = ecb_ews_disconnect_sync; - cal_meta_backend_class->get_changes_sync = ecb_ews_get_changes_sync; - cal_meta_backend_class->load_component_sync = ecb_ews_load_component_sync; - cal_meta_backend_class->save_component_sync = ecb_ews_save_component_sync; - cal_meta_backend_class->remove_component_sync = ecb_ews_remove_component_sync; - - cal_backend_sync_class = E_CAL_BACKEND_SYNC_CLASS (klass); - cal_backend_sync_class->discard_alarm_sync = ecb_ews_discard_alarm_sync; - cal_backend_sync_class->receive_objects_sync = ecb_ews_receive_objects_sync; - cal_backend_sync_class->send_objects_sync = ecb_ews_send_objects_sync; - cal_backend_sync_class->get_free_busy_sync = ecb_ews_get_free_busy_sync; - cal_backend_sync_class->get_timezone_sync = ecb_ews_get_timezone_sync; - - cal_backend_class = E_CAL_BACKEND_CLASS (klass); - cal_backend_class->get_backend_property = ecb_ews_get_backend_property; - - backend_class = E_BACKEND_CLASS (klass); - backend_class->get_destination_address = ecb_ews_get_destination_address; - - object_class = G_OBJECT_CLASS (klass); - object_class->constructed = ecb_ews_constructed; - object_class->dispose = ecb_ews_dispose; - object_class->finalize = ecb_ews_finalize; -} diff --git a/src/camel/camel-ews-store-summary.c b/src/camel/camel-ews-store-summary.c index 43ca032..2251199 100644 --- a/src/camel/camel-ews-store-summary.c +++ b/src/camel/camel-ews-store-summary.c @@ -31,7 +31,6 @@ #define S_UNLOCK(x) (g_rec_mutex_unlock(&(x)->priv->s_lock)) #define STORE_GROUP_NAME "##storepriv" -#define CATEGORIES_KEY "Categories" #define CURRENT_SUMMARY_VERSION 3 struct _CamelEwsStoreSummaryPrivate { @@ -1048,159 +1047,3 @@ camel_ews_store_summary_has_folder (CamelEwsStoreSummary *ews_summary, return ret; } - -static gchar * -camel_ews_category_to_string (const CamelEwsCategory *cat) -{ - gchar *guid, *name, *color_def = NULL, *str; - - g_return_val_if_fail (cat != NULL, NULL); - - guid = g_uri_escape_string (cat->guid, NULL, TRUE); - name = g_uri_escape_string (cat->name, NULL, TRUE); - - if (cat->color_def) - color_def = g_uri_escape_string (cat->color_def, NULL, TRUE); - - str = g_strconcat ( - guid ? guid : "", "\t", - name ? name : "", "\t", - color_def ? color_def : "", - NULL); - - g_free (guid); - g_free (name); - g_free (color_def); - - return str; -} - -static CamelEwsCategory * -camel_ews_category_from_string (const gchar *str) -{ - CamelEwsCategory *cat; - gchar **strv, *guid, *name, *color_def; - - g_return_val_if_fail (str != NULL, NULL); - - strv = g_strsplit (str, "\t", -1); - if (!strv || !strv[0] || !strv[1]) { - g_strfreev (strv); - return NULL; - } - - guid = g_uri_unescape_string (strv[0], NULL); - name = g_uri_unescape_string (strv[1], NULL); - color_def = (strv[2] && strv[2][0]) ? g_uri_unescape_string (strv[2], NULL) : NULL; - - cat = camel_ews_category_new (guid, name, color_def); - - g_free (guid); - g_free (name); - g_free (color_def); - g_strfreev (strv); - - return cat; -} - -GHashTable * /* gchar *guid ~> CamelEwsCategory * */ -camel_ews_store_summary_get_categories (CamelEwsStoreSummary *ews_summary) -{ - GHashTable *categories; - gchar **strv; - g_return_val_if_fail (CAMEL_IS_EWS_STORE_SUMMARY (ews_summary), NULL); - - S_LOCK (ews_summary); - - strv = g_key_file_get_string_list (ews_summary->priv->key_file, STORE_GROUP_NAME, CATEGORIES_KEY, NULL, NULL); - - S_UNLOCK (ews_summary); - - categories = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, camel_ews_category_free); - - if (strv) { - gint ii; - - for (ii = 0; strv[ii]; ii++) { - CamelEwsCategory *cat; - - cat = camel_ews_category_from_string (strv[ii]); - if (cat) - g_hash_table_insert (categories, cat->guid, cat); - } - - g_strfreev (strv); - } - - return categories; -} - -void -camel_ews_store_summary_set_categories (CamelEwsStoreSummary *ews_summary, - GHashTable *categories) /* gchar *guid ~> CamelEwsCategory * */ -{ - GPtrArray *array; - GHashTableIter iter; - gpointer value; - - g_return_if_fail (CAMEL_IS_EWS_STORE_SUMMARY (ews_summary)); - g_return_if_fail (categories != NULL); - - array = g_ptr_array_new_full (g_hash_table_size (categories), g_free); - - g_hash_table_iter_init (&iter, categories); - while (g_hash_table_iter_next (&iter, NULL, &value)) { - CamelEwsCategory *cat = value; - - if (cat) { - gchar *str; - - str = camel_ews_category_to_string (cat); - - if (str) - g_ptr_array_add (array, str); - } - } - - S_LOCK (ews_summary); - - g_key_file_set_string_list (ews_summary->priv->key_file, STORE_GROUP_NAME, CATEGORIES_KEY, - (const gchar * const *) array->pdata, array->len); - - ews_summary->priv->dirty = TRUE; - - S_UNLOCK (ews_summary); - - g_ptr_array_free (array, TRUE); -} - -CamelEwsCategory * -camel_ews_category_new (const gchar *guid, - const gchar *name, - const gchar *color_def) -{ - CamelEwsCategory *cat; - - g_return_val_if_fail (guid != NULL, NULL); - g_return_val_if_fail (name != NULL, NULL); - - cat = g_new0 (CamelEwsCategory, 1); - cat->guid = g_strdup (guid); - cat->name = g_strdup (name); - cat->color_def = g_strdup (color_def); - - return cat; -} - -void -camel_ews_category_free (gpointer ptr) -{ - CamelEwsCategory *cat = ptr; - - if (cat) { - g_free (cat->guid); - g_free (cat->name); - g_free (cat->color_def); - g_free (cat); - } -} diff --git a/src/camel/camel-ews-store-summary.c.sync-category-list b/src/camel/camel-ews-store-summary.c.sync-category-list deleted file mode 100644 index 2251199..0000000 --- a/src/camel/camel-ews-store-summary.c.sync-category-list +++ /dev/null @@ -1,1049 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU 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. - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include -#include "camel-ews-store-summary.h" - -#include "server/e-ews-folder.h" - -#define S_LOCK(x) (g_rec_mutex_lock(&(x)->priv->s_lock)) -#define S_UNLOCK(x) (g_rec_mutex_unlock(&(x)->priv->s_lock)) - -#define STORE_GROUP_NAME "##storepriv" -#define CURRENT_SUMMARY_VERSION 3 - -struct _CamelEwsStoreSummaryPrivate { - GKeyFile *key_file; - gboolean dirty; - gchar *path; - /* Note: We use the *same* strings in both of these hash tables, and - * only id_fname_hash has g_free() hooked up as the destructor func. - * So entries must always be removed from fname_id_hash *first*. */ - GHashTable *id_fname_hash; - GHashTable *fname_id_hash; - GRecMutex s_lock; - - GFileMonitor *monitor_delete; -}; - -G_DEFINE_TYPE (CamelEwsStoreSummary, camel_ews_store_summary, CAMEL_TYPE_OBJECT) - -static void -ews_store_summary_finalize (GObject *object) -{ - CamelEwsStoreSummary *ews_summary = CAMEL_EWS_STORE_SUMMARY (object); - CamelEwsStoreSummaryPrivate *priv = ews_summary->priv; - - g_key_file_free (priv->key_file); - g_free (priv->path); - g_hash_table_destroy (priv->fname_id_hash); - g_hash_table_destroy (priv->id_fname_hash); - g_rec_mutex_clear (&priv->s_lock); - if (priv->monitor_delete) - g_object_unref (priv->monitor_delete); - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (camel_ews_store_summary_parent_class)->finalize (object); -} - -static void -camel_ews_store_summary_class_init (CamelEwsStoreSummaryClass *class) -{ - GObjectClass *object_class; - - g_type_class_add_private (class, sizeof (CamelEwsStoreSummaryPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->finalize = ews_store_summary_finalize; -} - -static void -camel_ews_store_summary_init (CamelEwsStoreSummary *ews_summary) -{ - ews_summary->priv = G_TYPE_INSTANCE_GET_PRIVATE (ews_summary, CAMEL_TYPE_EWS_STORE_SUMMARY, CamelEwsStoreSummaryPrivate); - - ews_summary->priv->key_file = g_key_file_new (); - ews_summary->priv->dirty = FALSE; - ews_summary->priv->fname_id_hash = g_hash_table_new (g_str_hash, g_str_equal); - ews_summary->priv->id_fname_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - g_rec_mutex_init (&ews_summary->priv->s_lock); -} - -static gchar *build_full_name (CamelEwsStoreSummary *ews_summary, const gchar *fid) -{ - gchar *pfid, *dname, *ret; - gchar *pname = NULL; - - dname = camel_ews_store_summary_get_folder_name (ews_summary, fid, NULL); - if (!dname) - return NULL; - - pfid = camel_ews_store_summary_get_parent_folder_id (ews_summary, fid, NULL); - if (pfid) { - pname = build_full_name (ews_summary, pfid); - g_free (pfid); - } - - if (pname) { - ret = g_strdup_printf ("%s/%s", pname, dname); - g_free (pname); - g_free (dname); - } else - ret = dname; - - return ret; -} - -static void -load_id_fname_hash (CamelEwsStoreSummary *ews_summary) -{ - GSList *folders, *l; - - g_hash_table_remove_all (ews_summary->priv->fname_id_hash); - g_hash_table_remove_all (ews_summary->priv->id_fname_hash); - - folders = camel_ews_store_summary_get_folders (ews_summary, NULL); - - for (l = folders; l != NULL; l = g_slist_next (l)) { - gchar *id = l->data; - gchar *fname; - - fname = build_full_name (ews_summary, id); - - if (!fname) { - /* eep */ - g_warning ("Cannot build full name for folder %s", id); - g_free (id); - continue; - } - g_hash_table_insert (ews_summary->priv->fname_id_hash, fname, id); - g_hash_table_insert (ews_summary->priv->id_fname_hash, id, fname); - } - - g_slist_free (folders); -} - -/* we only care about delete and ignore create */ -static void -monitor_delete_cb (GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event, - gpointer user_data) -{ - CamelEwsStoreSummary *ews_summary = (CamelEwsStoreSummary *) user_data; - - if (event == G_FILE_MONITOR_EVENT_DELETED) { - S_LOCK (ews_summary); - - if (ews_summary->priv->key_file) - camel_ews_store_summary_clear (ews_summary); - - S_UNLOCK (ews_summary); - } -} - -CamelEwsStoreSummary * -camel_ews_store_summary_new (const gchar *path) -{ - CamelEwsStoreSummary *ews_summary; - GError *error = NULL; - GFile *file; - - ews_summary = g_object_new (CAMEL_TYPE_EWS_STORE_SUMMARY, NULL); - - ews_summary->priv->path = g_strdup (path); - file = g_file_new_for_path (path); - ews_summary->priv->monitor_delete = g_file_monitor_file ( - file, G_FILE_MONITOR_SEND_MOVED, NULL, &error); - - /* Remove this once we have camel_store_remove_storage api, - * which should be available from 3.2 */ - if (!error) { - g_signal_connect ( - ews_summary->priv->monitor_delete, "changed", - G_CALLBACK (monitor_delete_cb), ews_summary); - } else { - g_warning ( - "CamelEwsStoreSummary: " - "Error create monitor_delete: %s \n", - error->message); - g_clear_error (&error); - } - - g_object_unref (file); - - return ews_summary; -} - -gboolean -camel_ews_store_summary_load (CamelEwsStoreSummary *ews_summary, - GError **error) -{ - CamelEwsStoreSummaryPrivate *priv = ews_summary->priv; - gboolean ret; - gint version; - - S_LOCK (ews_summary); - - ret = g_key_file_load_from_file ( - priv->key_file, priv->path, 0, error); - - version = g_key_file_get_integer ( - priv->key_file, STORE_GROUP_NAME, "Version", NULL); - - if (version != CURRENT_SUMMARY_VERSION) { - /* version doesn't match, get folders again */ - camel_ews_store_summary_clear (ews_summary); - - g_key_file_set_integer ( - priv->key_file, STORE_GROUP_NAME, - "Version", CURRENT_SUMMARY_VERSION); - } - - load_id_fname_hash (ews_summary); - - S_UNLOCK (ews_summary); - - return ret; -} - -gboolean -camel_ews_store_summary_save (CamelEwsStoreSummary *ews_summary, - GError **error) -{ - CamelEwsStoreSummaryPrivate *priv = ews_summary->priv; - gboolean ret = TRUE; - GFile *file; - gchar *contents = NULL; - - S_LOCK (ews_summary); - - if (!priv->dirty) - goto exit; - - contents = g_key_file_to_data ( - priv->key_file, NULL, NULL); - file = g_file_new_for_path (priv->path); - ret = g_file_replace_contents ( - file, contents, strlen (contents), - NULL, FALSE, G_FILE_CREATE_PRIVATE, - NULL, NULL, error); - g_object_unref (file); - priv->dirty = FALSE; - -exit: - S_UNLOCK (ews_summary); - - g_free (contents); - return ret; -} - -gboolean -camel_ews_store_summary_clear (CamelEwsStoreSummary *ews_summary) -{ - - S_LOCK (ews_summary); - - g_key_file_free (ews_summary->priv->key_file); - ews_summary->priv->key_file = g_key_file_new (); - ews_summary->priv->dirty = TRUE; - - S_UNLOCK (ews_summary); - - return TRUE; -} - -gboolean -camel_ews_store_summary_remove (CamelEwsStoreSummary *ews_summary) -{ - gint ret; - - S_LOCK (ews_summary); - - if (ews_summary->priv->key_file) - camel_ews_store_summary_clear (ews_summary); - - ret = g_unlink (ews_summary->priv->path); - - S_UNLOCK (ews_summary); - - return (ret == 0); -} - -void -camel_ews_store_summary_rebuild_hashes (CamelEwsStoreSummary *ews_summary) -{ - g_return_if_fail (CAMEL_IS_EWS_STORE_SUMMARY (ews_summary)); - - S_LOCK (ews_summary); - load_id_fname_hash (ews_summary); - S_UNLOCK (ews_summary); -} - -struct subfolder_match { - GSList *ids; - gchar *match; - gsize matchlen; -}; - -static void -match_subfolder (gpointer key, - gpointer value, - gpointer user_data) -{ - struct subfolder_match *sm = user_data; - - if (!strncmp (key, sm->match, sm->matchlen)) - sm->ids = g_slist_prepend (sm->ids, g_strdup (value)); -} - -/* Must be called with the summary lock held, and gets to keep - * both its string arguments */ -static void -ews_ss_hash_replace (CamelEwsStoreSummary *ews_summary, - gchar *folder_id, - gchar *full_name, - gboolean recurse) -{ - const gchar *ofname; - struct subfolder_match sm = { NULL, NULL }; - - if (!full_name) - full_name = build_full_name (ews_summary, folder_id); - - ofname = g_hash_table_lookup ( - ews_summary->priv->id_fname_hash, folder_id); - /* Remove the old fullname->id hash entry *iff* it's pointing - * to this folder id. */ - if (ofname) { - gchar *ofid = g_hash_table_lookup ( - ews_summary->priv->fname_id_hash, ofname); - if (ofid && !strcmp (folder_id, ofid)) { - g_hash_table_remove ( - ews_summary->priv->fname_id_hash, ofname); - if (recurse) - sm.match = g_strdup_printf ("%s/", ofname); - } - } - - g_hash_table_insert (ews_summary->priv->fname_id_hash, full_name, folder_id); - - /* Replace, not insert. The difference is that it frees the *old* folder_id - * key, not the new one which we just inserted into fname_id_hash too. */ - g_hash_table_replace (ews_summary->priv->id_fname_hash, folder_id, full_name); - - if (sm.match) { - GSList *l; - - sm.matchlen = strlen (sm.match); - - g_hash_table_foreach ( - ews_summary->priv->fname_id_hash, - match_subfolder, &sm); - - for (l = sm.ids; l; l = g_slist_next (l)) - ews_ss_hash_replace (ews_summary, l->data, NULL, FALSE); - - g_slist_free (sm.ids); - g_free (sm.match); - } -} - -void -camel_ews_store_summary_set_folder_name (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - const gchar *display_name) -{ - S_LOCK (ews_summary); - - g_key_file_set_string ( - ews_summary->priv->key_file, - folder_id, "DisplayName", display_name); - - ews_ss_hash_replace (ews_summary, g_strdup (folder_id), NULL, TRUE); - ews_summary->priv->dirty = TRUE; - - S_UNLOCK (ews_summary); -} - -void -camel_ews_store_summary_new_folder (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - const gchar *parent_fid, - const gchar *change_key, - const gchar *display_name, - EEwsFolderType folder_type, - guint64 folder_flags, - guint64 total, - gboolean foreign, - gboolean public_folder) -{ - const gchar *folder_type_nick; - - /* Store the folder type by its nickname. */ - folder_type_nick = e_ews_folder_type_to_nick (folder_type); - g_return_if_fail (folder_type_nick != NULL); - - S_LOCK (ews_summary); - - if (parent_fid) - g_key_file_set_string ( - ews_summary->priv->key_file, - folder_id, "ParentFolderId", parent_fid); - if (change_key) - g_key_file_set_string ( - ews_summary->priv->key_file, - folder_id, "ChangeKey", change_key); - g_key_file_set_string ( - ews_summary->priv->key_file, - folder_id, "DisplayName", display_name); - g_key_file_set_string ( - ews_summary->priv->key_file, - folder_id, "FolderType", folder_type_nick); - if (folder_flags) - g_key_file_set_uint64 ( - ews_summary->priv->key_file, - folder_id, "Flags", folder_flags); - g_key_file_set_uint64 ( - ews_summary->priv->key_file, - folder_id, "Total", total); - g_key_file_set_boolean ( - ews_summary->priv->key_file, - folder_id, "Foreign", foreign); - g_key_file_set_boolean ( - ews_summary->priv->key_file, - folder_id, "Public", public_folder); - - ews_ss_hash_replace (ews_summary, g_strdup (folder_id), NULL, FALSE); - - ews_summary->priv->dirty = TRUE; - - S_UNLOCK (ews_summary); -} - -void -camel_ews_store_summary_set_parent_folder_id (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - const gchar *parent_id) -{ - S_LOCK (ews_summary); - - if (parent_id) - g_key_file_set_string ( - ews_summary->priv->key_file, - folder_id, "ParentFolderId", parent_id); - else - g_key_file_remove_key ( - ews_summary->priv->key_file, - folder_id, "ParentFolderId", NULL); - - ews_ss_hash_replace (ews_summary, g_strdup (folder_id), NULL, TRUE); - - ews_summary->priv->dirty = TRUE; - - S_UNLOCK (ews_summary); -} - -void -camel_ews_store_summary_set_change_key (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - const gchar *change_key) -{ - S_LOCK (ews_summary); - - g_key_file_set_string ( - ews_summary->priv->key_file, - folder_id, "ChangeKey", change_key); - ews_summary->priv->dirty = TRUE; - - S_UNLOCK (ews_summary); -} - -void -camel_ews_store_summary_set_sync_state (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - const gchar *sync_state) -{ - S_LOCK (ews_summary); - - g_key_file_set_string ( - ews_summary->priv->key_file, - folder_id, "SyncState", sync_state); - ews_summary->priv->dirty = TRUE; - - S_UNLOCK (ews_summary); -} - -void -camel_ews_store_summary_set_folder_flags (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - guint64 flags) -{ - S_LOCK (ews_summary); - - g_key_file_set_uint64 ( - ews_summary->priv->key_file, - folder_id, "Flags", flags); - ews_summary->priv->dirty = TRUE; - - S_UNLOCK (ews_summary); -} - -void -camel_ews_store_summary_set_folder_unread (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - guint64 unread) -{ - S_LOCK (ews_summary); - - g_key_file_set_uint64 ( - ews_summary->priv->key_file, - folder_id, "UnRead", unread); - ews_summary->priv->dirty = TRUE; - - S_UNLOCK (ews_summary); -} - -void -camel_ews_store_summary_set_folder_total (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - guint64 total) -{ - S_LOCK (ews_summary); - - g_key_file_set_uint64 ( - ews_summary->priv->key_file, - folder_id, "Total", total); - ews_summary->priv->dirty = TRUE; - - S_UNLOCK (ews_summary); -} - -void -camel_ews_store_summary_set_folder_type (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - EEwsFolderType folder_type) -{ - const gchar *folder_type_nick; - - /* Store the folder type by its nickname. */ - folder_type_nick = e_ews_folder_type_to_nick (folder_type); - g_return_if_fail (folder_type_nick != NULL); - - S_LOCK (ews_summary); - - g_key_file_set_string ( - ews_summary->priv->key_file, - folder_id, "FolderType", folder_type_nick); - ews_summary->priv->dirty = TRUE; - - S_UNLOCK (ews_summary); -} - -void -camel_ews_store_summary_set_foreign (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - gboolean is_foreign) -{ - S_LOCK (ews_summary); - - g_key_file_set_boolean ( - ews_summary->priv->key_file, - folder_id, "Foreign", is_foreign); - ews_summary->priv->dirty = TRUE; - - S_UNLOCK (ews_summary); -} - -void -camel_ews_store_summary_set_foreign_subfolders (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - gboolean foreign_subfolders) -{ - S_LOCK (ews_summary); - - g_key_file_set_boolean ( - ews_summary->priv->key_file, - folder_id, "ForeignSubfolders", foreign_subfolders); - ews_summary->priv->dirty = TRUE; - - S_UNLOCK (ews_summary); -} - -void -camel_ews_store_summary_set_public (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - gboolean is_public) -{ - S_LOCK (ews_summary); - - g_key_file_set_boolean ( - ews_summary->priv->key_file, - folder_id, "Public", is_public); - ews_summary->priv->dirty = TRUE; - - S_UNLOCK (ews_summary); -} - -void -camel_ews_store_summary_store_string_val (CamelEwsStoreSummary *ews_summary, - const gchar *key, - const gchar *value) -{ - S_LOCK (ews_summary); - - g_key_file_set_string ( - ews_summary->priv->key_file, - STORE_GROUP_NAME, key, value); - ews_summary->priv->dirty = TRUE; - - S_UNLOCK (ews_summary); -} - -gchar * -camel_ews_store_summary_get_folder_name (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error) -{ - gchar *ret; - - S_LOCK (ews_summary); - - ret = g_key_file_get_string ( - ews_summary->priv->key_file, folder_id, - "DisplayName", error); - - S_UNLOCK (ews_summary); - - return ret; -} - -gchar * -camel_ews_store_summary_get_folder_full_name (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error) -{ - gchar *ret; - - S_LOCK (ews_summary); - - ret = g_hash_table_lookup (ews_summary->priv->id_fname_hash, folder_id); - - if (ret) - ret = g_strdup (ret); - - S_UNLOCK (ews_summary); - - return ret; -} - -gchar * -camel_ews_store_summary_get_parent_folder_id (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error) -{ - gchar *ret; - - S_LOCK (ews_summary); - - ret = g_key_file_get_string ( - ews_summary->priv->key_file, folder_id, - "ParentFolderId", error); - - S_UNLOCK (ews_summary); - - return ret; -} - -gchar * -camel_ews_store_summary_get_change_key (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error) -{ - gchar *ret; - - S_LOCK (ews_summary); - - ret = g_key_file_get_string ( - ews_summary->priv->key_file, folder_id, - "ChangeKey", error); - - S_UNLOCK (ews_summary); - - return ret; -} - -gchar * -camel_ews_store_summary_get_sync_state (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error) -{ - gchar *ret; - - S_LOCK (ews_summary); - - ret = g_key_file_get_string ( - ews_summary->priv->key_file, folder_id, - "SyncState", error); - - S_UNLOCK (ews_summary); - - return ret; -} - -guint64 -camel_ews_store_summary_get_folder_flags (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error) -{ - guint64 ret; - - S_LOCK (ews_summary); - - ret = g_key_file_get_uint64 ( - ews_summary->priv->key_file, folder_id, - "Flags", error); - - S_UNLOCK (ews_summary); - - return ret; -} - -guint64 -camel_ews_store_summary_get_folder_unread (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error) -{ - guint64 ret; - - S_LOCK (ews_summary); - - ret = g_key_file_get_uint64 ( - ews_summary->priv->key_file, folder_id, - "UnRead", error); - - S_UNLOCK (ews_summary); - - return ret; -} - -guint64 -camel_ews_store_summary_get_folder_total (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error) -{ - guint64 ret; - - S_LOCK (ews_summary); - - ret = g_key_file_get_uint64 ( - ews_summary->priv->key_file, folder_id, - "Total", error); - - S_UNLOCK (ews_summary); - - return ret; -} - -EEwsFolderType -camel_ews_store_summary_get_folder_type (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error) -{ - EEwsFolderType folder_type; - gchar *folder_type_nick; - - S_LOCK (ews_summary); - - folder_type_nick = g_key_file_get_string ( - ews_summary->priv->key_file, - folder_id, "FolderType", error); - - S_UNLOCK (ews_summary); - - /* Look up the folder type by its nickname. */ - if (folder_type_nick != NULL) - folder_type = e_ews_folder_type_from_nick (folder_type_nick); - else - folder_type = E_EWS_FOLDER_TYPE_UNKNOWN; - - g_free (folder_type_nick); - - return folder_type; -} - -gboolean -camel_ews_store_summary_get_foreign (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error) -{ - gboolean ret; - - S_LOCK (ews_summary); - - ret = g_key_file_get_boolean ( - ews_summary->priv->key_file, folder_id, "Foreign", error); - - S_UNLOCK (ews_summary); - - return ret; -} - -gboolean -camel_ews_store_summary_get_foreign_subfolders (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error) -{ - gboolean ret; - - S_LOCK (ews_summary); - - ret = g_key_file_get_boolean ( - ews_summary->priv->key_file, folder_id, "ForeignSubfolders", error); - - S_UNLOCK (ews_summary); - - return ret; -} - -gboolean -camel_ews_store_summary_get_public (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error) -{ - gboolean ret; - - S_LOCK (ews_summary); - - ret = g_key_file_get_boolean ( - ews_summary->priv->key_file, folder_id, "Public", error); - - S_UNLOCK (ews_summary); - - return ret; -} - -gchar * -camel_ews_store_summary_get_string_val (CamelEwsStoreSummary *ews_summary, - const gchar *key, - GError **error) -{ - gchar *ret; - - S_LOCK (ews_summary); - - ret = g_key_file_get_string ( - ews_summary->priv->key_file, STORE_GROUP_NAME, - key, error); - - S_UNLOCK (ews_summary); - - return ret; -} - -GSList * -camel_ews_store_summary_get_folders (CamelEwsStoreSummary *ews_summary, - const gchar *prefix) -{ - GSList *folders = NULL; - gchar **groups = NULL; - gsize length; - gint prefixlen = 0; - gint i; - - if (prefix) - prefixlen = strlen (prefix); - - S_LOCK (ews_summary); - - groups = g_key_file_get_groups (ews_summary->priv->key_file, &length); - - S_UNLOCK (ews_summary); - - for (i = 0; i < length; i++) { - if (!g_ascii_strcasecmp (groups[i], STORE_GROUP_NAME)) - continue; - if (prefixlen) { - const gchar *fname; - - fname = g_hash_table_lookup ( - ews_summary->priv->id_fname_hash, groups[i]); - - if (!fname || strncmp (fname, prefix, prefixlen) || - (fname[prefixlen] && fname[prefixlen] != '/')) - continue; - } - folders = g_slist_append (folders, g_strdup (groups[i])); - } - - g_strfreev (groups); - return folders; -} - -/* get list of folder IDs, which are foreign folders */ -GSList * -camel_ews_store_summary_get_foreign_folders (CamelEwsStoreSummary *ews_summary, - const gchar *prefix) -{ - GSList *folders = NULL; - gchar **groups = NULL; - gsize length; - gint prefixlen = 0; - gint i; - - if (prefix) - prefixlen = strlen (prefix); - - S_LOCK (ews_summary); - - groups = g_key_file_get_groups (ews_summary->priv->key_file, &length); - - S_UNLOCK (ews_summary); - - for (i = 0; i < length; i++) { - if (!g_ascii_strcasecmp (groups[i], STORE_GROUP_NAME)) - continue; - - if (!camel_ews_store_summary_get_foreign (ews_summary, groups[i], NULL)) - continue; - - if (prefixlen) { - const gchar *fname; - - fname = g_hash_table_lookup ( - ews_summary->priv->id_fname_hash, groups[i]); - - if (!fname || strncmp (fname, prefix, prefixlen) || - (fname[prefixlen] && fname[prefixlen] != '/')) - continue; - } - - folders = g_slist_append (folders, g_strdup (groups[i])); - } - - g_strfreev (groups); - - return folders; -} - -gboolean -camel_ews_store_summary_remove_folder (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error) -{ - gboolean ret = FALSE; - gchar *full_name; - - S_LOCK (ews_summary); - - full_name = g_hash_table_lookup (ews_summary->priv->id_fname_hash, folder_id); - if (!full_name) - goto unlock; - - ret = g_key_file_remove_group ( - ews_summary->priv->key_file, folder_id, error); - - g_hash_table_remove (ews_summary->priv->fname_id_hash, full_name); - g_hash_table_remove (ews_summary->priv->id_fname_hash, folder_id); - - ews_summary->priv->dirty = TRUE; - - unlock: - S_UNLOCK (ews_summary); - - return ret; -} - -gchar * -camel_ews_store_summary_get_folder_id_from_name (CamelEwsStoreSummary *ews_summary, - const gchar *folder_name) -{ - gchar *folder_id; - - g_return_val_if_fail (ews_summary != NULL, NULL); - g_return_val_if_fail (folder_name != NULL, NULL); - - S_LOCK (ews_summary); - - folder_id = g_hash_table_lookup (ews_summary->priv->fname_id_hash, folder_name); - if (folder_id) - folder_id = g_strdup (folder_id); - - S_UNLOCK (ews_summary); - - return folder_id; -} - -gchar * -camel_ews_store_summary_get_folder_id_from_folder_type (CamelEwsStoreSummary *ews_summary, - guint64 folder_type) -{ - gchar *folder_id = NULL; - GSList *folders, *l; - - g_return_val_if_fail (ews_summary != NULL, NULL); - g_return_val_if_fail ((folder_type & CAMEL_FOLDER_TYPE_MASK) != 0, NULL); - - folder_type = folder_type & CAMEL_FOLDER_TYPE_MASK; - - S_LOCK (ews_summary); - - folders = camel_ews_store_summary_get_folders (ews_summary, NULL); - - for (l = folders; l != NULL; l = g_slist_next (l)) { - gchar *id = l->data; - guint64 folder_flags; - - folder_flags = camel_ews_store_summary_get_folder_flags ( - ews_summary, id, NULL); - if ((folder_flags & CAMEL_FOLDER_TYPE_MASK) == folder_type && - (folder_flags & CAMEL_FOLDER_SYSTEM) != 0) { - folder_id = id; - l->data = NULL; - break; - } - } - - g_slist_free_full (folders, g_free); - - S_UNLOCK (ews_summary); - - return folder_id; -} - -gboolean -camel_ews_store_summary_has_folder (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id) -{ - gboolean ret; - - S_LOCK (ews_summary); - - ret = g_key_file_has_group (ews_summary->priv->key_file, folder_id); - - S_UNLOCK (ews_summary); - - return ret; -} diff --git a/src/camel/camel-ews-store-summary.h b/src/camel/camel-ews-store-summary.h index 1c6723c..0ae13e3 100644 --- a/src/camel/camel-ews-store-summary.h +++ b/src/camel/camel-ews-store-summary.h @@ -50,12 +50,6 @@ G_BEGIN_DECLS -typedef struct _CamelEwsCategory { - gchar *guid; - gchar *name; - gchar *color_def; -} CamelEwsCategory; - typedef struct _CamelEwsStoreSummary CamelEwsStoreSummary; typedef struct _CamelEwsStoreSummaryClass CamelEwsStoreSummaryClass; typedef struct _CamelEwsStoreSummaryPrivate CamelEwsStoreSummaryPrivate; @@ -221,17 +215,6 @@ gchar * camel_ews_store_summary_get_folder_id_from_folder_type gboolean camel_ews_store_summary_has_folder (CamelEwsStoreSummary *ews_summary, const gchar *id); -GHashTable * camel_ews_store_summary_get_categories /* gchar *guid ~> CamelEwsCategory * */ - (CamelEwsStoreSummary *ews_summary); -void camel_ews_store_summary_set_categories - (CamelEwsStoreSummary *ews_summary, - GHashTable *categories); /* gchar *guid ~> CamelEwsCategory * */ - -CamelEwsCategory * - camel_ews_category_new (const gchar *guid, - const gchar *name, - const gchar *color_def); -void camel_ews_category_free (gpointer ptr); /* CamelEwsCategory * */ G_END_DECLS diff --git a/src/camel/camel-ews-store-summary.h.sync-category-list b/src/camel/camel-ews-store-summary.h.sync-category-list deleted file mode 100644 index 0ae13e3..0000000 --- a/src/camel/camel-ews-store-summary.h.sync-category-list +++ /dev/null @@ -1,221 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU 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. - */ - -#ifndef CAMEL_EWS_STORE_SUMMARY_H -#define CAMEL_EWS_STORE_SUMMARY_H - -#include - -#include "server/e-ews-enums.h" - -/* Standard GObject macros */ -#define CAMEL_TYPE_EWS_STORE_SUMMARY \ - (camel_ews_store_summary_get_type ()) -#define CAMEL_EWS_STORE_SUMMARY(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), CAMEL_TYPE_EWS_STORE_SUMMARY, CamelEwsStoreSummary)) -#define CAMEL_EWS_STORE_SUMMARY_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), CAMEL_TYPE_EWS_STORE_SUMMARY, CamelEwsStoreSummaryClass)) -#define CAMEL_IS_EWS_STORE_SUMMARY(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), CAMEL_TYPE_EWS_STORE_SUMMARY)) -#define CAMEL_IS_EWS_STORE_SUMMARY_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), CAMEL_TYPE_EWS_STORE_SUMMARY)) -#define CAMEL_EWS_STORE_SUMMARY_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), CAMEL_TYPE_EWS_STORE_SUMMARY, CamelEwsStoreSummaryClass)) - -/* the last possible value from CAMEL_FOLDER_TYPE_MASK range */ -#define CAMEL_EWS_FOLDER_TYPE_JOURNAL \ - (((CAMEL_FOLDER_TYPE_MASK >> CAMEL_FOLDER_TYPE_BIT) - 1) << \ - CAMEL_FOLDER_TYPE_BIT) - -G_BEGIN_DECLS - -typedef struct _CamelEwsStoreSummary CamelEwsStoreSummary; -typedef struct _CamelEwsStoreSummaryClass CamelEwsStoreSummaryClass; -typedef struct _CamelEwsStoreSummaryPrivate CamelEwsStoreSummaryPrivate; - -struct _CamelEwsStoreSummary { - CamelObject parent; - CamelEwsStoreSummaryPrivate *priv; -}; - -struct _CamelEwsStoreSummaryClass { - CamelObjectClass parent_class; -}; - -GType camel_ews_store_summary_get_type (void); - -CamelEwsStoreSummary * - camel_ews_store_summary_new (const gchar *path); -gboolean camel_ews_store_summary_load (CamelEwsStoreSummary *ews_summary, - GError **error); -gboolean camel_ews_store_summary_save (CamelEwsStoreSummary *ews_summary, - GError **error); -gboolean camel_ews_store_summary_clear (CamelEwsStoreSummary *ews_summary); -gboolean camel_ews_store_summary_remove (CamelEwsStoreSummary *ews_summary); -void camel_ews_store_summary_rebuild_hashes - (CamelEwsStoreSummary *ews_summary); - -void camel_ews_store_summary_set_folder_name - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - const gchar *display_name); -void camel_ews_store_summary_set_parent_folder_id - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - const gchar *parent_id); -void camel_ews_store_summary_set_change_key - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - const gchar *change_key); -void camel_ews_store_summary_set_sync_state - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - const gchar *sync_state); -void camel_ews_store_summary_set_folder_flags - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - guint64 flags); -void camel_ews_store_summary_set_folder_unread - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - guint64 unread); -void camel_ews_store_summary_set_folder_total - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - guint64 total); -void camel_ews_store_summary_set_folder_type - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - EEwsFolderType folder_type); -void camel_ews_store_summary_set_foreign - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - gboolean is_foreign); -void camel_ews_store_summary_set_foreign_subfolders - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - gboolean foreign_subfolders); -void camel_ews_store_summary_set_public - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - gboolean is_public); - -gchar * camel_ews_store_summary_get_folder_name - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error); -gchar *camel_ews_store_summary_get_folder_full_name - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error); -gchar * camel_ews_store_summary_get_parent_folder_id - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error); -gchar * camel_ews_store_summary_get_change_key - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error); -gchar * camel_ews_store_summary_get_sync_state - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error); -guint64 camel_ews_store_summary_get_folder_flags - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error); -guint64 camel_ews_store_summary_get_folder_unread - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error); -guint64 camel_ews_store_summary_get_folder_total - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error); -EEwsFolderType camel_ews_store_summary_get_folder_type - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error); -gboolean camel_ews_store_summary_get_foreign - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error); -gboolean camel_ews_store_summary_get_foreign_subfolders - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error); -gboolean camel_ews_store_summary_get_public - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error); - -GSList * camel_ews_store_summary_get_folders - (CamelEwsStoreSummary *ews_summary, - const gchar *prefix); -GSList * camel_ews_store_summary_get_foreign_folders - (CamelEwsStoreSummary *ews_summary, - const gchar *prefix); - -void camel_ews_store_summary_store_string_val - (CamelEwsStoreSummary *ews_summary, - const gchar *key, - const gchar *value); - -gchar * camel_ews_store_summary_get_string_val - (CamelEwsStoreSummary *ews_summary, - const gchar *key, - GError **error); - -gboolean camel_ews_store_summary_remove_folder - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - GError **error); - -void camel_ews_store_summary_new_folder - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_id, - const gchar *parent_fid, - const gchar *change_key, - const gchar *display_name, - EEwsFolderType folder_type, - guint64 folder_flags, - guint64 total, - gboolean foreign, - gboolean public_folder); - -gchar * camel_ews_store_summary_get_folder_id_from_name - (CamelEwsStoreSummary *ews_summary, - const gchar *folder_name); - -gchar * camel_ews_store_summary_get_folder_id_from_folder_type - (CamelEwsStoreSummary *ews_summary, - guint64 folder_type); - -gboolean camel_ews_store_summary_has_folder - (CamelEwsStoreSummary *ews_summary, - const gchar *id); - -G_END_DECLS - -#endif /* CAMEL_EWS_STORE_SUMMARY_H */ diff --git a/src/camel/camel-ews-store.c b/src/camel/camel-ews-store.c index cc9ae38..7cf14ca 100644 --- a/src/camel/camel-ews-store.c +++ b/src/camel/camel-ews-store.c @@ -673,43 +673,6 @@ ews_update_has_ooo_set (CamelSession *session, g_clear_object (&oof_settings); } -static void -ews_exchange_server_categories_cb (CamelSession *session, - GCancellable *cancellable, - gpointer user_data, - GError **error) -{ - CamelEwsStore *ews_store = user_data; - EEwsConnection *cnc; - EwsFolderId fid = { 0 }; - gchar *properties = NULL; - GError *local_error = NULL; - - cnc = camel_ews_store_ref_connection (ews_store); - if (!cnc) - return; - - fid.id = (gchar *) "calendar"; - fid.is_distinguished_id = TRUE; - - if (e_ews_connection_get_user_configuration_sync (cnc, G_PRIORITY_DEFAULT, &fid, "CategoryList", - E_EWS_USER_CONFIGURATION_PROPERTIES_XMLDATA, &properties, cancellable, &local_error) && properties) { - guchar *data; - gsize data_len = 0; - - data = g_base64_decode (properties, &data_len); - - if (data && data_len > 0) - camel_ews_utils_merge_category_list (ews_store, data, data_len); - - g_free (data); - } - - g_clear_error (&local_error); - g_clear_object (&cnc); - g_free (properties); -} - struct ScheduleUpdateData { GCancellable *cancellable; @@ -1289,12 +1252,6 @@ ews_connect_sync (CamelService *service, g_object_ref (ews_store), g_object_unref); - camel_session_submit_job ( - session, _("Look up Exchange server categories"), - ews_exchange_server_categories_cb, - g_object_ref (ews_store), - g_object_unref); - if (!priv->updates_cancellable) priv->updates_cancellable = g_cancellable_new (); @@ -1874,8 +1831,6 @@ ews_authenticate_sync (CamelService *service, const gchar *password; gchar *hosturl; gchar *old_sync_state = NULL, *new_sync_state = NULL; - gchar *certificate_pem = NULL; - GTlsCertificateFlags certificate_errors = 0; GError *local_error = NULL; ews_store = CAMEL_EWS_STORE (service); @@ -2004,18 +1959,6 @@ ews_authenticate_sync (CamelService *service, g_slist_free_full (created_folder_ids, g_free); - if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) && - e_ews_connection_get_ssl_error_details (connection, &certificate_pem, &certificate_errors)) { - source = e_ews_connection_get_source (connection); - - if (source) { - e_source_emit_credentials_required (source, E_SOURCE_CREDENTIALS_REASON_SSL_FAILED, - certificate_pem, certificate_errors, local_error); - } - - g_free (certificate_pem); - } - if (local_error == NULL) { result = CAMEL_AUTHENTICATION_ACCEPTED; } else if (g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED)) { @@ -2420,17 +2363,6 @@ ews_get_folder_info_sync (CamelStore *store, ews_store = (CamelEwsStore *) store; priv = ews_store->priv; - if ((flags & CAMEL_STORE_FOLDER_INFO_REFRESH) != 0 && - camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store))) { - CamelSession *session; - - session = camel_service_ref_session (CAMEL_SERVICE (ews_store)); - if (session) { - ews_exchange_server_categories_cb (session, cancellable, ews_store, NULL); - g_object_unref (session); - } - } - if ((flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST) != 0) { gboolean includes_last_folder = TRUE; GSList *folders = NULL, *to_check = NULL; diff --git a/src/camel/camel-ews-store.c.cve-2019-3890 b/src/camel/camel-ews-store.c.cve-2019-3890 deleted file mode 100644 index 7cf14ca..0000000 --- a/src/camel/camel-ews-store.c.cve-2019-3890 +++ /dev/null @@ -1,3783 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* camel-ews-store.c : class for an ews store */ - -/* - * Authors: - * Chenthill Palanisamy - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU 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. - * - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "server/camel-ews-settings.h" -#include "server/e-ews-item-change.h" -#include "server/e-ews-message.h" -#include "server/e-ews-oof-settings.h" - -#include "camel-ews-folder.h" -#include "camel-ews-store.h" -#include "camel-ews-summary.h" -#include "camel-ews-utils.h" -#include "camel-ews-enumtypes.h" - -#ifdef G_OS_WIN32 -#include -#include -#endif - -#define d(x) x -#define CURSOR_ITEM_LIMIT 100 - -#define CAMEL_EWS_STORE_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), CAMEL_TYPE_EWS_STORE, CamelEwsStorePrivate)) - -#define FINFO_REFRESH_INTERVAL 60 - -#define UPDATE_LOCK(x) (g_rec_mutex_lock(&(x)->priv->update_lock)) -#define UPDATE_UNLOCK(x) (g_rec_mutex_unlock(&(x)->priv->update_lock)) - -struct _CamelEwsStorePrivate { - time_t last_refresh_time; - GMutex get_finfo_lock; - EEwsConnection *connection; - GMutex connection_lock; - gboolean has_ooo_set; - CamelEwsStoreOooAlertState ooo_alert_state; - gint password_expires_in_days; - - gboolean listen_notifications; - guint subscription_key; - guint update_folder_id; - guint update_folder_list_id; - GCancellable *updates_cancellable; - GSList *update_folder_names; - GRecMutex update_lock; - - GSList *public_folders; /* EEwsFolder * objects */ -}; - -static gboolean ews_store_construct (CamelService *service, CamelSession *session, - CamelProvider *provider, GError **error); - -static void camel_ews_store_initable_init (GInitableIface *iface); -static void camel_ews_subscribable_init (CamelSubscribableInterface *iface); -static GInitableIface *parent_initable_interface; - -static CamelFolderInfo *folder_info_from_store_summary (CamelEwsStore *store, - const gchar *top, - guint32 flags, - GCancellable *cancellable, - GError **error); - -enum { - PROP_0, - PROP_HAS_OOO_SET, - PROP_OOO_ALERT_STATE, - PROP_CONNECTABLE, - PROP_HOST_REACHABLE -}; - -G_DEFINE_TYPE_WITH_CODE ( - CamelEwsStore, camel_ews_store, CAMEL_TYPE_OFFLINE_STORE, - G_IMPLEMENT_INTERFACE ( - G_TYPE_INITABLE, camel_ews_store_initable_init) - G_IMPLEMENT_INTERFACE ( - CAMEL_TYPE_NETWORK_SERVICE, NULL) - G_IMPLEMENT_INTERFACE ( - CAMEL_TYPE_SUBSCRIBABLE, camel_ews_subscribable_init)) - -static void -ews_store_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_HAS_OOO_SET: - camel_ews_store_set_has_ooo_set ( - CAMEL_EWS_STORE (object), - g_value_get_boolean (value)); - return; - case PROP_OOO_ALERT_STATE: - camel_ews_store_set_ooo_alert_state ( - CAMEL_EWS_STORE (object), - g_value_get_enum (value)); - return; - case PROP_CONNECTABLE: - camel_network_service_set_connectable ( - CAMEL_NETWORK_SERVICE (object), - g_value_get_object (value)); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -ews_store_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_HAS_OOO_SET: - g_value_set_boolean ( - value, - camel_ews_store_get_has_ooo_set ( - CAMEL_EWS_STORE (object))); - return; - case PROP_OOO_ALERT_STATE: - g_value_set_enum ( - value, - camel_ews_store_get_ooo_alert_state ( - CAMEL_EWS_STORE (object))); - return; - case PROP_CONNECTABLE: - g_value_take_object ( - value, - camel_network_service_ref_connectable ( - CAMEL_NETWORK_SERVICE (object))); - return; - case PROP_HOST_REACHABLE: - g_value_set_boolean ( - value, - camel_network_service_get_host_reachable ( - CAMEL_NETWORK_SERVICE (object))); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -void -camel_ews_store_set_has_ooo_set (CamelEwsStore *ews_store, - gboolean has_ooo_set) -{ - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - - if ((ews_store->priv->has_ooo_set ? 1 : 0) == (has_ooo_set ? 1 : 0)) - return; - - ews_store->priv->has_ooo_set = has_ooo_set; - g_object_notify (G_OBJECT (ews_store), "has-ooo-set"); -} - -gboolean -camel_ews_store_get_has_ooo_set (const CamelEwsStore *ews_store) -{ - g_return_val_if_fail (CAMEL_IS_EWS_STORE (ews_store), FALSE); - - return ews_store->priv->has_ooo_set; -} - -void -camel_ews_store_set_ooo_alert_state (CamelEwsStore *ews_store, - CamelEwsStoreOooAlertState state) -{ - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - - if (ews_store->priv->ooo_alert_state == state) - return; - - ews_store->priv->ooo_alert_state = state; - g_object_notify (G_OBJECT (ews_store), "ooo-alert-state"); -} - -CamelEwsStoreOooAlertState -camel_ews_store_get_ooo_alert_state (const CamelEwsStore *ews_store) -{ - g_return_val_if_fail ( - CAMEL_IS_EWS_STORE (ews_store), - CAMEL_EWS_STORE_OOO_ALERT_STATE_UNKNOWN); - - return ews_store->priv->ooo_alert_state; -} - -static void -ews_migrate_to_user_cache_dir (CamelService *service) -{ - const gchar *user_data_dir, *user_cache_dir; - - g_return_if_fail (service != NULL); - g_return_if_fail (CAMEL_IS_SERVICE (service)); - - user_data_dir = camel_service_get_user_data_dir (service); - user_cache_dir = camel_service_get_user_cache_dir (service); - - g_return_if_fail (user_data_dir != NULL); - g_return_if_fail (user_cache_dir != NULL); - - /* migrate only if the source directory exists and the destination doesn't */ - if (g_file_test (user_data_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) && - !g_file_test (user_cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { - gchar *parent_dir; - - parent_dir = g_path_get_dirname (user_cache_dir); - g_mkdir_with_parents (parent_dir, S_IRWXU); - g_free (parent_dir); - - if (g_rename (user_data_dir, user_cache_dir) == -1) { - g_debug ("%s: Failed to migrate '%s' to '%s': %s", G_STRFUNC, user_data_dir, user_cache_dir, g_strerror (errno)); - } else { - gchar *old_summary_file = g_build_filename (user_cache_dir, "folder-tree-v2", NULL); - - if (old_summary_file && g_file_test (old_summary_file, G_FILE_TEST_EXISTS)) { - gchar *new_summary_file = g_build_filename (user_cache_dir, "folder-tree", NULL); - - if (new_summary_file && g_rename (old_summary_file, new_summary_file) == -1) - g_debug ("%s: Failed to migrate '%s' to '%s': %s", G_STRFUNC, old_summary_file, new_summary_file, g_strerror (errno)); - - g_free (new_summary_file); - } - - g_free (old_summary_file); - } - } -} - -static gboolean -ews_store_initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) -{ - CamelService *service; - CamelSession *session; - CamelStore *store; - gboolean ret; - - store = CAMEL_STORE (initable); - service = CAMEL_SERVICE (initable); - - camel_store_set_flags (store, camel_store_get_flags (store) | CAMEL_STORE_USE_CACHE_DIR | CAMEL_STORE_SUPPORTS_INITIAL_SETUP); - ews_migrate_to_user_cache_dir (service); - - camel_store_set_flags (store, camel_store_get_flags (store) | CAMEL_STORE_CAN_DELETE_FOLDERS_AT_ONCE); - - /* Chain up to parent interface's init() method. */ - if (!parent_initable_interface->init (initable, cancellable, error)) - return FALSE; - - session = camel_service_ref_session (service); - - ret = ews_store_construct (service, session, NULL, error); - - g_object_unref (session); - - /* Add transport here ? */ - - return ret; -} - -static void -camel_ews_store_initable_init (GInitableIface *iface) -{ - parent_initable_interface = g_type_interface_peek_parent (iface); - - iface->init = ews_store_initable_init; -} - -static gboolean -ews_store_construct (CamelService *service, - CamelSession *session, - CamelProvider *provider, - GError **error) -{ - CamelEwsStore *ews_store; - gchar *summary_file, *session_storage_path; - guint32 store_flags; - - ews_store = (CamelEwsStore *) service; - - store_flags = camel_store_get_flags (CAMEL_STORE (ews_store)); - - /* Disable virtual trash and junk folders. Exchange has real - * folders for that */ - store_flags &= ~(CAMEL_STORE_VTRASH | CAMEL_STORE_VJUNK); - store_flags |= CAMEL_STORE_REAL_JUNK_FOLDER; - camel_store_set_flags (CAMEL_STORE (ews_store), store_flags); - - /*storage path*/ - session_storage_path = g_strdup (camel_service_get_user_cache_dir (service)); - if (!session_storage_path) { - g_set_error ( - error, CAMEL_STORE_ERROR, - CAMEL_STORE_ERROR_INVALID, - _("Session has no storage path")); - return FALSE; - } - ews_store->storage_path = session_storage_path; - - /* Note. update account-listener plugin if filename is changed here, as it would remove the summary - * by forming the path itself */ - g_mkdir_with_parents (ews_store->storage_path, 0700); - summary_file = g_build_filename (ews_store->storage_path, "folder-tree", NULL); - ews_store->summary = camel_ews_store_summary_new (summary_file); - camel_ews_store_summary_load (ews_store->summary, NULL); - - g_free (summary_file); - return TRUE; -} - -/* returns NULL when it's safe to use the default "Public Folders" name; otherwise g_free() it */ -static gchar * -ews_store_get_public_folders_name (CamelEwsStore *ews_store) -{ - gchar *use_name = NULL; - gchar *tmp_fid; - gint counter = 0; - - tmp_fid = camel_ews_store_summary_get_folder_id_from_name ( - ews_store->summary, EWS_PUBLIC_FOLDER_ROOT_DISPLAY_NAME); - while (tmp_fid) { - counter++; - - g_free (tmp_fid); - g_free (use_name); - - use_name = g_strdup_printf ( - /* Translators: This composes a "Public Folders" folder name for case when - * user has such in his store already. The %s is replaced with "Public Folders", - * the %d with counter, thus it composes name like "Public Folders_1" - */ - C_("PublicFolders", "%s_%d"), - EWS_PUBLIC_FOLDER_ROOT_DISPLAY_NAME, counter); - - tmp_fid = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, use_name); - } - - return use_name; -} - -void -camel_ews_store_ensure_virtual_folders (CamelEwsStore *ews_store) -{ - gboolean needs_foreign = FALSE, has_foreign = FALSE; - gboolean needs_public = FALSE, has_public = FALSE; - CamelFolderInfo *fi; - GSList *folders, *iter; - GHashTable *children_count; - GHashTableIter tab_iter; - gpointer key, value; - - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - - folders = camel_ews_store_summary_get_folders (ews_store->summary, NULL); - if (!folders) - return; - - children_count = g_hash_table_new (g_str_hash, g_str_equal); - - for (iter = folders; iter; iter = iter->next) { - const gchar *fid = iter->data; - GError *error = NULL; - - if (!fid) - continue; - - if (g_str_has_prefix (fid, "ForeignMailbox::") && - !g_hash_table_contains (children_count, fid)) - g_hash_table_insert (children_count, (gpointer) fid, GINT_TO_POINTER (0)); - - if (g_str_equal (fid, EWS_PUBLIC_FOLDER_ROOT_ID) && - !g_hash_table_contains (children_count, fid)) - g_hash_table_insert (children_count, (gpointer) fid, GINT_TO_POINTER (0)); - - if (!has_foreign && g_str_equal (fid, EWS_FOREIGN_FOLDER_ROOT_ID)) - has_foreign = TRUE; - else if (camel_ews_store_summary_get_foreign (ews_store->summary, fid, &error) && !error) { - gchar *pfid; - - needs_foreign = TRUE; - - pfid = camel_ews_store_summary_get_parent_folder_id (ews_store->summary, fid, NULL); - if (pfid && g_str_has_prefix (pfid, "ForeignMailbox::")) { - gint count = GPOINTER_TO_INT (g_hash_table_lookup (children_count, pfid)); - - g_hash_table_insert (children_count, (gpointer) pfid, GINT_TO_POINTER (count + 1)); - } - } - - g_clear_error (&error); - - if (!has_public && g_str_equal (fid, EWS_PUBLIC_FOLDER_ROOT_ID)) - has_public = TRUE; - else if (camel_ews_store_summary_get_public (ews_store->summary, fid, &error) && !error) { - EEwsFolderType ftype; - gchar *pfid; - - ftype = camel_ews_store_summary_get_folder_type (ews_store->summary, fid, &error); - if (ftype == E_EWS_FOLDER_TYPE_MAILBOX && !error) { - guint64 fflags; - - fflags = camel_ews_store_summary_get_folder_flags (ews_store->summary, fid, &error); - if ((fflags & CAMEL_FOLDER_SUBSCRIBED) != 0 && !error) { - needs_public = TRUE; - - pfid = camel_ews_store_summary_get_parent_folder_id (ews_store->summary, fid, NULL); - if (pfid && g_str_equal (pfid, EWS_PUBLIC_FOLDER_ROOT_ID)) { - gint count = GPOINTER_TO_INT (g_hash_table_lookup (children_count, pfid)); - - g_hash_table_insert (children_count, (gpointer) pfid, GINT_TO_POINTER (count + 1)); - } - } - } - } - - g_clear_error (&error); - } - - g_hash_table_iter_init (&tab_iter, children_count); - while (g_hash_table_iter_next (&tab_iter, &key, &value)) { - gint count = GPOINTER_TO_INT (value); - - if (!count) { - CamelFolderInfo *fi; - - if (has_foreign && g_str_equal (key, EWS_FOREIGN_FOLDER_ROOT_ID)) - has_foreign = FALSE; - - if (has_public && g_str_equal (key, EWS_PUBLIC_FOLDER_ROOT_ID)) - has_public = FALSE; - - fi = camel_ews_utils_build_folder_info (ews_store, key); - camel_ews_store_summary_remove_folder (ews_store->summary, key, NULL); - - camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_store_folder_deleted (CAMEL_STORE (ews_store), fi); - camel_folder_info_free (fi); - } - } - - g_hash_table_destroy (children_count); - - if (needs_foreign && !has_foreign) { - gchar *use_name = NULL; - gchar *tmp_fid; - gint counter = 0; - - tmp_fid = camel_ews_store_summary_get_folder_id_from_name ( - ews_store->summary, EWS_FOREIGN_FOLDER_ROOT_DISPLAY_NAME); - while (tmp_fid) { - counter++; - - g_free (tmp_fid); - g_free (use_name); - - use_name = g_strdup_printf ( - /* Translators: This composes a "Foreign Folders" folder name for case when - * user has such in his store already. The %s is replaced with "Foreign Folders", - * the %d with counter, thus it composes name like "Foreign Folders_1" - */ - C_("ForeignFolders", "%s_%d"), - EWS_FOREIGN_FOLDER_ROOT_DISPLAY_NAME, counter); - - tmp_fid = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, use_name); - } - - camel_ews_store_summary_new_folder ( - ews_store->summary, - EWS_FOREIGN_FOLDER_ROOT_ID, NULL, NULL, - use_name ? use_name : EWS_FOREIGN_FOLDER_ROOT_DISPLAY_NAME, - E_EWS_FOLDER_TYPE_MAILBOX, - CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_NOSELECT, - 0, FALSE, FALSE); - - g_free (use_name); - - fi = camel_ews_utils_build_folder_info (ews_store, EWS_FOREIGN_FOLDER_ROOT_ID); - camel_store_folder_created (CAMEL_STORE (ews_store), fi); - camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_folder_info_free (fi); - } else if (has_foreign && !needs_foreign) { - CamelFolderInfo *fi; - - fi = camel_ews_utils_build_folder_info (ews_store, EWS_FOREIGN_FOLDER_ROOT_ID); - camel_ews_store_summary_remove_folder (ews_store->summary, EWS_FOREIGN_FOLDER_ROOT_ID, NULL); - - camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_store_folder_deleted (CAMEL_STORE (ews_store), fi); - camel_folder_info_free (fi); - } - - if (needs_public && !has_public) { - gchar *use_name; - - use_name = ews_store_get_public_folders_name (ews_store); - - camel_ews_store_summary_new_folder ( - ews_store->summary, - EWS_PUBLIC_FOLDER_ROOT_ID, NULL, NULL, - use_name ? use_name : EWS_PUBLIC_FOLDER_ROOT_DISPLAY_NAME, - E_EWS_FOLDER_TYPE_MAILBOX, - CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_NOSELECT, - 0, FALSE, FALSE); - - g_free (use_name); - - fi = camel_ews_utils_build_folder_info (ews_store, EWS_PUBLIC_FOLDER_ROOT_ID); - camel_store_folder_created (CAMEL_STORE (ews_store), fi); - camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_folder_info_free (fi); - } else if (has_public && !needs_public) { - CamelFolderInfo *fi; - - fi = camel_ews_utils_build_folder_info (ews_store, EWS_PUBLIC_FOLDER_ROOT_ID); - camel_ews_store_summary_remove_folder (ews_store->summary, EWS_PUBLIC_FOLDER_ROOT_ID, NULL); - - camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_store_folder_deleted (CAMEL_STORE (ews_store), fi); - camel_folder_info_free (fi); - } - - camel_ews_store_summary_rebuild_hashes (ews_store->summary); - camel_ews_store_summary_save (ews_store->summary, NULL); - - g_slist_free_full (folders, g_free); -} - -void -camel_ews_store_ensure_unique_path (CamelEwsStore *ews_store, - gchar **ppath) -{ - gboolean done; - guint counter = 0; - gchar *base_path = NULL; - - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - g_return_if_fail (ews_store->summary != NULL); - g_return_if_fail (ppath != NULL); - g_return_if_fail (*ppath != NULL); - - done = FALSE; - while (!done) { - gchar *fid; - - done = TRUE; - - fid = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, *ppath); - if (fid) { - g_free (fid); - - done = FALSE; - counter++; - if (!counter) { - g_debug ("%s: Counter overflow", G_STRFUNC); - break; - } - - if (!base_path) - base_path = *ppath; - else - g_free (*ppath); - - *ppath = g_strdup_printf ("%s_%u", base_path, counter); - } - } - - g_free (base_path); -} - -static void -ews_update_folder_hierarchy (CamelEwsStore *ews_store, - gchar *sync_state, - gboolean includes_last_folder, - GSList *folders_created, - GSList *folders_deleted, - GSList *folders_updated, - GSList **created_folder_ids) -{ - ews_utils_sync_folders (ews_store, folders_created, folders_deleted, folders_updated, created_folder_ids); - camel_ews_store_ensure_virtual_folders (ews_store); - - camel_ews_store_summary_store_string_val (ews_store->summary, "sync_state", sync_state); - camel_ews_store_summary_save (ews_store->summary, NULL); - - g_slist_free_full (folders_created, g_object_unref); - g_slist_free_full (folders_updated, g_object_unref); - g_slist_free_full (folders_deleted, g_free); - g_free (sync_state); -} - -static void -ews_update_has_ooo_set (CamelSession *session, - GCancellable *cancellable, - gpointer user_data, - GError **error) -{ - CamelEwsStore *ews_store = user_data; - EEwsOofSettings *oof_settings; - EEwsOofState oof_state; - EEwsConnection *cnc; - GError *local_error = NULL; - - cnc = camel_ews_store_ref_connection (ews_store); - if (!cnc) - return; - - camel_operation_push_message (cancellable, _("Checking “Out of Office” settings")); - - oof_settings = e_ews_oof_settings_new_sync (cnc, cancellable, &local_error); - - g_clear_object (&cnc); - - if (local_error != NULL) { - g_propagate_error (error, local_error); - camel_operation_pop_message (cancellable); - return; - } - - oof_state = e_ews_oof_settings_get_state (oof_settings); - switch (oof_state) { - case E_EWS_OOF_STATE_ENABLED: - camel_ews_store_set_has_ooo_set (ews_store, TRUE); - break; - case E_EWS_OOF_STATE_DISABLED: - case E_EWS_OOF_STATE_SCHEDULED: - camel_ews_store_set_has_ooo_set (ews_store, FALSE); - break; - default: - break; - } - - camel_operation_pop_message (cancellable); - g_clear_object (&oof_settings); -} - -struct ScheduleUpdateData -{ - GCancellable *cancellable; - CamelEwsStore *ews_store; - guint expected_id; -}; - -static void -free_schedule_update_data (gpointer ptr) -{ - struct ScheduleUpdateData *sud = ptr; - - if (sud == NULL) - return; - - g_clear_object (&sud->cancellable); - g_clear_object (&sud->ews_store); - - g_free (sud); -} - -static gpointer -camel_ews_folder_list_update_thread (gpointer user_data) -{ - struct ScheduleUpdateData *sud = user_data; - CamelEwsStore *ews_store = sud->ews_store; - EEwsConnection *cnc = NULL; - GSList *created = NULL; - GSList *updated = NULL; - GSList *deleted = NULL; - gchar *old_sync_state = NULL; - gchar *new_sync_state; - gboolean includes_last; - GError *local_error = NULL; - - if (g_cancellable_is_cancelled (sud->cancellable)) - goto exit; - - cnc = camel_ews_store_ref_connection (ews_store); - if (!cnc) - goto exit; - - old_sync_state = camel_ews_store_summary_get_string_val (ews_store->summary, "sync_state", NULL); - if (!e_ews_connection_sync_folder_hierarchy_sync ( - cnc, - EWS_PRIORITY_LOW, - old_sync_state, - &new_sync_state, - &includes_last, - &created, - &updated, - &deleted, - sud->cancellable, - &local_error)) - goto exit; - - if (g_cancellable_is_cancelled (sud->cancellable)) { - g_slist_free_full (created, g_object_unref); - g_slist_free_full (updated, g_object_unref); - g_slist_free_full (deleted, g_free); - g_free (new_sync_state); - - goto exit; - } - - if (created != NULL || updated != NULL || deleted != NULL) { - ews_update_folder_hierarchy ( - ews_store, - new_sync_state, /* freed in the function */ - includes_last, - created, /* freed in the function */ - deleted, /* freed in the function */ - updated, /* freed in the function */ - NULL); - } else { - g_slist_free_full (created, g_object_unref); - g_slist_free_full (updated, g_object_unref); - g_slist_free_full (deleted, g_free); - g_free (new_sync_state); - } - - exit: - if (local_error) { - camel_ews_store_maybe_disconnect (ews_store, local_error); - g_clear_error (&local_error); - - g_mutex_lock (&ews_store->priv->get_finfo_lock); - ews_store->priv->last_refresh_time -= FINFO_REFRESH_INTERVAL; - g_mutex_unlock (&ews_store->priv->get_finfo_lock); - } else { - g_mutex_lock (&ews_store->priv->get_finfo_lock); - ews_store->priv->last_refresh_time = time (NULL); - g_mutex_unlock (&ews_store->priv->get_finfo_lock); - } - - g_free (old_sync_state); - g_clear_object (&cnc); - free_schedule_update_data (sud); - return NULL; -} - -static gpointer -camel_ews_folder_update_thread (gpointer user_data) -{ - struct ScheduleUpdateData *sud = user_data; - CamelEwsStore *ews_store = sud->ews_store; - GSList *update_folder_names, *l; - - g_return_val_if_fail (sud != NULL, NULL); - - UPDATE_LOCK (ews_store); - update_folder_names = ews_store->priv->update_folder_names; - ews_store->priv->update_folder_names = NULL; - UPDATE_UNLOCK (ews_store); - - for (l = update_folder_names; l != NULL && !g_cancellable_is_cancelled (sud->cancellable); l = l->next) { - const gchar *folder_name = l->data; - CamelFolder *folder; - GError *error = NULL; - - folder = camel_store_get_folder_sync (CAMEL_STORE (ews_store), folder_name, 0, sud->cancellable, NULL); - if (folder == NULL) - continue; - - camel_folder_refresh_info_sync (folder, sud->cancellable, &error); - g_object_unref (folder); - - if (error != NULL) { - g_warning ("%s: %s\n", G_STRFUNC, error->message); - g_clear_error (&error); - break; - } - } - - g_slist_free_full (update_folder_names, g_free); - update_folder_names = NULL; - free_schedule_update_data (sud); - - return NULL; -} - -static void -run_update_thread (CamelEwsStore *ews_store, - gboolean folder_list, - GCancellable *cancellable) -{ - GThread *thread; - struct ScheduleUpdateData *sud; - - g_return_if_fail (ews_store != NULL); - g_return_if_fail (cancellable != NULL); - - sud = g_new0 (struct ScheduleUpdateData, 1); - sud->ews_store = g_object_ref (ews_store); - sud->cancellable = g_object_ref (cancellable); - - thread = g_thread_new ( - NULL, - folder_list ? camel_ews_folder_list_update_thread : camel_ews_folder_update_thread, - sud); - g_thread_unref (thread); -} - -static gboolean -folder_update_cb (gpointer user_data) -{ - struct ScheduleUpdateData *sud = user_data; - - g_return_val_if_fail (sud != NULL, FALSE); - - if (g_cancellable_is_cancelled (sud->cancellable)) - return FALSE; - - g_return_val_if_fail (sud->ews_store != NULL, FALSE); - g_return_val_if_fail (sud->ews_store->priv != NULL, FALSE); - - UPDATE_LOCK (sud->ews_store); - if (sud->expected_id != sud->ews_store->priv->update_folder_id) - goto exit; - - sud->ews_store->priv->update_folder_id = 0; - - if (!g_cancellable_is_cancelled (sud->cancellable)) - run_update_thread (sud->ews_store, FALSE, sud->cancellable); - -exit: - UPDATE_UNLOCK (sud->ews_store); - return FALSE; -} - -static void -get_folder_names_to_update (gpointer key, - gpointer value, - gpointer user_data) -{ - CamelEwsStore *ews_store = user_data; - const gchar *folder_id = key; - gchar *folder_name; - - folder_name = camel_ews_store_summary_get_folder_full_name (ews_store->summary, folder_id, NULL); - if (folder_name != NULL) - ews_store->priv->update_folder_names = g_slist_prepend (ews_store->priv->update_folder_names, folder_name); -} - -static void -schedule_folder_update (CamelEwsStore *ews_store, - GHashTable *folder_ids) -{ - struct ScheduleUpdateData *sud; - CamelSettings *settings; - - g_return_if_fail (ews_store != NULL); - g_return_if_fail (ews_store->priv != NULL); - - UPDATE_LOCK (ews_store); - g_hash_table_foreach (folder_ids, get_folder_names_to_update, ews_store); - - if (ews_store->priv->update_folder_names == NULL) - goto exit; - - sud = g_new0 (struct ScheduleUpdateData, 1); - sud->ews_store = g_object_ref (ews_store); - sud->cancellable = g_object_ref (ews_store->priv->updates_cancellable); - - if (ews_store->priv->update_folder_id > 0) - g_source_remove (ews_store->priv->update_folder_id); - - settings = camel_service_ref_settings (CAMEL_SERVICE (ews_store)); - - ews_store->priv->update_folder_id = e_named_timeout_add_seconds_full ( - G_PRIORITY_LOW, - 1, - folder_update_cb, - sud, - free_schedule_update_data); - sud->expected_id = ews_store->priv->update_folder_id; - - g_object_unref (settings); - -exit: - UPDATE_UNLOCK (ews_store); -} - -static gboolean -folder_list_update_cb (gpointer user_data) -{ - struct ScheduleUpdateData *sud = user_data; - - g_return_val_if_fail (sud != NULL, FALSE); - - if (g_cancellable_is_cancelled (sud->cancellable)) - return FALSE; - - g_return_val_if_fail (sud->ews_store != NULL, FALSE); - g_return_val_if_fail (sud->ews_store->priv != NULL, FALSE); - - UPDATE_LOCK (sud->ews_store); - if (sud->expected_id != sud->ews_store->priv->update_folder_list_id) - goto exit; - - sud->ews_store->priv->update_folder_list_id = 0; - - if (!g_cancellable_is_cancelled (sud->cancellable)) - run_update_thread (sud->ews_store, TRUE, sud->cancellable); - -exit: - UPDATE_UNLOCK (sud->ews_store); - - return FALSE; -} - -static void -schedule_folder_list_update (CamelEwsStore *ews_store) -{ - struct ScheduleUpdateData *sud; - CamelSettings *settings; - - g_return_if_fail (ews_store != NULL); - g_return_if_fail (ews_store->priv != NULL); - - UPDATE_LOCK (ews_store); - if (!ews_store->priv->updates_cancellable) - goto exit; - - sud = g_new0 (struct ScheduleUpdateData, 1); - sud->ews_store = g_object_ref (ews_store); - sud->cancellable = g_object_ref (ews_store->priv->updates_cancellable); - - if (ews_store->priv->update_folder_list_id > 0) - g_source_remove (ews_store->priv->update_folder_list_id); - - settings = camel_service_ref_settings (CAMEL_SERVICE (ews_store)); - - ews_store->priv->update_folder_list_id = e_named_timeout_add_seconds_full ( - G_PRIORITY_LOW, - 1, - folder_list_update_cb, - sud, - free_schedule_update_data); - sud->expected_id = ews_store->priv->update_folder_list_id; - - g_object_unref (settings); - -exit: - UPDATE_UNLOCK (ews_store); -} - -static void -camel_ews_store_server_notification_cb (CamelEwsStore *ews_store, - GSList *events, - EEwsConnection *cnc) -{ - GSList *l; - gboolean update_folder = FALSE; - gboolean update_folder_list = FALSE; - GHashTable *folder_ids; - - g_return_if_fail (ews_store != NULL); - g_return_if_fail (ews_store->priv != NULL); - - folder_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - for (l = events; l != NULL; l = l->next) { - EEwsNotificationEvent *event = l->data; - - switch (event->type) { - case E_EWS_NOTIFICATION_EVENT_CREATED: - case E_EWS_NOTIFICATION_EVENT_DELETED: - case E_EWS_NOTIFICATION_EVENT_MODIFIED: - UPDATE_LOCK (ews_store); - if (event->is_item) { - update_folder = TRUE; - if (!g_hash_table_lookup (folder_ids, event->folder_id)) - g_hash_table_insert ( - folder_ids, g_strdup (event->folder_id), GINT_TO_POINTER (1)); - } else { - update_folder_list = TRUE; - } - UPDATE_UNLOCK (ews_store); - break; - case E_EWS_NOTIFICATION_EVENT_MOVED: - case E_EWS_NOTIFICATION_EVENT_COPIED: - UPDATE_LOCK (ews_store); - if (event->is_item) { - update_folder = TRUE; - if (!g_hash_table_lookup (folder_ids, event->old_folder_id)) - g_hash_table_insert ( - folder_ids, g_strdup (event->old_folder_id), GINT_TO_POINTER (1)); - - if (!g_hash_table_lookup (folder_ids, event->folder_id)) - g_hash_table_insert ( - folder_ids, g_strdup (event->folder_id), GINT_TO_POINTER (1)); - } else { - update_folder_list = TRUE; - } - UPDATE_UNLOCK (ews_store); - break; - default: - break; - } - } - - if (update_folder) - schedule_folder_update (ews_store, folder_ids); - if (update_folder_list) - schedule_folder_list_update (ews_store); - - g_hash_table_destroy (folder_ids); -} - -struct HandleNotificationsData { - CamelEwsStore *ews_store; - GSList *folders; -}; - -static void -handle_notifications_data_free (struct HandleNotificationsData *hnd) -{ - if (hnd == NULL) - return; - - if (hnd->ews_store) - g_object_unref (hnd->ews_store); - - g_slist_free_full (hnd->folders, g_free); - g_free (hnd); -} - -static gpointer -start_notifications_thread (gpointer data) -{ - struct HandleNotificationsData *hnd = data; - CamelEwsStore *ews_store = hnd->ews_store; - EEwsConnection *cnc; - - cnc = camel_ews_store_ref_connection (ews_store); - if (!cnc) - goto exit; - - if (ews_store->priv->listen_notifications) { - if (ews_store->priv->subscription_key != 0) - goto exit; - - e_ews_connection_enable_notifications_sync ( - cnc, - hnd->folders, - &ews_store->priv->subscription_key); - } else { - if (ews_store->priv->subscription_key == 0) - goto exit; - - e_ews_connection_disable_notifications_sync ( - cnc, - ews_store->priv->subscription_key); - - ews_store->priv->subscription_key = 0; - } - -exit: - handle_notifications_data_free (hnd); - g_clear_object (&cnc); - - return NULL; -} - -static void -folder_ids_populate (CamelFolderInfo *folder_info, - gpointer data) -{ - struct HandleNotificationsData *hnd = data; - - while (folder_info != NULL) { - gchar *id; - - id = camel_ews_store_summary_get_folder_id_from_name (hnd->ews_store->summary, folder_info->full_name); - if (id && !g_str_has_prefix (id, "ForeignMailbox::") && - !g_str_equal (id, EWS_PUBLIC_FOLDER_ROOT_ID) && - !g_str_equal (id, EWS_FOREIGN_FOLDER_ROOT_ID) && - !camel_ews_store_summary_get_foreign (hnd->ews_store->summary, id, NULL) && - !camel_ews_store_summary_get_public (hnd->ews_store->summary, id, NULL)) - hnd->folders = g_slist_prepend (hnd->folders, id); - else - g_free (id); - - if (folder_info->child != NULL) - folder_ids_populate (folder_info->child, hnd); - - folder_info = folder_info->next; - } -} - -static void -camel_ews_store_handle_notifications (CamelEwsStore *ews_store, - CamelEwsSettings *ews_settings) -{ - GThread *thread; - EEwsConnection *cnc; - struct HandleNotificationsData *hnd; - - cnc = camel_ews_store_ref_connection (ews_store); - - if (!cnc) - return; - - if (!e_ews_connection_satisfies_server_version (cnc, E_EWS_EXCHANGE_2010_SP1)) { - g_clear_object (&cnc); - return; - } - - hnd = g_new0 (struct HandleNotificationsData, 1); - hnd->ews_store = g_object_ref (ews_store); - - if (!camel_ews_settings_get_check_all (ews_settings)) { - gchar *inbox; - - inbox = camel_ews_store_summary_get_folder_id_from_folder_type ( - ews_store->summary, CAMEL_FOLDER_TYPE_INBOX); - hnd->folders = g_slist_prepend (hnd->folders, inbox); - } else { - CamelFolderInfo *fi; - - fi = folder_info_from_store_summary ( - ews_store, - NULL, - CAMEL_STORE_FOLDER_INFO_RECURSIVE, - NULL, - NULL); - - folder_ids_populate (fi, hnd); - - camel_folder_info_free (fi); - } - - g_clear_object (&cnc); - - thread = g_thread_new (NULL, start_notifications_thread, hnd); - g_thread_unref (thread); -} - -static void -camel_ews_store_listen_notifications_cb (CamelEwsStore *ews_store, - GParamSpec *spec, - CamelEwsSettings *ews_settings) -{ - if (ews_store->priv->listen_notifications == camel_ews_settings_get_listen_notifications (ews_settings)) - return; - - ews_store->priv->listen_notifications = !ews_store->priv->listen_notifications; - - camel_ews_store_handle_notifications (ews_store, ews_settings); -} - -static void -camel_ews_store_check_all_cb (CamelEwsStore *ews_store, - GParamSpec *spec, - CamelEwsSettings *ews_settings) -{ - if (!ews_store->priv->listen_notifications) - return; - - camel_ews_store_handle_notifications (ews_store, ews_settings); -} - -static gboolean -ews_connect_sync (CamelService *service, - GCancellable *cancellable, - GError **error) -{ - EEwsConnection *connection; - CamelEwsStore *ews_store; - CamelEwsStorePrivate *priv; - CamelEwsSettings *ews_settings; - CamelSession *session; - CamelSettings *settings; - gchar *auth_mech; - gboolean success; - - /* Chain up to parent's method. */ - if (!CAMEL_SERVICE_CLASS (camel_ews_store_parent_class)->connect_sync (service, cancellable, error)) - return FALSE; - - ews_store = CAMEL_EWS_STORE (service); - priv = ews_store->priv; - - if (camel_service_get_connection_status (service) == CAMEL_SERVICE_DISCONNECTED) - return FALSE; - - connection = camel_ews_store_ref_connection (ews_store); - if (connection != NULL) { - g_object_unref (connection); - return TRUE; - } - - session = camel_service_ref_session (service); - settings = camel_service_ref_settings (service); - ews_settings = CAMEL_EWS_SETTINGS (settings); - - /* Try running an operation that requires authentication - * to make sure we have valid credentials available. */ - auth_mech = camel_network_settings_dup_auth_mechanism ( - CAMEL_NETWORK_SETTINGS (settings)); - success = camel_session_authenticate_sync (session, service, - auth_mech ? auth_mech : "NTLM", cancellable, error); - - g_free (auth_mech); - - priv->listen_notifications = FALSE; - - if (success) { - CamelEwsStoreOooAlertState state; - - state = camel_ews_store_get_ooo_alert_state (ews_store); - if (state == CAMEL_EWS_STORE_OOO_ALERT_STATE_UNKNOWN) - camel_session_submit_job ( - session, _("Checking “Out of Office” settings"), - ews_update_has_ooo_set, - g_object_ref (ews_store), - g_object_unref); - - if (!priv->updates_cancellable) - priv->updates_cancellable = g_cancellable_new (); - - if (camel_ews_settings_get_listen_notifications (ews_settings)) - camel_ews_store_listen_notifications_cb (ews_store, NULL, ews_settings); - - camel_offline_store_set_online_sync ( - CAMEL_OFFLINE_STORE (ews_store), - TRUE, cancellable, NULL); - - connection = camel_ews_store_ref_connection (ews_store); - if (connection) { - g_signal_connect_swapped ( - connection, - "server-notification", - G_CALLBACK (camel_ews_store_server_notification_cb), - ews_store); - g_clear_object (&connection); - } - } - - g_signal_connect_swapped ( - ews_settings, - "notify::listen-notifications", - G_CALLBACK (camel_ews_store_listen_notifications_cb), - ews_store); - - g_signal_connect_swapped ( - ews_settings, - "notify::check-all", - G_CALLBACK (camel_ews_store_check_all_cb), - ews_store); - - g_object_unref (session); - g_object_unref (settings); - - return success; -} - -static void -camel_ews_store_password_will_expire_cb (EEwsConnection *connection, - gint in_days, - const gchar *service_url, - gpointer user_data) -{ - CamelEwsStore *ews_store = user_data; - - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - - if (ews_store->priv->password_expires_in_days < 0 || - ews_store->priv->password_expires_in_days > in_days) { - CamelService *service; - CamelSession *session; - - ews_store->priv->password_expires_in_days = in_days; - - service = CAMEL_SERVICE (ews_store); - session = camel_service_ref_session (service); - - if (session) { - gchar *msg; - - if (service_url) { - msg = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, - /* Translators: The "%s" is a service URL, provided by the server */ - "Password will expire in %d day. Open “%s” to change it.", - "Password will expire in %d days. Open “%s” to change it.", - in_days), - in_days, service_url); - } else { - msg = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, - "Password will expire in one day.", - "Password will expire in %d days.", - in_days), - in_days); - } - - camel_session_user_alert (session, service, CAMEL_SESSION_ALERT_WARNING, msg); - - g_object_unref (session); - g_free (msg); - } - } -} - -static void -stop_pending_updates (CamelEwsStore *ews_store) -{ - CamelEwsStorePrivate *priv; - - g_return_if_fail (ews_store != NULL); - g_return_if_fail (ews_store->priv != NULL); - - priv = ews_store->priv; - - UPDATE_LOCK (ews_store); - if (priv->updates_cancellable) { - g_cancellable_cancel (priv->updates_cancellable); - g_object_unref (priv->updates_cancellable); - priv->updates_cancellable = NULL; - } - - g_slist_free_full (priv->update_folder_names, g_free); - priv->update_folder_names = NULL; - UPDATE_UNLOCK (ews_store); -} - -static void -ews_store_unset_connection_locked (CamelEwsStore *ews_store) -{ - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - - /* TODO cancel all operations in the connection */ - if (ews_store->priv->connection != NULL) { - CamelSettings *settings; - - /* FIXME This is somewhat broken, since the CamelSettings - * instance returned here may not be the same instance - * that we connected a signal handler to. Need to keep - * our own reference to that CamelSettings instance, or - * better yet avoid connecting signal handlers to it in - * the first place. */ - settings = camel_service_ref_settings (CAMEL_SERVICE (ews_store)); - g_signal_handlers_disconnect_by_data (settings, ews_store); - g_signal_handlers_disconnect_by_func ( - ews_store->priv->connection, camel_ews_store_server_notification_cb, ews_store); - - g_object_unref (settings); - - if (ews_store->priv->listen_notifications) { - stop_pending_updates (ews_store); - - if (ews_store->priv->subscription_key != 0) { - e_ews_connection_disable_notifications_sync ( - ews_store->priv->connection, - ews_store->priv->subscription_key); - - ews_store->priv->subscription_key = 0; - } - - ews_store->priv->listen_notifications = FALSE; - } - - e_ews_connection_set_password (ews_store->priv->connection, NULL); - e_ews_connection_set_disconnected_flag (ews_store->priv->connection, TRUE); - g_signal_handlers_disconnect_by_func (ews_store->priv->connection, - G_CALLBACK (camel_ews_store_password_will_expire_cb), ews_store); - g_object_unref (ews_store->priv->connection); - ews_store->priv->connection = NULL; - } -} - -static gboolean -ews_disconnect_sync (CamelService *service, - gboolean clean, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store = CAMEL_EWS_STORE (service); - CamelServiceClass *service_class; - - g_mutex_lock (&ews_store->priv->connection_lock); - ews_store_unset_connection_locked (ews_store); - g_mutex_unlock (&ews_store->priv->connection_lock); - - service_class = CAMEL_SERVICE_CLASS (camel_ews_store_parent_class); - return service_class->disconnect_sync (service, clean, cancellable, error); -} - -typedef struct { - const gchar *dist_folder_id; - gint info_flags; -} SystemFolder; - -static SystemFolder system_folder[] = { - {"calendar", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_EVENTS}, - {"contacts", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_CONTACTS}, - {"deleteditems", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_TRASH}, - {"drafts", CAMEL_FOLDER_SYSTEM}, - {"inbox", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_INBOX}, - {"journal", CAMEL_FOLDER_SYSTEM | CAMEL_EWS_FOLDER_TYPE_JOURNAL}, - {"notes", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_MEMOS}, - {"outbox", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_OUTBOX}, - {"sentitems", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_SENT}, - {"tasks", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_TASKS}, - {"msgfolderroot", CAMEL_FOLDER_SYSTEM}, - {"root", CAMEL_FOLDER_SYSTEM}, - {"junkemail", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_JUNK}, - {"searchfolders", CAMEL_FOLDER_SYSTEM}, -}; - -static void -ews_store_set_flags (CamelEwsStore *ews_store, - GSList *folders) -{ - GSList *temp = NULL; - EEwsFolder *folder = NULL; - const EwsFolderId *fid = NULL; - gint n = 0; - - temp = folders; - while (temp != NULL) { - folder = (EEwsFolder *) temp->data; - - if (folder && !e_ews_folder_is_error (folder)) { - fid = e_ews_folder_get_id (folder); - - if (camel_ews_store_summary_has_folder (ews_store->summary, fid->id)) - camel_ews_store_summary_set_folder_flags (ews_store->summary, fid->id, system_folder[n].info_flags); - } - - temp = temp->next; - n++; - } -} - -static void -ews_store_forget_all_folders (CamelEwsStore *ews_store) -{ - CamelStore *store; - CamelSubscribable *subscribable; - GSList *folders, *l; - - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - - store = CAMEL_STORE (ews_store); - subscribable = CAMEL_SUBSCRIBABLE (ews_store); - folders = camel_ews_store_summary_get_folders (ews_store->summary, NULL); - - if (!folders) - return; - - for (l = folders; l != NULL; l = g_slist_next (l)) { - CamelFolderInfo *fi; - EEwsFolderType ftype; - - ftype = camel_ews_store_summary_get_folder_type (ews_store->summary, l->data, NULL); - if (ftype != E_EWS_FOLDER_TYPE_MAILBOX) - continue; - - fi = camel_ews_utils_build_folder_info (ews_store, l->data); - camel_subscribable_folder_unsubscribed (subscribable, fi); - camel_store_folder_deleted (store, fi); - camel_folder_info_free (fi); - } - - g_slist_free_full (folders, g_free); -} - -struct EwsUpdateForeignSubfoldersData -{ - CamelEwsStore *ews_store; - gchar *folder_id; -}; - -static void -ews_update_foreign_subfolders_data_free (gpointer data) -{ - struct EwsUpdateForeignSubfoldersData *euf = data; - - if (euf) { - g_object_unref (euf->ews_store); - g_free (euf->folder_id); - g_free (euf); - } -} - -static void -ews_store_update_foreign_subfolders (CamelSession *session, - GCancellable *cancellable, - gpointer user_data, - GError **error) -{ - struct EwsUpdateForeignSubfoldersData *euf = user_data; - CamelEwsStore *ews_store; - EEwsConnection *conn; - GSList *tocheck = NULL, *remote_folders = NULL, *local_folders = NULL; - const gchar *fid; - GError *local_error = NULL; - - g_return_if_fail (euf != NULL); - - ews_store = euf->ews_store; - fid = euf->folder_id; - - if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store))) - return; - - conn = camel_ews_store_ref_connection (ews_store); - g_return_if_fail (conn != NULL); - - camel_operation_push_message (cancellable, _("Updating foreign folder structure")); - - /* read remote folder structure at the server */ - while (fid && !g_cancellable_is_cancelled (cancellable) && !local_error) { - gboolean includes_last_item = FALSE; - EwsFolderId *folder_id = e_ews_folder_id_new (fid, NULL, FALSE); - - while (!includes_last_item && !g_cancellable_is_cancelled (cancellable) && !local_error) { - GSList *folders = NULL, *ff; - - if (!e_ews_connection_find_folder_sync (conn, EWS_PRIORITY_MEDIUM, folder_id, - &includes_last_item, &folders, cancellable, &local_error)) - break; - - for (ff = folders; ff != NULL; ff = ff->next) { - EEwsFolder *folder = ff->data; - - e_ews_folder_set_parent_id (folder, e_ews_folder_id_new (fid, NULL, FALSE)); - - remote_folders = g_slist_prepend (remote_folders, folder); - - if (e_ews_folder_get_child_count (folder) > 0 && e_ews_folder_get_id (folder)) - tocheck = g_slist_prepend (tocheck, e_ews_folder_get_id (folder)->id); - } - } - - e_ews_folder_id_free (folder_id); - - if (tocheck) { - fid = g_slist_last (tocheck)->data; - tocheck = g_slist_remove (tocheck, fid); - } else { - fid = NULL; - } - } - - /* get local folder structure */ - if (!local_error && !g_cancellable_is_cancelled (cancellable)) { - gchar *full_name = camel_ews_store_summary_get_folder_full_name (ews_store->summary, euf->folder_id, NULL); - if (full_name) { - local_folders = camel_ews_store_summary_get_folders (ews_store->summary, full_name); - } - g_free (full_name); - } - - /* merge local and remote folder structures */ - if (!local_error && !g_cancellable_is_cancelled (cancellable)) { - GHashTable *locals = g_hash_table_new (g_str_hash, g_str_equal); - GSList *ii; - - remote_folders = g_slist_reverse (remote_folders); - - for (ii = local_folders; ii; ii = ii->next) { - g_hash_table_insert (locals, ii->data, ii->data); - } - - for (ii = remote_folders; ii; ii = ii->next) { - EEwsFolder *folder = ii->data; - const EwsFolderId *folder_id = e_ews_folder_get_id (folder); - const EwsFolderId *parent_fid = e_ews_folder_get_parent_id (folder); - - if (e_ews_folder_get_folder_type (folder) == E_EWS_FOLDER_TYPE_MAILBOX && - folder_id && folder_id->id) { - if (!g_hash_table_remove (locals, folder_id->id)) { - CamelFolderInfo *fi; - - /* it's a new folder, add it */ - camel_ews_store_summary_new_folder ( - ews_store->summary, - folder_id->id, parent_fid ? parent_fid->id : euf->folder_id, folder_id->change_key, - e_ews_folder_get_escaped_name (folder), E_EWS_FOLDER_TYPE_MAILBOX, - CAMEL_FOLDER_SUBSCRIBED, e_ews_folder_get_total_count (folder), TRUE, FALSE); - - fi = camel_ews_utils_build_folder_info (ews_store, folder_id->id); - camel_store_folder_created (CAMEL_STORE (ews_store), fi); - camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_folder_info_free (fi); - } - } - } - - /* to not remove the parent */ - g_hash_table_remove (locals, euf->folder_id); - - /* and now the locals contains only folders which were removed */ - if (g_hash_table_size (locals) > 0) { - CamelSubscribable *subscribable = CAMEL_SUBSCRIBABLE (ews_store); - CamelStore *store = CAMEL_STORE (ews_store); - GHashTableIter iter; - gpointer key, value; - - g_hash_table_iter_init (&iter, locals); - while (g_hash_table_iter_next (&iter, &key, &value)) { - CamelFolderInfo *fi; - - fi = camel_ews_utils_build_folder_info (ews_store, key); - camel_subscribable_folder_unsubscribed (subscribable, fi); - camel_store_folder_deleted (store, fi); - camel_folder_info_free (fi); - } - } - - g_hash_table_destroy (locals); - - camel_ews_store_summary_save (ews_store->summary, &local_error); - } - - if (local_error) - g_propagate_error (error, local_error); - - camel_operation_pop_message (cancellable); - - g_slist_free_full (remote_folders, g_object_unref); - g_slist_free_full (local_folders, g_free); - g_slist_free (tocheck); - g_object_unref (conn); -} - -void -camel_ews_store_update_foreign_subfolders (CamelEwsStore *ews_store, - const gchar *fid) -{ - struct EwsUpdateForeignSubfoldersData *euf; - CamelSession *session; - - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - g_return_if_fail (fid != NULL); - - session = camel_service_ref_session (CAMEL_SERVICE (ews_store)); - g_return_if_fail (session != NULL); - - euf = g_new0 (struct EwsUpdateForeignSubfoldersData, 1); - euf->ews_store = g_object_ref (ews_store); - euf->folder_id = g_strdup (fid); - - camel_session_submit_job ( - session, _("Updating foreign folders"), ews_store_update_foreign_subfolders, - euf, ews_update_foreign_subfolders_data_free); - - g_object_unref (session); -} - -static gboolean -ews_initial_setup_with_connection_sync (CamelStore *store, - GHashTable *save_setup, - EEwsConnection *connection, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store; - GSList *folders = NULL, *folder_ids = NULL; - gint nn; - GError *local_error = NULL; - - g_return_val_if_fail (CAMEL_IS_EWS_STORE (store), FALSE); - - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return FALSE; - - ews_store = CAMEL_EWS_STORE (store); - if (connection) { - g_object_ref (connection); - } else { - if (!camel_ews_store_connected (ews_store, cancellable, error)) - return FALSE; - - connection = camel_ews_store_ref_connection (ews_store); - g_return_val_if_fail (connection != NULL, FALSE); - } - - for (nn = 0; nn < G_N_ELEMENTS (system_folder); nn++) { - EwsFolderId *fid = NULL; - - fid = g_new0 (EwsFolderId, 1); - fid->id = g_strdup (system_folder[nn].dist_folder_id); - fid->is_distinguished_id = TRUE; - - folder_ids = g_slist_append (folder_ids, fid); - } - - /* fetch system folders first using getfolder operation */ - if (!e_ews_connection_get_folder_sync ( - connection, EWS_PRIORITY_MEDIUM, "IdOnly", - NULL, folder_ids, &folders, - cancellable, &local_error)) { - g_clear_object (&connection); - g_propagate_error (error, local_error); - return FALSE; - } - - if (folders && (g_slist_length (folders) != G_N_ELEMENTS (system_folder))) - d (printf ("Error : not all folders are returned by getfolder operation")); - else if (!local_error && folders) - ews_store_set_flags (ews_store, folders); - else if (local_error) { - /* report error and make sure we are not leaking anything */ - g_warn_if_fail (folders == NULL); - } else - d (printf ("folders for respective distinguished ids don't exist")); - - if (save_setup) { - gchar *folder_id; - - folder_id = camel_ews_store_summary_get_folder_id_from_folder_type (ews_store->summary, CAMEL_FOLDER_TYPE_SENT); - if (folder_id) { - gchar *fullname; - - fullname = camel_ews_store_summary_get_folder_full_name (ews_store->summary, folder_id, NULL); - if (fullname && *fullname) { - g_hash_table_insert (save_setup, - g_strdup (CAMEL_STORE_SETUP_SENT_FOLDER), - g_strdup (fullname)); - } - - g_free (fullname); - g_free (folder_id); - } - - if (g_slist_length (folders) == G_N_ELEMENTS (system_folder)) { - gint ii; - - for (ii = 0; ii < G_N_ELEMENTS (system_folder); ii++) { - if (g_str_equal ("drafts", system_folder[ii].dist_folder_id)) { - break; - } - } - - if (ii < G_N_ELEMENTS (system_folder)) { - EEwsFolder *drafts = g_slist_nth (folders, ii)->data; - if (drafts && !e_ews_folder_is_error (drafts)) { - const EwsFolderId *fid = e_ews_folder_get_id (drafts); - - if (fid && fid->id) { - gchar *fullname; - - fullname = camel_ews_store_summary_get_folder_full_name (ews_store->summary, fid->id, NULL); - if (fullname && *fullname) { - g_hash_table_insert (save_setup, - g_strdup (CAMEL_STORE_SETUP_DRAFTS_FOLDER), - g_strdup (fullname)); - } - - g_free (fullname); - } - } - } - } - } - - g_slist_free_full (folders, g_object_unref); - g_slist_free_full (folder_ids, (GDestroyNotify) e_ews_folder_id_free); - g_clear_object (&connection); - g_clear_error (&local_error); - - return TRUE; -} - -static gboolean -ews_initial_setup_sync (CamelStore *store, - GHashTable *save_setup, - GCancellable *cancellable, - GError **error) -{ - return ews_initial_setup_with_connection_sync (store, save_setup, NULL, cancellable, error); -} - -static CamelAuthenticationResult -ews_authenticate_sync (CamelService *service, - const gchar *mechanism, - GCancellable *cancellable, - GError **error) -{ - CamelAuthenticationResult result; - CamelEwsStore *ews_store; - CamelSettings *settings; - CamelEwsSettings *ews_settings; - EEwsConnection *connection; - ESource *source; - GSList *folders_created = NULL; - GSList *folders_updated = NULL; - GSList *folders_deleted = NULL; - GSList *folder_ids = NULL; - GSList *created_folder_ids = NULL; - gboolean includes_last_folder = FALSE; - gboolean initial_setup = FALSE; - const gchar *password; - gchar *hosturl; - gchar *old_sync_state = NULL, *new_sync_state = NULL; - GError *local_error = NULL; - - ews_store = CAMEL_EWS_STORE (service); - - password = camel_service_get_password (service); - - settings = camel_service_ref_settings (service); - - ews_settings = CAMEL_EWS_SETTINGS (settings); - hosturl = camel_ews_settings_dup_hosturl (ews_settings); - source = camel_ews_utils_ref_corresponding_source (service, cancellable); - - connection = e_ews_connection_new (source, hosturl, ews_settings); - e_ews_connection_set_password (connection, password); - - g_clear_object (&source); - g_free (hosturl); - - g_object_unref (settings); - - e_binding_bind_property ( - service, "proxy-resolver", - connection, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - /* XXX We need to run some operation that requires authentication - * but does not change any server-side state, so we can check - * the error status and determine if our password is valid. - * David suggested e_ews_connection_sync_folder_hierarchy(), - * since we have to do that eventually anyway. */ - - /*use old sync_state from summary*/ - old_sync_state = camel_ews_store_summary_get_string_val (ews_store->summary, "sync_state", NULL); - if (!old_sync_state) { - initial_setup = TRUE; - } else { - gchar *inbox_folder_id; - - inbox_folder_id = camel_ews_store_summary_get_folder_id_from_folder_type (ews_store->summary, CAMEL_FOLDER_TYPE_INBOX); - if (!inbox_folder_id || !*inbox_folder_id) - initial_setup = TRUE; - - g_free (inbox_folder_id); - } - - e_ews_connection_sync_folder_hierarchy_sync (connection, EWS_PRIORITY_MEDIUM, old_sync_state, - &new_sync_state, &includes_last_folder, &folders_created, &folders_updated, &folders_deleted, - cancellable, &local_error); - - g_free (old_sync_state); - old_sync_state = NULL; - - if (g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_UNAVAILABLE)) { - local_error->domain = CAMEL_SERVICE_ERROR; - local_error->code = CAMEL_SERVICE_ERROR_UNAVAILABLE; - } - - if (!initial_setup && g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) { - g_clear_error (&local_error); - ews_store_forget_all_folders (ews_store); - camel_ews_store_summary_store_string_val (ews_store->summary, "sync_state", ""); - camel_ews_store_summary_clear (ews_store->summary); - - initial_setup = TRUE; - - e_ews_connection_sync_folder_hierarchy_sync (connection, EWS_PRIORITY_MEDIUM, NULL, - &new_sync_state, &includes_last_folder, &folders_created, &folders_updated, &folders_deleted, - cancellable, &local_error); - } - - if (local_error == NULL) { - GSList *foreign_fids, *ff; - - g_mutex_lock (&ews_store->priv->connection_lock); - ews_store_unset_connection_locked (ews_store); - ews_store->priv->connection = g_object_ref (connection); - g_signal_connect (ews_store->priv->connection, "password-will-expire", - G_CALLBACK (camel_ews_store_password_will_expire_cb), ews_store); - g_mutex_unlock (&ews_store->priv->connection_lock); - - /* This consumes all allocated result data. */ - ews_update_folder_hierarchy ( - ews_store, new_sync_state, includes_last_folder, - folders_created, folders_deleted, folders_updated, &created_folder_ids); - - /* Also update folder structures of foreign folders, - those which are subscribed with subfolders */ - foreign_fids = camel_ews_store_summary_get_foreign_folders (ews_store->summary, NULL); - for (ff = foreign_fids; ff != NULL; ff = ff->next) { - const gchar *fid = ff->data; - - if (camel_ews_store_summary_get_foreign_subfolders (ews_store->summary, fid, NULL)) { - camel_ews_store_update_foreign_subfolders (ews_store, fid); - } - } - - g_slist_free_full (foreign_fids, g_free); - } else { - g_mutex_lock (&ews_store->priv->connection_lock); - ews_store_unset_connection_locked (ews_store); - g_mutex_unlock (&ews_store->priv->connection_lock); - - g_free (new_sync_state); - - /* Make sure we're not leaking anything. */ - g_warn_if_fail (folders_created == NULL); - g_warn_if_fail (folders_updated == NULL); - g_warn_if_fail (folders_deleted == NULL); - } - - /*get folders using distinguished id by GetFolder operation and set system flags to folders, only for first time*/ - if (!local_error && initial_setup && connection) { - ews_initial_setup_with_connection_sync (CAMEL_STORE (ews_store), NULL, connection, cancellable, NULL); - } - - /* postpone notification of new folders to time when also folder flags are known, - thus the view in evolution sows Inbox with an Inbox icon. */ - for (folder_ids = created_folder_ids; folder_ids; folder_ids = folder_ids->next) { - CamelFolderInfo *fi; - - fi = camel_ews_utils_build_folder_info (ews_store, folder_ids->data); - camel_store_folder_created (CAMEL_STORE (ews_store), fi); - camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_folder_info_free (fi); - } - - g_slist_free_full (created_folder_ids, g_free); - - if (local_error == NULL) { - result = CAMEL_AUTHENTICATION_ACCEPTED; - } else if (g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED)) { - g_clear_error (&local_error); - result = CAMEL_AUTHENTICATION_REJECTED; - } else { - g_propagate_error (error, local_error); - result = CAMEL_AUTHENTICATION_ERROR; - } - - g_object_unref (connection); - - return result; -} - -static GList * -ews_store_query_auth_types_sync (CamelService *service, - GCancellable *cancellable, - GError **error) -{ - EEwsConnection *connection; - ESource *source; - CamelSettings *settings; - CamelEwsSettings *ews_settings; - GList *auth_types = NULL; - GSList *auth_methods = NULL, *aiter; - gchar *hosturl; - - g_return_val_if_fail (CAMEL_IS_EWS_STORE (service), NULL); - - settings = camel_service_ref_settings (service); - ews_settings = CAMEL_EWS_SETTINGS (settings); - hosturl = camel_ews_settings_dup_hosturl (ews_settings); - source = camel_ews_utils_ref_corresponding_source (service, cancellable); - - connection = e_ews_connection_new_full (source, hosturl, ews_settings, FALSE); - - g_clear_object (&source); - g_free (hosturl); - g_object_unref (settings); - - e_binding_bind_property ( - service, "proxy-resolver", - connection, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - if (e_ews_connection_query_auth_methods_sync (connection, G_PRIORITY_DEFAULT, &auth_methods, cancellable, error)) { - CamelProvider *provider; - CamelServiceAuthType *authtype; - - provider = camel_service_get_provider (service); - g_return_val_if_fail (provider != NULL, NULL); - - for (aiter = auth_methods; aiter; aiter = aiter->next) { - GList *siter; - const gchar *auth = aiter->data; - - if (!auth) - continue; - - if (g_ascii_strcasecmp (auth, "NTLM") == 0) - auth = ""; - else if (g_ascii_strcasecmp (auth, "Basic") == 0) - auth = "PLAIN"; - else if (g_ascii_strcasecmp (auth, "Negotiate") == 0) - auth = "GSSAPI"; - else if (e_oauth2_services_is_supported () && - g_ascii_strcasecmp (auth, "Bearer") == 0) { - /* Use Camel name for OAuth2. It's up to the caller to decide whether - it can be used or not. */ - authtype = camel_sasl_authtype ("XOAUTH2"); - if (authtype) - auth_types = g_list_prepend (auth_types, authtype); - - continue; - } - - for (siter = provider->authtypes; siter; siter = siter->next) { - authtype = siter->data; - - if (g_ascii_strcasecmp (authtype->authproto, auth) == 0) - auth_types = g_list_prepend (auth_types, authtype); - } - } - - g_slist_free_full (auth_methods, g_free); - } - - g_object_unref (connection); - - return g_list_reverse (auth_types); -} - -static CamelFolderInfo * ews_create_folder_sync (CamelStore *store, const gchar *parent_name,const gchar *folder_name, GCancellable *cancellable, GError **error); - -static CamelFolder * -ews_get_folder_sync (CamelStore *store, - const gchar *folder_name, - guint32 flags, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store; - CamelFolder *folder = NULL; - gchar *fid, *folder_dir; - - ews_store = (CamelEwsStore *) store; - - fid = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, folder_name); - - if (!fid) { - g_set_error ( - error, CAMEL_STORE_ERROR, - CAMEL_STORE_ERROR_NO_FOLDER, - _("No such folder: %s"), folder_name); - return NULL; - } else { - /* We don't actually care what it is; only that it exists */ - g_free (fid); - } - - folder_dir = g_build_filename (ews_store->storage_path, "folders", folder_name, NULL); - folder = camel_ews_folder_new (store, folder_name, folder_dir, cancellable, error); - - g_free (folder_dir); - - if ((flags & CAMEL_STORE_FOLDER_INFO_REFRESH) != 0) - camel_folder_prepare_content_refresh (folder); - - return folder; -} - -static gchar * -get_public_folder_full_name (EEwsFolder *folder, - GHashTable *folders_by_id) -{ - const EwsFolderId *parent_fid; - GString *full_name; - - g_return_val_if_fail (folder != NULL, NULL); - g_return_val_if_fail (folders_by_id != NULL, NULL); - - full_name = g_string_new (e_ews_folder_get_escaped_name (folder)); - while (folder) { - parent_fid = e_ews_folder_get_parent_id (folder); - if (!parent_fid || !parent_fid->id) - break; - - folder = g_hash_table_lookup (folders_by_id, parent_fid->id); - if (folder) { - g_string_prepend (full_name, "/"); - g_string_prepend (full_name, e_ews_folder_get_escaped_name (folder)); - } - } - - g_string_prepend (full_name, "/"); - g_string_prepend (full_name, EWS_PUBLIC_FOLDER_ROOT_DISPLAY_NAME); - - return g_string_free (full_name, FALSE); -} - -static gboolean -ews_store_has_as_parent_id (CamelEwsStoreSummary *ews_summary, - const gchar *fid, - const gchar *mailroot_fid) -{ - gchar *parent = NULL; - gboolean found = FALSE; - - g_return_val_if_fail (CAMEL_IS_EWS_STORE_SUMMARY (ews_summary), FALSE); - g_return_val_if_fail (fid != NULL, FALSE); - g_return_val_if_fail (mailroot_fid != NULL, FALSE); - - while (found = g_strcmp0 (fid, mailroot_fid) == 0, fid && !found) { - gchar *tmp = parent; - - parent = camel_ews_store_summary_get_parent_folder_id (ews_summary, fid, NULL); - fid = parent; - - g_free (tmp); - } - - g_free (parent); - - return found; -} - -static CamelFolderInfo * -folder_info_from_store_summary (CamelEwsStore *store, - const gchar *top, - guint32 flags, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStoreSummary *ews_summary; - GPtrArray *folder_infos = NULL; - CamelFolderInfo *root_fi = NULL, *fi; - - /* search in public folders */ - if ((flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST) != 0) { - GHashTable *folders_by_id; - GSList *fiter; - GList *esources = NULL; - gchar *hosturl = NULL, *username = NULL; - - g_mutex_lock (&store->priv->get_finfo_lock); - - if (!store->priv->public_folders) { - g_mutex_unlock (&store->priv->get_finfo_lock); - return NULL; - } - - folder_infos = g_ptr_array_new (); - folders_by_id = g_hash_table_new (g_str_hash, g_str_equal); - - for (fiter = store->priv->public_folders; fiter != NULL; fiter = g_slist_next (fiter)) { - EEwsFolder *folder = fiter->data; - const EwsFolderId *fid; - - if (!folder) - continue; - - fid = e_ews_folder_get_id (folder); - if (!fid || !fid->id) - continue; - - g_hash_table_insert (folders_by_id, fid->id, folder); - } - - fi = camel_folder_info_new (); - fi->full_name = g_strdup (EWS_PUBLIC_FOLDER_ROOT_DISPLAY_NAME); - fi->display_name = g_strdup (fi->full_name); - fi->flags = CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_NOSELECT; - fi->unread = -1; - fi->total = -1; - - g_ptr_array_add (folder_infos, fi); - - for (fiter = store->priv->public_folders; fiter != NULL; fiter = g_slist_next (fiter)) { - EEwsFolder *folder = fiter->data; - const EwsFolderId *fid; - - if (!folder) - continue; - - fid = e_ews_folder_get_id (folder); - if (!fid || !fid->id) - continue; - - fi = camel_folder_info_new (); - fi->full_name = get_public_folder_full_name (folder, folders_by_id); - fi->display_name = g_strdup (e_ews_folder_get_name (folder)); - fi->flags = 0; - fi->unread = e_ews_folder_get_unread_count (folder); - fi->total = e_ews_folder_get_total_count (folder); - - switch (e_ews_folder_get_folder_type (folder)) { - case E_EWS_FOLDER_TYPE_CALENDAR: - fi->flags |= CAMEL_FOLDER_TYPE_EVENTS; - break; - case E_EWS_FOLDER_TYPE_CONTACTS: - fi->flags |= CAMEL_FOLDER_TYPE_CONTACTS; - break; - case E_EWS_FOLDER_TYPE_TASKS: - fi->flags |= CAMEL_FOLDER_TYPE_TASKS; - break; - case E_EWS_FOLDER_TYPE_MEMOS: - fi->flags |= CAMEL_FOLDER_TYPE_MEMOS; - break; - default: - break; - } - - if (camel_ews_store_summary_has_folder (store->summary, fid->id)) { - guint64 fflags = camel_ews_store_summary_get_folder_flags (store->summary, fid->id, NULL); - - if ((fflags & CAMEL_FOLDER_SUBSCRIBED) != 0) - fi->flags |= CAMEL_FOLDER_SUBSCRIBED; - } - - if (!(fi->flags & CAMEL_FOLDER_SUBSCRIBED) && - e_ews_folder_get_folder_type (folder) != E_EWS_FOLDER_TYPE_MAILBOX) { - if (!hosturl && !username && !esources) { - CamelSession *session; - CamelSettings *settings; - CamelEwsSettings *ews_settings; - ESourceRegistry *registry = NULL; - - settings = camel_service_ref_settings (CAMEL_SERVICE (store)); - ews_settings = CAMEL_EWS_SETTINGS (settings); - session = camel_service_ref_session (CAMEL_SERVICE (store)); - if (E_IS_MAIL_SESSION (session)) - registry = e_mail_session_get_registry (E_MAIL_SESSION (session)); - - hosturl = camel_ews_settings_dup_hosturl (ews_settings); - username = camel_network_settings_dup_user (CAMEL_NETWORK_SETTINGS (ews_settings)); - esources = e_ews_folder_utils_get_esources (registry, hosturl, username, cancellable, NULL); - - g_object_unref (settings); - g_object_unref (session); - } - - if (e_ews_folder_utils_is_subscribed_as_esource (esources, hosturl, username, fid->id)) - fi->flags |= CAMEL_FOLDER_SUBSCRIBED; - } - - g_ptr_array_add (folder_infos, fi); - } - - g_list_free_full (esources, g_object_unref); - g_hash_table_destroy (folders_by_id); - g_free (hosturl); - g_free (username); - g_mutex_unlock (&store->priv->get_finfo_lock); - - /* search in regular/subscribed folders */ - } else { - GSList *folders, *fiter; - gchar *mailroot_fid = NULL, *inbox_fid; - - ews_summary = store->summary; - folders = camel_ews_store_summary_get_folders (ews_summary, top); - if (!folders) - return NULL; - - inbox_fid = camel_ews_store_summary_get_folder_id_from_folder_type (ews_summary, CAMEL_FOLDER_TYPE_INBOX); - if (inbox_fid) { - mailroot_fid = camel_ews_store_summary_get_parent_folder_id (ews_summary, inbox_fid, NULL); - } - g_free (inbox_fid); - inbox_fid = NULL; - - folder_infos = g_ptr_array_new (); - - for (fiter = folders; fiter != NULL; fiter = g_slist_next (fiter)) { - EEwsFolderType ftype; - const gchar *fid = fiter->data; - - ftype = camel_ews_store_summary_get_folder_type (ews_summary, fid, NULL); - if (ftype != E_EWS_FOLDER_TYPE_MAILBOX) - continue; - - if (camel_ews_store_summary_get_public (ews_summary, fid, NULL)) { - guint64 fflags; - - fflags = camel_ews_store_summary_get_folder_flags (ews_summary, fid, NULL); - if (!(fflags & CAMEL_FOLDER_SUBSCRIBED)) - continue; - } else if (!camel_ews_store_summary_get_foreign (ews_summary, fid, NULL) && - mailroot_fid && !ews_store_has_as_parent_id (ews_summary, fid, mailroot_fid)) { - /* Skip mail folders out of the msgfolderroot hierarchy */ - continue; - } - - fi = camel_ews_utils_build_folder_info (store, fid); - g_ptr_array_add (folder_infos, fi); - } - - g_slist_free_full (folders, g_free); - g_free (mailroot_fid); - } - - root_fi = camel_folder_info_build (folder_infos, top, '/', TRUE); - g_ptr_array_free (folder_infos, TRUE); - - return root_fi; -} - -static gboolean -ews_refresh_finfo (CamelEwsStore *ews_store) -{ - if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store))) - return FALSE; - - if (!ews_store->priv->updates_cancellable) - ews_store->priv->updates_cancellable = g_cancellable_new (); - - run_update_thread (ews_store, TRUE, ews_store->priv->updates_cancellable); - - return TRUE; -} - -static CamelFolderInfo * -ews_get_folder_info_sync (CamelStore *store, - const gchar *top, - guint32 flags, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store; - CamelEwsStorePrivate *priv; - CamelFolderInfo *fi = NULL; - EEwsConnection *connection; - gchar *old_sync_state, *new_sync_state = NULL; - gboolean initial_setup = FALSE; - GSList *folders_created = NULL, *folders_updated = NULL; - GSList *folders_deleted = NULL; - gboolean includes_last_folder; - gboolean success; - GError *local_error = NULL; - - ews_store = (CamelEwsStore *) store; - priv = ews_store->priv; - - if ((flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST) != 0) { - gboolean includes_last_folder = TRUE; - GSList *folders = NULL, *to_check = NULL; - EwsFolderId *folder_id; - - if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store))) { - g_set_error_literal ( - error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE, - _("Cannot list EWS public folders in offline mode")); - return NULL; - } - - g_mutex_lock (&priv->get_finfo_lock); - - g_slist_free_full (priv->public_folders, g_object_unref); - priv->public_folders = NULL; - - connection = camel_ews_store_ref_connection (ews_store); - folder_id = e_ews_folder_id_new ("publicfoldersroot", NULL, TRUE); - to_check = g_slist_append (to_check, folder_id); - - while (!local_error && !g_cancellable_is_cancelled (cancellable) && to_check) { - folder_id = to_check->data; - to_check = g_slist_remove (to_check, folder_id); - - while (e_ews_connection_find_folder_sync (connection, EWS_PRIORITY_MEDIUM, folder_id, &includes_last_folder, &folders, - cancellable, &local_error) && !local_error && - !g_cancellable_is_cancelled (cancellable)) { - GSList *fiter; - - if (!folders) - break; - - for (fiter = folders; fiter != NULL; fiter = fiter->next) { - EEwsFolder *folder = fiter->data; - - if (e_ews_folder_get_child_count (folder) > 0) { - const EwsFolderId *fid = e_ews_folder_get_id (folder); - - if (fid) - to_check = g_slist_prepend (to_check, - e_ews_folder_id_new (fid->id, fid->change_key, fid->is_distinguished_id)); - } - - if (!e_ews_folder_get_parent_id (folder)) { - if (!folder_id->is_distinguished_id) { - e_ews_folder_set_parent_id (folder, - e_ews_folder_id_new (folder_id->id, folder_id->change_key, folder_id->is_distinguished_id)); - } else { - e_ews_folder_set_parent_id (folder, e_ews_folder_id_new (EWS_PUBLIC_FOLDER_ROOT_ID, NULL, FALSE)); - } - } - } - - priv->public_folders = g_slist_concat (priv->public_folders, folders); - folders = NULL; - - if (includes_last_folder) - break; - } - - e_ews_folder_id_free (folder_id); - } - - g_mutex_unlock (&priv->get_finfo_lock); - - g_object_unref (connection); - g_slist_free_full (to_check, (GDestroyNotify) e_ews_folder_id_free); - - camel_ews_store_ensure_virtual_folders (ews_store); - - if (local_error) { - camel_ews_store_maybe_disconnect (ews_store, local_error); - g_propagate_error (error, local_error); - - return NULL; - } - - if (!priv->public_folders) { - g_set_error_literal ( - error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE, - _("Cannot find any EWS public folders")); - return NULL; - } - - goto offline; - } - - g_mutex_lock (&priv->get_finfo_lock); - if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) { - camel_ews_store_ensure_virtual_folders (ews_store); - g_mutex_unlock (&priv->get_finfo_lock); - goto offline; - } - - if (!camel_service_connect_sync ((CamelService *) store, cancellable, error)) { - camel_offline_store_set_online_sync (CAMEL_OFFLINE_STORE (store), FALSE, NULL, NULL); - camel_ews_store_ensure_virtual_folders (ews_store); - g_mutex_unlock (&priv->get_finfo_lock); - return NULL; - } - - old_sync_state = camel_ews_store_summary_get_string_val (ews_store->summary, "sync_state", NULL); - if (!old_sync_state) - initial_setup = TRUE; - - if (!initial_setup && (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) != 0) { - time_t now = time (NULL); - - g_free (old_sync_state); - if (now - priv->last_refresh_time > FINFO_REFRESH_INTERVAL && ews_refresh_finfo (ews_store)) - ews_store->priv->last_refresh_time = time (NULL); - - g_mutex_unlock (&priv->get_finfo_lock); - goto offline; - } - - connection = camel_ews_store_ref_connection (ews_store); - - success = e_ews_connection_sync_folder_hierarchy_sync (connection, EWS_PRIORITY_MEDIUM, old_sync_state, - &new_sync_state, &includes_last_folder, &folders_created, &folders_updated, &folders_deleted, - cancellable, &local_error); - - g_free (old_sync_state); - old_sync_state = NULL; - - if (!initial_setup && g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) { - g_clear_error (&local_error); - ews_store_forget_all_folders (ews_store); - camel_ews_store_summary_store_string_val (ews_store->summary, "sync_state", ""); - camel_ews_store_summary_clear (ews_store->summary); - - success = e_ews_connection_sync_folder_hierarchy_sync (connection, EWS_PRIORITY_MEDIUM, NULL, - &new_sync_state, &includes_last_folder, &folders_created, &folders_updated, &folders_deleted, - cancellable, &local_error); - } - - g_object_unref (connection); - - if (!success) { - if (local_error) - g_warning ( - "Unable to fetch the folder hierarchy: %s :%d \n", - local_error->message, local_error->code); - else - g_warning ("Unable to fetch the folder hierarchy.\n"); - - camel_ews_store_maybe_disconnect (ews_store, local_error); - g_propagate_error (error, local_error); - - g_mutex_unlock (&priv->get_finfo_lock); - return NULL; - } - ews_update_folder_hierarchy ( - ews_store, new_sync_state, includes_last_folder, - folders_created, folders_deleted, folders_updated, NULL); - g_mutex_unlock (&priv->get_finfo_lock); - -offline: - fi = folder_info_from_store_summary (ews_store, top, flags, cancellable, error); - return fi; -} - -static CamelFolderInfo * -ews_create_folder_sync (CamelStore *store, - const gchar *parent_name, - const gchar *folder_name, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store = CAMEL_EWS_STORE (store); - CamelEwsStoreSummary *ews_summary = ews_store->summary; - gchar *fid = NULL; - gchar *full_name; - EwsFolderId *folder_id; - EEwsConnection *connection; - CamelFolderInfo *fi = NULL; - gboolean success; - GError *local_error = NULL; - - if (parent_name && *parent_name) - full_name = g_strdup_printf ("%s/%s", parent_name, folder_name); - else - full_name = g_strdup (folder_name); - - fid = camel_ews_store_summary_get_folder_id_from_name (ews_summary, full_name); - if (fid) { - g_free (fid); - g_set_error ( - error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, - _("Cannot create folder “%s”, folder already exists"), - full_name); - g_free (full_name); - return NULL; - } - - g_free (full_name); - - /* Get Parent folder ID */ - if (parent_name && parent_name[0]) { - fid = camel_ews_store_summary_get_folder_id_from_name ( - ews_summary, parent_name); - if (!fid) { - g_set_error ( - error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, - _("Parent folder %s does not exist"), - parent_name); - return NULL; - } - - if (g_str_equal (fid, EWS_FOREIGN_FOLDER_ROOT_ID)) { - g_free (fid); - - g_set_error ( - error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, - _("Cannot create folder under “%s”, it is used for folders of other users only"), - parent_name); - return NULL; - } - - if (g_str_equal (fid, EWS_PUBLIC_FOLDER_ROOT_ID)) { - g_free (fid); - - g_set_error ( - error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, - _("Cannot create folder under “%s”, it is used for public folders only"), - parent_name); - return NULL; - } - } - - if (!camel_ews_store_connected (ews_store, cancellable, error)) { - if (fid) g_free (fid); - return NULL; - } - - connection = camel_ews_store_ref_connection (ews_store); - - success = e_ews_connection_create_folder_sync ( - connection, - EWS_PRIORITY_MEDIUM, fid, - FALSE, folder_name, E_EWS_FOLDER_TYPE_MAILBOX, - &folder_id, cancellable, &local_error); - - g_object_unref (connection); - - if (!success) { - camel_ews_store_maybe_disconnect (ews_store, local_error); - g_propagate_error (error, local_error); - g_free (fid); - return NULL; - } - - /* Translate & store returned folder id */ - if (fid) - full_name = g_strdup_printf ("%s/%s", parent_name, folder_name); - else - full_name = g_strdup (folder_name); - - camel_ews_store_summary_new_folder ( - ews_summary, folder_id->id, - fid, folder_id->change_key, - folder_name, - E_EWS_FOLDER_TYPE_MAILBOX, - 0, 0, FALSE, FALSE); - fi = camel_ews_utils_build_folder_info (ews_store, folder_id->id); - e_ews_folder_id_free (folder_id); - - camel_store_folder_created (store, fi); - camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - - g_free (full_name); - g_free (fid); - return fi; -} - -static void -ews_update_store_move_recursive (CamelEwsStore *ews_store, - CamelFolderInfo *folder_info) -{ - while (folder_info != NULL) { - if (folder_info->child != NULL) - ews_update_store_move_recursive (ews_store, folder_info->child); - - camel_store_folder_created (CAMEL_STORE (ews_store), folder_info); - camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (ews_store), folder_info); - - folder_info = folder_info->next; - } -} - -static gboolean -ews_delete_folder_sync (CamelStore *store, - const gchar *folder_name, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store = CAMEL_EWS_STORE (store); - gchar *fid; - gchar *trash_fid; - gchar *trash_name; - CamelFolderInfo *folder_info; - CamelFolderInfo *to_update; - gboolean success; - gboolean is_under_trash_folder; - GError *local_error = NULL; - - folder_info = camel_store_get_folder_info_sync ( - store, folder_name, - CAMEL_STORE_FOLDER_INFO_RECURSIVE | - CAMEL_STORE_FOLDER_INFO_SUBSCRIBED, - cancellable, &local_error); - - if (folder_info == NULL) { - g_propagate_error (error, local_error); - - return FALSE; - } - - to_update = folder_info; - - fid = camel_ews_store_summary_get_folder_id_from_name ( - ews_store->summary, folder_name); - - if (!fid) { - camel_folder_info_free (folder_info); - - g_set_error ( - error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, - _("Folder does not exist")); - return FALSE; - } - - if (g_str_equal (fid, EWS_FOREIGN_FOLDER_ROOT_ID)) { - g_free (fid); - camel_folder_info_free (folder_info); - - g_set_error ( - error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, - _("Cannot remove folder “%s”, it is used for folders of other users only"), - folder_name); - return FALSE; - } - - if (g_str_equal (fid, EWS_PUBLIC_FOLDER_ROOT_ID)) { - g_free (fid); - camel_folder_info_free (folder_info); - - g_set_error ( - error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, - _("Cannot remove folder “%s”, it is used for public folders only"), - folder_name); - return FALSE; - } - - trash_fid = camel_ews_store_summary_get_folder_id_from_folder_type (ews_store->summary, CAMEL_FOLDER_TYPE_TRASH); - trash_name = camel_ews_store_summary_get_folder_full_name (ews_store->summary, trash_fid, &local_error); - if (!trash_name) { - g_free (trash_fid); - g_free (fid); - camel_folder_info_free (folder_info); - - g_propagate_error (error, local_error); - return FALSE; - } - - is_under_trash_folder = g_str_has_prefix (folder_name, trash_name); - - g_free (trash_name); - - if (!camel_ews_store_connected (ews_store, cancellable, &local_error)) { - g_free (trash_fid); - g_free (fid); - camel_folder_info_free (folder_info); - - g_propagate_error (error, local_error); - - return FALSE; - } - - if (camel_ews_store_summary_get_foreign (ews_store->summary, fid, NULL) || - camel_ews_store_summary_get_public (ews_store->summary, fid, NULL)) { - /* do not delete foreign or public folders, - * only remove them from local store */ - success = TRUE; - } else { - EEwsConnection *connection; - - connection = camel_ews_store_ref_connection (ews_store); - - success = e_ews_connection_delete_folder_sync ( - connection, - EWS_PRIORITY_MEDIUM, - fid, FALSE, is_under_trash_folder ? "HardDelete" : "MoveToDeletedItems", - cancellable, &local_error); - - g_object_unref (connection); - } - - if (!success) { - g_free (trash_fid); - g_free (fid); - camel_folder_info_free (folder_info); - - camel_ews_store_maybe_disconnect (ews_store, local_error); - g_propagate_error (error, local_error); - - return FALSE; - } - - if (is_under_trash_folder) { - success = camel_ews_utils_delete_folders_from_summary_recursive (ews_store, to_update, FALSE, error); - - if (!success) { - g_free (trash_fid); - g_free (fid); - camel_folder_info_free (folder_info); - - g_propagate_error (error, local_error); - return FALSE; - } - } else { - camel_ews_store_summary_set_parent_folder_id (ews_store->summary, fid, trash_fid); - } - - camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (ews_store), folder_info); - camel_store_folder_deleted (CAMEL_STORE (ews_store), folder_info); - - camel_folder_info_free (folder_info); - - if (!is_under_trash_folder) { - camel_ews_store_summary_rebuild_hashes (ews_store->summary); - - folder_info = camel_ews_utils_build_folder_info (ews_store, fid); - - to_update = folder_info; - ews_update_store_move_recursive (ews_store, to_update); - camel_folder_info_free (folder_info); - } - - camel_ews_store_ensure_virtual_folders (ews_store); - camel_ews_store_summary_save (ews_store->summary, NULL); - - g_free (trash_fid); - g_free (fid); - - return TRUE; -} - -struct _rename_cb_data { - const gchar *display_name; - const gchar *change_key; - const gchar *folder_id; -}; - -static void -rename_folder_cb (ESoapMessage *msg, - gpointer user_data) -{ - struct _rename_cb_data *rename_data = user_data; - - e_ews_message_start_item_change ( - msg, E_EWS_ITEMCHANGE_TYPE_FOLDER, - rename_data->folder_id, rename_data->change_key, 0); - e_soap_message_start_element (msg, "SetFolderField", NULL, NULL); - e_ews_message_write_string_parameter_with_attribute ( - msg, "FieldURI", NULL, NULL, - "FieldURI", "folder:DisplayName"); - - e_soap_message_start_element (msg, "Folder", NULL, NULL); - e_ews_message_write_string_parameter (msg, "DisplayName", NULL, rename_data->display_name); - e_soap_message_end_element (msg); /* Folder */ - - e_soap_message_end_element (msg); /* SetFolderField */ - - e_ews_message_end_item_change (msg); -} - -static gboolean -ews_rename_folder_sync (CamelStore *store, - const gchar *old_name, - const gchar *new_name, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store = CAMEL_EWS_STORE (store); - CamelEwsStoreSummary *ews_summary = ews_store->summary; - EEwsConnection *connection; - const gchar *old_slash, *new_slash; - gchar *fid; - gchar *changekey; - gboolean res = FALSE; - GError *local_error = NULL; - - if (!strcmp (old_name, new_name)) - return TRUE; - - if (!camel_ews_store_connected (ews_store, cancellable, error)) { - return FALSE; - } - - fid = camel_ews_store_summary_get_folder_id_from_name (ews_summary, old_name); - if (!fid) { - g_set_error ( - error, CAMEL_STORE_ERROR, - CAMEL_STORE_ERROR_NO_FOLDER, - _("Folder %s does not exist"), old_name); - return FALSE; - } - - changekey = camel_ews_store_summary_get_change_key (ews_summary, fid, error); - if (!changekey) { - g_set_error ( - error, CAMEL_STORE_ERROR, - CAMEL_STORE_ERROR_NO_FOLDER, - _("No change key record for folder %s"), fid); - g_free (fid); - return FALSE; - } - - connection = camel_ews_store_ref_connection (ews_store); - - old_slash = g_strrstr (old_name, "/"); - new_slash = g_strrstr (new_name, "/"); - - if (old_slash) - old_slash++; - else - old_slash = old_name; - - if (new_slash) - new_slash++; - else - new_slash = new_name; - - if (strcmp (old_slash, new_slash)) { - gint parent_len = old_slash - old_name; - struct _rename_cb_data *rename_data; - - /* Folder basename changed (i.e. UpdateFolder needed). - * Therefore, we can only do it if the folder hasn't also - * been moved from one parent folder to another. - * - * Strictly speaking, we could probably handle this, even - * if there are name collisions. We could UpdateFolder to - * a new temporary name that doesn't exist in either the - * old or new parent folders, then MoveFolder, then issue - * another UpdateFolder to the name we actually wanted. - * But since the Evolution UI doesn't seem to let us - * make both changes at the same time anyway, we'll just - * bail out for now; we can deal with it later if we need - * to. - */ - if (new_slash - new_name != parent_len || - strncmp (old_name, new_name, parent_len)) { - g_set_error ( - error, CAMEL_STORE_ERROR, - CAMEL_STORE_ERROR_INVALID, - _("Cannot both rename and move a folder at the same time")); - goto out; - } - - rename_data = g_new0 (struct _rename_cb_data, 1); - rename_data->display_name = new_slash; - rename_data->folder_id = fid; - rename_data->change_key = changekey; - - res = e_ews_connection_update_folder_sync ( - connection, EWS_PRIORITY_MEDIUM, - rename_folder_cb, rename_data, - cancellable, &local_error); - - if (!res) { - g_free (rename_data); - goto out; - } - g_free (rename_data); - camel_ews_store_summary_set_folder_name (ews_summary, fid, new_slash); - } else { - gchar *pfid = NULL; - gchar *parent_name; - - /* If we are not moving to the root folder, work out the ItemId of - * the new parent folder */ - if (new_slash != new_name) { - parent_name = g_strndup (new_name, new_slash - new_name - 1); - pfid = camel_ews_store_summary_get_folder_id_from_name ( - ews_summary, parent_name); - if (!pfid) { - g_set_error ( - error, CAMEL_STORE_ERROR, - CAMEL_STORE_ERROR_NO_FOLDER, - _("Cannot find folder ID for parent folder %s"), - parent_name); - g_free (parent_name); - goto out; - } - g_free (parent_name); - } - - res = e_ews_connection_move_folder_sync ( - connection, EWS_PRIORITY_MEDIUM, - pfid, fid, cancellable, &local_error); - - if (!res) { - g_free (pfid); - goto out; - } - camel_ews_store_summary_set_parent_folder_id (ews_summary, fid, pfid); - g_free (pfid); - } - - res = TRUE; - out: - if (local_error) { - camel_ews_store_maybe_disconnect (ews_store, local_error); - g_propagate_error (error, local_error); - } - - g_object_unref (connection); - - g_free (changekey); - g_free (fid); - return res; -} - -gchar * -ews_get_name (CamelService *service, - gboolean brief) -{ - CamelSettings *settings; - gchar *name; - gchar *host; - gchar *user; - - settings = camel_service_ref_settings (service); - - user = camel_network_settings_dup_user (CAMEL_NETWORK_SETTINGS (settings)); - host = camel_ews_utils_get_host_name (settings); - - g_object_unref (settings); - - if (brief) - name = g_strdup_printf ( - _("Exchange server %s"), host); - else - name = g_strdup_printf ( - _("Exchange service for %s on %s"), user, host); - - g_free (host); - g_free (user); - - return name; -} - -EEwsConnection * -camel_ews_store_ref_connection (CamelEwsStore *ews_store) -{ - EEwsConnection *connection = NULL; - - g_return_val_if_fail (CAMEL_IS_EWS_STORE (ews_store), NULL); - - g_mutex_lock (&ews_store->priv->connection_lock); - - if (ews_store->priv->connection != NULL) - connection = g_object_ref (ews_store->priv->connection); - - g_mutex_unlock (&ews_store->priv->connection_lock); - - return connection; -} - -static CamelFolder * -ews_get_trash_folder_sync (CamelStore *store, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store; - CamelFolder *folder = NULL; - gchar *folder_id, *folder_name; - - g_return_val_if_fail (CAMEL_IS_EWS_STORE (store), NULL); - - ews_store = CAMEL_EWS_STORE (store); - folder_id = camel_ews_store_summary_get_folder_id_from_folder_type ( - ews_store->summary, CAMEL_FOLDER_TYPE_TRASH); - - if (folder_id == NULL) { - g_set_error ( - error, CAMEL_STORE_ERROR, - CAMEL_STORE_ERROR_NO_FOLDER, - _("Could not locate Trash folder")); - return NULL; - } - - folder_name = camel_ews_store_summary_get_folder_full_name ( - ews_store->summary, folder_id, NULL); - - folder = camel_store_get_folder_sync ( - store, folder_name, 0, cancellable, error); - - g_free (folder_name); - g_free (folder_id); - - if (folder) { - GPtrArray *folders; - gboolean can = TRUE; - gint ii; - - /* Save content of all opened folders, thus any messages deleted in them - are moved to the Deleted Items folder first, thus in case of the trash - folder instance being used to expunge messages will contain all of them. - */ - folders = camel_store_dup_opened_folders (store); - for (ii = 0; ii < folders->len; ii++) { - CamelFolder *secfolder = folders->pdata[ii]; - - if (secfolder != folder && can) - can = camel_folder_synchronize_sync (secfolder, FALSE, cancellable, NULL); - - g_object_unref (secfolder); - } - g_ptr_array_free (folders, TRUE); - - /* To return 'Deleted Items' folder with current content, - not with possibly stale locally cached copy. */ - camel_folder_refresh_info_sync (folder, cancellable, NULL); - } - - return folder; -} - -static CamelFolder * -ews_get_junk_folder_sync (CamelStore *store, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store; - CamelFolder *folder = NULL; - gchar *folder_id, *folder_name; - - g_return_val_if_fail (CAMEL_IS_EWS_STORE (store), NULL); - - ews_store = CAMEL_EWS_STORE (store); - folder_id = camel_ews_store_summary_get_folder_id_from_folder_type ( - ews_store->summary, CAMEL_FOLDER_TYPE_JUNK); - - if (folder_id == NULL) { - g_set_error ( - error, CAMEL_STORE_ERROR, - CAMEL_STORE_ERROR_NO_FOLDER, - _("Could not locate Junk folder")); - return NULL; - } - - folder_name = camel_ews_store_summary_get_folder_full_name ( - ews_store->summary, folder_id, NULL); - - folder = camel_store_get_folder_sync ( - store, folder_name, 0, cancellable, error); - - g_free (folder_name); - g_free (folder_id); - - return folder; -} - -static gboolean -ews_can_refresh_folder (CamelStore *store, - CamelFolderInfo *info, - GError **error) -{ - CamelSettings *settings; - CamelEwsSettings *ews_settings; - gboolean check_all; - - /* Skip unselectable folders from automatic refresh */ - if (info && (info->flags & CAMEL_FOLDER_NOSELECT) != 0) - return FALSE; - - settings = camel_service_ref_settings (CAMEL_SERVICE (store)); - - ews_settings = CAMEL_EWS_SETTINGS (settings); - check_all = camel_ews_settings_get_check_all (ews_settings); - - g_object_unref (settings); - - if (check_all) - return TRUE; - - /* Delegate decision to parent class */ - return CAMEL_STORE_CLASS (camel_ews_store_parent_class)-> - can_refresh_folder (store, info, error); -} - -static gboolean -ews_store_folder_is_subscribed (CamelSubscribable *subscribable, - const gchar *folder_name) -{ - CamelEwsStore *ews_store = CAMEL_EWS_STORE (subscribable); - gchar *fid; - gboolean truth = FALSE; - GError *error = NULL; - - fid = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, folder_name); - if (!fid) - return FALSE; - - if (camel_ews_store_summary_get_foreign (ews_store->summary, fid, &error) && !error) { - truth = TRUE; - } - g_clear_error (&error); - - if (!truth && camel_ews_store_summary_get_public (ews_store->summary, fid, &error) && !error) { - truth = TRUE; - } - g_clear_error (&error); - - g_free (fid); - - return truth; -} - -/* caller should hold ews_store->priv->get_finfo_lock already */ -static EEwsFolder * -ews_store_find_public_folder (CamelEwsStore *ews_store, - const gchar *folder_name) -{ - EEwsFolder *folder = NULL; - GSList *piter; - gchar **folders; - gint ii; - - g_return_val_if_fail (CAMEL_IS_EWS_STORE (ews_store), NULL); - g_return_val_if_fail (folder_name != NULL, NULL); - - folders = g_strsplit (folder_name, "/", -1); - if (!folders || !folders[0] || g_strcmp0 (folders[0], EWS_PUBLIC_FOLDER_ROOT_DISPLAY_NAME) != 0) { - g_strfreev (folders); - return NULL; - } - - /* they are stored in public_folders from top level to bottom level */ - piter = ews_store->priv->public_folders; - for (ii = 1; folders[ii] && piter; ii++) { - const gchar *fname = folders[ii]; - - while (piter) { - EEwsFolder *subf = piter->data; - const EwsFolderId *parent_id; - - if (!subf) { - piter = NULL; - break; - } - - if (g_strcmp0 (e_ews_folder_get_name (subf), fname) == 0 || - g_strcmp0 (e_ews_folder_get_escaped_name (subf), fname) == 0) { - parent_id = e_ews_folder_get_parent_id (subf); - if (!folder && (!parent_id || g_strcmp0 (parent_id->id, EWS_PUBLIC_FOLDER_ROOT_ID) == 0)) { - folder = subf; - break; - } else if (parent_id && folder) { - const EwsFolderId *fid = e_ews_folder_get_id (folder); - - if (fid && g_strcmp0 (fid->id, parent_id->id) == 0) { - folder = subf; - break; - } - } - } - - piter = piter->next; - } - } - - if (!piter || folders[ii]) - folder = NULL; - - g_strfreev (folders); - - return folder; -} - -/* ppath contains proposed path, this only makes sure that it's a unique path */ -static void -ews_store_ensure_unique_path (CamelEwsStore *ews_store, - gchar **ppath) -{ - gboolean done; - guint counter = 0; - gchar *base_path = NULL; - - g_return_if_fail (ews_store != NULL); - g_return_if_fail (ews_store->summary != NULL); - g_return_if_fail (ppath != NULL); - g_return_if_fail (*ppath != NULL); - - done = FALSE; - while (!done) { - gchar *fid; - - done = TRUE; - - fid = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, *ppath); - if (fid) { - g_free (fid); - - done = FALSE; - counter++; - if (!counter) { - g_debug ("%s: Counter overflow", G_STRFUNC); - break; - } - - if (!base_path) - base_path = *ppath; - else - g_free (*ppath); - - *ppath = g_strdup_printf ("%s_%u", base_path, counter); - } - } - - g_free (base_path); -} - -static gboolean -ews_store_subscribe_folder_sync (CamelSubscribable *subscribable, - const gchar *folder_name, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store = CAMEL_EWS_STORE (subscribable); - EEwsFolder *folder; - const EwsFolderId *fid; - gboolean res = TRUE; - gchar *tmp; - - if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store))) { - g_set_error_literal ( - error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE, - _("Cannot subscribe EWS folders in offline mode")); - return FALSE; - } - - /* Folder's name is not stored with the "/" in the beginning, even when the - folder is in the root (same level as "Inbox", "Drafts", etc. */ - if (*folder_name == '/') - folder_name++; - - /* can subscribe only public folders, - thus skip anything known */ - tmp = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, folder_name); - if (tmp) { - g_free (tmp); - return TRUE; - } - - g_mutex_lock (&ews_store->priv->get_finfo_lock); - if (!ews_store->priv->public_folders) { - g_mutex_unlock (&ews_store->priv->get_finfo_lock); - - g_set_error ( - error, CAMEL_STORE_ERROR, CAMEL_STORE_ERROR_NO_FOLDER, - _("Cannot subscribe folder “%s”, no public folder available"), folder_name); - return FALSE; - } - - folder = ews_store_find_public_folder (ews_store, folder_name); - if (!folder) { - g_mutex_unlock (&ews_store->priv->get_finfo_lock); - - g_set_error ( - error, CAMEL_STORE_ERROR, CAMEL_STORE_ERROR_NO_FOLDER, - _("Cannot subscribe folder “%s”, folder not found"), folder_name); - return FALSE; - } - - fid = e_ews_folder_get_id (folder); - - g_return_val_if_fail (fid != NULL, FALSE); - - if (camel_ews_store_summary_has_folder (ews_store->summary, EWS_PUBLIC_FOLDER_ROOT_ID)) { - gchar *parent_name = camel_ews_store_summary_get_folder_name (ews_store->summary, EWS_PUBLIC_FOLDER_ROOT_ID, NULL); - - g_return_val_if_fail (parent_name != NULL, FALSE); - - tmp = g_strconcat (parent_name, "/", e_ews_folder_get_escaped_name (folder), NULL); - g_free (parent_name); - } else { - tmp = g_strconcat (EWS_PUBLIC_FOLDER_ROOT_DISPLAY_NAME, "/", e_ews_folder_get_escaped_name (folder), NULL); - } - - if (e_ews_folder_get_folder_type (folder) != E_EWS_FOLDER_TYPE_MAILBOX) { - CamelSession *session; - CamelSettings *settings; - CamelEwsSettings *ews_settings; - ESourceRegistry *registry = NULL; - - settings = camel_service_ref_settings (CAMEL_SERVICE (ews_store)); - ews_settings = CAMEL_EWS_SETTINGS (settings); - session = camel_service_ref_session (CAMEL_SERVICE (ews_store)); - if (E_IS_MAIL_SESSION (session)) - registry = e_mail_session_get_registry (E_MAIL_SESSION (session)); - - res = e_ews_folder_utils_add_as_esource (registry, - camel_ews_settings_get_hosturl (ews_settings), - camel_network_settings_get_user (CAMEL_NETWORK_SETTINGS (ews_settings)), - folder, - E_EWS_ESOURCE_FLAG_OFFLINE_SYNC | E_EWS_ESOURCE_FLAG_PUBLIC_FOLDER, - 0, - cancellable, - error); - - g_object_unref (session); - g_object_unref (settings); - } - - if (res) { - ews_store_ensure_unique_path (ews_store, &tmp); - - camel_ews_store_summary_new_folder (ews_store->summary, fid->id, EWS_PUBLIC_FOLDER_ROOT_ID, - NULL, - strrchr (tmp, '/') + 1, - e_ews_folder_get_folder_type (folder), - CAMEL_FOLDER_SUBSCRIBED, - e_ews_folder_get_total_count (folder), - FALSE, TRUE); - - if (e_ews_folder_get_folder_type (folder) == E_EWS_FOLDER_TYPE_MAILBOX) { - CamelFolderInfo *fi; - - camel_ews_store_ensure_virtual_folders (ews_store); - - fi = camel_ews_utils_build_folder_info (ews_store, fid->id); - camel_store_folder_created (CAMEL_STORE (ews_store), fi); - camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_folder_info_free (fi); - } - } - - camel_ews_store_summary_save (ews_store->summary, NULL); - - g_free (tmp); - g_mutex_unlock (&ews_store->priv->get_finfo_lock); - - return res; -} - -static gboolean -ews_store_unsubscribe_folder_sync (CamelSubscribable *subscribable, - const gchar *folder_name, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store = CAMEL_EWS_STORE (subscribable); - EEwsFolderType folder_type; - EEwsFolder *folder; - gboolean is_public; - gboolean res = TRUE; - gchar *fid = NULL; - - if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store))) { - g_set_error_literal ( - error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE, - _("Cannot unsubscribe EWS folders in offline mode")); - return FALSE; - } - - folder = ews_store_find_public_folder (ews_store, folder_name); - if (folder) { - const EwsFolderId *folder_id = e_ews_folder_get_id (folder); - - if (folder_id) { - fid = g_strdup (folder_id->id); - folder_type = e_ews_folder_get_folder_type (folder); - } - } - - if (!fid) { - fid = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, folder_name); - if (fid) - folder_type = camel_ews_store_summary_get_folder_type (ews_store->summary, fid, NULL); - } - - if (!fid) { - /* no such folder in the cache, might be unsubscribed already */ - return TRUE; - } - - is_public = camel_ews_store_summary_get_public (ews_store->summary, fid, NULL); - if (!is_public && !camel_ews_store_summary_get_foreign (ews_store->summary, fid, NULL)) { - /* nothing to do for regular folders */ - res = TRUE; - } else { - CamelFolderInfo *fi; - - if (!is_public && camel_ews_store_summary_get_foreign_subfolders (ews_store->summary, fid, NULL)) { - /* when subscribed with subfolders, then unsubscribe with subfolders as well */ - GSList *local_folders = NULL, *ii; - gchar *full_name = camel_ews_store_summary_get_folder_full_name (ews_store->summary, fid, NULL); - if (full_name) { - local_folders = camel_ews_store_summary_get_folders (ews_store->summary, full_name); - } - g_free (full_name); - - for (ii = local_folders; ii != NULL; ii = ii->next) { - const gchar *lfid = ii->data; - EEwsFolderType ftype; - - if (g_strcmp0 (lfid, fid) == 0) - continue; - - ftype = camel_ews_store_summary_get_folder_type (ews_store->summary, lfid, NULL); - if (ftype != E_EWS_FOLDER_TYPE_MAILBOX) - continue; - - fi = camel_ews_utils_build_folder_info (ews_store, lfid); - camel_ews_store_summary_remove_folder (ews_store->summary, lfid, NULL); - - camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_store_folder_deleted (CAMEL_STORE (ews_store), fi); - camel_folder_info_free (fi); - } - - g_slist_free_full (local_folders, g_free); - } - - if (folder_type != E_EWS_FOLDER_TYPE_MAILBOX) { - CamelSession *session; - CamelSettings *settings; - CamelEwsSettings *ews_settings; - ESourceRegistry *registry = NULL; - - settings = camel_service_ref_settings (CAMEL_SERVICE (ews_store)); - ews_settings = CAMEL_EWS_SETTINGS (settings); - session = camel_service_ref_session (CAMEL_SERVICE (ews_store)); - if (E_IS_MAIL_SESSION (session)) - registry = e_mail_session_get_registry (E_MAIL_SESSION (session)); - - res = e_ews_folder_utils_remove_as_esource (registry, - camel_ews_settings_get_hosturl (ews_settings), - camel_network_settings_get_user (CAMEL_NETWORK_SETTINGS (ews_settings)), - fid, - cancellable, - error); - - g_object_unref (session); - g_object_unref (settings); - } - - if (res) { - fi = camel_ews_utils_build_folder_info (ews_store, fid); - camel_ews_store_summary_remove_folder (ews_store->summary, fid, error); - - if (folder_type == E_EWS_FOLDER_TYPE_MAILBOX) { - camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_store_folder_deleted (CAMEL_STORE (ews_store), fi); - camel_folder_info_free (fi); - - camel_ews_store_ensure_virtual_folders (ews_store); - } - } - - camel_ews_store_summary_save (ews_store->summary, NULL); - } - - g_free (fid); - - return res; -} - -gboolean -camel_ews_store_connected (CamelEwsStore *ews_store, - GCancellable *cancellable, - GError **error) -{ - - if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store))) { - g_set_error ( - error, CAMEL_SERVICE_ERROR, - CAMEL_SERVICE_ERROR_UNAVAILABLE, - _("You must be working online to complete this operation")); - return FALSE; - } - - if (!camel_service_connect_sync ((CamelService *) ews_store, cancellable, error)) - return FALSE; - - return TRUE; -} - -void -camel_ews_store_maybe_disconnect (CamelEwsStore *store, - const GError *error) -{ - CamelService *service; - - g_return_if_fail (store != NULL); - - if (!error) - return; - - service = CAMEL_SERVICE (store); - - if (camel_service_get_connection_status (service) != CAMEL_SERVICE_CONNECTED) - return; - - if (g_error_matches (error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NORESPONSE) || - g_error_matches (error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED)) - camel_service_disconnect_sync (service, FALSE, NULL, NULL); -} - -static void -ews_store_unset_oof_settings_state (CamelSession *session, - GCancellable *cancellable, - gpointer user_data, - GError **error) -{ - - CamelEwsStore *ews_store = user_data; - EEwsConnection *connection; - EEwsOofSettings *oof_settings; - EEwsOofState state; - GError *local_error = NULL; - - camel_operation_push_message (cancellable, _("Unsetting the “Out of Office” status")); - - connection = camel_ews_store_ref_connection (ews_store); - oof_settings = e_ews_oof_settings_new_sync (connection, cancellable, &local_error); - g_object_unref (connection); - if (local_error != NULL) { - g_propagate_error (error, local_error); - camel_operation_pop_message (cancellable); - return; - } - - state = e_ews_oof_settings_get_state (oof_settings); - if (state == E_EWS_OOF_STATE_DISABLED) { - g_object_unref (oof_settings); - camel_operation_pop_message (cancellable); - return; - } - - e_ews_oof_settings_set_state (oof_settings, E_EWS_OOF_STATE_DISABLED); - e_ews_oof_settings_submit_sync (oof_settings, cancellable, error); - g_object_unref (oof_settings); - - camel_operation_pop_message (cancellable); -} - -void -camel_ews_store_unset_oof_settings_state (CamelEwsStore *ews_store) -{ - CamelService *service; - CamelSession *session; - - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - - service = CAMEL_SERVICE (ews_store); - session = camel_service_ref_session (service); - - camel_session_submit_job ( - session, _("Unsetting the “Out of Office” status"), - ews_store_unset_oof_settings_state, - g_object_ref (ews_store), - g_object_unref); - - g_object_unref (session); -} - -static void -ews_store_dispose (GObject *object) -{ - CamelEwsStore *ews_store; - CamelEwsSettings *ews_settings; - - ews_store = CAMEL_EWS_STORE (object); - - ews_settings = CAMEL_EWS_SETTINGS (camel_service_ref_settings (CAMEL_SERVICE (ews_store))); - g_signal_handlers_disconnect_by_func (ews_settings, camel_ews_store_listen_notifications_cb, ews_store); - g_signal_handlers_disconnect_by_func (ews_settings, camel_ews_store_check_all_cb, ews_store); - g_object_unref (ews_settings); - - if (ews_store->summary != NULL) { - camel_ews_store_summary_save (ews_store->summary, NULL); - g_object_unref (ews_store->summary); - ews_store->summary = NULL; - } - - g_mutex_lock (&ews_store->priv->connection_lock); - ews_store_unset_connection_locked (ews_store); - g_mutex_unlock (&ews_store->priv->connection_lock); - - g_slist_free_full (ews_store->priv->update_folder_names, g_free); - ews_store->priv->update_folder_names = NULL; - - g_slist_free_full (ews_store->priv->public_folders, g_object_unref); - ews_store->priv->public_folders = NULL; - - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (camel_ews_store_parent_class)->dispose (object); -} - -static void -ews_store_finalize (GObject *object) -{ - CamelEwsStore *ews_store; - - ews_store = CAMEL_EWS_STORE (object); - - g_free (ews_store->storage_path); - g_mutex_clear (&ews_store->priv->get_finfo_lock); - g_mutex_clear (&ews_store->priv->connection_lock); - g_rec_mutex_clear (&ews_store->priv->update_lock); - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (camel_ews_store_parent_class)->finalize (object); -} - -static void -camel_ews_store_class_init (CamelEwsStoreClass *class) -{ - GObjectClass *object_class; - CamelServiceClass *service_class; - CamelStoreClass *store_class; - - g_type_class_add_private (class, sizeof (CamelEwsStorePrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->set_property = ews_store_set_property; - object_class->get_property = ews_store_get_property; - object_class->dispose = ews_store_dispose; - object_class->finalize = ews_store_finalize; - - g_object_class_install_property ( - object_class, - PROP_HAS_OOO_SET, - g_param_spec_boolean ( - "has-ooo-set", - "Has OOO Set", - "Has Out of Office state set", - FALSE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property ( - object_class, - PROP_OOO_ALERT_STATE, - g_param_spec_enum ( - "ooo-alert-state", - "Out of Office Alert State", - "The state of the Out of Office Alert", - CAMEL_TYPE_EWS_STORE_OOO_ALERT_STATE, - CAMEL_EWS_STORE_OOO_ALERT_STATE_UNKNOWN, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS)); - - /* Inherited from CamelNetworkService */ - g_object_class_override_property ( - object_class, - PROP_CONNECTABLE, - "connectable"); - - /* Inherited from CamelNetworkService */ - g_object_class_override_property ( - object_class, - PROP_HOST_REACHABLE, - "host-reachable"); - - service_class = CAMEL_SERVICE_CLASS (class); - service_class->settings_type = CAMEL_TYPE_EWS_SETTINGS; - service_class->query_auth_types_sync = ews_store_query_auth_types_sync; - service_class->get_name = ews_get_name; - service_class->connect_sync = ews_connect_sync; - service_class->disconnect_sync = ews_disconnect_sync; - service_class->authenticate_sync = ews_authenticate_sync; - - store_class = CAMEL_STORE_CLASS (class); - store_class->get_folder_sync = ews_get_folder_sync; - store_class->create_folder_sync = ews_create_folder_sync; - store_class->delete_folder_sync = ews_delete_folder_sync; - store_class->rename_folder_sync = ews_rename_folder_sync; - store_class->get_folder_info_sync = ews_get_folder_info_sync; - store_class->initial_setup_sync = ews_initial_setup_sync; - - store_class->get_trash_folder_sync = ews_get_trash_folder_sync; - store_class->get_junk_folder_sync = ews_get_junk_folder_sync; - store_class->can_refresh_folder = ews_can_refresh_folder; -} - -static void -camel_ews_subscribable_init (CamelSubscribableInterface *iface) -{ - iface->folder_is_subscribed = ews_store_folder_is_subscribed; - iface->subscribe_folder_sync = ews_store_subscribe_folder_sync; - iface->unsubscribe_folder_sync = ews_store_unsubscribe_folder_sync; -} - -static void -camel_ews_store_init (CamelEwsStore *ews_store) -{ - ews_store->priv = CAMEL_EWS_STORE_GET_PRIVATE (ews_store); - - ews_store->priv->last_refresh_time = time (NULL) - (FINFO_REFRESH_INTERVAL + 10); - ews_store->priv->updates_cancellable = NULL; - ews_store->priv->update_folder_names = NULL; - ews_store->priv->subscription_key = 0; - ews_store->priv->update_folder_id = 0; - ews_store->priv->update_folder_list_id = 0; - ews_store->priv->password_expires_in_days = -1; - g_mutex_init (&ews_store->priv->get_finfo_lock); - g_mutex_init (&ews_store->priv->connection_lock); - g_rec_mutex_init (&ews_store->priv->update_lock); -} diff --git a/src/camel/camel-ews-store.c.sync-category-list b/src/camel/camel-ews-store.c.sync-category-list deleted file mode 100644 index 6e263ee..0000000 --- a/src/camel/camel-ews-store.c.sync-category-list +++ /dev/null @@ -1,3797 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* camel-ews-store.c : class for an ews store */ - -/* - * Authors: - * Chenthill Palanisamy - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU 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. - * - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include "server/camel-ews-settings.h" -#include "server/e-ews-item-change.h" -#include "server/e-ews-message.h" -#include "server/e-ews-oof-settings.h" - -#include "camel-ews-folder.h" -#include "camel-ews-store.h" -#include "camel-ews-summary.h" -#include "camel-ews-utils.h" -#include "camel-ews-enumtypes.h" - -#ifdef G_OS_WIN32 -#include -#include -#endif - -#define d(x) x -#define CURSOR_ITEM_LIMIT 100 - -#define CAMEL_EWS_STORE_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), CAMEL_TYPE_EWS_STORE, CamelEwsStorePrivate)) - -#define FINFO_REFRESH_INTERVAL 60 - -#define UPDATE_LOCK(x) (g_rec_mutex_lock(&(x)->priv->update_lock)) -#define UPDATE_UNLOCK(x) (g_rec_mutex_unlock(&(x)->priv->update_lock)) - -struct _CamelEwsStorePrivate { - time_t last_refresh_time; - GMutex get_finfo_lock; - EEwsConnection *connection; - GMutex connection_lock; - gboolean has_ooo_set; - CamelEwsStoreOooAlertState ooo_alert_state; - gint password_expires_in_days; - - gboolean listen_notifications; - guint subscription_key; - guint update_folder_id; - guint update_folder_list_id; - GCancellable *updates_cancellable; - GSList *update_folder_names; - GRecMutex update_lock; - - GSList *public_folders; /* EEwsFolder * objects */ -}; - -static gboolean ews_store_construct (CamelService *service, CamelSession *session, - CamelProvider *provider, GError **error); - -static void camel_ews_store_initable_init (GInitableIface *iface); -static void camel_ews_subscribable_init (CamelSubscribableInterface *iface); -static GInitableIface *parent_initable_interface; - -static CamelFolderInfo *folder_info_from_store_summary (CamelEwsStore *store, - const gchar *top, - guint32 flags, - GCancellable *cancellable, - GError **error); - -enum { - PROP_0, - PROP_HAS_OOO_SET, - PROP_OOO_ALERT_STATE, - PROP_CONNECTABLE, - PROP_HOST_REACHABLE -}; - -G_DEFINE_TYPE_WITH_CODE ( - CamelEwsStore, camel_ews_store, CAMEL_TYPE_OFFLINE_STORE, - G_IMPLEMENT_INTERFACE ( - G_TYPE_INITABLE, camel_ews_store_initable_init) - G_IMPLEMENT_INTERFACE ( - CAMEL_TYPE_NETWORK_SERVICE, NULL) - G_IMPLEMENT_INTERFACE ( - CAMEL_TYPE_SUBSCRIBABLE, camel_ews_subscribable_init)) - -static void -ews_store_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_HAS_OOO_SET: - camel_ews_store_set_has_ooo_set ( - CAMEL_EWS_STORE (object), - g_value_get_boolean (value)); - return; - case PROP_OOO_ALERT_STATE: - camel_ews_store_set_ooo_alert_state ( - CAMEL_EWS_STORE (object), - g_value_get_enum (value)); - return; - case PROP_CONNECTABLE: - camel_network_service_set_connectable ( - CAMEL_NETWORK_SERVICE (object), - g_value_get_object (value)); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -ews_store_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_HAS_OOO_SET: - g_value_set_boolean ( - value, - camel_ews_store_get_has_ooo_set ( - CAMEL_EWS_STORE (object))); - return; - case PROP_OOO_ALERT_STATE: - g_value_set_enum ( - value, - camel_ews_store_get_ooo_alert_state ( - CAMEL_EWS_STORE (object))); - return; - case PROP_CONNECTABLE: - g_value_take_object ( - value, - camel_network_service_ref_connectable ( - CAMEL_NETWORK_SERVICE (object))); - return; - case PROP_HOST_REACHABLE: - g_value_set_boolean ( - value, - camel_network_service_get_host_reachable ( - CAMEL_NETWORK_SERVICE (object))); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -void -camel_ews_store_set_has_ooo_set (CamelEwsStore *ews_store, - gboolean has_ooo_set) -{ - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - - if ((ews_store->priv->has_ooo_set ? 1 : 0) == (has_ooo_set ? 1 : 0)) - return; - - ews_store->priv->has_ooo_set = has_ooo_set; - g_object_notify (G_OBJECT (ews_store), "has-ooo-set"); -} - -gboolean -camel_ews_store_get_has_ooo_set (const CamelEwsStore *ews_store) -{ - g_return_val_if_fail (CAMEL_IS_EWS_STORE (ews_store), FALSE); - - return ews_store->priv->has_ooo_set; -} - -void -camel_ews_store_set_ooo_alert_state (CamelEwsStore *ews_store, - CamelEwsStoreOooAlertState state) -{ - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - - if (ews_store->priv->ooo_alert_state == state) - return; - - ews_store->priv->ooo_alert_state = state; - g_object_notify (G_OBJECT (ews_store), "ooo-alert-state"); -} - -CamelEwsStoreOooAlertState -camel_ews_store_get_ooo_alert_state (const CamelEwsStore *ews_store) -{ - g_return_val_if_fail ( - CAMEL_IS_EWS_STORE (ews_store), - CAMEL_EWS_STORE_OOO_ALERT_STATE_UNKNOWN); - - return ews_store->priv->ooo_alert_state; -} - -static void -ews_migrate_to_user_cache_dir (CamelService *service) -{ - const gchar *user_data_dir, *user_cache_dir; - - g_return_if_fail (service != NULL); - g_return_if_fail (CAMEL_IS_SERVICE (service)); - - user_data_dir = camel_service_get_user_data_dir (service); - user_cache_dir = camel_service_get_user_cache_dir (service); - - g_return_if_fail (user_data_dir != NULL); - g_return_if_fail (user_cache_dir != NULL); - - /* migrate only if the source directory exists and the destination doesn't */ - if (g_file_test (user_data_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR) && - !g_file_test (user_cache_dir, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { - gchar *parent_dir; - - parent_dir = g_path_get_dirname (user_cache_dir); - g_mkdir_with_parents (parent_dir, S_IRWXU); - g_free (parent_dir); - - if (g_rename (user_data_dir, user_cache_dir) == -1) { - g_debug ("%s: Failed to migrate '%s' to '%s': %s", G_STRFUNC, user_data_dir, user_cache_dir, g_strerror (errno)); - } else { - gchar *old_summary_file = g_build_filename (user_cache_dir, "folder-tree-v2", NULL); - - if (old_summary_file && g_file_test (old_summary_file, G_FILE_TEST_EXISTS)) { - gchar *new_summary_file = g_build_filename (user_cache_dir, "folder-tree", NULL); - - if (new_summary_file && g_rename (old_summary_file, new_summary_file) == -1) - g_debug ("%s: Failed to migrate '%s' to '%s': %s", G_STRFUNC, old_summary_file, new_summary_file, g_strerror (errno)); - - g_free (new_summary_file); - } - - g_free (old_summary_file); - } - } -} - -static gboolean -ews_store_initable_init (GInitable *initable, - GCancellable *cancellable, - GError **error) -{ - CamelService *service; - CamelSession *session; - CamelStore *store; - gboolean ret; - - store = CAMEL_STORE (initable); - service = CAMEL_SERVICE (initable); - - camel_store_set_flags (store, camel_store_get_flags (store) | CAMEL_STORE_USE_CACHE_DIR | CAMEL_STORE_SUPPORTS_INITIAL_SETUP); - ews_migrate_to_user_cache_dir (service); - - camel_store_set_flags (store, camel_store_get_flags (store) | CAMEL_STORE_CAN_DELETE_FOLDERS_AT_ONCE); - - /* Chain up to parent interface's init() method. */ - if (!parent_initable_interface->init (initable, cancellable, error)) - return FALSE; - - session = camel_service_ref_session (service); - - ret = ews_store_construct (service, session, NULL, error); - - g_object_unref (session); - - /* Add transport here ? */ - - return ret; -} - -static void -camel_ews_store_initable_init (GInitableIface *iface) -{ - parent_initable_interface = g_type_interface_peek_parent (iface); - - iface->init = ews_store_initable_init; -} - -static gboolean -ews_store_construct (CamelService *service, - CamelSession *session, - CamelProvider *provider, - GError **error) -{ - CamelEwsStore *ews_store; - gchar *summary_file, *session_storage_path; - guint32 store_flags; - - ews_store = (CamelEwsStore *) service; - - store_flags = camel_store_get_flags (CAMEL_STORE (ews_store)); - - /* Disable virtual trash and junk folders. Exchange has real - * folders for that */ - store_flags &= ~(CAMEL_STORE_VTRASH | CAMEL_STORE_VJUNK); - store_flags |= CAMEL_STORE_REAL_JUNK_FOLDER; - camel_store_set_flags (CAMEL_STORE (ews_store), store_flags); - - /*storage path*/ - session_storage_path = g_strdup (camel_service_get_user_cache_dir (service)); - if (!session_storage_path) { - g_set_error ( - error, CAMEL_STORE_ERROR, - CAMEL_STORE_ERROR_INVALID, - _("Session has no storage path")); - return FALSE; - } - ews_store->storage_path = session_storage_path; - - /* Note. update account-listener plugin if filename is changed here, as it would remove the summary - * by forming the path itself */ - g_mkdir_with_parents (ews_store->storage_path, 0700); - summary_file = g_build_filename (ews_store->storage_path, "folder-tree", NULL); - ews_store->summary = camel_ews_store_summary_new (summary_file); - camel_ews_store_summary_load (ews_store->summary, NULL); - - g_free (summary_file); - return TRUE; -} - -/* returns NULL when it's safe to use the default "Public Folders" name; otherwise g_free() it */ -static gchar * -ews_store_get_public_folders_name (CamelEwsStore *ews_store) -{ - gchar *use_name = NULL; - gchar *tmp_fid; - gint counter = 0; - - tmp_fid = camel_ews_store_summary_get_folder_id_from_name ( - ews_store->summary, EWS_PUBLIC_FOLDER_ROOT_DISPLAY_NAME); - while (tmp_fid) { - counter++; - - g_free (tmp_fid); - g_free (use_name); - - use_name = g_strdup_printf ( - /* Translators: This composes a "Public Folders" folder name for case when - * user has such in his store already. The %s is replaced with "Public Folders", - * the %d with counter, thus it composes name like "Public Folders_1" - */ - C_("PublicFolders", "%s_%d"), - EWS_PUBLIC_FOLDER_ROOT_DISPLAY_NAME, counter); - - tmp_fid = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, use_name); - } - - return use_name; -} - -void -camel_ews_store_ensure_virtual_folders (CamelEwsStore *ews_store) -{ - gboolean needs_foreign = FALSE, has_foreign = FALSE; - gboolean needs_public = FALSE, has_public = FALSE; - CamelFolderInfo *fi; - GSList *folders, *iter; - GHashTable *children_count; - GHashTableIter tab_iter; - gpointer key, value; - - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - - folders = camel_ews_store_summary_get_folders (ews_store->summary, NULL); - if (!folders) - return; - - children_count = g_hash_table_new (g_str_hash, g_str_equal); - - for (iter = folders; iter; iter = iter->next) { - const gchar *fid = iter->data; - GError *error = NULL; - - if (!fid) - continue; - - if (g_str_has_prefix (fid, "ForeignMailbox::") && - !g_hash_table_contains (children_count, fid)) - g_hash_table_insert (children_count, (gpointer) fid, GINT_TO_POINTER (0)); - - if (g_str_equal (fid, EWS_PUBLIC_FOLDER_ROOT_ID) && - !g_hash_table_contains (children_count, fid)) - g_hash_table_insert (children_count, (gpointer) fid, GINT_TO_POINTER (0)); - - if (!has_foreign && g_str_equal (fid, EWS_FOREIGN_FOLDER_ROOT_ID)) - has_foreign = TRUE; - else if (camel_ews_store_summary_get_foreign (ews_store->summary, fid, &error) && !error) { - gchar *pfid; - - needs_foreign = TRUE; - - pfid = camel_ews_store_summary_get_parent_folder_id (ews_store->summary, fid, NULL); - if (pfid && g_str_has_prefix (pfid, "ForeignMailbox::")) { - gint count = GPOINTER_TO_INT (g_hash_table_lookup (children_count, pfid)); - - g_hash_table_insert (children_count, (gpointer) pfid, GINT_TO_POINTER (count + 1)); - } - } - - g_clear_error (&error); - - if (!has_public && g_str_equal (fid, EWS_PUBLIC_FOLDER_ROOT_ID)) - has_public = TRUE; - else if (camel_ews_store_summary_get_public (ews_store->summary, fid, &error) && !error) { - EEwsFolderType ftype; - gchar *pfid; - - ftype = camel_ews_store_summary_get_folder_type (ews_store->summary, fid, &error); - if (ftype == E_EWS_FOLDER_TYPE_MAILBOX && !error) { - guint64 fflags; - - fflags = camel_ews_store_summary_get_folder_flags (ews_store->summary, fid, &error); - if ((fflags & CAMEL_FOLDER_SUBSCRIBED) != 0 && !error) { - needs_public = TRUE; - - pfid = camel_ews_store_summary_get_parent_folder_id (ews_store->summary, fid, NULL); - if (pfid && g_str_equal (pfid, EWS_PUBLIC_FOLDER_ROOT_ID)) { - gint count = GPOINTER_TO_INT (g_hash_table_lookup (children_count, pfid)); - - g_hash_table_insert (children_count, (gpointer) pfid, GINT_TO_POINTER (count + 1)); - } - } - } - } - - g_clear_error (&error); - } - - g_hash_table_iter_init (&tab_iter, children_count); - while (g_hash_table_iter_next (&tab_iter, &key, &value)) { - gint count = GPOINTER_TO_INT (value); - - if (!count) { - CamelFolderInfo *fi; - - if (has_foreign && g_str_equal (key, EWS_FOREIGN_FOLDER_ROOT_ID)) - has_foreign = FALSE; - - if (has_public && g_str_equal (key, EWS_PUBLIC_FOLDER_ROOT_ID)) - has_public = FALSE; - - fi = camel_ews_utils_build_folder_info (ews_store, key); - camel_ews_store_summary_remove_folder (ews_store->summary, key, NULL); - - camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_store_folder_deleted (CAMEL_STORE (ews_store), fi); - camel_folder_info_free (fi); - } - } - - g_hash_table_destroy (children_count); - - if (needs_foreign && !has_foreign) { - gchar *use_name = NULL; - gchar *tmp_fid; - gint counter = 0; - - tmp_fid = camel_ews_store_summary_get_folder_id_from_name ( - ews_store->summary, EWS_FOREIGN_FOLDER_ROOT_DISPLAY_NAME); - while (tmp_fid) { - counter++; - - g_free (tmp_fid); - g_free (use_name); - - use_name = g_strdup_printf ( - /* Translators: This composes a "Foreign Folders" folder name for case when - * user has such in his store already. The %s is replaced with "Foreign Folders", - * the %d with counter, thus it composes name like "Foreign Folders_1" - */ - C_("ForeignFolders", "%s_%d"), - EWS_FOREIGN_FOLDER_ROOT_DISPLAY_NAME, counter); - - tmp_fid = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, use_name); - } - - camel_ews_store_summary_new_folder ( - ews_store->summary, - EWS_FOREIGN_FOLDER_ROOT_ID, NULL, NULL, - use_name ? use_name : EWS_FOREIGN_FOLDER_ROOT_DISPLAY_NAME, - E_EWS_FOLDER_TYPE_MAILBOX, - CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_NOSELECT, - 0, FALSE, FALSE); - - g_free (use_name); - - fi = camel_ews_utils_build_folder_info (ews_store, EWS_FOREIGN_FOLDER_ROOT_ID); - camel_store_folder_created (CAMEL_STORE (ews_store), fi); - camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_folder_info_free (fi); - } else if (has_foreign && !needs_foreign) { - CamelFolderInfo *fi; - - fi = camel_ews_utils_build_folder_info (ews_store, EWS_FOREIGN_FOLDER_ROOT_ID); - camel_ews_store_summary_remove_folder (ews_store->summary, EWS_FOREIGN_FOLDER_ROOT_ID, NULL); - - camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_store_folder_deleted (CAMEL_STORE (ews_store), fi); - camel_folder_info_free (fi); - } - - if (needs_public && !has_public) { - gchar *use_name; - - use_name = ews_store_get_public_folders_name (ews_store); - - camel_ews_store_summary_new_folder ( - ews_store->summary, - EWS_PUBLIC_FOLDER_ROOT_ID, NULL, NULL, - use_name ? use_name : EWS_PUBLIC_FOLDER_ROOT_DISPLAY_NAME, - E_EWS_FOLDER_TYPE_MAILBOX, - CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_NOSELECT, - 0, FALSE, FALSE); - - g_free (use_name); - - fi = camel_ews_utils_build_folder_info (ews_store, EWS_PUBLIC_FOLDER_ROOT_ID); - camel_store_folder_created (CAMEL_STORE (ews_store), fi); - camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_folder_info_free (fi); - } else if (has_public && !needs_public) { - CamelFolderInfo *fi; - - fi = camel_ews_utils_build_folder_info (ews_store, EWS_PUBLIC_FOLDER_ROOT_ID); - camel_ews_store_summary_remove_folder (ews_store->summary, EWS_PUBLIC_FOLDER_ROOT_ID, NULL); - - camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_store_folder_deleted (CAMEL_STORE (ews_store), fi); - camel_folder_info_free (fi); - } - - camel_ews_store_summary_rebuild_hashes (ews_store->summary); - camel_ews_store_summary_save (ews_store->summary, NULL); - - g_slist_free_full (folders, g_free); -} - -void -camel_ews_store_ensure_unique_path (CamelEwsStore *ews_store, - gchar **ppath) -{ - gboolean done; - guint counter = 0; - gchar *base_path = NULL; - - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - g_return_if_fail (ews_store->summary != NULL); - g_return_if_fail (ppath != NULL); - g_return_if_fail (*ppath != NULL); - - done = FALSE; - while (!done) { - gchar *fid; - - done = TRUE; - - fid = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, *ppath); - if (fid) { - g_free (fid); - - done = FALSE; - counter++; - if (!counter) { - g_debug ("%s: Counter overflow", G_STRFUNC); - break; - } - - if (!base_path) - base_path = *ppath; - else - g_free (*ppath); - - *ppath = g_strdup_printf ("%s_%u", base_path, counter); - } - } - - g_free (base_path); -} - -static void -ews_update_folder_hierarchy (CamelEwsStore *ews_store, - gchar *sync_state, - gboolean includes_last_folder, - GSList *folders_created, - GSList *folders_deleted, - GSList *folders_updated, - GSList **created_folder_ids) -{ - ews_utils_sync_folders (ews_store, folders_created, folders_deleted, folders_updated, created_folder_ids); - camel_ews_store_ensure_virtual_folders (ews_store); - - camel_ews_store_summary_store_string_val (ews_store->summary, "sync_state", sync_state); - camel_ews_store_summary_save (ews_store->summary, NULL); - - g_slist_free_full (folders_created, g_object_unref); - g_slist_free_full (folders_updated, g_object_unref); - g_slist_free_full (folders_deleted, g_free); - g_free (sync_state); -} - -static void -ews_update_has_ooo_set (CamelSession *session, - GCancellable *cancellable, - gpointer user_data, - GError **error) -{ - CamelEwsStore *ews_store = user_data; - EEwsOofSettings *oof_settings; - EEwsOofState oof_state; - EEwsConnection *cnc; - GError *local_error = NULL; - - cnc = camel_ews_store_ref_connection (ews_store); - if (!cnc) - return; - - camel_operation_push_message (cancellable, _("Checking “Out of Office” settings")); - - oof_settings = e_ews_oof_settings_new_sync (cnc, cancellable, &local_error); - - g_clear_object (&cnc); - - if (local_error != NULL) { - g_propagate_error (error, local_error); - camel_operation_pop_message (cancellable); - return; - } - - oof_state = e_ews_oof_settings_get_state (oof_settings); - switch (oof_state) { - case E_EWS_OOF_STATE_ENABLED: - camel_ews_store_set_has_ooo_set (ews_store, TRUE); - break; - case E_EWS_OOF_STATE_DISABLED: - case E_EWS_OOF_STATE_SCHEDULED: - camel_ews_store_set_has_ooo_set (ews_store, FALSE); - break; - default: - break; - } - - camel_operation_pop_message (cancellable); - g_clear_object (&oof_settings); -} - -struct ScheduleUpdateData -{ - GCancellable *cancellable; - CamelEwsStore *ews_store; - guint expected_id; -}; - -static void -free_schedule_update_data (gpointer ptr) -{ - struct ScheduleUpdateData *sud = ptr; - - if (sud == NULL) - return; - - g_clear_object (&sud->cancellable); - g_clear_object (&sud->ews_store); - - g_free (sud); -} - -static gpointer -camel_ews_folder_list_update_thread (gpointer user_data) -{ - struct ScheduleUpdateData *sud = user_data; - CamelEwsStore *ews_store = sud->ews_store; - EEwsConnection *cnc = NULL; - GSList *created = NULL; - GSList *updated = NULL; - GSList *deleted = NULL; - gchar *old_sync_state = NULL; - gchar *new_sync_state; - gboolean includes_last; - GError *local_error = NULL; - - if (g_cancellable_is_cancelled (sud->cancellable)) - goto exit; - - cnc = camel_ews_store_ref_connection (ews_store); - if (!cnc) - goto exit; - - old_sync_state = camel_ews_store_summary_get_string_val (ews_store->summary, "sync_state", NULL); - if (!e_ews_connection_sync_folder_hierarchy_sync ( - cnc, - EWS_PRIORITY_LOW, - old_sync_state, - &new_sync_state, - &includes_last, - &created, - &updated, - &deleted, - sud->cancellable, - &local_error)) - goto exit; - - if (g_cancellable_is_cancelled (sud->cancellable)) { - g_slist_free_full (created, g_object_unref); - g_slist_free_full (updated, g_object_unref); - g_slist_free_full (deleted, g_free); - g_free (new_sync_state); - - goto exit; - } - - if (created != NULL || updated != NULL || deleted != NULL) { - ews_update_folder_hierarchy ( - ews_store, - new_sync_state, /* freed in the function */ - includes_last, - created, /* freed in the function */ - deleted, /* freed in the function */ - updated, /* freed in the function */ - NULL); - } else { - g_slist_free_full (created, g_object_unref); - g_slist_free_full (updated, g_object_unref); - g_slist_free_full (deleted, g_free); - g_free (new_sync_state); - } - - exit: - if (local_error) { - camel_ews_store_maybe_disconnect (ews_store, local_error); - g_clear_error (&local_error); - - g_mutex_lock (&ews_store->priv->get_finfo_lock); - ews_store->priv->last_refresh_time -= FINFO_REFRESH_INTERVAL; - g_mutex_unlock (&ews_store->priv->get_finfo_lock); - } else { - g_mutex_lock (&ews_store->priv->get_finfo_lock); - ews_store->priv->last_refresh_time = time (NULL); - g_mutex_unlock (&ews_store->priv->get_finfo_lock); - } - - g_free (old_sync_state); - g_clear_object (&cnc); - free_schedule_update_data (sud); - return NULL; -} - -static gpointer -camel_ews_folder_update_thread (gpointer user_data) -{ - struct ScheduleUpdateData *sud = user_data; - CamelEwsStore *ews_store = sud->ews_store; - GSList *update_folder_names, *l; - - g_return_val_if_fail (sud != NULL, NULL); - - UPDATE_LOCK (ews_store); - update_folder_names = ews_store->priv->update_folder_names; - ews_store->priv->update_folder_names = NULL; - UPDATE_UNLOCK (ews_store); - - for (l = update_folder_names; l != NULL && !g_cancellable_is_cancelled (sud->cancellable); l = l->next) { - const gchar *folder_name = l->data; - CamelFolder *folder; - GError *error = NULL; - - folder = camel_store_get_folder_sync (CAMEL_STORE (ews_store), folder_name, 0, sud->cancellable, NULL); - if (folder == NULL) - continue; - - camel_folder_refresh_info_sync (folder, sud->cancellable, &error); - g_object_unref (folder); - - if (error != NULL) { - g_warning ("%s: %s\n", G_STRFUNC, error->message); - g_clear_error (&error); - break; - } - } - - g_slist_free_full (update_folder_names, g_free); - update_folder_names = NULL; - free_schedule_update_data (sud); - - return NULL; -} - -static void -run_update_thread (CamelEwsStore *ews_store, - gboolean folder_list, - GCancellable *cancellable) -{ - GThread *thread; - struct ScheduleUpdateData *sud; - - g_return_if_fail (ews_store != NULL); - g_return_if_fail (cancellable != NULL); - - sud = g_new0 (struct ScheduleUpdateData, 1); - sud->ews_store = g_object_ref (ews_store); - sud->cancellable = g_object_ref (cancellable); - - thread = g_thread_new ( - NULL, - folder_list ? camel_ews_folder_list_update_thread : camel_ews_folder_update_thread, - sud); - g_thread_unref (thread); -} - -static gboolean -folder_update_cb (gpointer user_data) -{ - struct ScheduleUpdateData *sud = user_data; - - g_return_val_if_fail (sud != NULL, FALSE); - - if (g_cancellable_is_cancelled (sud->cancellable)) - return FALSE; - - g_return_val_if_fail (sud->ews_store != NULL, FALSE); - g_return_val_if_fail (sud->ews_store->priv != NULL, FALSE); - - UPDATE_LOCK (sud->ews_store); - if (sud->expected_id != sud->ews_store->priv->update_folder_id) - goto exit; - - sud->ews_store->priv->update_folder_id = 0; - - if (!g_cancellable_is_cancelled (sud->cancellable)) - run_update_thread (sud->ews_store, FALSE, sud->cancellable); - -exit: - UPDATE_UNLOCK (sud->ews_store); - return FALSE; -} - -static void -get_folder_names_to_update (gpointer key, - gpointer value, - gpointer user_data) -{ - CamelEwsStore *ews_store = user_data; - const gchar *folder_id = key; - gchar *folder_name; - - folder_name = camel_ews_store_summary_get_folder_full_name (ews_store->summary, folder_id, NULL); - if (folder_name != NULL) - ews_store->priv->update_folder_names = g_slist_prepend (ews_store->priv->update_folder_names, folder_name); -} - -static void -schedule_folder_update (CamelEwsStore *ews_store, - GHashTable *folder_ids) -{ - struct ScheduleUpdateData *sud; - CamelSettings *settings; - - g_return_if_fail (ews_store != NULL); - g_return_if_fail (ews_store->priv != NULL); - - UPDATE_LOCK (ews_store); - g_hash_table_foreach (folder_ids, get_folder_names_to_update, ews_store); - - if (ews_store->priv->update_folder_names == NULL) - goto exit; - - sud = g_new0 (struct ScheduleUpdateData, 1); - sud->ews_store = g_object_ref (ews_store); - sud->cancellable = g_object_ref (ews_store->priv->updates_cancellable); - - if (ews_store->priv->update_folder_id > 0) - g_source_remove (ews_store->priv->update_folder_id); - - settings = camel_service_ref_settings (CAMEL_SERVICE (ews_store)); - - ews_store->priv->update_folder_id = e_named_timeout_add_seconds_full ( - G_PRIORITY_LOW, - 1, - folder_update_cb, - sud, - free_schedule_update_data); - sud->expected_id = ews_store->priv->update_folder_id; - - g_object_unref (settings); - -exit: - UPDATE_UNLOCK (ews_store); -} - -static gboolean -folder_list_update_cb (gpointer user_data) -{ - struct ScheduleUpdateData *sud = user_data; - - g_return_val_if_fail (sud != NULL, FALSE); - - if (g_cancellable_is_cancelled (sud->cancellable)) - return FALSE; - - g_return_val_if_fail (sud->ews_store != NULL, FALSE); - g_return_val_if_fail (sud->ews_store->priv != NULL, FALSE); - - UPDATE_LOCK (sud->ews_store); - if (sud->expected_id != sud->ews_store->priv->update_folder_list_id) - goto exit; - - sud->ews_store->priv->update_folder_list_id = 0; - - if (!g_cancellable_is_cancelled (sud->cancellable)) - run_update_thread (sud->ews_store, TRUE, sud->cancellable); - -exit: - UPDATE_UNLOCK (sud->ews_store); - - return FALSE; -} - -static void -schedule_folder_list_update (CamelEwsStore *ews_store) -{ - struct ScheduleUpdateData *sud; - CamelSettings *settings; - - g_return_if_fail (ews_store != NULL); - g_return_if_fail (ews_store->priv != NULL); - - UPDATE_LOCK (ews_store); - if (!ews_store->priv->updates_cancellable) - goto exit; - - sud = g_new0 (struct ScheduleUpdateData, 1); - sud->ews_store = g_object_ref (ews_store); - sud->cancellable = g_object_ref (ews_store->priv->updates_cancellable); - - if (ews_store->priv->update_folder_list_id > 0) - g_source_remove (ews_store->priv->update_folder_list_id); - - settings = camel_service_ref_settings (CAMEL_SERVICE (ews_store)); - - ews_store->priv->update_folder_list_id = e_named_timeout_add_seconds_full ( - G_PRIORITY_LOW, - 1, - folder_list_update_cb, - sud, - free_schedule_update_data); - sud->expected_id = ews_store->priv->update_folder_list_id; - - g_object_unref (settings); - -exit: - UPDATE_UNLOCK (ews_store); -} - -static void -camel_ews_store_server_notification_cb (CamelEwsStore *ews_store, - GSList *events, - EEwsConnection *cnc) -{ - GSList *l; - gboolean update_folder = FALSE; - gboolean update_folder_list = FALSE; - GHashTable *folder_ids; - - g_return_if_fail (ews_store != NULL); - g_return_if_fail (ews_store->priv != NULL); - - folder_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - - for (l = events; l != NULL; l = l->next) { - EEwsNotificationEvent *event = l->data; - - switch (event->type) { - case E_EWS_NOTIFICATION_EVENT_CREATED: - case E_EWS_NOTIFICATION_EVENT_DELETED: - case E_EWS_NOTIFICATION_EVENT_MODIFIED: - UPDATE_LOCK (ews_store); - if (event->is_item) { - update_folder = TRUE; - if (!g_hash_table_lookup (folder_ids, event->folder_id)) - g_hash_table_insert ( - folder_ids, g_strdup (event->folder_id), GINT_TO_POINTER (1)); - } else { - update_folder_list = TRUE; - } - UPDATE_UNLOCK (ews_store); - break; - case E_EWS_NOTIFICATION_EVENT_MOVED: - case E_EWS_NOTIFICATION_EVENT_COPIED: - UPDATE_LOCK (ews_store); - if (event->is_item) { - update_folder = TRUE; - if (!g_hash_table_lookup (folder_ids, event->old_folder_id)) - g_hash_table_insert ( - folder_ids, g_strdup (event->old_folder_id), GINT_TO_POINTER (1)); - - if (!g_hash_table_lookup (folder_ids, event->folder_id)) - g_hash_table_insert ( - folder_ids, g_strdup (event->folder_id), GINT_TO_POINTER (1)); - } else { - update_folder_list = TRUE; - } - UPDATE_UNLOCK (ews_store); - break; - default: - break; - } - } - - if (update_folder) - schedule_folder_update (ews_store, folder_ids); - if (update_folder_list) - schedule_folder_list_update (ews_store); - - g_hash_table_destroy (folder_ids); -} - -struct HandleNotificationsData { - CamelEwsStore *ews_store; - GSList *folders; -}; - -static void -handle_notifications_data_free (struct HandleNotificationsData *hnd) -{ - if (hnd == NULL) - return; - - if (hnd->ews_store) - g_object_unref (hnd->ews_store); - - g_slist_free_full (hnd->folders, g_free); - g_free (hnd); -} - -static gpointer -start_notifications_thread (gpointer data) -{ - struct HandleNotificationsData *hnd = data; - CamelEwsStore *ews_store = hnd->ews_store; - EEwsConnection *cnc; - - cnc = camel_ews_store_ref_connection (ews_store); - if (!cnc) - goto exit; - - if (ews_store->priv->listen_notifications) { - if (ews_store->priv->subscription_key != 0) - goto exit; - - e_ews_connection_enable_notifications_sync ( - cnc, - hnd->folders, - &ews_store->priv->subscription_key); - } else { - if (ews_store->priv->subscription_key == 0) - goto exit; - - e_ews_connection_disable_notifications_sync ( - cnc, - ews_store->priv->subscription_key); - - ews_store->priv->subscription_key = 0; - } - -exit: - handle_notifications_data_free (hnd); - g_clear_object (&cnc); - - return NULL; -} - -static void -folder_ids_populate (CamelFolderInfo *folder_info, - gpointer data) -{ - struct HandleNotificationsData *hnd = data; - - while (folder_info != NULL) { - gchar *id; - - id = camel_ews_store_summary_get_folder_id_from_name (hnd->ews_store->summary, folder_info->full_name); - if (id && !g_str_has_prefix (id, "ForeignMailbox::") && - !g_str_equal (id, EWS_PUBLIC_FOLDER_ROOT_ID) && - !g_str_equal (id, EWS_FOREIGN_FOLDER_ROOT_ID) && - !camel_ews_store_summary_get_foreign (hnd->ews_store->summary, id, NULL) && - !camel_ews_store_summary_get_public (hnd->ews_store->summary, id, NULL)) - hnd->folders = g_slist_prepend (hnd->folders, id); - else - g_free (id); - - if (folder_info->child != NULL) - folder_ids_populate (folder_info->child, hnd); - - folder_info = folder_info->next; - } -} - -static void -camel_ews_store_handle_notifications (CamelEwsStore *ews_store, - CamelEwsSettings *ews_settings) -{ - GThread *thread; - EEwsConnection *cnc; - struct HandleNotificationsData *hnd; - - cnc = camel_ews_store_ref_connection (ews_store); - - if (!cnc) - return; - - if (!e_ews_connection_satisfies_server_version (cnc, E_EWS_EXCHANGE_2010_SP1)) { - g_clear_object (&cnc); - return; - } - - hnd = g_new0 (struct HandleNotificationsData, 1); - hnd->ews_store = g_object_ref (ews_store); - - if (!camel_ews_settings_get_check_all (ews_settings)) { - gchar *inbox; - - inbox = camel_ews_store_summary_get_folder_id_from_folder_type ( - ews_store->summary, CAMEL_FOLDER_TYPE_INBOX); - hnd->folders = g_slist_prepend (hnd->folders, inbox); - } else { - CamelFolderInfo *fi; - - fi = folder_info_from_store_summary ( - ews_store, - NULL, - CAMEL_STORE_FOLDER_INFO_RECURSIVE, - NULL, - NULL); - - folder_ids_populate (fi, hnd); - - camel_folder_info_free (fi); - } - - g_clear_object (&cnc); - - thread = g_thread_new (NULL, start_notifications_thread, hnd); - g_thread_unref (thread); -} - -static void -camel_ews_store_listen_notifications_cb (CamelEwsStore *ews_store, - GParamSpec *spec, - CamelEwsSettings *ews_settings) -{ - if (ews_store->priv->listen_notifications == camel_ews_settings_get_listen_notifications (ews_settings)) - return; - - ews_store->priv->listen_notifications = !ews_store->priv->listen_notifications; - - camel_ews_store_handle_notifications (ews_store, ews_settings); -} - -static void -camel_ews_store_check_all_cb (CamelEwsStore *ews_store, - GParamSpec *spec, - CamelEwsSettings *ews_settings) -{ - if (!ews_store->priv->listen_notifications) - return; - - camel_ews_store_handle_notifications (ews_store, ews_settings); -} - -static gboolean -ews_connect_sync (CamelService *service, - GCancellable *cancellable, - GError **error) -{ - EEwsConnection *connection; - CamelEwsStore *ews_store; - CamelEwsStorePrivate *priv; - CamelEwsSettings *ews_settings; - CamelSession *session; - CamelSettings *settings; - gchar *auth_mech; - gboolean success; - - /* Chain up to parent's method. */ - if (!CAMEL_SERVICE_CLASS (camel_ews_store_parent_class)->connect_sync (service, cancellable, error)) - return FALSE; - - ews_store = CAMEL_EWS_STORE (service); - priv = ews_store->priv; - - if (camel_service_get_connection_status (service) == CAMEL_SERVICE_DISCONNECTED) - return FALSE; - - connection = camel_ews_store_ref_connection (ews_store); - if (connection != NULL) { - g_object_unref (connection); - return TRUE; - } - - session = camel_service_ref_session (service); - settings = camel_service_ref_settings (service); - ews_settings = CAMEL_EWS_SETTINGS (settings); - - /* Try running an operation that requires authentication - * to make sure we have valid credentials available. */ - auth_mech = camel_network_settings_dup_auth_mechanism ( - CAMEL_NETWORK_SETTINGS (settings)); - success = camel_session_authenticate_sync (session, service, - auth_mech ? auth_mech : "NTLM", cancellable, error); - - g_free (auth_mech); - - priv->listen_notifications = FALSE; - - if (success) { - CamelEwsStoreOooAlertState state; - - state = camel_ews_store_get_ooo_alert_state (ews_store); - if (state == CAMEL_EWS_STORE_OOO_ALERT_STATE_UNKNOWN) - camel_session_submit_job ( - session, _("Checking “Out of Office” settings"), - ews_update_has_ooo_set, - g_object_ref (ews_store), - g_object_unref); - - if (!priv->updates_cancellable) - priv->updates_cancellable = g_cancellable_new (); - - if (camel_ews_settings_get_listen_notifications (ews_settings)) - camel_ews_store_listen_notifications_cb (ews_store, NULL, ews_settings); - - camel_offline_store_set_online_sync ( - CAMEL_OFFLINE_STORE (ews_store), - TRUE, cancellable, NULL); - - connection = camel_ews_store_ref_connection (ews_store); - if (connection) { - g_signal_connect_swapped ( - connection, - "server-notification", - G_CALLBACK (camel_ews_store_server_notification_cb), - ews_store); - g_clear_object (&connection); - } - } - - g_signal_connect_swapped ( - ews_settings, - "notify::listen-notifications", - G_CALLBACK (camel_ews_store_listen_notifications_cb), - ews_store); - - g_signal_connect_swapped ( - ews_settings, - "notify::check-all", - G_CALLBACK (camel_ews_store_check_all_cb), - ews_store); - - g_object_unref (session); - g_object_unref (settings); - - return success; -} - -static void -camel_ews_store_password_will_expire_cb (EEwsConnection *connection, - gint in_days, - const gchar *service_url, - gpointer user_data) -{ - CamelEwsStore *ews_store = user_data; - - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - - if (ews_store->priv->password_expires_in_days < 0 || - ews_store->priv->password_expires_in_days > in_days) { - CamelService *service; - CamelSession *session; - - ews_store->priv->password_expires_in_days = in_days; - - service = CAMEL_SERVICE (ews_store); - session = camel_service_ref_session (service); - - if (session) { - gchar *msg; - - if (service_url) { - msg = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, - /* Translators: The "%s" is a service URL, provided by the server */ - "Password will expire in %d day. Open “%s” to change it.", - "Password will expire in %d days. Open “%s” to change it.", - in_days), - in_days, service_url); - } else { - msg = g_strdup_printf (g_dngettext (GETTEXT_PACKAGE, - "Password will expire in one day.", - "Password will expire in %d days.", - in_days), - in_days); - } - - camel_session_user_alert (session, service, CAMEL_SESSION_ALERT_WARNING, msg); - - g_object_unref (session); - g_free (msg); - } - } -} - -static void -stop_pending_updates (CamelEwsStore *ews_store) -{ - CamelEwsStorePrivate *priv; - - g_return_if_fail (ews_store != NULL); - g_return_if_fail (ews_store->priv != NULL); - - priv = ews_store->priv; - - UPDATE_LOCK (ews_store); - if (priv->updates_cancellable) { - g_cancellable_cancel (priv->updates_cancellable); - g_object_unref (priv->updates_cancellable); - priv->updates_cancellable = NULL; - } - - g_slist_free_full (priv->update_folder_names, g_free); - priv->update_folder_names = NULL; - UPDATE_UNLOCK (ews_store); -} - -static void -ews_store_unset_connection_locked (CamelEwsStore *ews_store) -{ - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - - /* TODO cancel all operations in the connection */ - if (ews_store->priv->connection != NULL) { - CamelSettings *settings; - - /* FIXME This is somewhat broken, since the CamelSettings - * instance returned here may not be the same instance - * that we connected a signal handler to. Need to keep - * our own reference to that CamelSettings instance, or - * better yet avoid connecting signal handlers to it in - * the first place. */ - settings = camel_service_ref_settings (CAMEL_SERVICE (ews_store)); - g_signal_handlers_disconnect_by_data (settings, ews_store); - g_signal_handlers_disconnect_by_func ( - ews_store->priv->connection, camel_ews_store_server_notification_cb, ews_store); - - g_object_unref (settings); - - if (ews_store->priv->listen_notifications) { - stop_pending_updates (ews_store); - - if (ews_store->priv->subscription_key != 0) { - e_ews_connection_disable_notifications_sync ( - ews_store->priv->connection, - ews_store->priv->subscription_key); - - ews_store->priv->subscription_key = 0; - } - - ews_store->priv->listen_notifications = FALSE; - } - - e_ews_connection_set_password (ews_store->priv->connection, NULL); - e_ews_connection_set_disconnected_flag (ews_store->priv->connection, TRUE); - g_signal_handlers_disconnect_by_func (ews_store->priv->connection, - G_CALLBACK (camel_ews_store_password_will_expire_cb), ews_store); - g_object_unref (ews_store->priv->connection); - ews_store->priv->connection = NULL; - } -} - -static gboolean -ews_disconnect_sync (CamelService *service, - gboolean clean, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store = CAMEL_EWS_STORE (service); - CamelServiceClass *service_class; - - g_mutex_lock (&ews_store->priv->connection_lock); - ews_store_unset_connection_locked (ews_store); - g_mutex_unlock (&ews_store->priv->connection_lock); - - service_class = CAMEL_SERVICE_CLASS (camel_ews_store_parent_class); - return service_class->disconnect_sync (service, clean, cancellable, error); -} - -typedef struct { - const gchar *dist_folder_id; - gint info_flags; -} SystemFolder; - -static SystemFolder system_folder[] = { - {"calendar", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_EVENTS}, - {"contacts", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_CONTACTS}, - {"deleteditems", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_TRASH}, - {"drafts", CAMEL_FOLDER_SYSTEM}, - {"inbox", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_INBOX}, - {"journal", CAMEL_FOLDER_SYSTEM | CAMEL_EWS_FOLDER_TYPE_JOURNAL}, - {"notes", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_MEMOS}, - {"outbox", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_OUTBOX}, - {"sentitems", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_SENT}, - {"tasks", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_TASKS}, - {"msgfolderroot", CAMEL_FOLDER_SYSTEM}, - {"root", CAMEL_FOLDER_SYSTEM}, - {"junkemail", CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_TYPE_JUNK}, - {"searchfolders", CAMEL_FOLDER_SYSTEM}, -}; - -static void -ews_store_set_flags (CamelEwsStore *ews_store, - GSList *folders) -{ - GSList *temp = NULL; - EEwsFolder *folder = NULL; - const EwsFolderId *fid = NULL; - gint n = 0; - - temp = folders; - while (temp != NULL) { - folder = (EEwsFolder *) temp->data; - - if (folder && !e_ews_folder_is_error (folder)) { - fid = e_ews_folder_get_id (folder); - - if (camel_ews_store_summary_has_folder (ews_store->summary, fid->id)) - camel_ews_store_summary_set_folder_flags (ews_store->summary, fid->id, system_folder[n].info_flags); - } - - temp = temp->next; - n++; - } -} - -static void -ews_store_forget_all_folders (CamelEwsStore *ews_store) -{ - CamelStore *store; - CamelSubscribable *subscribable; - GSList *folders, *l; - - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - - store = CAMEL_STORE (ews_store); - subscribable = CAMEL_SUBSCRIBABLE (ews_store); - folders = camel_ews_store_summary_get_folders (ews_store->summary, NULL); - - if (!folders) - return; - - for (l = folders; l != NULL; l = g_slist_next (l)) { - CamelFolderInfo *fi; - EEwsFolderType ftype; - - ftype = camel_ews_store_summary_get_folder_type (ews_store->summary, l->data, NULL); - if (ftype != E_EWS_FOLDER_TYPE_MAILBOX) - continue; - - fi = camel_ews_utils_build_folder_info (ews_store, l->data); - camel_subscribable_folder_unsubscribed (subscribable, fi); - camel_store_folder_deleted (store, fi); - camel_folder_info_free (fi); - } - - g_slist_free_full (folders, g_free); -} - -struct EwsUpdateForeignSubfoldersData -{ - CamelEwsStore *ews_store; - gchar *folder_id; -}; - -static void -ews_update_foreign_subfolders_data_free (gpointer data) -{ - struct EwsUpdateForeignSubfoldersData *euf = data; - - if (euf) { - g_object_unref (euf->ews_store); - g_free (euf->folder_id); - g_free (euf); - } -} - -static void -ews_store_update_foreign_subfolders (CamelSession *session, - GCancellable *cancellable, - gpointer user_data, - GError **error) -{ - struct EwsUpdateForeignSubfoldersData *euf = user_data; - CamelEwsStore *ews_store; - EEwsConnection *conn; - GSList *tocheck = NULL, *remote_folders = NULL, *local_folders = NULL; - const gchar *fid; - GError *local_error = NULL; - - g_return_if_fail (euf != NULL); - - ews_store = euf->ews_store; - fid = euf->folder_id; - - if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store))) - return; - - conn = camel_ews_store_ref_connection (ews_store); - g_return_if_fail (conn != NULL); - - camel_operation_push_message (cancellable, _("Updating foreign folder structure")); - - /* read remote folder structure at the server */ - while (fid && !g_cancellable_is_cancelled (cancellable) && !local_error) { - gboolean includes_last_item = FALSE; - EwsFolderId *folder_id = e_ews_folder_id_new (fid, NULL, FALSE); - - while (!includes_last_item && !g_cancellable_is_cancelled (cancellable) && !local_error) { - GSList *folders = NULL, *ff; - - if (!e_ews_connection_find_folder_sync (conn, EWS_PRIORITY_MEDIUM, folder_id, - &includes_last_item, &folders, cancellable, &local_error)) - break; - - for (ff = folders; ff != NULL; ff = ff->next) { - EEwsFolder *folder = ff->data; - - e_ews_folder_set_parent_id (folder, e_ews_folder_id_new (fid, NULL, FALSE)); - - remote_folders = g_slist_prepend (remote_folders, folder); - - if (e_ews_folder_get_child_count (folder) > 0 && e_ews_folder_get_id (folder)) - tocheck = g_slist_prepend (tocheck, e_ews_folder_get_id (folder)->id); - } - } - - e_ews_folder_id_free (folder_id); - - if (tocheck) { - fid = g_slist_last (tocheck)->data; - tocheck = g_slist_remove (tocheck, fid); - } else { - fid = NULL; - } - } - - /* get local folder structure */ - if (!local_error && !g_cancellable_is_cancelled (cancellable)) { - gchar *full_name = camel_ews_store_summary_get_folder_full_name (ews_store->summary, euf->folder_id, NULL); - if (full_name) { - local_folders = camel_ews_store_summary_get_folders (ews_store->summary, full_name); - } - g_free (full_name); - } - - /* merge local and remote folder structures */ - if (!local_error && !g_cancellable_is_cancelled (cancellable)) { - GHashTable *locals = g_hash_table_new (g_str_hash, g_str_equal); - GSList *ii; - - remote_folders = g_slist_reverse (remote_folders); - - for (ii = local_folders; ii; ii = ii->next) { - g_hash_table_insert (locals, ii->data, ii->data); - } - - for (ii = remote_folders; ii; ii = ii->next) { - EEwsFolder *folder = ii->data; - const EwsFolderId *folder_id = e_ews_folder_get_id (folder); - const EwsFolderId *parent_fid = e_ews_folder_get_parent_id (folder); - - if (e_ews_folder_get_folder_type (folder) == E_EWS_FOLDER_TYPE_MAILBOX && - folder_id && folder_id->id) { - if (!g_hash_table_remove (locals, folder_id->id)) { - CamelFolderInfo *fi; - - /* it's a new folder, add it */ - camel_ews_store_summary_new_folder ( - ews_store->summary, - folder_id->id, parent_fid ? parent_fid->id : euf->folder_id, folder_id->change_key, - e_ews_folder_get_escaped_name (folder), E_EWS_FOLDER_TYPE_MAILBOX, - CAMEL_FOLDER_SUBSCRIBED, e_ews_folder_get_total_count (folder), TRUE, FALSE); - - fi = camel_ews_utils_build_folder_info (ews_store, folder_id->id); - camel_store_folder_created (CAMEL_STORE (ews_store), fi); - camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_folder_info_free (fi); - } - } - } - - /* to not remove the parent */ - g_hash_table_remove (locals, euf->folder_id); - - /* and now the locals contains only folders which were removed */ - if (g_hash_table_size (locals) > 0) { - CamelSubscribable *subscribable = CAMEL_SUBSCRIBABLE (ews_store); - CamelStore *store = CAMEL_STORE (ews_store); - GHashTableIter iter; - gpointer key, value; - - g_hash_table_iter_init (&iter, locals); - while (g_hash_table_iter_next (&iter, &key, &value)) { - CamelFolderInfo *fi; - - fi = camel_ews_utils_build_folder_info (ews_store, key); - camel_subscribable_folder_unsubscribed (subscribable, fi); - camel_store_folder_deleted (store, fi); - camel_folder_info_free (fi); - } - } - - g_hash_table_destroy (locals); - - camel_ews_store_summary_save (ews_store->summary, &local_error); - } - - if (local_error) - g_propagate_error (error, local_error); - - camel_operation_pop_message (cancellable); - - g_slist_free_full (remote_folders, g_object_unref); - g_slist_free_full (local_folders, g_free); - g_slist_free (tocheck); - g_object_unref (conn); -} - -void -camel_ews_store_update_foreign_subfolders (CamelEwsStore *ews_store, - const gchar *fid) -{ - struct EwsUpdateForeignSubfoldersData *euf; - CamelSession *session; - - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - g_return_if_fail (fid != NULL); - - session = camel_service_ref_session (CAMEL_SERVICE (ews_store)); - g_return_if_fail (session != NULL); - - euf = g_new0 (struct EwsUpdateForeignSubfoldersData, 1); - euf->ews_store = g_object_ref (ews_store); - euf->folder_id = g_strdup (fid); - - camel_session_submit_job ( - session, _("Updating foreign folders"), ews_store_update_foreign_subfolders, - euf, ews_update_foreign_subfolders_data_free); - - g_object_unref (session); -} - -static gboolean -ews_initial_setup_with_connection_sync (CamelStore *store, - GHashTable *save_setup, - EEwsConnection *connection, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store; - GSList *folders = NULL, *folder_ids = NULL; - gint nn; - GError *local_error = NULL; - - g_return_val_if_fail (CAMEL_IS_EWS_STORE (store), FALSE); - - if (g_cancellable_set_error_if_cancelled (cancellable, error)) - return FALSE; - - ews_store = CAMEL_EWS_STORE (store); - if (connection) { - g_object_ref (connection); - } else { - if (!camel_ews_store_connected (ews_store, cancellable, error)) - return FALSE; - - connection = camel_ews_store_ref_connection (ews_store); - g_return_val_if_fail (connection != NULL, FALSE); - } - - for (nn = 0; nn < G_N_ELEMENTS (system_folder); nn++) { - EwsFolderId *fid = NULL; - - fid = g_new0 (EwsFolderId, 1); - fid->id = g_strdup (system_folder[nn].dist_folder_id); - fid->is_distinguished_id = TRUE; - - folder_ids = g_slist_append (folder_ids, fid); - } - - /* fetch system folders first using getfolder operation */ - if (!e_ews_connection_get_folder_sync ( - connection, EWS_PRIORITY_MEDIUM, "IdOnly", - NULL, folder_ids, &folders, - cancellable, &local_error)) { - g_clear_object (&connection); - g_propagate_error (error, local_error); - return FALSE; - } - - if (folders && (g_slist_length (folders) != G_N_ELEMENTS (system_folder))) - d (printf ("Error : not all folders are returned by getfolder operation")); - else if (!local_error && folders) - ews_store_set_flags (ews_store, folders); - else if (local_error) { - /* report error and make sure we are not leaking anything */ - g_warn_if_fail (folders == NULL); - } else - d (printf ("folders for respective distinguished ids don't exist")); - - if (save_setup) { - gchar *folder_id; - - folder_id = camel_ews_store_summary_get_folder_id_from_folder_type (ews_store->summary, CAMEL_FOLDER_TYPE_SENT); - if (folder_id) { - gchar *fullname; - - fullname = camel_ews_store_summary_get_folder_full_name (ews_store->summary, folder_id, NULL); - if (fullname && *fullname) { - g_hash_table_insert (save_setup, - g_strdup (CAMEL_STORE_SETUP_SENT_FOLDER), - g_strdup (fullname)); - } - - g_free (fullname); - g_free (folder_id); - } - - if (g_slist_length (folders) == G_N_ELEMENTS (system_folder)) { - gint ii; - - for (ii = 0; ii < G_N_ELEMENTS (system_folder); ii++) { - if (g_str_equal ("drafts", system_folder[ii].dist_folder_id)) { - break; - } - } - - if (ii < G_N_ELEMENTS (system_folder)) { - EEwsFolder *drafts = g_slist_nth (folders, ii)->data; - if (drafts && !e_ews_folder_is_error (drafts)) { - const EwsFolderId *fid = e_ews_folder_get_id (drafts); - - if (fid && fid->id) { - gchar *fullname; - - fullname = camel_ews_store_summary_get_folder_full_name (ews_store->summary, fid->id, NULL); - if (fullname && *fullname) { - g_hash_table_insert (save_setup, - g_strdup (CAMEL_STORE_SETUP_DRAFTS_FOLDER), - g_strdup (fullname)); - } - - g_free (fullname); - } - } - } - } - } - - g_slist_free_full (folders, g_object_unref); - g_slist_free_full (folder_ids, (GDestroyNotify) e_ews_folder_id_free); - g_clear_object (&connection); - g_clear_error (&local_error); - - return TRUE; -} - -static gboolean -ews_initial_setup_sync (CamelStore *store, - GHashTable *save_setup, - GCancellable *cancellable, - GError **error) -{ - return ews_initial_setup_with_connection_sync (store, save_setup, NULL, cancellable, error); -} - -static CamelAuthenticationResult -ews_authenticate_sync (CamelService *service, - const gchar *mechanism, - GCancellable *cancellable, - GError **error) -{ - CamelAuthenticationResult result; - CamelEwsStore *ews_store; - CamelSettings *settings; - CamelEwsSettings *ews_settings; - EEwsConnection *connection; - ESource *source; - GSList *folders_created = NULL; - GSList *folders_updated = NULL; - GSList *folders_deleted = NULL; - GSList *folder_ids = NULL; - GSList *created_folder_ids = NULL; - gboolean includes_last_folder = FALSE; - gboolean initial_setup = FALSE; - const gchar *password; - gchar *hosturl; - gchar *old_sync_state = NULL, *new_sync_state = NULL; - gchar *certificate_pem = NULL; - GTlsCertificateFlags certificate_errors = 0; - GError *local_error = NULL; - - ews_store = CAMEL_EWS_STORE (service); - - password = camel_service_get_password (service); - - settings = camel_service_ref_settings (service); - - ews_settings = CAMEL_EWS_SETTINGS (settings); - hosturl = camel_ews_settings_dup_hosturl (ews_settings); - source = camel_ews_utils_ref_corresponding_source (service, cancellable); - - connection = e_ews_connection_new (source, hosturl, ews_settings); - e_ews_connection_set_password (connection, password); - - g_clear_object (&source); - g_free (hosturl); - - g_object_unref (settings); - - e_binding_bind_property ( - service, "proxy-resolver", - connection, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - /* XXX We need to run some operation that requires authentication - * but does not change any server-side state, so we can check - * the error status and determine if our password is valid. - * David suggested e_ews_connection_sync_folder_hierarchy(), - * since we have to do that eventually anyway. */ - - /*use old sync_state from summary*/ - old_sync_state = camel_ews_store_summary_get_string_val (ews_store->summary, "sync_state", NULL); - if (!old_sync_state) { - initial_setup = TRUE; - } else { - gchar *inbox_folder_id; - - inbox_folder_id = camel_ews_store_summary_get_folder_id_from_folder_type (ews_store->summary, CAMEL_FOLDER_TYPE_INBOX); - if (!inbox_folder_id || !*inbox_folder_id) - initial_setup = TRUE; - - g_free (inbox_folder_id); - } - - e_ews_connection_sync_folder_hierarchy_sync (connection, EWS_PRIORITY_MEDIUM, old_sync_state, - &new_sync_state, &includes_last_folder, &folders_created, &folders_updated, &folders_deleted, - cancellable, &local_error); - - g_free (old_sync_state); - old_sync_state = NULL; - - if (g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_UNAVAILABLE)) { - local_error->domain = CAMEL_SERVICE_ERROR; - local_error->code = CAMEL_SERVICE_ERROR_UNAVAILABLE; - } - - if (!initial_setup && g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) { - g_clear_error (&local_error); - ews_store_forget_all_folders (ews_store); - camel_ews_store_summary_store_string_val (ews_store->summary, "sync_state", ""); - camel_ews_store_summary_clear (ews_store->summary); - - initial_setup = TRUE; - - e_ews_connection_sync_folder_hierarchy_sync (connection, EWS_PRIORITY_MEDIUM, NULL, - &new_sync_state, &includes_last_folder, &folders_created, &folders_updated, &folders_deleted, - cancellable, &local_error); - } - - if (local_error == NULL) { - GSList *foreign_fids, *ff; - - g_mutex_lock (&ews_store->priv->connection_lock); - ews_store_unset_connection_locked (ews_store); - ews_store->priv->connection = g_object_ref (connection); - g_signal_connect (ews_store->priv->connection, "password-will-expire", - G_CALLBACK (camel_ews_store_password_will_expire_cb), ews_store); - g_mutex_unlock (&ews_store->priv->connection_lock); - - /* This consumes all allocated result data. */ - ews_update_folder_hierarchy ( - ews_store, new_sync_state, includes_last_folder, - folders_created, folders_deleted, folders_updated, &created_folder_ids); - - /* Also update folder structures of foreign folders, - those which are subscribed with subfolders */ - foreign_fids = camel_ews_store_summary_get_foreign_folders (ews_store->summary, NULL); - for (ff = foreign_fids; ff != NULL; ff = ff->next) { - const gchar *fid = ff->data; - - if (camel_ews_store_summary_get_foreign_subfolders (ews_store->summary, fid, NULL)) { - camel_ews_store_update_foreign_subfolders (ews_store, fid); - } - } - - g_slist_free_full (foreign_fids, g_free); - } else { - g_mutex_lock (&ews_store->priv->connection_lock); - ews_store_unset_connection_locked (ews_store); - g_mutex_unlock (&ews_store->priv->connection_lock); - - g_free (new_sync_state); - - /* Make sure we're not leaking anything. */ - g_warn_if_fail (folders_created == NULL); - g_warn_if_fail (folders_updated == NULL); - g_warn_if_fail (folders_deleted == NULL); - } - - /*get folders using distinguished id by GetFolder operation and set system flags to folders, only for first time*/ - if (!local_error && initial_setup && connection) { - ews_initial_setup_with_connection_sync (CAMEL_STORE (ews_store), NULL, connection, cancellable, NULL); - } - - /* postpone notification of new folders to time when also folder flags are known, - thus the view in evolution sows Inbox with an Inbox icon. */ - for (folder_ids = created_folder_ids; folder_ids; folder_ids = folder_ids->next) { - CamelFolderInfo *fi; - - fi = camel_ews_utils_build_folder_info (ews_store, folder_ids->data); - camel_store_folder_created (CAMEL_STORE (ews_store), fi); - camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_folder_info_free (fi); - } - - g_slist_free_full (created_folder_ids, g_free); - - if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) && - e_ews_connection_get_ssl_error_details (connection, &certificate_pem, &certificate_errors)) { - source = e_ews_connection_get_source (connection); - - if (source) { - e_source_emit_credentials_required (source, E_SOURCE_CREDENTIALS_REASON_SSL_FAILED, - certificate_pem, certificate_errors, local_error); - } - - g_free (certificate_pem); - } - - if (local_error == NULL) { - result = CAMEL_AUTHENTICATION_ACCEPTED; - } else if (g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED)) { - g_clear_error (&local_error); - result = CAMEL_AUTHENTICATION_REJECTED; - } else { - g_propagate_error (error, local_error); - result = CAMEL_AUTHENTICATION_ERROR; - } - - g_object_unref (connection); - - return result; -} - -static GList * -ews_store_query_auth_types_sync (CamelService *service, - GCancellable *cancellable, - GError **error) -{ - EEwsConnection *connection; - ESource *source; - CamelSettings *settings; - CamelEwsSettings *ews_settings; - GList *auth_types = NULL; - GSList *auth_methods = NULL, *aiter; - gchar *hosturl; - - g_return_val_if_fail (CAMEL_IS_EWS_STORE (service), NULL); - - settings = camel_service_ref_settings (service); - ews_settings = CAMEL_EWS_SETTINGS (settings); - hosturl = camel_ews_settings_dup_hosturl (ews_settings); - source = camel_ews_utils_ref_corresponding_source (service, cancellable); - - connection = e_ews_connection_new_full (source, hosturl, ews_settings, FALSE); - - g_clear_object (&source); - g_free (hosturl); - g_object_unref (settings); - - e_binding_bind_property ( - service, "proxy-resolver", - connection, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - if (e_ews_connection_query_auth_methods_sync (connection, G_PRIORITY_DEFAULT, &auth_methods, cancellable, error)) { - CamelProvider *provider; - CamelServiceAuthType *authtype; - - provider = camel_service_get_provider (service); - g_return_val_if_fail (provider != NULL, NULL); - - for (aiter = auth_methods; aiter; aiter = aiter->next) { - GList *siter; - const gchar *auth = aiter->data; - - if (!auth) - continue; - - if (g_ascii_strcasecmp (auth, "NTLM") == 0) - auth = ""; - else if (g_ascii_strcasecmp (auth, "Basic") == 0) - auth = "PLAIN"; - else if (g_ascii_strcasecmp (auth, "Negotiate") == 0) - auth = "GSSAPI"; - else if (e_oauth2_services_is_supported () && - g_ascii_strcasecmp (auth, "Bearer") == 0) { - /* Use Camel name for OAuth2. It's up to the caller to decide whether - it can be used or not. */ - authtype = camel_sasl_authtype ("XOAUTH2"); - if (authtype) - auth_types = g_list_prepend (auth_types, authtype); - - continue; - } - - for (siter = provider->authtypes; siter; siter = siter->next) { - authtype = siter->data; - - if (g_ascii_strcasecmp (authtype->authproto, auth) == 0) - auth_types = g_list_prepend (auth_types, authtype); - } - } - - g_slist_free_full (auth_methods, g_free); - } - - g_object_unref (connection); - - return g_list_reverse (auth_types); -} - -static CamelFolderInfo * ews_create_folder_sync (CamelStore *store, const gchar *parent_name,const gchar *folder_name, GCancellable *cancellable, GError **error); - -static CamelFolder * -ews_get_folder_sync (CamelStore *store, - const gchar *folder_name, - guint32 flags, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store; - CamelFolder *folder = NULL; - gchar *fid, *folder_dir; - - ews_store = (CamelEwsStore *) store; - - fid = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, folder_name); - - if (!fid) { - g_set_error ( - error, CAMEL_STORE_ERROR, - CAMEL_STORE_ERROR_NO_FOLDER, - _("No such folder: %s"), folder_name); - return NULL; - } else { - /* We don't actually care what it is; only that it exists */ - g_free (fid); - } - - folder_dir = g_build_filename (ews_store->storage_path, "folders", folder_name, NULL); - folder = camel_ews_folder_new (store, folder_name, folder_dir, cancellable, error); - - g_free (folder_dir); - - if ((flags & CAMEL_STORE_FOLDER_INFO_REFRESH) != 0) - camel_folder_prepare_content_refresh (folder); - - return folder; -} - -static gchar * -get_public_folder_full_name (EEwsFolder *folder, - GHashTable *folders_by_id) -{ - const EwsFolderId *parent_fid; - GString *full_name; - - g_return_val_if_fail (folder != NULL, NULL); - g_return_val_if_fail (folders_by_id != NULL, NULL); - - full_name = g_string_new (e_ews_folder_get_escaped_name (folder)); - while (folder) { - parent_fid = e_ews_folder_get_parent_id (folder); - if (!parent_fid || !parent_fid->id) - break; - - folder = g_hash_table_lookup (folders_by_id, parent_fid->id); - if (folder) { - g_string_prepend (full_name, "/"); - g_string_prepend (full_name, e_ews_folder_get_escaped_name (folder)); - } - } - - g_string_prepend (full_name, "/"); - g_string_prepend (full_name, EWS_PUBLIC_FOLDER_ROOT_DISPLAY_NAME); - - return g_string_free (full_name, FALSE); -} - -static gboolean -ews_store_has_as_parent_id (CamelEwsStoreSummary *ews_summary, - const gchar *fid, - const gchar *mailroot_fid) -{ - gchar *parent = NULL; - gboolean found = FALSE; - - g_return_val_if_fail (CAMEL_IS_EWS_STORE_SUMMARY (ews_summary), FALSE); - g_return_val_if_fail (fid != NULL, FALSE); - g_return_val_if_fail (mailroot_fid != NULL, FALSE); - - while (found = g_strcmp0 (fid, mailroot_fid) == 0, fid && !found) { - gchar *tmp = parent; - - parent = camel_ews_store_summary_get_parent_folder_id (ews_summary, fid, NULL); - fid = parent; - - g_free (tmp); - } - - g_free (parent); - - return found; -} - -static CamelFolderInfo * -folder_info_from_store_summary (CamelEwsStore *store, - const gchar *top, - guint32 flags, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStoreSummary *ews_summary; - GPtrArray *folder_infos = NULL; - CamelFolderInfo *root_fi = NULL, *fi; - - /* search in public folders */ - if ((flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST) != 0) { - GHashTable *folders_by_id; - GSList *fiter; - GList *esources = NULL; - gchar *hosturl = NULL, *username = NULL; - - g_mutex_lock (&store->priv->get_finfo_lock); - - if (!store->priv->public_folders) { - g_mutex_unlock (&store->priv->get_finfo_lock); - return NULL; - } - - folder_infos = g_ptr_array_new (); - folders_by_id = g_hash_table_new (g_str_hash, g_str_equal); - - for (fiter = store->priv->public_folders; fiter != NULL; fiter = g_slist_next (fiter)) { - EEwsFolder *folder = fiter->data; - const EwsFolderId *fid; - - if (!folder) - continue; - - fid = e_ews_folder_get_id (folder); - if (!fid || !fid->id) - continue; - - g_hash_table_insert (folders_by_id, fid->id, folder); - } - - fi = camel_folder_info_new (); - fi->full_name = g_strdup (EWS_PUBLIC_FOLDER_ROOT_DISPLAY_NAME); - fi->display_name = g_strdup (fi->full_name); - fi->flags = CAMEL_FOLDER_SYSTEM | CAMEL_FOLDER_NOSELECT; - fi->unread = -1; - fi->total = -1; - - g_ptr_array_add (folder_infos, fi); - - for (fiter = store->priv->public_folders; fiter != NULL; fiter = g_slist_next (fiter)) { - EEwsFolder *folder = fiter->data; - const EwsFolderId *fid; - - if (!folder) - continue; - - fid = e_ews_folder_get_id (folder); - if (!fid || !fid->id) - continue; - - fi = camel_folder_info_new (); - fi->full_name = get_public_folder_full_name (folder, folders_by_id); - fi->display_name = g_strdup (e_ews_folder_get_name (folder)); - fi->flags = 0; - fi->unread = e_ews_folder_get_unread_count (folder); - fi->total = e_ews_folder_get_total_count (folder); - - switch (e_ews_folder_get_folder_type (folder)) { - case E_EWS_FOLDER_TYPE_CALENDAR: - fi->flags |= CAMEL_FOLDER_TYPE_EVENTS; - break; - case E_EWS_FOLDER_TYPE_CONTACTS: - fi->flags |= CAMEL_FOLDER_TYPE_CONTACTS; - break; - case E_EWS_FOLDER_TYPE_TASKS: - fi->flags |= CAMEL_FOLDER_TYPE_TASKS; - break; - case E_EWS_FOLDER_TYPE_MEMOS: - fi->flags |= CAMEL_FOLDER_TYPE_MEMOS; - break; - default: - break; - } - - if (camel_ews_store_summary_has_folder (store->summary, fid->id)) { - guint64 fflags = camel_ews_store_summary_get_folder_flags (store->summary, fid->id, NULL); - - if ((fflags & CAMEL_FOLDER_SUBSCRIBED) != 0) - fi->flags |= CAMEL_FOLDER_SUBSCRIBED; - } - - if (!(fi->flags & CAMEL_FOLDER_SUBSCRIBED) && - e_ews_folder_get_folder_type (folder) != E_EWS_FOLDER_TYPE_MAILBOX) { - if (!hosturl && !username && !esources) { - CamelSession *session; - CamelSettings *settings; - CamelEwsSettings *ews_settings; - ESourceRegistry *registry = NULL; - - settings = camel_service_ref_settings (CAMEL_SERVICE (store)); - ews_settings = CAMEL_EWS_SETTINGS (settings); - session = camel_service_ref_session (CAMEL_SERVICE (store)); - if (E_IS_MAIL_SESSION (session)) - registry = e_mail_session_get_registry (E_MAIL_SESSION (session)); - - hosturl = camel_ews_settings_dup_hosturl (ews_settings); - username = camel_network_settings_dup_user (CAMEL_NETWORK_SETTINGS (ews_settings)); - esources = e_ews_folder_utils_get_esources (registry, hosturl, username, cancellable, NULL); - - g_object_unref (settings); - g_object_unref (session); - } - - if (e_ews_folder_utils_is_subscribed_as_esource (esources, hosturl, username, fid->id)) - fi->flags |= CAMEL_FOLDER_SUBSCRIBED; - } - - g_ptr_array_add (folder_infos, fi); - } - - g_list_free_full (esources, g_object_unref); - g_hash_table_destroy (folders_by_id); - g_free (hosturl); - g_free (username); - g_mutex_unlock (&store->priv->get_finfo_lock); - - /* search in regular/subscribed folders */ - } else { - GSList *folders, *fiter; - gchar *mailroot_fid = NULL, *inbox_fid; - - ews_summary = store->summary; - folders = camel_ews_store_summary_get_folders (ews_summary, top); - if (!folders) - return NULL; - - inbox_fid = camel_ews_store_summary_get_folder_id_from_folder_type (ews_summary, CAMEL_FOLDER_TYPE_INBOX); - if (inbox_fid) { - mailroot_fid = camel_ews_store_summary_get_parent_folder_id (ews_summary, inbox_fid, NULL); - } - g_free (inbox_fid); - inbox_fid = NULL; - - folder_infos = g_ptr_array_new (); - - for (fiter = folders; fiter != NULL; fiter = g_slist_next (fiter)) { - EEwsFolderType ftype; - const gchar *fid = fiter->data; - - ftype = camel_ews_store_summary_get_folder_type (ews_summary, fid, NULL); - if (ftype != E_EWS_FOLDER_TYPE_MAILBOX) - continue; - - if (camel_ews_store_summary_get_public (ews_summary, fid, NULL)) { - guint64 fflags; - - fflags = camel_ews_store_summary_get_folder_flags (ews_summary, fid, NULL); - if (!(fflags & CAMEL_FOLDER_SUBSCRIBED)) - continue; - } else if (!camel_ews_store_summary_get_foreign (ews_summary, fid, NULL) && - mailroot_fid && !ews_store_has_as_parent_id (ews_summary, fid, mailroot_fid)) { - /* Skip mail folders out of the msgfolderroot hierarchy */ - continue; - } - - fi = camel_ews_utils_build_folder_info (store, fid); - g_ptr_array_add (folder_infos, fi); - } - - g_slist_free_full (folders, g_free); - g_free (mailroot_fid); - } - - root_fi = camel_folder_info_build (folder_infos, top, '/', TRUE); - g_ptr_array_free (folder_infos, TRUE); - - return root_fi; -} - -static gboolean -ews_refresh_finfo (CamelEwsStore *ews_store) -{ - if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store))) - return FALSE; - - if (!ews_store->priv->updates_cancellable) - ews_store->priv->updates_cancellable = g_cancellable_new (); - - run_update_thread (ews_store, TRUE, ews_store->priv->updates_cancellable); - - return TRUE; -} - -static CamelFolderInfo * -ews_get_folder_info_sync (CamelStore *store, - const gchar *top, - guint32 flags, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store; - CamelEwsStorePrivate *priv; - CamelFolderInfo *fi = NULL; - EEwsConnection *connection; - gchar *old_sync_state, *new_sync_state = NULL; - gboolean initial_setup = FALSE; - GSList *folders_created = NULL, *folders_updated = NULL; - GSList *folders_deleted = NULL; - gboolean includes_last_folder; - gboolean success; - GError *local_error = NULL; - - ews_store = (CamelEwsStore *) store; - priv = ews_store->priv; - - if ((flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST) != 0) { - gboolean includes_last_folder = TRUE; - GSList *folders = NULL, *to_check = NULL; - EwsFolderId *folder_id; - - if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store))) { - g_set_error_literal ( - error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE, - _("Cannot list EWS public folders in offline mode")); - return NULL; - } - - g_mutex_lock (&priv->get_finfo_lock); - - g_slist_free_full (priv->public_folders, g_object_unref); - priv->public_folders = NULL; - - connection = camel_ews_store_ref_connection (ews_store); - folder_id = e_ews_folder_id_new ("publicfoldersroot", NULL, TRUE); - to_check = g_slist_append (to_check, folder_id); - - while (!local_error && !g_cancellable_is_cancelled (cancellable) && to_check) { - folder_id = to_check->data; - to_check = g_slist_remove (to_check, folder_id); - - while (e_ews_connection_find_folder_sync (connection, EWS_PRIORITY_MEDIUM, folder_id, &includes_last_folder, &folders, - cancellable, &local_error) && !local_error && - !g_cancellable_is_cancelled (cancellable)) { - GSList *fiter; - - if (!folders) - break; - - for (fiter = folders; fiter != NULL; fiter = fiter->next) { - EEwsFolder *folder = fiter->data; - - if (e_ews_folder_get_child_count (folder) > 0) { - const EwsFolderId *fid = e_ews_folder_get_id (folder); - - if (fid) - to_check = g_slist_prepend (to_check, - e_ews_folder_id_new (fid->id, fid->change_key, fid->is_distinguished_id)); - } - - if (!e_ews_folder_get_parent_id (folder)) { - if (!folder_id->is_distinguished_id) { - e_ews_folder_set_parent_id (folder, - e_ews_folder_id_new (folder_id->id, folder_id->change_key, folder_id->is_distinguished_id)); - } else { - e_ews_folder_set_parent_id (folder, e_ews_folder_id_new (EWS_PUBLIC_FOLDER_ROOT_ID, NULL, FALSE)); - } - } - } - - priv->public_folders = g_slist_concat (priv->public_folders, folders); - folders = NULL; - - if (includes_last_folder) - break; - } - - e_ews_folder_id_free (folder_id); - } - - g_mutex_unlock (&priv->get_finfo_lock); - - g_object_unref (connection); - g_slist_free_full (to_check, (GDestroyNotify) e_ews_folder_id_free); - - camel_ews_store_ensure_virtual_folders (ews_store); - - if (local_error) { - camel_ews_store_maybe_disconnect (ews_store, local_error); - g_propagate_error (error, local_error); - - return NULL; - } - - if (!priv->public_folders) { - g_set_error_literal ( - error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE, - _("Cannot find any EWS public folders")); - return NULL; - } - - goto offline; - } - - g_mutex_lock (&priv->get_finfo_lock); - if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) { - camel_ews_store_ensure_virtual_folders (ews_store); - g_mutex_unlock (&priv->get_finfo_lock); - goto offline; - } - - if (!camel_service_connect_sync ((CamelService *) store, cancellable, error)) { - camel_offline_store_set_online_sync (CAMEL_OFFLINE_STORE (store), FALSE, NULL, NULL); - camel_ews_store_ensure_virtual_folders (ews_store); - g_mutex_unlock (&priv->get_finfo_lock); - return NULL; - } - - old_sync_state = camel_ews_store_summary_get_string_val (ews_store->summary, "sync_state", NULL); - if (!old_sync_state) - initial_setup = TRUE; - - if (!initial_setup && (flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED) != 0) { - time_t now = time (NULL); - - g_free (old_sync_state); - if (now - priv->last_refresh_time > FINFO_REFRESH_INTERVAL && ews_refresh_finfo (ews_store)) - ews_store->priv->last_refresh_time = time (NULL); - - g_mutex_unlock (&priv->get_finfo_lock); - goto offline; - } - - connection = camel_ews_store_ref_connection (ews_store); - - success = e_ews_connection_sync_folder_hierarchy_sync (connection, EWS_PRIORITY_MEDIUM, old_sync_state, - &new_sync_state, &includes_last_folder, &folders_created, &folders_updated, &folders_deleted, - cancellable, &local_error); - - g_free (old_sync_state); - old_sync_state = NULL; - - if (!initial_setup && g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) { - g_clear_error (&local_error); - ews_store_forget_all_folders (ews_store); - camel_ews_store_summary_store_string_val (ews_store->summary, "sync_state", ""); - camel_ews_store_summary_clear (ews_store->summary); - - success = e_ews_connection_sync_folder_hierarchy_sync (connection, EWS_PRIORITY_MEDIUM, NULL, - &new_sync_state, &includes_last_folder, &folders_created, &folders_updated, &folders_deleted, - cancellable, &local_error); - } - - g_object_unref (connection); - - if (!success) { - if (local_error) - g_warning ( - "Unable to fetch the folder hierarchy: %s :%d \n", - local_error->message, local_error->code); - else - g_warning ("Unable to fetch the folder hierarchy.\n"); - - camel_ews_store_maybe_disconnect (ews_store, local_error); - g_propagate_error (error, local_error); - - g_mutex_unlock (&priv->get_finfo_lock); - return NULL; - } - ews_update_folder_hierarchy ( - ews_store, new_sync_state, includes_last_folder, - folders_created, folders_deleted, folders_updated, NULL); - g_mutex_unlock (&priv->get_finfo_lock); - -offline: - fi = folder_info_from_store_summary (ews_store, top, flags, cancellable, error); - return fi; -} - -static CamelFolderInfo * -ews_create_folder_sync (CamelStore *store, - const gchar *parent_name, - const gchar *folder_name, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store = CAMEL_EWS_STORE (store); - CamelEwsStoreSummary *ews_summary = ews_store->summary; - gchar *fid = NULL; - gchar *full_name; - EwsFolderId *folder_id; - EEwsConnection *connection; - CamelFolderInfo *fi = NULL; - gboolean success; - GError *local_error = NULL; - - if (parent_name && *parent_name) - full_name = g_strdup_printf ("%s/%s", parent_name, folder_name); - else - full_name = g_strdup (folder_name); - - fid = camel_ews_store_summary_get_folder_id_from_name (ews_summary, full_name); - if (fid) { - g_free (fid); - g_set_error ( - error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, - _("Cannot create folder “%s”, folder already exists"), - full_name); - g_free (full_name); - return NULL; - } - - g_free (full_name); - - /* Get Parent folder ID */ - if (parent_name && parent_name[0]) { - fid = camel_ews_store_summary_get_folder_id_from_name ( - ews_summary, parent_name); - if (!fid) { - g_set_error ( - error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, - _("Parent folder %s does not exist"), - parent_name); - return NULL; - } - - if (g_str_equal (fid, EWS_FOREIGN_FOLDER_ROOT_ID)) { - g_free (fid); - - g_set_error ( - error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, - _("Cannot create folder under “%s”, it is used for folders of other users only"), - parent_name); - return NULL; - } - - if (g_str_equal (fid, EWS_PUBLIC_FOLDER_ROOT_ID)) { - g_free (fid); - - g_set_error ( - error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, - _("Cannot create folder under “%s”, it is used for public folders only"), - parent_name); - return NULL; - } - } - - if (!camel_ews_store_connected (ews_store, cancellable, error)) { - if (fid) g_free (fid); - return NULL; - } - - connection = camel_ews_store_ref_connection (ews_store); - - success = e_ews_connection_create_folder_sync ( - connection, - EWS_PRIORITY_MEDIUM, fid, - FALSE, folder_name, E_EWS_FOLDER_TYPE_MAILBOX, - &folder_id, cancellable, &local_error); - - g_object_unref (connection); - - if (!success) { - camel_ews_store_maybe_disconnect (ews_store, local_error); - g_propagate_error (error, local_error); - g_free (fid); - return NULL; - } - - /* Translate & store returned folder id */ - if (fid) - full_name = g_strdup_printf ("%s/%s", parent_name, folder_name); - else - full_name = g_strdup (folder_name); - - camel_ews_store_summary_new_folder ( - ews_summary, folder_id->id, - fid, folder_id->change_key, - folder_name, - E_EWS_FOLDER_TYPE_MAILBOX, - 0, 0, FALSE, FALSE); - fi = camel_ews_utils_build_folder_info (ews_store, folder_id->id); - e_ews_folder_id_free (folder_id); - - camel_store_folder_created (store, fi); - camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - - g_free (full_name); - g_free (fid); - return fi; -} - -static void -ews_update_store_move_recursive (CamelEwsStore *ews_store, - CamelFolderInfo *folder_info) -{ - while (folder_info != NULL) { - if (folder_info->child != NULL) - ews_update_store_move_recursive (ews_store, folder_info->child); - - camel_store_folder_created (CAMEL_STORE (ews_store), folder_info); - camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (ews_store), folder_info); - - folder_info = folder_info->next; - } -} - -static gboolean -ews_delete_folder_sync (CamelStore *store, - const gchar *folder_name, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store = CAMEL_EWS_STORE (store); - gchar *fid; - gchar *trash_fid; - gchar *trash_name; - CamelFolderInfo *folder_info; - CamelFolderInfo *to_update; - gboolean success; - gboolean is_under_trash_folder; - GError *local_error = NULL; - - folder_info = camel_store_get_folder_info_sync ( - store, folder_name, - CAMEL_STORE_FOLDER_INFO_RECURSIVE | - CAMEL_STORE_FOLDER_INFO_SUBSCRIBED, - cancellable, &local_error); - - if (folder_info == NULL) { - g_propagate_error (error, local_error); - - return FALSE; - } - - to_update = folder_info; - - fid = camel_ews_store_summary_get_folder_id_from_name ( - ews_store->summary, folder_name); - - if (!fid) { - camel_folder_info_free (folder_info); - - g_set_error ( - error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, - _("Folder does not exist")); - return FALSE; - } - - if (g_str_equal (fid, EWS_FOREIGN_FOLDER_ROOT_ID)) { - g_free (fid); - camel_folder_info_free (folder_info); - - g_set_error ( - error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, - _("Cannot remove folder “%s”, it is used for folders of other users only"), - folder_name); - return FALSE; - } - - if (g_str_equal (fid, EWS_PUBLIC_FOLDER_ROOT_ID)) { - g_free (fid); - camel_folder_info_free (folder_info); - - g_set_error ( - error, CAMEL_ERROR, CAMEL_ERROR_GENERIC, - _("Cannot remove folder “%s”, it is used for public folders only"), - folder_name); - return FALSE; - } - - trash_fid = camel_ews_store_summary_get_folder_id_from_folder_type (ews_store->summary, CAMEL_FOLDER_TYPE_TRASH); - trash_name = camel_ews_store_summary_get_folder_full_name (ews_store->summary, trash_fid, &local_error); - if (!trash_name) { - g_free (trash_fid); - g_free (fid); - camel_folder_info_free (folder_info); - - g_propagate_error (error, local_error); - return FALSE; - } - - is_under_trash_folder = g_str_has_prefix (folder_name, trash_name); - - g_free (trash_name); - - if (!camel_ews_store_connected (ews_store, cancellable, &local_error)) { - g_free (trash_fid); - g_free (fid); - camel_folder_info_free (folder_info); - - g_propagate_error (error, local_error); - - return FALSE; - } - - if (camel_ews_store_summary_get_foreign (ews_store->summary, fid, NULL) || - camel_ews_store_summary_get_public (ews_store->summary, fid, NULL)) { - /* do not delete foreign or public folders, - * only remove them from local store */ - success = TRUE; - } else { - EEwsConnection *connection; - - connection = camel_ews_store_ref_connection (ews_store); - - success = e_ews_connection_delete_folder_sync ( - connection, - EWS_PRIORITY_MEDIUM, - fid, FALSE, is_under_trash_folder ? "HardDelete" : "MoveToDeletedItems", - cancellable, &local_error); - - g_object_unref (connection); - } - - if (!success) { - g_free (trash_fid); - g_free (fid); - camel_folder_info_free (folder_info); - - camel_ews_store_maybe_disconnect (ews_store, local_error); - g_propagate_error (error, local_error); - - return FALSE; - } - - if (is_under_trash_folder) { - success = camel_ews_utils_delete_folders_from_summary_recursive (ews_store, to_update, FALSE, error); - - if (!success) { - g_free (trash_fid); - g_free (fid); - camel_folder_info_free (folder_info); - - g_propagate_error (error, local_error); - return FALSE; - } - } else { - camel_ews_store_summary_set_parent_folder_id (ews_store->summary, fid, trash_fid); - } - - camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (ews_store), folder_info); - camel_store_folder_deleted (CAMEL_STORE (ews_store), folder_info); - - camel_folder_info_free (folder_info); - - if (!is_under_trash_folder) { - camel_ews_store_summary_rebuild_hashes (ews_store->summary); - - folder_info = camel_ews_utils_build_folder_info (ews_store, fid); - - to_update = folder_info; - ews_update_store_move_recursive (ews_store, to_update); - camel_folder_info_free (folder_info); - } - - camel_ews_store_ensure_virtual_folders (ews_store); - camel_ews_store_summary_save (ews_store->summary, NULL); - - g_free (trash_fid); - g_free (fid); - - return TRUE; -} - -struct _rename_cb_data { - const gchar *display_name; - const gchar *change_key; - const gchar *folder_id; -}; - -static void -rename_folder_cb (ESoapMessage *msg, - gpointer user_data) -{ - struct _rename_cb_data *rename_data = user_data; - - e_ews_message_start_item_change ( - msg, E_EWS_ITEMCHANGE_TYPE_FOLDER, - rename_data->folder_id, rename_data->change_key, 0); - e_soap_message_start_element (msg, "SetFolderField", NULL, NULL); - e_ews_message_write_string_parameter_with_attribute ( - msg, "FieldURI", NULL, NULL, - "FieldURI", "folder:DisplayName"); - - e_soap_message_start_element (msg, "Folder", NULL, NULL); - e_ews_message_write_string_parameter (msg, "DisplayName", NULL, rename_data->display_name); - e_soap_message_end_element (msg); /* Folder */ - - e_soap_message_end_element (msg); /* SetFolderField */ - - e_ews_message_end_item_change (msg); -} - -static gboolean -ews_rename_folder_sync (CamelStore *store, - const gchar *old_name, - const gchar *new_name, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store = CAMEL_EWS_STORE (store); - CamelEwsStoreSummary *ews_summary = ews_store->summary; - EEwsConnection *connection; - const gchar *old_slash, *new_slash; - gchar *fid; - gchar *changekey; - gboolean res = FALSE; - GError *local_error = NULL; - - if (!strcmp (old_name, new_name)) - return TRUE; - - if (!camel_ews_store_connected (ews_store, cancellable, error)) { - return FALSE; - } - - fid = camel_ews_store_summary_get_folder_id_from_name (ews_summary, old_name); - if (!fid) { - g_set_error ( - error, CAMEL_STORE_ERROR, - CAMEL_STORE_ERROR_NO_FOLDER, - _("Folder %s does not exist"), old_name); - return FALSE; - } - - changekey = camel_ews_store_summary_get_change_key (ews_summary, fid, error); - if (!changekey) { - g_set_error ( - error, CAMEL_STORE_ERROR, - CAMEL_STORE_ERROR_NO_FOLDER, - _("No change key record for folder %s"), fid); - g_free (fid); - return FALSE; - } - - connection = camel_ews_store_ref_connection (ews_store); - - old_slash = g_strrstr (old_name, "/"); - new_slash = g_strrstr (new_name, "/"); - - if (old_slash) - old_slash++; - else - old_slash = old_name; - - if (new_slash) - new_slash++; - else - new_slash = new_name; - - if (strcmp (old_slash, new_slash)) { - gint parent_len = old_slash - old_name; - struct _rename_cb_data *rename_data; - - /* Folder basename changed (i.e. UpdateFolder needed). - * Therefore, we can only do it if the folder hasn't also - * been moved from one parent folder to another. - * - * Strictly speaking, we could probably handle this, even - * if there are name collisions. We could UpdateFolder to - * a new temporary name that doesn't exist in either the - * old or new parent folders, then MoveFolder, then issue - * another UpdateFolder to the name we actually wanted. - * But since the Evolution UI doesn't seem to let us - * make both changes at the same time anyway, we'll just - * bail out for now; we can deal with it later if we need - * to. - */ - if (new_slash - new_name != parent_len || - strncmp (old_name, new_name, parent_len)) { - g_set_error ( - error, CAMEL_STORE_ERROR, - CAMEL_STORE_ERROR_INVALID, - _("Cannot both rename and move a folder at the same time")); - goto out; - } - - rename_data = g_new0 (struct _rename_cb_data, 1); - rename_data->display_name = new_slash; - rename_data->folder_id = fid; - rename_data->change_key = changekey; - - res = e_ews_connection_update_folder_sync ( - connection, EWS_PRIORITY_MEDIUM, - rename_folder_cb, rename_data, - cancellable, &local_error); - - if (!res) { - g_free (rename_data); - goto out; - } - g_free (rename_data); - camel_ews_store_summary_set_folder_name (ews_summary, fid, new_slash); - } else { - gchar *pfid = NULL; - gchar *parent_name; - - /* If we are not moving to the root folder, work out the ItemId of - * the new parent folder */ - if (new_slash != new_name) { - parent_name = g_strndup (new_name, new_slash - new_name - 1); - pfid = camel_ews_store_summary_get_folder_id_from_name ( - ews_summary, parent_name); - if (!pfid) { - g_set_error ( - error, CAMEL_STORE_ERROR, - CAMEL_STORE_ERROR_NO_FOLDER, - _("Cannot find folder ID for parent folder %s"), - parent_name); - g_free (parent_name); - goto out; - } - g_free (parent_name); - } - - res = e_ews_connection_move_folder_sync ( - connection, EWS_PRIORITY_MEDIUM, - pfid, fid, cancellable, &local_error); - - if (!res) { - g_free (pfid); - goto out; - } - camel_ews_store_summary_set_parent_folder_id (ews_summary, fid, pfid); - g_free (pfid); - } - - res = TRUE; - out: - if (local_error) { - camel_ews_store_maybe_disconnect (ews_store, local_error); - g_propagate_error (error, local_error); - } - - g_object_unref (connection); - - g_free (changekey); - g_free (fid); - return res; -} - -gchar * -ews_get_name (CamelService *service, - gboolean brief) -{ - CamelSettings *settings; - gchar *name; - gchar *host; - gchar *user; - - settings = camel_service_ref_settings (service); - - user = camel_network_settings_dup_user (CAMEL_NETWORK_SETTINGS (settings)); - host = camel_ews_utils_get_host_name (settings); - - g_object_unref (settings); - - if (brief) - name = g_strdup_printf ( - _("Exchange server %s"), host); - else - name = g_strdup_printf ( - _("Exchange service for %s on %s"), user, host); - - g_free (host); - g_free (user); - - return name; -} - -EEwsConnection * -camel_ews_store_ref_connection (CamelEwsStore *ews_store) -{ - EEwsConnection *connection = NULL; - - g_return_val_if_fail (CAMEL_IS_EWS_STORE (ews_store), NULL); - - g_mutex_lock (&ews_store->priv->connection_lock); - - if (ews_store->priv->connection != NULL) - connection = g_object_ref (ews_store->priv->connection); - - g_mutex_unlock (&ews_store->priv->connection_lock); - - return connection; -} - -static CamelFolder * -ews_get_trash_folder_sync (CamelStore *store, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store; - CamelFolder *folder = NULL; - gchar *folder_id, *folder_name; - - g_return_val_if_fail (CAMEL_IS_EWS_STORE (store), NULL); - - ews_store = CAMEL_EWS_STORE (store); - folder_id = camel_ews_store_summary_get_folder_id_from_folder_type ( - ews_store->summary, CAMEL_FOLDER_TYPE_TRASH); - - if (folder_id == NULL) { - g_set_error ( - error, CAMEL_STORE_ERROR, - CAMEL_STORE_ERROR_NO_FOLDER, - _("Could not locate Trash folder")); - return NULL; - } - - folder_name = camel_ews_store_summary_get_folder_full_name ( - ews_store->summary, folder_id, NULL); - - folder = camel_store_get_folder_sync ( - store, folder_name, 0, cancellable, error); - - g_free (folder_name); - g_free (folder_id); - - if (folder) { - GPtrArray *folders; - gboolean can = TRUE; - gint ii; - - /* Save content of all opened folders, thus any messages deleted in them - are moved to the Deleted Items folder first, thus in case of the trash - folder instance being used to expunge messages will contain all of them. - */ - folders = camel_store_dup_opened_folders (store); - for (ii = 0; ii < folders->len; ii++) { - CamelFolder *secfolder = folders->pdata[ii]; - - if (secfolder != folder && can) - can = camel_folder_synchronize_sync (secfolder, FALSE, cancellable, NULL); - - g_object_unref (secfolder); - } - g_ptr_array_free (folders, TRUE); - - /* To return 'Deleted Items' folder with current content, - not with possibly stale locally cached copy. */ - camel_folder_refresh_info_sync (folder, cancellable, NULL); - } - - return folder; -} - -static CamelFolder * -ews_get_junk_folder_sync (CamelStore *store, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store; - CamelFolder *folder = NULL; - gchar *folder_id, *folder_name; - - g_return_val_if_fail (CAMEL_IS_EWS_STORE (store), NULL); - - ews_store = CAMEL_EWS_STORE (store); - folder_id = camel_ews_store_summary_get_folder_id_from_folder_type ( - ews_store->summary, CAMEL_FOLDER_TYPE_JUNK); - - if (folder_id == NULL) { - g_set_error ( - error, CAMEL_STORE_ERROR, - CAMEL_STORE_ERROR_NO_FOLDER, - _("Could not locate Junk folder")); - return NULL; - } - - folder_name = camel_ews_store_summary_get_folder_full_name ( - ews_store->summary, folder_id, NULL); - - folder = camel_store_get_folder_sync ( - store, folder_name, 0, cancellable, error); - - g_free (folder_name); - g_free (folder_id); - - return folder; -} - -static gboolean -ews_can_refresh_folder (CamelStore *store, - CamelFolderInfo *info, - GError **error) -{ - CamelSettings *settings; - CamelEwsSettings *ews_settings; - gboolean check_all; - - /* Skip unselectable folders from automatic refresh */ - if (info && (info->flags & CAMEL_FOLDER_NOSELECT) != 0) - return FALSE; - - settings = camel_service_ref_settings (CAMEL_SERVICE (store)); - - ews_settings = CAMEL_EWS_SETTINGS (settings); - check_all = camel_ews_settings_get_check_all (ews_settings); - - g_object_unref (settings); - - if (check_all) - return TRUE; - - /* Delegate decision to parent class */ - return CAMEL_STORE_CLASS (camel_ews_store_parent_class)-> - can_refresh_folder (store, info, error); -} - -static gboolean -ews_store_folder_is_subscribed (CamelSubscribable *subscribable, - const gchar *folder_name) -{ - CamelEwsStore *ews_store = CAMEL_EWS_STORE (subscribable); - gchar *fid; - gboolean truth = FALSE; - GError *error = NULL; - - fid = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, folder_name); - if (!fid) - return FALSE; - - if (camel_ews_store_summary_get_foreign (ews_store->summary, fid, &error) && !error) { - truth = TRUE; - } - g_clear_error (&error); - - if (!truth && camel_ews_store_summary_get_public (ews_store->summary, fid, &error) && !error) { - truth = TRUE; - } - g_clear_error (&error); - - g_free (fid); - - return truth; -} - -/* caller should hold ews_store->priv->get_finfo_lock already */ -static EEwsFolder * -ews_store_find_public_folder (CamelEwsStore *ews_store, - const gchar *folder_name) -{ - EEwsFolder *folder = NULL; - GSList *piter; - gchar **folders; - gint ii; - - g_return_val_if_fail (CAMEL_IS_EWS_STORE (ews_store), NULL); - g_return_val_if_fail (folder_name != NULL, NULL); - - folders = g_strsplit (folder_name, "/", -1); - if (!folders || !folders[0] || g_strcmp0 (folders[0], EWS_PUBLIC_FOLDER_ROOT_DISPLAY_NAME) != 0) { - g_strfreev (folders); - return NULL; - } - - /* they are stored in public_folders from top level to bottom level */ - piter = ews_store->priv->public_folders; - for (ii = 1; folders[ii] && piter; ii++) { - const gchar *fname = folders[ii]; - - while (piter) { - EEwsFolder *subf = piter->data; - const EwsFolderId *parent_id; - - if (!subf) { - piter = NULL; - break; - } - - if (g_strcmp0 (e_ews_folder_get_name (subf), fname) == 0 || - g_strcmp0 (e_ews_folder_get_escaped_name (subf), fname) == 0) { - parent_id = e_ews_folder_get_parent_id (subf); - if (!folder && (!parent_id || g_strcmp0 (parent_id->id, EWS_PUBLIC_FOLDER_ROOT_ID) == 0)) { - folder = subf; - break; - } else if (parent_id && folder) { - const EwsFolderId *fid = e_ews_folder_get_id (folder); - - if (fid && g_strcmp0 (fid->id, parent_id->id) == 0) { - folder = subf; - break; - } - } - } - - piter = piter->next; - } - } - - if (!piter || folders[ii]) - folder = NULL; - - g_strfreev (folders); - - return folder; -} - -/* ppath contains proposed path, this only makes sure that it's a unique path */ -static void -ews_store_ensure_unique_path (CamelEwsStore *ews_store, - gchar **ppath) -{ - gboolean done; - guint counter = 0; - gchar *base_path = NULL; - - g_return_if_fail (ews_store != NULL); - g_return_if_fail (ews_store->summary != NULL); - g_return_if_fail (ppath != NULL); - g_return_if_fail (*ppath != NULL); - - done = FALSE; - while (!done) { - gchar *fid; - - done = TRUE; - - fid = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, *ppath); - if (fid) { - g_free (fid); - - done = FALSE; - counter++; - if (!counter) { - g_debug ("%s: Counter overflow", G_STRFUNC); - break; - } - - if (!base_path) - base_path = *ppath; - else - g_free (*ppath); - - *ppath = g_strdup_printf ("%s_%u", base_path, counter); - } - } - - g_free (base_path); -} - -static gboolean -ews_store_subscribe_folder_sync (CamelSubscribable *subscribable, - const gchar *folder_name, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store = CAMEL_EWS_STORE (subscribable); - EEwsFolder *folder; - const EwsFolderId *fid; - gboolean res = TRUE; - gchar *tmp; - - if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store))) { - g_set_error_literal ( - error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE, - _("Cannot subscribe EWS folders in offline mode")); - return FALSE; - } - - /* Folder's name is not stored with the "/" in the beginning, even when the - folder is in the root (same level as "Inbox", "Drafts", etc. */ - if (*folder_name == '/') - folder_name++; - - /* can subscribe only public folders, - thus skip anything known */ - tmp = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, folder_name); - if (tmp) { - g_free (tmp); - return TRUE; - } - - g_mutex_lock (&ews_store->priv->get_finfo_lock); - if (!ews_store->priv->public_folders) { - g_mutex_unlock (&ews_store->priv->get_finfo_lock); - - g_set_error ( - error, CAMEL_STORE_ERROR, CAMEL_STORE_ERROR_NO_FOLDER, - _("Cannot subscribe folder “%s”, no public folder available"), folder_name); - return FALSE; - } - - folder = ews_store_find_public_folder (ews_store, folder_name); - if (!folder) { - g_mutex_unlock (&ews_store->priv->get_finfo_lock); - - g_set_error ( - error, CAMEL_STORE_ERROR, CAMEL_STORE_ERROR_NO_FOLDER, - _("Cannot subscribe folder “%s”, folder not found"), folder_name); - return FALSE; - } - - fid = e_ews_folder_get_id (folder); - - g_return_val_if_fail (fid != NULL, FALSE); - - if (camel_ews_store_summary_has_folder (ews_store->summary, EWS_PUBLIC_FOLDER_ROOT_ID)) { - gchar *parent_name = camel_ews_store_summary_get_folder_name (ews_store->summary, EWS_PUBLIC_FOLDER_ROOT_ID, NULL); - - g_return_val_if_fail (parent_name != NULL, FALSE); - - tmp = g_strconcat (parent_name, "/", e_ews_folder_get_escaped_name (folder), NULL); - g_free (parent_name); - } else { - tmp = g_strconcat (EWS_PUBLIC_FOLDER_ROOT_DISPLAY_NAME, "/", e_ews_folder_get_escaped_name (folder), NULL); - } - - if (e_ews_folder_get_folder_type (folder) != E_EWS_FOLDER_TYPE_MAILBOX) { - CamelSession *session; - CamelSettings *settings; - CamelEwsSettings *ews_settings; - ESourceRegistry *registry = NULL; - - settings = camel_service_ref_settings (CAMEL_SERVICE (ews_store)); - ews_settings = CAMEL_EWS_SETTINGS (settings); - session = camel_service_ref_session (CAMEL_SERVICE (ews_store)); - if (E_IS_MAIL_SESSION (session)) - registry = e_mail_session_get_registry (E_MAIL_SESSION (session)); - - res = e_ews_folder_utils_add_as_esource (registry, - camel_ews_settings_get_hosturl (ews_settings), - camel_network_settings_get_user (CAMEL_NETWORK_SETTINGS (ews_settings)), - folder, - E_EWS_ESOURCE_FLAG_OFFLINE_SYNC | E_EWS_ESOURCE_FLAG_PUBLIC_FOLDER, - 0, - cancellable, - error); - - g_object_unref (session); - g_object_unref (settings); - } - - if (res) { - ews_store_ensure_unique_path (ews_store, &tmp); - - camel_ews_store_summary_new_folder (ews_store->summary, fid->id, EWS_PUBLIC_FOLDER_ROOT_ID, - NULL, - strrchr (tmp, '/') + 1, - e_ews_folder_get_folder_type (folder), - CAMEL_FOLDER_SUBSCRIBED, - e_ews_folder_get_total_count (folder), - FALSE, TRUE); - - if (e_ews_folder_get_folder_type (folder) == E_EWS_FOLDER_TYPE_MAILBOX) { - CamelFolderInfo *fi; - - camel_ews_store_ensure_virtual_folders (ews_store); - - fi = camel_ews_utils_build_folder_info (ews_store, fid->id); - camel_store_folder_created (CAMEL_STORE (ews_store), fi); - camel_subscribable_folder_subscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_folder_info_free (fi); - } - } - - camel_ews_store_summary_save (ews_store->summary, NULL); - - g_free (tmp); - g_mutex_unlock (&ews_store->priv->get_finfo_lock); - - return res; -} - -static gboolean -ews_store_unsubscribe_folder_sync (CamelSubscribable *subscribable, - const gchar *folder_name, - GCancellable *cancellable, - GError **error) -{ - CamelEwsStore *ews_store = CAMEL_EWS_STORE (subscribable); - EEwsFolderType folder_type; - EEwsFolder *folder; - gboolean is_public; - gboolean res = TRUE; - gchar *fid = NULL; - - if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store))) { - g_set_error_literal ( - error, CAMEL_SERVICE_ERROR, CAMEL_SERVICE_ERROR_UNAVAILABLE, - _("Cannot unsubscribe EWS folders in offline mode")); - return FALSE; - } - - folder = ews_store_find_public_folder (ews_store, folder_name); - if (folder) { - const EwsFolderId *folder_id = e_ews_folder_get_id (folder); - - if (folder_id) { - fid = g_strdup (folder_id->id); - folder_type = e_ews_folder_get_folder_type (folder); - } - } - - if (!fid) { - fid = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, folder_name); - if (fid) - folder_type = camel_ews_store_summary_get_folder_type (ews_store->summary, fid, NULL); - } - - if (!fid) { - /* no such folder in the cache, might be unsubscribed already */ - return TRUE; - } - - is_public = camel_ews_store_summary_get_public (ews_store->summary, fid, NULL); - if (!is_public && !camel_ews_store_summary_get_foreign (ews_store->summary, fid, NULL)) { - /* nothing to do for regular folders */ - res = TRUE; - } else { - CamelFolderInfo *fi; - - if (!is_public && camel_ews_store_summary_get_foreign_subfolders (ews_store->summary, fid, NULL)) { - /* when subscribed with subfolders, then unsubscribe with subfolders as well */ - GSList *local_folders = NULL, *ii; - gchar *full_name = camel_ews_store_summary_get_folder_full_name (ews_store->summary, fid, NULL); - if (full_name) { - local_folders = camel_ews_store_summary_get_folders (ews_store->summary, full_name); - } - g_free (full_name); - - for (ii = local_folders; ii != NULL; ii = ii->next) { - const gchar *lfid = ii->data; - EEwsFolderType ftype; - - if (g_strcmp0 (lfid, fid) == 0) - continue; - - ftype = camel_ews_store_summary_get_folder_type (ews_store->summary, lfid, NULL); - if (ftype != E_EWS_FOLDER_TYPE_MAILBOX) - continue; - - fi = camel_ews_utils_build_folder_info (ews_store, lfid); - camel_ews_store_summary_remove_folder (ews_store->summary, lfid, NULL); - - camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_store_folder_deleted (CAMEL_STORE (ews_store), fi); - camel_folder_info_free (fi); - } - - g_slist_free_full (local_folders, g_free); - } - - if (folder_type != E_EWS_FOLDER_TYPE_MAILBOX) { - CamelSession *session; - CamelSettings *settings; - CamelEwsSettings *ews_settings; - ESourceRegistry *registry = NULL; - - settings = camel_service_ref_settings (CAMEL_SERVICE (ews_store)); - ews_settings = CAMEL_EWS_SETTINGS (settings); - session = camel_service_ref_session (CAMEL_SERVICE (ews_store)); - if (E_IS_MAIL_SESSION (session)) - registry = e_mail_session_get_registry (E_MAIL_SESSION (session)); - - res = e_ews_folder_utils_remove_as_esource (registry, - camel_ews_settings_get_hosturl (ews_settings), - camel_network_settings_get_user (CAMEL_NETWORK_SETTINGS (ews_settings)), - fid, - cancellable, - error); - - g_object_unref (session); - g_object_unref (settings); - } - - if (res) { - fi = camel_ews_utils_build_folder_info (ews_store, fid); - camel_ews_store_summary_remove_folder (ews_store->summary, fid, error); - - if (folder_type == E_EWS_FOLDER_TYPE_MAILBOX) { - camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_store_folder_deleted (CAMEL_STORE (ews_store), fi); - camel_folder_info_free (fi); - - camel_ews_store_ensure_virtual_folders (ews_store); - } - } - - camel_ews_store_summary_save (ews_store->summary, NULL); - } - - g_free (fid); - - return res; -} - -gboolean -camel_ews_store_connected (CamelEwsStore *ews_store, - GCancellable *cancellable, - GError **error) -{ - - if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store))) { - g_set_error ( - error, CAMEL_SERVICE_ERROR, - CAMEL_SERVICE_ERROR_UNAVAILABLE, - _("You must be working online to complete this operation")); - return FALSE; - } - - if (!camel_service_connect_sync ((CamelService *) ews_store, cancellable, error)) - return FALSE; - - return TRUE; -} - -void -camel_ews_store_maybe_disconnect (CamelEwsStore *store, - const GError *error) -{ - CamelService *service; - - g_return_if_fail (store != NULL); - - if (!error) - return; - - service = CAMEL_SERVICE (store); - - if (camel_service_get_connection_status (service) != CAMEL_SERVICE_CONNECTED) - return; - - if (g_error_matches (error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NORESPONSE) || - g_error_matches (error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED)) - camel_service_disconnect_sync (service, FALSE, NULL, NULL); -} - -static void -ews_store_unset_oof_settings_state (CamelSession *session, - GCancellable *cancellable, - gpointer user_data, - GError **error) -{ - - CamelEwsStore *ews_store = user_data; - EEwsConnection *connection; - EEwsOofSettings *oof_settings; - EEwsOofState state; - GError *local_error = NULL; - - camel_operation_push_message (cancellable, _("Unsetting the “Out of Office” status")); - - connection = camel_ews_store_ref_connection (ews_store); - oof_settings = e_ews_oof_settings_new_sync (connection, cancellable, &local_error); - g_object_unref (connection); - if (local_error != NULL) { - g_propagate_error (error, local_error); - camel_operation_pop_message (cancellable); - return; - } - - state = e_ews_oof_settings_get_state (oof_settings); - if (state == E_EWS_OOF_STATE_DISABLED) { - g_object_unref (oof_settings); - camel_operation_pop_message (cancellable); - return; - } - - e_ews_oof_settings_set_state (oof_settings, E_EWS_OOF_STATE_DISABLED); - e_ews_oof_settings_submit_sync (oof_settings, cancellable, error); - g_object_unref (oof_settings); - - camel_operation_pop_message (cancellable); -} - -void -camel_ews_store_unset_oof_settings_state (CamelEwsStore *ews_store) -{ - CamelService *service; - CamelSession *session; - - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - - service = CAMEL_SERVICE (ews_store); - session = camel_service_ref_session (service); - - camel_session_submit_job ( - session, _("Unsetting the “Out of Office” status"), - ews_store_unset_oof_settings_state, - g_object_ref (ews_store), - g_object_unref); - - g_object_unref (session); -} - -static void -ews_store_dispose (GObject *object) -{ - CamelEwsStore *ews_store; - CamelEwsSettings *ews_settings; - - ews_store = CAMEL_EWS_STORE (object); - - ews_settings = CAMEL_EWS_SETTINGS (camel_service_ref_settings (CAMEL_SERVICE (ews_store))); - g_signal_handlers_disconnect_by_func (ews_settings, camel_ews_store_listen_notifications_cb, ews_store); - g_signal_handlers_disconnect_by_func (ews_settings, camel_ews_store_check_all_cb, ews_store); - g_object_unref (ews_settings); - - if (ews_store->summary != NULL) { - camel_ews_store_summary_save (ews_store->summary, NULL); - g_object_unref (ews_store->summary); - ews_store->summary = NULL; - } - - g_mutex_lock (&ews_store->priv->connection_lock); - ews_store_unset_connection_locked (ews_store); - g_mutex_unlock (&ews_store->priv->connection_lock); - - g_slist_free_full (ews_store->priv->update_folder_names, g_free); - ews_store->priv->update_folder_names = NULL; - - g_slist_free_full (ews_store->priv->public_folders, g_object_unref); - ews_store->priv->public_folders = NULL; - - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (camel_ews_store_parent_class)->dispose (object); -} - -static void -ews_store_finalize (GObject *object) -{ - CamelEwsStore *ews_store; - - ews_store = CAMEL_EWS_STORE (object); - - g_free (ews_store->storage_path); - g_mutex_clear (&ews_store->priv->get_finfo_lock); - g_mutex_clear (&ews_store->priv->connection_lock); - g_rec_mutex_clear (&ews_store->priv->update_lock); - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (camel_ews_store_parent_class)->finalize (object); -} - -static void -camel_ews_store_class_init (CamelEwsStoreClass *class) -{ - GObjectClass *object_class; - CamelServiceClass *service_class; - CamelStoreClass *store_class; - - g_type_class_add_private (class, sizeof (CamelEwsStorePrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->set_property = ews_store_set_property; - object_class->get_property = ews_store_get_property; - object_class->dispose = ews_store_dispose; - object_class->finalize = ews_store_finalize; - - g_object_class_install_property ( - object_class, - PROP_HAS_OOO_SET, - g_param_spec_boolean ( - "has-ooo-set", - "Has OOO Set", - "Has Out of Office state set", - FALSE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property ( - object_class, - PROP_OOO_ALERT_STATE, - g_param_spec_enum ( - "ooo-alert-state", - "Out of Office Alert State", - "The state of the Out of Office Alert", - CAMEL_TYPE_EWS_STORE_OOO_ALERT_STATE, - CAMEL_EWS_STORE_OOO_ALERT_STATE_UNKNOWN, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT | - G_PARAM_STATIC_STRINGS)); - - /* Inherited from CamelNetworkService */ - g_object_class_override_property ( - object_class, - PROP_CONNECTABLE, - "connectable"); - - /* Inherited from CamelNetworkService */ - g_object_class_override_property ( - object_class, - PROP_HOST_REACHABLE, - "host-reachable"); - - service_class = CAMEL_SERVICE_CLASS (class); - service_class->settings_type = CAMEL_TYPE_EWS_SETTINGS; - service_class->query_auth_types_sync = ews_store_query_auth_types_sync; - service_class->get_name = ews_get_name; - service_class->connect_sync = ews_connect_sync; - service_class->disconnect_sync = ews_disconnect_sync; - service_class->authenticate_sync = ews_authenticate_sync; - - store_class = CAMEL_STORE_CLASS (class); - store_class->get_folder_sync = ews_get_folder_sync; - store_class->create_folder_sync = ews_create_folder_sync; - store_class->delete_folder_sync = ews_delete_folder_sync; - store_class->rename_folder_sync = ews_rename_folder_sync; - store_class->get_folder_info_sync = ews_get_folder_info_sync; - store_class->initial_setup_sync = ews_initial_setup_sync; - - store_class->get_trash_folder_sync = ews_get_trash_folder_sync; - store_class->get_junk_folder_sync = ews_get_junk_folder_sync; - store_class->can_refresh_folder = ews_can_refresh_folder; -} - -static void -camel_ews_subscribable_init (CamelSubscribableInterface *iface) -{ - iface->folder_is_subscribed = ews_store_folder_is_subscribed; - iface->subscribe_folder_sync = ews_store_subscribe_folder_sync; - iface->unsubscribe_folder_sync = ews_store_unsubscribe_folder_sync; -} - -static void -camel_ews_store_init (CamelEwsStore *ews_store) -{ - ews_store->priv = CAMEL_EWS_STORE_GET_PRIVATE (ews_store); - - ews_store->priv->last_refresh_time = time (NULL) - (FINFO_REFRESH_INTERVAL + 10); - ews_store->priv->updates_cancellable = NULL; - ews_store->priv->update_folder_names = NULL; - ews_store->priv->subscription_key = 0; - ews_store->priv->update_folder_id = 0; - ews_store->priv->update_folder_list_id = 0; - ews_store->priv->password_expires_in_days = -1; - g_mutex_init (&ews_store->priv->get_finfo_lock); - g_mutex_init (&ews_store->priv->connection_lock); - g_rec_mutex_init (&ews_store->priv->update_lock); -} diff --git a/src/camel/camel-ews-utils.c b/src/camel/camel-ews-utils.c index ece71ac..ec76dd7 100644 --- a/src/camel/camel-ews-utils.c +++ b/src/camel/camel-ews-utils.c @@ -29,7 +29,6 @@ #include #include -#include #include "server/camel-ews-settings.h" #include "server/e-ews-camel-common.h" @@ -382,43 +381,6 @@ camel_ews_utils_sync_deleted_items (CamelEwsFolder *ews_folder, } static const gchar * -ews_utils_outlook_color_index_to_color_def (gint color_index) -{ - const gchar *colors_array[] = { - "#ff1a36", /* Red */ - "#ff8c00", /* Orange */ - "#f4b10b", /* Peach */ - "#fff100", /* Yellow */ - "#009e48", /* Green */ - "#00b294", /* Teal */ - "#89933f", /* Olive */ - "#00bcf2", /* Blue */ - "#8e69df", /* Purple */ - "#f30092", /* Maroon */ - "#6c7e9a", /* Steel */ - "#425066", /* DarkSteel */ - "#969696", /* Gray */ - "#525552", /* DarkGray */ - "#282828", /* Black */ - "#a00023", /* DarkRed */ - "#c45502", /* DarkOrange */ - "#af7000", /* DarkPeach */ - "#b59b02", /* DarkYellow */ - "#176002", /* DarkGreen */ - "#00725c", /* DarkTeal */ - "#5c6022", /* DarkOlive */ - "#036393", /* DarkBlue */ - "#422f8e", /* DarkPurple */ - "#960269" /* DarkMaroon */ - }; - - if (color_index >= 0 && color_index < G_N_ELEMENTS (colors_array)) - return colors_array[color_index]; - - return NULL; -} - -static const gchar * ews_utils_rename_label (const gchar *cat, gboolean from_cat) { @@ -460,58 +422,6 @@ ews_utils_is_system_user_flag (const gchar *name) g_str_equal (name, "$has-cal"); } -/* From Exchange name (which allows spaces) to evolution-name */ -static gchar * -camel_ews_utils_encode_category_name (const gchar *name) -{ - if (name && strchr (name, ' ')) { - GString *str; - - str = g_string_sized_new (strlen (name) + 16); - - while (*name) { - if (*name == '_') - g_string_append_c (str, '_'); - - g_string_append_c (str, *name == ' ' ? '_' : *name); - - name++; - } - - return g_string_free (str, FALSE); - } - - return g_strdup (name); -} - -/* From evolution-name to Exchange name (which allows spaces) */ -static gchar * -camel_ews_utils_decode_category_name (const gchar *flag) -{ - if (flag && strchr (flag, '_')) { - GString *str = g_string_sized_new (strlen (flag)); - - while (*flag) { - if (*flag == '_') { - if (flag[1] == '_') { - g_string_append_c (str, '_'); - flag++; - } else { - g_string_append_c (str, ' '); - } - } else { - g_string_append_c (str, *flag); - } - - flag++; - } - - return g_string_free (str, FALSE); - } - - return g_strdup (flag); -} - /* free with g_slist_free_full (flags, g_free); the lists' members are values for the String xml element. */ GSList * @@ -531,7 +441,6 @@ ews_utils_gather_server_user_flags (ESoapMessage *msg, * array of strings */ for (ii = 0; ii < len; ii++) { const gchar *n = ews_utils_rename_label (camel_named_flags_get (user_flags, ii), FALSE); - if (*n == '\0') continue; @@ -540,7 +449,26 @@ ews_utils_gather_server_user_flags (ESoapMessage *msg, if (ews_utils_is_system_user_flag (n)) continue; - out_user_flags = g_slist_prepend (out_user_flags, camel_ews_utils_decode_category_name (n)); + if (strchr (n, '_')) { + GString *str = g_string_sized_new (strlen (n)); + + while (*n) { + if (*n == '_') { + if (n[1] == '_') + g_string_append_c (str, '_'); + else + g_string_append_c (str, ' '); + } else { + g_string_append_c (str, *n); + } + + n++; + } + + out_user_flags = g_slist_prepend (out_user_flags, g_string_free (str, FALSE)); + } else { + out_user_flags = g_slist_prepend (out_user_flags, g_strdup (n)); + } } camel_message_info_property_unlock (mi); @@ -584,17 +512,33 @@ ews_utils_merge_server_user_flags (EEwsItem *item, /* now transfer over all the categories */ for (p = e_ews_item_get_categories (item); p; p = p->next) { - const gchar *name = ews_utils_rename_label (p->data, 1); - gchar *flag; + const gchar *flag = ews_utils_rename_label (p->data, 1); + gchar *underscored = NULL; - if (!name || !*name) + if (!flag || !*flag) continue; - flag = camel_ews_utils_encode_category_name (name); + if (strchr (flag, ' ')) { + GString *str; + + str = g_string_sized_new (strlen (flag) + 16); + + while (*flag) { + if (*flag == '_') + g_string_append_c (str, '_'); + + g_string_append_c (str, *flag == ' ' ? '_' : *flag); + + flag++; + } + + underscored = g_string_free (str, FALSE); + flag = underscored; + } camel_message_info_set_user_flag (mi, flag, TRUE); - g_free (flag); + g_free (underscored); } camel_message_info_thaw_notifications (mi); @@ -1337,279 +1281,3 @@ camel_ews_utils_ref_corresponding_source (CamelService *service, return source; } - -static gboolean -ews_util_equal_label_tag_cb (gconstpointer ptr1, - gconstpointer ptr2) -{ - const gchar *evo_label_def = ptr1; - const gchar *tag = ptr2; - const gchar *pos; - - if (!evo_label_def || !tag || !*tag) - return FALSE; - - pos = g_strrstr (evo_label_def, tag); - - return pos > evo_label_def && pos[-1] == '|' && !pos[strlen (tag)]; -} - -static gboolean -ews_utils_find_in_ptr_array (GPtrArray *haystack, - gconstpointer needle, - GEqualFunc equal_func, - guint *out_index) -{ - guint ii; - - if (!haystack) - return FALSE; - - if (!equal_func) - equal_func = g_direct_equal; - - for (ii = 0; ii < haystack->len; ii++) { - if (equal_func (haystack->pdata[ii], needle)) { - if (out_index) - *out_index = ii; - - return TRUE; - } - } - - return FALSE; -} - -/* Returns whether had been done any changes */ -static gboolean -ews_utils_save_category_changes (GHashTable *old_categories, /* gchar *guid ~> CamelEwsCategory * */ - GHashTable *new_categories) /* gchar *guid ~> CamelEwsCategory * */ -{ - GHashTableIter iter; - GSettings *settings; - GPtrArray *evo_labels; /* gchar * (encoded label definition) */ - gchar **strv; - gint ii; - gpointer value; - gboolean changed = FALSE; - - if (!old_categories || !new_categories) - return new_categories != NULL; - - evo_labels = g_ptr_array_new_full (5, g_free); - - settings = e_util_ref_settings ("org.gnome.evolution.mail"); - strv = g_settings_get_strv (settings, "labels"); - - for (ii = 0; strv && strv[ii]; ii++) { - g_ptr_array_add (evo_labels, g_strdup (strv[ii])); - } - - g_strfreev (strv); - - g_hash_table_iter_init (&iter, new_categories); - while (g_hash_table_iter_next (&iter, NULL, &value)) { - CamelEwsCategory *new_cat = value, *old_cat; - gchar *tag = NULL; - - if (!new_cat) - continue; - - old_cat = g_hash_table_lookup (old_categories, new_cat->guid); - if (old_cat) { - if (g_strcmp0 (old_cat->name, new_cat->name) != 0 || - g_strcmp0 (old_cat->color_def, new_cat->color_def) != 0) { - /* Old category changed name or color */ - tag = camel_ews_utils_encode_category_name (new_cat->name); - } - } else { - /* This is a new category */ - tag = camel_ews_utils_encode_category_name (new_cat->name); - } - - if (tag && *tag) { - guint index = (guint) -1; - gchar *label_def; - - changed = TRUE; - - /* Sanitize value */ - for (ii = 0; tag[ii]; ii++) { - if (tag[ii] == '|') - tag[ii] = '-'; - } - - if (old_cat && g_strcmp0 (old_cat->name, new_cat->name) != 0) { - gchar *old_tag = camel_ews_utils_encode_category_name (old_cat->name); - - if (old_tag && *old_tag) { - if (!ews_utils_find_in_ptr_array (evo_labels, old_tag, ews_util_equal_label_tag_cb, &index)) - index = (guint) -1; - } - - g_free (old_tag); - } - - for (ii = 0; new_cat->name[ii]; ii++) { - if (new_cat->name[ii] == '|') - new_cat->name[ii] = '-'; - } - - if (index == (guint) -1 && - !ews_utils_find_in_ptr_array (evo_labels, tag, ews_util_equal_label_tag_cb, &index)) - index = (guint) -1; - - label_def = g_strconcat (new_cat->name, "|", new_cat->color_def ? new_cat->color_def : "#FF0000", "|", tag, NULL); - - if (index == (guint) -1 || index >= (gint) evo_labels->len) { - g_ptr_array_add (evo_labels, label_def); - } else { - g_free (evo_labels->pdata[index]); - evo_labels->pdata[index] = label_def; - } - } - - g_hash_table_remove (old_categories, new_cat->guid); - - g_free (tag); - } - - if (g_hash_table_size (old_categories) > 0) { - /* Some categories had been removed */ - changed = TRUE; - - g_hash_table_iter_init (&iter, old_categories); - while (g_hash_table_iter_next (&iter, NULL, &value)) { - CamelEwsCategory *old_cat = value; - gchar *old_tag; - guint index; - - if (!old_cat) - continue; - - old_tag = camel_ews_utils_encode_category_name (old_cat->name); - - for (ii = 0; old_tag && old_tag[ii]; ii++) { - if (old_tag[ii] == '|') - old_tag[ii] = '-'; - } - - if (old_tag && - ews_utils_find_in_ptr_array (evo_labels, old_tag, ews_util_equal_label_tag_cb, &index)) - g_ptr_array_remove_index (evo_labels, index); - - g_free (old_tag); - } - } - - if (changed) { - /* NULL-terminated array of strings */ - g_ptr_array_add (evo_labels, NULL); - - g_settings_set_strv (settings, "labels", (const gchar * const *) evo_labels->pdata); - } - - g_ptr_array_free (evo_labels, TRUE); - g_object_unref (settings); - - return changed; -} - -void -camel_ews_utils_merge_category_list (CamelEwsStore *ews_store, - const guchar *xml_data, - gsize xml_data_len) -{ - xmlDocPtr doc; - xmlXPathContextPtr xpath_ctx; - - g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store)); - g_return_if_fail (xml_data != NULL); - - doc = e_xml_parse_data (xml_data, xml_data_len); - if (!doc) - return; - - xpath_ctx = e_xml_new_xpath_context_with_namespaces (doc, "C", "CategoryList.xsd", NULL); - - if (xpath_ctx) { - xmlXPathObjectPtr xpath_obj_categories; - - xpath_obj_categories = e_xml_xpath_eval (xpath_ctx, "%s", "/C:categories/C:category"); - - if (xpath_obj_categories) { - GHashTable *old_categories, *new_categories; - gint response_index, response_length; - - new_categories = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, camel_ews_category_free); - - response_length = xmlXPathNodeSetGetLength (xpath_obj_categories->nodesetval); - - for (response_index = 0; response_index < response_length; response_index++) { - xmlXPathObjectPtr xpath_obj_category; - - xpath_obj_category = e_xml_xpath_eval (xpath_ctx, - "/C:categories/C:category[%d]", - response_index + 1); - - if (xpath_obj_category) { - gchar *name; - - name = e_xml_xpath_eval_as_string (xpath_ctx, "/C:categories/C:category[%d]/@name", response_index + 1); - - if (name && ews_utils_rename_label (name, 1) == name) { - const gchar *color_def = NULL; - gchar *color, *guid; - gint color_index = -1; - - color = e_xml_xpath_eval_as_string (xpath_ctx, "/C:categories/C:category[%d]/@color", response_index + 1); - if (color) { - gchar *endptr = NULL; - - color_index = (gint) g_ascii_strtoll (color, &endptr, 10); - - if (endptr == color) - color_index = -1; - } - - g_free (color); - - if (color_index >= 0) - color_def = ews_utils_outlook_color_index_to_color_def (color_index); - - guid = e_xml_xpath_eval_as_string (xpath_ctx, "/C:categories/C:category[%d]/@guid", response_index + 1); - - if (guid && *guid) { - CamelEwsCategory *cat; - - cat = camel_ews_category_new (guid, name, color_def); - if (cat) - g_hash_table_insert (new_categories, cat->guid, cat); - } - - g_free (guid); - } - - g_free (name); - xmlXPathFreeObject (xpath_obj_category); - } - } - - xmlXPathFreeObject (xpath_obj_categories); - - old_categories = camel_ews_store_summary_get_categories (ews_store->summary); - - if (ews_utils_save_category_changes (old_categories, new_categories)) { - camel_ews_store_summary_set_categories (ews_store->summary, new_categories); - camel_ews_store_summary_save (ews_store->summary, NULL); - } - - g_hash_table_destroy (new_categories); - g_hash_table_destroy (old_categories); - } - } - - if (xpath_ctx) - xmlXPathFreeContext (xpath_ctx); - xmlFreeDoc (doc); -} diff --git a/src/camel/camel-ews-utils.c.sync-category-list b/src/camel/camel-ews-utils.c.sync-category-list deleted file mode 100644 index ec76dd7..0000000 --- a/src/camel/camel-ews-utils.c.sync-category-list +++ /dev/null @@ -1,1283 +0,0 @@ -/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */ -/* camel-ews-utils.c - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU 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. - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include - -#include -#include - -#include - -#include "server/camel-ews-settings.h" -#include "server/e-ews-camel-common.h" -#include "server/e-ews-item-change.h" -#include "server/e-ews-message.h" - -#include "camel-ews-utils.h" - -#define SUBFOLDER_DIR_NAME "subfolders" -#define SUBFOLDER_DIR_NAME_LEN 10 - -#define EWS_MAPI_MSGFLAG_RN_PENDING 0x100 - -CamelFolderInfo * -camel_ews_utils_build_folder_info (CamelEwsStore *store, - const gchar *fid) -{ - CamelEwsStoreSummary *ews_summary = store->summary; - CamelFolderInfo *fi; - gchar *folder_name; - - fi = camel_folder_info_new (); - fi->full_name = camel_ews_store_summary_get_folder_full_name ( - ews_summary, fid, NULL); - - if (!fi->full_name) { - camel_folder_info_free (fi); - g_warn_if_reached (); - - return NULL; - } - - folder_name = camel_ews_store_summary_get_folder_name (ews_summary, fid, NULL); - - fi->display_name = e_ews_folder_utils_unescape_name (folder_name); - fi->flags = camel_ews_store_summary_get_folder_flags (ews_summary, fid, NULL); - fi->unread = camel_ews_store_summary_get_folder_unread (ews_summary, fid, NULL); - fi->total = camel_ews_store_summary_get_folder_total (ews_summary, fid, NULL); - - g_free (folder_name); - - if (!(fi->flags & CAMEL_FOLDER_TYPE_MASK)) { - switch (camel_ews_store_summary_get_folder_type (ews_summary, fid, NULL)) { - case E_EWS_FOLDER_TYPE_CALENDAR: - fi->flags |= CAMEL_FOLDER_TYPE_EVENTS; - break; - case E_EWS_FOLDER_TYPE_CONTACTS: - fi->flags |= CAMEL_FOLDER_TYPE_CONTACTS; - break; - case E_EWS_FOLDER_TYPE_TASKS: - fi->flags |= CAMEL_FOLDER_TYPE_TASKS; - break; - case E_EWS_FOLDER_TYPE_MEMOS: - fi->flags |= CAMEL_FOLDER_TYPE_MEMOS; - break; - default: - break; - } - } - - return fi; -} - -static void -add_folder_to_summary (CamelEwsStore *store, - EEwsFolder *folder); - -static void -sync_deleted_folders (CamelEwsStore *store, - GSList *deleted_folders) -{ - CamelEwsStoreSummary *ews_summary = store->summary; - GSList *l; - - for (l = deleted_folders; l != NULL; l = g_slist_next (l)) { - const gchar *fid = l->data; - EEwsFolderType ftype; - CamelFolderInfo *fi; - GError *error = NULL; - - if (!camel_ews_store_summary_has_folder (ews_summary, fid)) - continue; - - ftype = camel_ews_store_summary_get_folder_type ( - ews_summary, fid, NULL); - if (ftype == E_EWS_FOLDER_TYPE_MAILBOX) { - fi = camel_ews_utils_build_folder_info (store, fid); - - if (!camel_ews_store_summary_remove_folder (ews_summary, fid, &error)) { - if (error != NULL) { - g_warning ("%s: %s", G_STRFUNC, error->message); - g_clear_error (&error); - } - continue; - } - - camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (store), fi); - camel_store_folder_deleted (CAMEL_STORE (store), fi); - } - } -} - -static gboolean -ews_utils_rename_folder (CamelEwsStore *store, - EEwsFolderType ftype, - const gchar *fid, - const gchar *changekey, - const gchar *pfid, - const gchar *display_name, - const gchar *old_fname, - GError **error) -{ - CamelEwsStoreSummary *ews_summary = store->summary; - CamelFolderInfo *fi; - - camel_ews_store_summary_set_change_key (ews_summary, fid, changekey); - if (display_name) - camel_ews_store_summary_set_folder_name ( - ews_summary, fid, display_name); - if (pfid) - camel_ews_store_summary_set_parent_folder_id ( - ews_summary, fid, pfid); - - if (ftype == E_EWS_FOLDER_TYPE_MAILBOX) { - fi = camel_ews_utils_build_folder_info (store, fid); - camel_store_folder_renamed (CAMEL_STORE (store), old_fname, fi); - } - - return TRUE; -} - -static void -sync_updated_folders (CamelEwsStore *store, - GSList *updated_folders) -{ - CamelEwsStoreSummary *ews_summary = store->summary; - GSList *l; - - for (l = updated_folders; l != NULL; l = g_slist_next (l)) { - EEwsFolder *ews_folder = (EEwsFolder *) l->data; - EEwsFolderType ftype; - gchar *folder_name; - gchar *display_name; - const EwsFolderId *fid, *pfid; - - ftype = e_ews_folder_get_folder_type (ews_folder); - if (ftype != E_EWS_FOLDER_TYPE_MAILBOX) - continue; - - fid = e_ews_folder_get_id (ews_folder); - folder_name = camel_ews_store_summary_get_folder_full_name ( - ews_summary, fid->id, NULL); - - if (!folder_name) { - /* in case the folder is not in the local store summary, - just add it as a new folder */ - add_folder_to_summary (store, ews_folder); - continue; - } - - pfid = e_ews_folder_get_parent_id (ews_folder); - display_name = g_strdup (e_ews_folder_get_escaped_name (ews_folder)); - - /* If the folder is moved or renamed (which are separate - * operations in Exchange, unfortunately, then the name - * or parent folder will change. Handle both... */ - if (pfid || display_name) { - GError *error = NULL; - gchar *new_fname = NULL; - - if (pfid) { - gchar *pfname; - - /* If the display name wasn't changed, its basename is still - * the same as it was before... */ - if (!display_name) - display_name = camel_ews_store_summary_get_folder_name ( - ews_summary, fid->id, NULL); - if (!display_name) - goto done; - - pfname = camel_ews_store_summary_get_folder_full_name ( - ews_summary, pfid->id, NULL); - - /* If the lookup failed, it'll be because the new parent folder - * is the message folder root. */ - if (pfname) { - new_fname = g_strconcat ( - pfname, "/", display_name, NULL); - g_free (pfname); - } else - new_fname = g_strdup (display_name); - } else { - /* Parent folder not changed; just basename */ - const gchar *last_slash; - - /* Append new display_name to old parent directory name... */ - last_slash = g_strrstr (folder_name, "/"); - if (last_slash) - new_fname = g_strdup_printf ( - "%.*s/%s", - (gint)(last_slash - folder_name), - folder_name, display_name); - else /* ...unless it was a child of the root folder */ - new_fname = g_strdup (display_name); - } - - if (strcmp (new_fname, folder_name)) - ews_utils_rename_folder ( - store, ftype, - fid->id, fid->change_key, - pfid ? pfid->id : NULL, - display_name, folder_name, &error); - g_free (new_fname); - g_clear_error (&error); - } - done: - g_free (folder_name); - g_free (display_name); - } -} - -/* FIXME get the real folder ids of the system folders using - * by fetching them using distinguished folder ids once */ -static void -add_folder_to_summary (CamelEwsStore *store, - EEwsFolder *folder) -{ - CamelEwsStoreSummary *ews_summary = store->summary; - const EwsFolderId *pfid, *fid; - const gchar *dname; - gint64 unread, total; - EEwsFolderType ftype; - - fid = e_ews_folder_get_id (folder); - pfid = e_ews_folder_get_parent_id (folder); - dname = e_ews_folder_get_escaped_name (folder); - total = e_ews_folder_get_total_count (folder); - unread = e_ews_folder_get_unread_count (folder); - ftype = e_ews_folder_get_folder_type (folder); - - camel_ews_store_summary_new_folder ( - ews_summary, fid->id, - pfid ? pfid->id : NULL, fid->change_key, - dname, ftype, 0, total, - e_ews_folder_get_foreign (folder), - FALSE); - camel_ews_store_summary_set_folder_unread ( - ews_summary, fid->id, unread); -} - -static void -sync_created_folders (CamelEwsStore *ews_store, - GSList *created_folders, - GSList **created_folder_ids) -{ - GSList *l; - - for (l = created_folders; l != NULL; l = g_slist_next (l)) { - EEwsFolder *folder = (EEwsFolder *) l->data; - EEwsFolderType ftype; - CamelFolderInfo *fi; - const EwsFolderId *fid; - - ftype = e_ews_folder_get_folder_type (folder); - if (ftype != E_EWS_FOLDER_TYPE_MAILBOX) - continue; - - fid = e_ews_folder_get_id (folder); - - /* FIXME: Sort folders so that a child is always added *after* - * its parent. But since the old code was already completely - * broken and would just go into an endless loop if the server - * didn't return the folders in the 'right' order for that, - * let's worry about that in a later commit. */ - add_folder_to_summary (ews_store, folder); - - if (created_folder_ids) { - *created_folder_ids = g_slist_append (*created_folder_ids, g_strdup (fid->id)); - } else { - fi = camel_ews_utils_build_folder_info ( - ews_store, fid->id); - camel_store_folder_created ( - CAMEL_STORE (ews_store), fi); - camel_subscribable_folder_subscribed ( - CAMEL_SUBSCRIBABLE (ews_store), fi); - camel_folder_info_free (fi); - } - } -} - -void -ews_utils_sync_folders (CamelEwsStore *ews_store, - GSList *created_folders, - GSList *deleted_folders, - GSList *updated_folders, - GSList **created_folder_ids) -{ - GError *error = NULL; - - sync_deleted_folders (ews_store, deleted_folders); - sync_updated_folders (ews_store, updated_folders); - sync_created_folders (ews_store, created_folders, created_folder_ids); - - camel_ews_store_summary_save (ews_store->summary, &error); - if (error != NULL) { - g_print ( - "Error while saving store summary %s \n", - error->message); - g_clear_error (&error); - } -} - -void -camel_ews_utils_sync_deleted_items (CamelEwsFolder *ews_folder, - GSList *items_deleted, - CamelFolderChangeInfo *change_info) -{ - CamelStore *store; - CamelFolder *folder; - const gchar *full_name; - CamelEwsStore *ews_store; - GSList *l; - GList *items_deleted_list = NULL; - - folder = CAMEL_FOLDER (ews_folder); - full_name = camel_folder_get_full_name (folder); - - store = camel_folder_get_parent_store (folder); - ews_store = CAMEL_EWS_STORE (store); - - for (l = items_deleted; l != NULL; l = g_slist_next (l)) { - const gchar *id = l->data; - - items_deleted_list = g_list_prepend ( - items_deleted_list, (gpointer) id); - - camel_folder_summary_remove_uid (camel_folder_get_folder_summary (folder), id); - camel_folder_change_info_remove_uid (change_info, id); - } - - items_deleted_list = g_list_reverse (items_deleted_list); - camel_db_delete_uids ( - camel_store_get_db (CAMEL_STORE (ews_store)), - full_name, items_deleted_list, NULL); - g_list_free (items_deleted_list); - - g_slist_foreach (items_deleted, (GFunc) g_free, NULL); - g_slist_free (items_deleted); -} - -static const gchar * -ews_utils_rename_label (const gchar *cat, - gboolean from_cat) -{ - gint i; - - /* this is a mapping from Exchange/Outlook categories to - * evolution labels based on the standard colours */ - const gchar *labels[] = { - "Red Category", "$Labelimportant", - "Orange Category", "$Labelwork", - "Green Category", "$Labelpersonal", - "Blue Category", "$Labeltodo", - "Purple Category", "$Labellater", - NULL, NULL - }; - - if (!cat || !*cat) - return ""; - - for (i = 0; labels[i]; i += 2) { - if (from_cat) { - if (!g_ascii_strcasecmp (cat, labels[i])) - return labels[i + 1]; - } else { - if (!g_ascii_strcasecmp (cat, labels[i + 1])) - return labels[i]; - } - } - return cat; -} - -static gboolean -ews_utils_is_system_user_flag (const gchar *name) -{ - if (!name) - return FALSE; - - return g_str_equal (name, "receipt-handled") || - g_str_equal (name, "$has-cal"); -} - -/* free with g_slist_free_full (flags, g_free); - the lists' members are values for the String xml element. */ -GSList * -ews_utils_gather_server_user_flags (ESoapMessage *msg, - CamelMessageInfo *mi) -{ - GSList *out_user_flags = NULL; - const CamelNamedFlags *user_flags; - guint ii, len; - - camel_message_info_property_lock (mi); - - user_flags = camel_message_info_get_user_flags (mi); - len = camel_named_flags_get_length (user_flags); - - /* transfer camel flags to become the categories as an XML - * array of strings */ - for (ii = 0; ii < len; ii++) { - const gchar *n = ews_utils_rename_label (camel_named_flags_get (user_flags, ii), FALSE); - if (*n == '\0') - continue; - - /* Skip evolution-defined flags which are not supposed to - be categories on an Exchange server */ - if (ews_utils_is_system_user_flag (n)) - continue; - - if (strchr (n, '_')) { - GString *str = g_string_sized_new (strlen (n)); - - while (*n) { - if (*n == '_') { - if (n[1] == '_') - g_string_append_c (str, '_'); - else - g_string_append_c (str, ' '); - } else { - g_string_append_c (str, *n); - } - - n++; - } - - out_user_flags = g_slist_prepend (out_user_flags, g_string_free (str, FALSE)); - } else { - out_user_flags = g_slist_prepend (out_user_flags, g_strdup (n)); - } - } - - camel_message_info_property_unlock (mi); - - return g_slist_reverse (out_user_flags); -} - -static void -ews_utils_merge_server_user_flags (EEwsItem *item, - CamelMessageInfo *mi) -{ - CamelFolderSummary *summary; - GSList *list = NULL; - const GSList *p; - const CamelNamedFlags *user_flags; - guint ii, len; - - summary = camel_message_info_ref_summary (mi); - if (summary) - camel_folder_summary_lock (summary); - camel_message_info_property_lock (mi); - camel_message_info_freeze_notifications (mi); - - user_flags = camel_message_info_get_user_flags (mi); - len = camel_named_flags_get_length (user_flags); - - /* transfer camel flags to a list */ - for (ii = 0; ii < len; ii++) { - const gchar *name = camel_named_flags_get (user_flags, ii); - - if (!ews_utils_is_system_user_flag (name)) - list = g_slist_prepend (list, (gchar *) name); - } - - for (p = list; p; p = p->next) { - /* remove custom user flags */ - camel_message_info_set_user_flag (mi, p->data, FALSE); - } - - g_slist_free (list); - - /* now transfer over all the categories */ - for (p = e_ews_item_get_categories (item); p; p = p->next) { - const gchar *flag = ews_utils_rename_label (p->data, 1); - gchar *underscored = NULL; - - if (!flag || !*flag) - continue; - - if (strchr (flag, ' ')) { - GString *str; - - str = g_string_sized_new (strlen (flag) + 16); - - while (*flag) { - if (*flag == '_') - g_string_append_c (str, '_'); - - g_string_append_c (str, *flag == ' ' ? '_' : *flag); - - flag++; - } - - underscored = g_string_free (str, FALSE); - flag = underscored; - } - - camel_message_info_set_user_flag (mi, flag, TRUE); - - g_free (underscored); - } - - camel_message_info_thaw_notifications (mi); - camel_message_info_property_unlock (mi); - if (summary) - camel_folder_summary_unlock (summary); - g_clear_object (&summary); -} - -static guint32 -ews_utils_get_server_flags (EEwsItem *item) -{ - gboolean flag; - EwsImportance importance; - guint32 server_flags = 0, msg_flags; - - e_ews_item_is_read (item, &flag); - if (flag) - server_flags |= CAMEL_MESSAGE_SEEN; - else - server_flags &= ~CAMEL_MESSAGE_SEEN; - - e_ews_item_is_forwarded (item, &flag); - if (flag) - server_flags |= CAMEL_MESSAGE_FORWARDED; - else - server_flags &= ~CAMEL_MESSAGE_FORWARDED; - - e_ews_item_is_answered (item, &flag); - if (flag) - server_flags |= CAMEL_MESSAGE_ANSWERED; - else - server_flags &= ~CAMEL_MESSAGE_ANSWERED; - - importance = e_ews_item_get_importance (item); - if (importance == EWS_ITEM_HIGH) - server_flags |= CAMEL_MESSAGE_FLAGGED; - - msg_flags = e_ews_item_get_message_flags (item); - if ((msg_flags & EWS_MAPI_MSGFLAG_RN_PENDING) != 0) - server_flags |= CAMEL_EWS_MESSAGE_MSGFLAG_RN_PENDING; - - /* TODO Update replied flags */ - - return server_flags; -} - -static gchar * -form_email_string_from_mb (EEwsConnection *cnc, - const EwsMailbox *mb, - GCancellable *cancellable) -{ - if (mb) { - GString *str; - const gchar *email = NULL; - - if (g_strcmp0 (mb->routing_type, "EX") == 0) - email = e_ews_item_util_strip_ex_address (mb->email); - - str = g_string_new (""); - if (mb->name && mb->name[0]) { - g_string_append (str, mb->name); - g_string_append (str, " "); - } - - if (mb->email || email) { - g_string_append (str, "<"); - g_string_append (str, email ? email : mb->email); - g_string_append (str, ">"); - } - - return g_string_free (str, FALSE); - } else - return NULL; -} - -static gchar * -form_recipient_list (EEwsConnection *cnc, - const GSList *recipients, - GCancellable *cancellable) -{ - const GSList *l; - GString *str = NULL; - - if (!recipients) - return NULL; - - for (l = recipients; l != NULL; l = g_slist_next (l)) { - EwsMailbox *mb = (EwsMailbox *) l->data; - gchar *mb_str = form_email_string_from_mb (cnc, mb, cancellable); - - if (!str) - str = g_string_new (""); - else - str = g_string_append (str, ", "); - - str = g_string_append (str, mb_str); - - g_free (mb_str); - } - - return g_string_free (str, FALSE); -} - -static guint8 * -get_md5_digest (const guchar *str) -{ - guint8 *digest; - gsize length; - GChecksum *checksum; - - length = g_checksum_type_get_length (G_CHECKSUM_MD5); - digest = g_malloc0 (length); - - checksum = g_checksum_new (G_CHECKSUM_MD5); - g_checksum_update (checksum, str, -1); - g_checksum_get_digest (checksum, digest, &length); - g_checksum_free (checksum); - - return digest; -} - -static void -ews_set_threading_data (CamelMessageInfo *mi, - EEwsItem *item) -{ - const gchar *references_str, *inreplyto_str; - const gchar *message_id; - GSList *refs, *irt, *link; - guint8 *digest; - gchar *msgid; - CamelSummaryMessageID tmp_msgid; - GArray *references; - - /* set message id */ - message_id = e_ews_item_get_msg_id (item); - msgid = camel_header_msgid_decode (message_id); - if (msgid) { - digest = get_md5_digest ((const guchar *) msgid); - memcpy (tmp_msgid.id.hash, digest, sizeof (tmp_msgid.id.hash)); - g_free (digest); - g_free (msgid); - - camel_message_info_set_message_id (mi, tmp_msgid.id.id); - } - - /* Process References: header */ - references_str = e_ews_item_get_references (item); - refs = camel_header_references_decode (references_str); - - /* Prepend In-Reply-To: contents to References: for summary info */ - inreplyto_str = e_ews_item_get_in_replyto (item); - irt = camel_header_references_decode (inreplyto_str); - if (irt) { - refs = g_slist_concat (irt, refs); - } - if (!refs) - return; - - references = g_array_sized_new (FALSE, FALSE, sizeof (guint64), g_slist_length (refs)); - - for (link = refs; link; link = g_slist_next (link)) { - digest = get_md5_digest ((const guchar *) link->data); - memcpy (tmp_msgid.id.hash, digest, sizeof (tmp_msgid.id.hash)); - g_free (digest); - - g_array_append_val (references, tmp_msgid.id.id); - } - - g_slist_free_full (refs, g_free); - - camel_message_info_take_references (mi, references); -} - -static gboolean -camel_ews_utils_update_follow_up_flags (EEwsItem *item, - CamelMessageInfo *info) -{ - gboolean changed = FALSE, found; - time_t completed_tt, dueby_tt; - const gchar *followup_name; - gint flag_status; - - /* PidTagFlagStatus */ - found = FALSE; - flag_status = e_ews_item_get_extended_property_as_int (item, NULL, 0x1090, &found); - if (!found) - flag_status = 0; - - /* PidTagFlagCompleteTime */ - found = FALSE; - completed_tt = e_ews_item_get_extended_property_as_time (item, NULL, 0x1091, &found); - if (!found) - completed_tt = (time_t) 0; - - /* PidLidFlagRequest */ - found = FALSE; - followup_name = e_ews_item_get_extended_property_as_string (item, "Common", 0x8530, &found); - if (!found) - followup_name = NULL; - - /* PidLidTaskDueDate */ - found = FALSE; - dueby_tt = e_ews_item_get_extended_property_as_time (item, "Task", 0x8105, &found); - if (!found) - dueby_tt = (time_t) 0; - - if (flag_status == 1) { - /* complete */ - if (!camel_message_info_get_user_tag (info, "follow-up")) - changed = camel_message_info_set_user_tag (info, "follow-up", followup_name ? followup_name : "follow-up") || changed; - if (completed_tt != (time_t) 0) { - gchar *text = camel_header_format_date (completed_tt, 0); - changed = camel_message_info_set_user_tag (info, "completed-on", text) || changed; - g_free (text); - } else { - changed = camel_message_info_set_user_tag (info, "completed-on", NULL) || changed; - } - } else if (flag_status == 2) { - /* follow-up */ - changed = camel_message_info_set_user_tag (info, "follow-up", followup_name ? followup_name : "follow-up") || changed; - changed = camel_message_info_set_user_tag (info, "completed-on", NULL) || changed; - if (dueby_tt != (time_t) 0) { - gchar *text = camel_header_format_date (dueby_tt, 0); - changed = camel_message_info_set_user_tag (info, "due-by", text) || changed; - g_free (text); - } else { - changed = camel_message_info_set_user_tag (info, "due-by", NULL) || changed; - } - } else { - changed = camel_message_info_set_user_tag (info, "follow-up", NULL) || changed; - changed = camel_message_info_set_user_tag (info, "completed-on", NULL) || changed; - changed = camel_message_info_set_user_tag (info, "due-by", NULL) || changed; - } - - return changed; -} - -static gboolean -camel_ews_utils_update_read_receipt_flags (EEwsItem *item, - CamelMessageInfo *info, - guint32 server_flags, - gboolean requests_read_receipt) -{ - gboolean changed = FALSE; - - /* PidTagReadReceiptRequested */ - if ((requests_read_receipt || e_ews_item_get_extended_property_as_boolean (item, NULL, 0x0029, NULL)) && - (server_flags & CAMEL_EWS_MESSAGE_MSGFLAG_RN_PENDING) == 0) { - changed = camel_message_info_set_user_flag (info, "receipt-handled", TRUE) || changed; - } - - return changed; -} - -void -camel_ews_utils_sync_updated_items (CamelEwsFolder *ews_folder, - GSList *items_updated, - CamelFolderChangeInfo *change_info) -{ - CamelFolder *folder; - CamelFolderSummary *folder_summary; - GSList *l; - - folder = CAMEL_FOLDER (ews_folder); - folder_summary = camel_folder_get_folder_summary (folder); - - for (l = items_updated; l != NULL; l = g_slist_next (l)) { - EEwsItem *item = (EEwsItem *) l->data; - const EwsId *id; - CamelMessageInfo *mi; - - if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) { - g_object_unref (item); - continue; - } - - id = e_ews_item_get_id (item); - if (!id) { - g_warning ("%s: Missing ItemId for item type %d (subject:%s)", G_STRFUNC, e_ews_item_get_item_type (item), - e_ews_item_get_subject (item) ? e_ews_item_get_subject (item) : "???"); - g_object_unref (item); - continue; - } - - mi = camel_folder_summary_get (folder_summary, id->id); - if (mi) { - guint32 server_flags; - gboolean changed, was_changed; - - camel_message_info_freeze_notifications (mi); - was_changed = camel_message_info_get_folder_flagged (mi); - - server_flags = ews_utils_get_server_flags (item); - ews_utils_merge_server_user_flags (item, mi); - changed = camel_ews_update_message_info_flags (folder_summary, mi, server_flags, NULL); - changed = camel_ews_utils_update_follow_up_flags (item, mi) || changed; - changed = camel_ews_utils_update_read_receipt_flags (item, mi, server_flags, FALSE) || changed; - - if (changed) - camel_folder_change_info_change_uid (change_info, id->id); - - camel_ews_message_info_set_change_key (CAMEL_EWS_MESSAGE_INFO (mi), id->change_key); - if (!was_changed) { - /* do not save to the server what was just read, when did not change locally before */ - camel_message_info_set_folder_flagged (mi, FALSE); - } - - camel_message_info_thaw_notifications (mi); - g_clear_object (&mi); - g_object_unref (item); - continue; - } - - g_object_unref (item); - } - - g_slist_free (items_updated); -} - -CamelMessageInfo * /* (transfer full) */ -camel_ews_utils_item_to_message_info (CamelEwsFolder *ews_folder, - EEwsConnection *cnc, - EEwsItem *item, - GCancellable *cancellable) -{ - CamelFolderSummary *folder_summary; - CamelMessageInfo *mi = NULL; - const EwsId *id; - const EwsMailbox *from; - gchar *tmp; - EEwsItemType item_type; - const gchar *msg_headers; - gboolean has_attachments, found_property, message_requests_read_receipt = FALSE; - guint32 server_flags; - - g_return_val_if_fail (CAMEL_IS_EWS_FOLDER (ews_folder), NULL); - - if (!item || e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) - return NULL; - - id = e_ews_item_get_id (item); - if (!id) - return NULL; - - folder_summary = camel_folder_get_folder_summary (CAMEL_FOLDER (ews_folder)); - - /* PidTagTransportMessageHeaders */ - found_property = FALSE; - msg_headers = e_ews_item_get_extended_property_as_string (item, NULL, 0x007D, &found_property); - if (!found_property) - msg_headers = NULL; - - if (msg_headers && *msg_headers) { - CamelMimePart *part = camel_mime_part_new (); - CamelStream *stream; - CamelMimeParser *parser; - - stream = camel_stream_mem_new_with_buffer (msg_headers, strlen (msg_headers)); - parser = camel_mime_parser_new (); - camel_mime_parser_init_with_stream (parser, stream, NULL); - camel_mime_parser_scan_from (parser, FALSE); - g_object_unref (stream); - - if (camel_mime_part_construct_from_parser_sync (part, parser, NULL, NULL)) { - mi = camel_folder_summary_info_new_from_headers (folder_summary, camel_medium_get_headers (CAMEL_MEDIUM (part))); - if (camel_medium_get_header (CAMEL_MEDIUM (part), "Disposition-Notification-To")) - message_requests_read_receipt = TRUE; - } - - g_object_unref (parser); - g_object_unref (part); - } - - if (!mi) - mi = camel_message_info_new (folder_summary); - - camel_message_info_set_abort_notifications (mi, TRUE); - - item_type = e_ews_item_get_item_type (item); - if (item_type == E_EWS_ITEM_TYPE_EVENT || - item_type == E_EWS_ITEM_TYPE_MEETING_MESSAGE || - item_type == E_EWS_ITEM_TYPE_MEETING_REQUEST || - item_type == E_EWS_ITEM_TYPE_MEETING_RESPONSE || - item_type == E_EWS_ITEM_TYPE_MEETING_RESPONSE) - camel_message_info_set_user_flag (mi, "$has_cal", TRUE); - - camel_message_info_set_uid (mi, id->id); - camel_message_info_set_size (mi, e_ews_item_get_size (item)); - camel_message_info_set_subject (mi, e_ews_item_get_subject (item)); - camel_ews_message_info_set_item_type (CAMEL_EWS_MESSAGE_INFO (mi), item_type); - camel_ews_message_info_set_change_key (CAMEL_EWS_MESSAGE_INFO (mi), id->change_key); - - camel_message_info_set_date_sent (mi, e_ews_item_get_date_sent (item)); - camel_message_info_set_date_received (mi, e_ews_item_get_date_received (item)); - - from = e_ews_item_get_from (item); - if (!from) - from = e_ews_item_get_sender (item); - tmp = form_email_string_from_mb (cnc, from, cancellable); - camel_message_info_set_from (mi, tmp); - g_free (tmp); - - tmp = form_recipient_list (cnc, e_ews_item_get_to_recipients (item), cancellable); - camel_message_info_set_to (mi, tmp); - g_free (tmp); - - tmp = form_recipient_list (cnc, e_ews_item_get_cc_recipients (item), cancellable); - camel_message_info_set_cc (mi, tmp); - g_free (tmp); - - e_ews_item_has_attachments (item, &has_attachments); - if (has_attachments) - camel_message_info_set_flags (mi, CAMEL_MESSAGE_ATTACHMENTS, CAMEL_MESSAGE_ATTACHMENTS); - - ews_set_threading_data (mi, item); - server_flags = ews_utils_get_server_flags (item); - ews_utils_merge_server_user_flags (item, mi); - - camel_message_info_set_flags (mi, server_flags, server_flags); - camel_ews_message_info_set_server_flags (CAMEL_EWS_MESSAGE_INFO (mi), server_flags); - - camel_ews_utils_update_follow_up_flags (item, mi); - camel_ews_utils_update_read_receipt_flags (item, mi, server_flags, message_requests_read_receipt); - - camel_message_info_set_abort_notifications (mi, FALSE); - - return mi; -} - -void -camel_ews_utils_sync_created_items (CamelEwsFolder *ews_folder, - EEwsConnection *cnc, - GSList *items_created, - CamelFolderChangeInfo *change_info, - GCancellable *cancellable) -{ - CamelFolder *folder; - CamelFolderSummary *folder_summary; - GSList *l; - - if (!items_created) - return; - - folder = CAMEL_FOLDER (ews_folder); - folder_summary = camel_folder_get_folder_summary (folder); - - for (l = items_created; l != NULL; l = g_slist_next (l)) { - EEwsItem *item = (EEwsItem *) l->data; - CamelMessageInfo *mi; - const EwsId *id; - - if (!item) - continue; - - if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) { - g_object_unref (item); - continue; - } - - id = e_ews_item_get_id (item); - if (!id) { - g_warning ("%s: Missing ItemId for item type %d (subject:%s)", G_STRFUNC, e_ews_item_get_item_type (item), - e_ews_item_get_subject (item) ? e_ews_item_get_subject (item) : "???"); - g_object_unref (item); - continue; - } - - mi = camel_folder_summary_get (folder_summary, id->id); - if (mi) { - g_clear_object (&mi); - g_object_unref (item); - continue; - } - - mi = camel_ews_utils_item_to_message_info (ews_folder, cnc, item, cancellable); - if (!mi) { - g_warn_if_reached (); - g_object_unref (item); - continue; - } - - camel_folder_summary_add (folder_summary, mi, FALSE); - - /* camel_folder_summary_add() sets folder_flagged flag - * on the message info, but this is a fresh item downloaded - * from the server, thus unset it, to avoid resync up to the server - * on folder leave/store - */ - camel_message_info_set_folder_flagged (mi, FALSE); - - camel_folder_change_info_add_uid (change_info, id->id); - camel_folder_change_info_recent_uid (change_info, id->id); - - g_object_unref (mi); - g_object_unref (item); - } - - g_slist_free (items_created); -} - -gchar * -camel_ews_utils_get_host_name (CamelSettings *settings) -{ - CamelURL *url; - gchar *host = NULL, *hosturl; - - g_return_val_if_fail (settings != NULL, NULL); - - hosturl = camel_ews_settings_dup_hosturl (CAMEL_EWS_SETTINGS (settings)); - - url = camel_url_new (hosturl, NULL); - if (url) { - host = g_strdup (url->host); - camel_url_free (url); - } - - if (!host || !*host) { - g_free (host); - host = camel_network_settings_dup_host (CAMEL_NETWORK_SETTINGS (settings)); - } - - g_free (hosturl); - - return host; -} - -void -ews_utils_update_followup_flags (ESoapMessage *msg, - CamelMessageInfo *mi) -{ - const gchar *followup, *completed, *dueby; - time_t completed_tt = (time_t) 0 , dueby_tt = (time_t) 0; - - g_return_if_fail (msg != NULL); - g_return_if_fail (mi != NULL); - - followup = camel_message_info_get_user_tag (mi, "follow-up"); - completed = camel_message_info_get_user_tag (mi, "completed-on"); - dueby = camel_message_info_get_user_tag (mi, "due-by"); - - if (followup && !*followup) - followup = NULL; - - if (completed && *completed) - completed_tt = camel_header_decode_date (completed, NULL); - - if (dueby && *dueby) - dueby_tt = camel_header_decode_date (dueby, NULL); - - if (followup) { - time_t now_tt = time (NULL); - - /* PidTagFlagStatus */ - e_ews_message_add_set_item_field_extended_tag_int (msg, NULL, "Message", 0x1090, - completed_tt != (time_t) 0 ? 0x01 /* followupComplete */: 0x02 /* followupFlagged */); - - /* PidLidFlagRequest */ - e_ews_message_add_set_item_field_extended_distinguished_tag_string (msg, NULL, "Message", "Common", 0x8530, followup); - - /* PidTagToDoItemFlags */ - e_ews_message_add_set_item_field_extended_tag_int (msg, NULL, "Message", 0x0e2b, 1); - - if (completed_tt == (time_t) 0 && dueby_tt == (time_t) 0) { - /* PidLidTaskStatus */ - e_ews_message_add_set_item_field_extended_distinguished_tag_int (msg, NULL, "Message", "Task", 0x8101, 0); - - /* PidLidPercentComplete */ - e_ews_message_add_set_item_field_extended_distinguished_tag_double (msg, NULL, "Message", "Task", 0x8102, 0.0); - - /* PidLidTaskStartDate */ - e_ews_message_add_set_item_field_extended_distinguished_tag_time (msg, NULL, "Message", "Task", 0x8104, now_tt); - - /* PidLidTaskDueDate */ - e_ews_message_add_set_item_field_extended_distinguished_tag_time (msg, NULL, "Message", "Task", 0x8105, now_tt); - - /* PidLidTaskComplete */ - e_ews_message_add_set_item_field_extended_distinguished_tag_boolean (msg, NULL, "Message", "Task", 0x811c, FALSE); - } - } else { - /* PidTagFlagStatus */ - e_ews_message_add_delete_item_field_extended_tag (msg, 0x1090, E_EWS_MESSAGE_DATA_TYPE_INT); - - /* PidTagFlagCompleteTime */ - e_ews_message_add_delete_item_field_extended_tag (msg, 0x1091, E_EWS_MESSAGE_DATA_TYPE_TIME); - - /* PidTagToDoItemFlags */ - e_ews_message_add_delete_item_field_extended_tag (msg, 0x0e2b, E_EWS_MESSAGE_DATA_TYPE_INT); - - /* PidTagFollowupIcon */ - e_ews_message_add_delete_item_field_extended_tag (msg, 0x1095, E_EWS_MESSAGE_DATA_TYPE_INT); - - /* PidLidFlagRequest */ - e_ews_message_add_delete_item_field_extended_distinguished_tag (msg, "Common", 0x8530, E_EWS_MESSAGE_DATA_TYPE_STRING); - - /* PidLidFlagString */ - e_ews_message_add_delete_item_field_extended_distinguished_tag (msg, "Common", 0x85c0, E_EWS_MESSAGE_DATA_TYPE_INT); - - /* PidLidTaskStatus */ - e_ews_message_add_delete_item_field_extended_distinguished_tag (msg, "Task", 0x8101, E_EWS_MESSAGE_DATA_TYPE_INT); - - /* PidLidPercentComplete */ - e_ews_message_add_delete_item_field_extended_distinguished_tag (msg, "Task", 0x8102, E_EWS_MESSAGE_DATA_TYPE_DOUBLE); - - /* PidLidTaskStartDate */ - e_ews_message_add_delete_item_field_extended_distinguished_tag (msg, "Task", 0x8104, E_EWS_MESSAGE_DATA_TYPE_TIME); - - /* PidLidTaskDueDate */ - e_ews_message_add_delete_item_field_extended_distinguished_tag (msg, "Task", 0x8105, E_EWS_MESSAGE_DATA_TYPE_TIME); - - /* PidLidTaskDateCompleted */ - e_ews_message_add_delete_item_field_extended_distinguished_tag (msg, "Task", 0x810f, E_EWS_MESSAGE_DATA_TYPE_TIME); - - /* PidLidTaskComplete */ - e_ews_message_add_delete_item_field_extended_distinguished_tag (msg, "Task", 0x811c, E_EWS_MESSAGE_DATA_TYPE_BOOLEAN); - } - - if (followup && completed_tt != (time_t) 0) { - /* minute precision */ - completed_tt = completed_tt - (completed_tt % 60); - - /* PidTagFlagCompleteTime */ - e_ews_message_add_set_item_field_extended_tag_time (msg, NULL, "Message", 0x1091, completed_tt); - - /* PidTagFollowupIcon */ - e_ews_message_add_delete_item_field_extended_tag (msg, 0x1095, E_EWS_MESSAGE_DATA_TYPE_INT); - - /* PidLidTaskDateCompleted */ - e_ews_message_add_set_item_field_extended_distinguished_tag_time (msg, NULL, "Message", "Task", 0x810f, completed_tt); - - /* PidLidTaskStatus */ - e_ews_message_add_set_item_field_extended_distinguished_tag_int (msg, NULL, "Message", "Task", 0x8101, 2); - - /* PidLidPercentComplete */ - e_ews_message_add_set_item_field_extended_distinguished_tag_double (msg, NULL, "Message", "Task", 0x8102, 1.0); - - /* PidLidTaskComplete */ - e_ews_message_add_set_item_field_extended_distinguished_tag_boolean (msg, NULL, "Message", "Task", 0x811c, TRUE); - } - - if (followup && dueby_tt != (time_t) 0 && completed_tt == (time_t) 0) { - time_t now_tt = time (NULL); - - if (now_tt > dueby_tt) - now_tt = dueby_tt - 1; - - /* PidLidTaskStatus */ - e_ews_message_add_set_item_field_extended_distinguished_tag_int (msg, NULL, "Message", "Task", 0x8101, 0); - - /* PidLidPercentComplete */ - e_ews_message_add_set_item_field_extended_distinguished_tag_double (msg, NULL, "Message", "Task", 0x8102, 0.0); - - /* PidLidTaskStartDate */ - e_ews_message_add_set_item_field_extended_distinguished_tag_time (msg, NULL, "Message", "Task", 0x8104, now_tt); - - /* PidLidTaskDueDate */ - e_ews_message_add_set_item_field_extended_distinguished_tag_time (msg, NULL, "Message", "Task", 0x8105, dueby_tt); - - /* PidLidTaskComplete */ - e_ews_message_add_set_item_field_extended_distinguished_tag_boolean (msg, NULL, "Message", "Task", 0x811c, FALSE); - } -} -gboolean -camel_ews_utils_delete_folders_from_summary_recursive (CamelEwsStore *ews_store, - CamelFolderInfo *folder_info, - gboolean send_signals, - GError **error) -{ - gboolean success = TRUE; - - while (folder_info != NULL) { - gchar *fid; - - if (folder_info->child != NULL) { - success = camel_ews_utils_delete_folders_from_summary_recursive ( - ews_store, folder_info->child, send_signals, error); - - if (!success) - break; - } - - fid = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, folder_info->full_name); - success = camel_ews_store_summary_remove_folder (ews_store->summary, fid, error); - g_free (fid); - - if (!success) - break; - - if (send_signals) { - camel_subscribable_folder_unsubscribed (CAMEL_SUBSCRIBABLE (ews_store), folder_info); - camel_store_folder_deleted (CAMEL_STORE (ews_store), folder_info); - } - - folder_info = folder_info->next; - } - - return success; -} - -/* Unref with g_object_unref() when done with it */ -ESource * -camel_ews_utils_ref_corresponding_source (CamelService *service, - GCancellable *cancellable) -{ - ESourceRegistry *registry = NULL; - CamelSession *session; - ESource *source = NULL; - - g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL); - - session = camel_service_ref_session (service); - if (E_IS_MAIL_SESSION (session)) { - registry = e_mail_session_get_registry (E_MAIL_SESSION (session)); - if (registry) - g_object_ref (registry); - } - - g_clear_object (&session); - - if (!registry) - registry = e_source_registry_new_sync (cancellable, NULL); - - if (registry) { - source = e_source_registry_ref_source (registry, camel_service_get_uid (service)); - - while (source && e_source_get_parent (source) && - !e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION)) { - ESource *parent; - - parent = e_source_registry_ref_source (registry, e_source_get_parent (source)); - if (!parent) - break; - - g_clear_object (&source); - source = parent; - } - } - - g_clear_object (®istry); - - return source; -} diff --git a/src/camel/camel-ews-utils.h b/src/camel/camel-ews-utils.h index dcac9ad..7234539 100644 --- a/src/camel/camel-ews-utils.h +++ b/src/camel/camel-ews-utils.h @@ -94,10 +94,6 @@ CamelMessageInfo * /* (transfer full) */ EEwsConnection *cnc, EEwsItem *item, GCancellable *cancellable); -void camel_ews_utils_merge_category_list - (CamelEwsStore *ews_store, - const guchar *xml_data, - gsize xml_data_len); G_END_DECLS diff --git a/src/camel/camel-ews-utils.h.sync-category-list b/src/camel/camel-ews-utils.h.sync-category-list deleted file mode 100644 index 7234539..0000000 --- a/src/camel/camel-ews-utils.h.sync-category-list +++ /dev/null @@ -1,100 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - * This library 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef CAMEL_EWS_UTILS_H -#define CAMEL_EWS_UTILS_H - -#include - -#include "server/e-ews-connection.h" - -#include "camel-ews-store.h" -#include "camel-ews-folder.h" - -/*Headers for send options*/ -#define X_SEND_OPTIONS "X-ews-send-options" -/*General Options*/ -#define X_SEND_OPT_PRIORITY "X-ews-send-opt-priority" -#define X_SEND_OPT_SECURITY "X-ews-send-opt-security" -#define X_REPLY_CONVENIENT "X-reply-convenient" -#define X_REPLY_WITHIN "X-reply-within" -#define X_EXPIRE_AFTER "X-expire-after" -#define X_DELAY_UNTIL "X-delay-until" - -/*Status Tracking Options*/ -#define X_TRACK_WHEN "X-track-when" -#define X_AUTODELETE "X-auto-delete" -#define X_RETURN_NOTIFY_OPEN "X-return-notify-open" -#define X_RETURN_NOTIFY_DELETE "X-return-notify-delete" - -/* Folder types for source */ -#define RECEIVED "Mailbox" -#define SENT "Sent Items" -#define DRAFT "" -#define PERSONAL "Cabinet" - -G_BEGIN_DECLS - -void ews_utils_sync_folders (CamelEwsStore *ews_store, - GSList *created_folders, - GSList *deleted_folders, - GSList *updated_folders, - GSList **created_folder_ids); -CamelFolderInfo * - camel_ews_utils_build_folder_info - (CamelEwsStore *store, - const gchar *fid); -void camel_ews_utils_sync_deleted_items - (CamelEwsFolder *ews_folder, - GSList *items_deleted, - CamelFolderChangeInfo *change_info); -void camel_ews_utils_sync_created_items - (CamelEwsFolder *ews_folder, - EEwsConnection *cnc, - GSList *items_created, - CamelFolderChangeInfo *change_info, - GCancellable *cancellable); -void camel_ews_utils_sync_updated_items - (CamelEwsFolder *ews_folder, - GSList *items_updated, - CamelFolderChangeInfo *change_info); -GSList * ews_utils_gather_server_user_flags - (ESoapMessage *msg, - CamelMessageInfo *mi); -void ews_utils_update_followup_flags (ESoapMessage *msg, - CamelMessageInfo *mi); -gchar * camel_ews_utils_get_host_name (CamelSettings *settings); -gboolean camel_ews_utils_delete_folders_from_summary_recursive - (CamelEwsStore *ews_store, - CamelFolderInfo *folder_info, - gboolean send_signals, - GError **error); -ESource * camel_ews_utils_ref_corresponding_source - (CamelService *service, - GCancellable *cancellable); -CamelMessageInfo * /* (transfer full) */ - camel_ews_utils_item_to_message_info - (CamelEwsFolder *ews_folder, - EEwsConnection *cnc, - EEwsItem *item, - GCancellable *cancellable); - -G_END_DECLS - -#endif diff --git a/src/collection/e-ews-backend.c b/src/collection/e-ews-backend.c index 74cea1d..651694b 100644 --- a/src/collection/e-ews-backend.c +++ b/src/collection/e-ews-backend.c @@ -48,7 +48,7 @@ struct _EEwsBackendPrivate { gboolean need_update_folders; - gulong source_changed_id; + gulong notify_online_id; }; struct _SyncFoldersClosure { @@ -647,21 +647,15 @@ static void ews_backend_dispose (GObject *object) { EEwsBackendPrivate *priv; - ESource *source; priv = E_EWS_BACKEND_GET_PRIVATE (object); - source = e_backend_get_source (E_BACKEND (object)); - if (source && priv->source_changed_id) { - g_signal_handler_disconnect (source, priv->source_changed_id); - priv->source_changed_id = 0; - } - g_hash_table_remove_all (priv->folders); - g_mutex_lock (&priv->connection_lock); - g_clear_object (&priv->connection); - g_mutex_unlock (&priv->connection_lock); + if (priv->connection != NULL) { + g_object_unref (priv->connection); + priv->connection = NULL; + } /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (e_ews_backend_parent_class)->dispose (object); @@ -727,15 +721,6 @@ ews_backend_constructed (GObject *object) /* Reset the connectable, it steals data from Authentication extension, where is written incorrect address */ e_backend_set_connectable (backend, NULL); - - /* Eventually unset temporary SSL trust, but only once, when the process started. - It might bee too often anywhere lease (like in the authenticate callback) */ - if (e_source_has_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND)) { - ESourceWebdav *webdav_extension; - - webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND); - e_source_webdav_unset_temporary_ssl_trust (webdav_extension); - } } static void @@ -785,8 +770,12 @@ ews_backend_populate (ECollectionBackend *backend) ews_backend->priv->need_update_folders = TRUE; - if (!ews_backend->priv->source_changed_id) { - ews_backend->priv->source_changed_id = g_signal_connect ( + if (!ews_backend->priv->notify_online_id) { + ews_backend->priv->notify_online_id = g_signal_connect ( + backend, "notify::online", + G_CALLBACK (ews_backend_populate), NULL); + + g_signal_connect ( source, "changed", G_CALLBACK (ews_backend_source_changed_cb), ews_backend); } @@ -939,7 +928,7 @@ ews_backend_create_resource_sync (ECollectionBackend *backend, } if (!success) { - connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (backend), NULL, NULL, NULL, cancellable, error); + connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (backend), NULL, cancellable, error); if (connection == NULL) return FALSE; @@ -1046,7 +1035,7 @@ ews_backend_delete_resource_sync (ECollectionBackend *backend, const gchar *extension_name; gboolean success = FALSE; - connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (backend), NULL, NULL, NULL, cancellable, error); + connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (backend), NULL, cancellable, error); if (connection == NULL) return FALSE; @@ -1151,7 +1140,7 @@ ews_backend_authenticate_sync (EBackend *backend, ews_backend->priv->credentials = e_named_parameters_new_clone (credentials); g_mutex_unlock (&ews_backend->priv->connection_lock); - connection = e_ews_backend_ref_connection_sync (ews_backend, &result, out_certificate_pem, out_certificate_errors, cancellable, error); + connection = e_ews_backend_ref_connection_sync (ews_backend, &result, cancellable, error); g_clear_object (&connection); if (result == E_SOURCE_AUTHENTICATION_ACCEPTED) { @@ -1232,7 +1221,7 @@ ews_backend_ref_connection_thread (GSimpleAsyncResult *simple, EEwsConnection *connection; GError *error = NULL; - connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (object), NULL, NULL, NULL, cancellable, &error); + connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (object), NULL, cancellable, &error); /* Sanity check. */ g_return_if_fail ( @@ -1250,8 +1239,6 @@ ews_backend_ref_connection_thread (GSimpleAsyncResult *simple, EEwsConnection * e_ews_backend_ref_connection_sync (EEwsBackend *backend, ESourceAuthenticationResult *result, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, GCancellable *cancellable, GError **error) { @@ -1283,8 +1270,7 @@ e_ews_backend_ref_connection_sync (EEwsBackend *backend, connection, "proxy-resolver", G_BINDING_SYNC_CREATE); - local_result = e_ews_connection_try_credentials_sync (connection, backend->priv->credentials, NULL, - out_certificate_pem, out_certificate_errors, cancellable, error); + local_result = e_ews_connection_try_credentials_sync (connection, backend->priv->credentials, cancellable, error); if (result) *result = local_result; @@ -1425,7 +1411,7 @@ e_ews_backend_sync_folders_sync (EEwsBackend *backend, return TRUE; } - connection = e_ews_backend_ref_connection_sync (backend, NULL, NULL, NULL, cancellable, error); + connection = e_ews_backend_ref_connection_sync (backend, NULL, cancellable, error); if (connection == NULL) { backend->priv->need_update_folders = TRUE; diff --git a/src/collection/e-ews-backend.c.cve-2019-3890 b/src/collection/e-ews-backend.c.cve-2019-3890 deleted file mode 100644 index d9a973a..0000000 --- a/src/collection/e-ews-backend.c.cve-2019-3890 +++ /dev/null @@ -1,1529 +0,0 @@ -/* - * e-ews-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 - * - */ - -#include "evolution-ews-config.h" - -#include "e-ews-backend.h" - -#include - -#include "server/e-ews-connection-utils.h" -#include "server/e-source-ews-folder.h" - -#define E_EWS_BACKEND_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_EWS_BACKEND, EEwsBackendPrivate)) - -typedef struct _SyncFoldersClosure SyncFoldersClosure; - -struct _EEwsBackendPrivate { - /* Folder ID -> ESource */ - GHashTable *folders; - GMutex folders_lock; - - ESource *gal_source; - gchar *oal_selected; - - gchar *sync_state; - GMutex sync_state_lock; - - ENamedParameters *credentials; - EEwsConnection *connection; - GMutex connection_lock; - - gboolean need_update_folders; - - gulong source_changed_id; -}; - -struct _SyncFoldersClosure { - EEwsBackend *backend; - GSList *folders_created; - GSList *folders_deleted; - GSList *folders_updated; -}; - -G_DEFINE_DYNAMIC_TYPE ( - EEwsBackend, - e_ews_backend, - E_TYPE_COLLECTION_BACKEND) - -static void -sync_folders_closure_free (SyncFoldersClosure *closure) -{ - g_object_unref (closure->backend); - - /* List of EEwsFolder instances. */ - g_slist_free_full (closure->folders_created, g_object_unref); - - /* List of folder ID strings. */ - g_slist_free_full (closure->folders_deleted, g_free); - - /* List of EEwsFolder instances. */ - g_slist_free_full (closure->folders_updated, g_object_unref); - - g_slice_free (SyncFoldersClosure, closure); -} - -static gboolean -ews_backend_folders_contains (EEwsBackend *backend, - const gchar *folder_id) -{ - gboolean contains; - - g_return_val_if_fail (folder_id != NULL, FALSE); - - g_mutex_lock (&backend->priv->folders_lock); - - contains = g_hash_table_contains (backend->priv->folders, folder_id); - - g_mutex_unlock (&backend->priv->folders_lock); - - return contains; -} - -static void -ews_backend_folders_insert (EEwsBackend *backend, - const gchar *folder_id, - ESource *source) -{ - g_return_if_fail (folder_id != NULL); - g_return_if_fail (E_IS_SOURCE (source)); - - g_mutex_lock (&backend->priv->folders_lock); - - g_hash_table_insert ( - backend->priv->folders, - g_strdup (folder_id), - g_object_ref (source)); - - g_mutex_unlock (&backend->priv->folders_lock); -} - -static ESource * -ews_backend_folders_lookup (EEwsBackend *backend, - const gchar *folder_id) -{ - ESource *source; - - g_return_val_if_fail (folder_id != NULL, NULL); - - g_mutex_lock (&backend->priv->folders_lock); - - source = g_hash_table_lookup (backend->priv->folders, folder_id); - - if (source != NULL) - g_object_ref (source); - - g_mutex_unlock (&backend->priv->folders_lock); - - return source; -} - -static gboolean -ews_backend_folders_remove (EEwsBackend *backend, - const gchar *folder_id) -{ - gboolean removed; - - g_return_val_if_fail (folder_id != NULL, FALSE); - - g_mutex_lock (&backend->priv->folders_lock); - - removed = g_hash_table_remove (backend->priv->folders, folder_id); - - g_mutex_unlock (&backend->priv->folders_lock); - - return removed; -} - -static CamelEwsSettings * -ews_backend_get_settings (EEwsBackend *backend) -{ - ESource *source; - ESourceCamel *extension; - CamelSettings *settings; - const gchar *extension_name; - - source = e_backend_get_source (E_BACKEND (backend)); - extension_name = e_source_camel_get_extension_name ("ews"); - extension = e_source_get_extension (source, extension_name); - settings = e_source_camel_get_settings (extension); - - return CAMEL_EWS_SETTINGS (settings); -} - -static void -ews_backend_update_enabled (ESource *data_source, - ESource *collection_source) -{ - ESourceCollection *collection_extension = NULL; - gboolean part_enabled = TRUE; - - g_return_if_fail (E_IS_SOURCE (data_source)); - - if (!collection_source || !e_source_get_enabled (collection_source)) { - e_source_set_enabled (data_source, FALSE); - return; - } - - if (e_source_has_extension (collection_source, E_SOURCE_EXTENSION_COLLECTION)) - collection_extension = e_source_get_extension (collection_source, E_SOURCE_EXTENSION_COLLECTION); - - if (e_source_has_extension (data_source, E_SOURCE_EXTENSION_CALENDAR) || - e_source_has_extension (data_source, E_SOURCE_EXTENSION_TASK_LIST) || - e_source_has_extension (data_source, E_SOURCE_EXTENSION_MEMO_LIST)) { - part_enabled = !collection_extension || e_source_collection_get_calendar_enabled (collection_extension); - } else if (e_source_has_extension (data_source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) { - part_enabled = !collection_extension || e_source_collection_get_contacts_enabled (collection_extension); - } else if (e_source_has_extension (data_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT) || - e_source_has_extension (data_source, E_SOURCE_EXTENSION_MAIL_IDENTITY) || - e_source_has_extension (data_source, E_SOURCE_EXTENSION_MAIL_TRANSPORT)) { - part_enabled = !collection_extension || e_source_collection_get_mail_enabled (collection_extension); - } - - e_source_set_enabled (data_source, part_enabled); -} - -static void -ews_backend_sync_authentication (EEwsBackend *ews_backend, - ESource *child_source) -{ - ESourceAuthentication *coll_authentication_extension, *child_authentication_extension; - ESource *collection_source; - - g_return_if_fail (E_IS_EWS_BACKEND (ews_backend)); - g_return_if_fail (E_IS_SOURCE (child_source)); - - collection_source = e_backend_get_source (E_BACKEND (ews_backend)); - - coll_authentication_extension = e_source_get_extension (collection_source, E_SOURCE_EXTENSION_AUTHENTICATION); - child_authentication_extension = e_source_get_extension (child_source, E_SOURCE_EXTENSION_AUTHENTICATION); - - e_source_authentication_set_host (child_authentication_extension, - e_source_authentication_get_host (coll_authentication_extension)); - - e_source_authentication_set_user (child_authentication_extension, - e_source_authentication_get_user (coll_authentication_extension)); -} - -static gboolean -ews_backend_is_uuid_like_name (const gchar *name) -{ - const gchar *uuid_mask = "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"; - const gchar *ptr, *msk_ptr; - gint len; - - if (!name || *name != '{') - return FALSE; - - len = strlen (name); - if (name[len - 1] != '}' || len != strlen (uuid_mask)) - return FALSE; - - for (ptr = name, msk_ptr = uuid_mask; *ptr && *msk_ptr; ptr++, msk_ptr++) { - if (*msk_ptr == 'X') { - if (!g_ascii_isxdigit (*ptr)) - break; - } else if (*msk_ptr != *ptr) { - break; - } - } - - return *msk_ptr == *ptr && !*msk_ptr; -} - -static ESource * -ews_backend_new_child (EEwsBackend *backend, - EEwsFolder *folder) -{ - ECollectionBackend *collection_backend; - ESourceExtension *extension; - ESource *source; - const EwsFolderId *fid; - const gchar *display_name; - const gchar *extension_name; - - fid = e_ews_folder_get_id (folder); - - g_return_val_if_fail (fid != NULL, NULL); - - display_name = e_ews_folder_get_name (folder); - - if (e_ews_folder_get_folder_type (folder) == E_EWS_FOLDER_TYPE_CONTACTS && - ews_backend_is_uuid_like_name (display_name)) { - /* Ignore address books with UUID-like name */ - return NULL; - } - - collection_backend = E_COLLECTION_BACKEND (backend); - source = e_collection_backend_new_child (collection_backend, fid->id); - - e_source_set_display_name (source, display_name); - - switch (e_ews_folder_get_folder_type (folder)) { - case E_EWS_FOLDER_TYPE_CALENDAR: - extension_name = E_SOURCE_EXTENSION_CALENDAR; - break; - case E_EWS_FOLDER_TYPE_TASKS: - extension_name = E_SOURCE_EXTENSION_TASK_LIST; - break; - case E_EWS_FOLDER_TYPE_MEMOS: - extension_name = E_SOURCE_EXTENSION_MEMO_LIST; - break; - case E_EWS_FOLDER_TYPE_CONTACTS: - extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK; - break; - default: - g_object_unref (source); - g_return_val_if_reached (NULL); - } - extension = e_source_get_extension (source, extension_name); - e_source_backend_set_backend_name ( - E_SOURCE_BACKEND (extension), "ews"); - ews_backend_sync_authentication (backend, source); - ews_backend_update_enabled (source, e_backend_get_source (E_BACKEND (backend))); - - if (e_ews_folder_get_folder_type (folder) != E_EWS_FOLDER_TYPE_CONTACTS && - !e_source_has_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER) && - !e_source_has_extension (source, E_SOURCE_EXTENSION_ALARMS)) { - /* a completely new ESource, do not notify with too old reminders */ - ESourceAlarms *alarms; - gchar *today; - GTimeVal today_tv; - GDate dt; - - g_date_clear (&dt, 1); - g_get_current_time (&today_tv); - g_date_set_time_val (&dt, &today_tv); - - /* midnight UTC */ - today = g_strdup_printf ("%04d-%02d-%02dT00:00:00Z", g_date_get_year (&dt), g_date_get_month (&dt), g_date_get_day (&dt)); - - alarms = e_source_get_extension (source, E_SOURCE_EXTENSION_ALARMS); - e_source_alarms_set_last_notified (alarms, today); - - g_free (today); - } - - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - extension = e_source_get_extension (source, extension_name); - e_source_ews_folder_set_id ( - E_SOURCE_EWS_FOLDER (extension), fid->id); - e_source_ews_folder_set_change_key ( - E_SOURCE_EWS_FOLDER (extension), fid->change_key); - - extension_name = E_SOURCE_EXTENSION_OFFLINE; - extension = e_source_get_extension (source, extension_name); - e_source_offline_set_stay_synchronized ( - E_SOURCE_OFFLINE (extension), TRUE); - - e_server_side_source_set_remote_deletable ( - E_SERVER_SIDE_SOURCE (source), TRUE); - - return source; -} - -static ESource * -ews_backend_new_calendar (EEwsBackend *backend, - EEwsFolder *folder) -{ - /* No extra configuration to do. */ - return ews_backend_new_child (backend, folder); -} - -static ESource * -ews_backend_new_task_list (EEwsBackend *backend, - EEwsFolder *folder) -{ - /* No extra configuration to do. */ - return ews_backend_new_child (backend, folder); -} - -static ESource * -ews_backend_new_memo_list (EEwsBackend *backend, - EEwsFolder *folder) -{ - /* No extra configuration to do. */ - return ews_backend_new_child (backend, folder); -} - -static ESource * -ews_backend_new_address_book (EEwsBackend *backend, - EEwsFolder *folder) -{ - /* No extra configuration to do. */ - return ews_backend_new_child (backend, folder); -} - -static void -ews_backend_sync_created_folders (EEwsBackend *backend, - GSList *list) -{ - ECollectionBackend *collection_backend; - ESourceRegistryServer *server; - GSList *link; - - collection_backend = E_COLLECTION_BACKEND (backend); - server = e_collection_backend_ref_server (collection_backend); - - for (link = list; link != NULL; link = g_slist_next (link)) { - EEwsFolder *folder = E_EWS_FOLDER (link->data); - const EwsFolderId *fid; - ESource *source = NULL; - - /* If we already know about this folder, skip it. */ - fid = e_ews_folder_get_id (folder); - if (!fid || !fid->id) - continue; /* not a valid ID anyway */ - if (ews_backend_folders_contains (backend, fid->id)) - continue; - - switch (e_ews_folder_get_folder_type (folder)) { - case E_EWS_FOLDER_TYPE_CALENDAR: - source = ews_backend_new_calendar ( - backend, folder); - break; - case E_EWS_FOLDER_TYPE_TASKS: - source = ews_backend_new_task_list ( - backend, folder); - break; - case E_EWS_FOLDER_TYPE_MEMOS: - source = ews_backend_new_memo_list ( - backend, folder); - break; - case E_EWS_FOLDER_TYPE_CONTACTS: - source = ews_backend_new_address_book ( - backend, folder); - break; - default: - break; - } - - if (source != NULL) { - e_source_registry_server_add_source (server, source); - g_object_unref (source); - } - } - - g_object_unref (server); -} - -static void -ews_backend_sync_deleted_folders (EEwsBackend *backend, - GSList *list) -{ - GSList *link; - - for (link = list; link != NULL; link = g_slist_next (link)) { - const gchar *folder_id = link->data; - ESource *source = NULL; - - if (folder_id != NULL) - source = ews_backend_folders_lookup ( - backend, folder_id); - - if (source == NULL) - continue; - - /* This will trigger a "child-removed" signal and - * our handler will remove the hash table entry. */ - e_source_remove_sync (source, NULL, NULL); - - g_object_unref (source); - } -} - -static void -ews_backend_add_gal_source (EEwsBackend *backend) -{ - ECollectionBackend *collection_backend; - ESourceAutocomplete *autocomplete_extension; - ESourceBackend *backend_extension; - ESourceEwsFolder *folder_extension; - ESourceOffline *offline_extension; - ESourceRegistryServer *server; - ESource *source = NULL; - CamelEwsSettings *settings; - const gchar *display_name; - const gchar *extension_name; - const gchar *gal_uid; - const gchar *oal_id = NULL; - const gchar *uid; - gchar *oal_selected; - gboolean can_enable; - - settings = ews_backend_get_settings (backend); - collection_backend = E_COLLECTION_BACKEND (backend); - source = e_backend_get_source (E_BACKEND (backend)); - if (source) { - ESourceCollection *collection_extension = NULL; - - if (e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION)) - collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION); - - can_enable = !collection_extension || (e_source_get_enabled (source) && - e_source_collection_get_contacts_enabled (collection_extension)); - } else { - can_enable = FALSE; - } - - gal_uid = camel_ews_settings_get_gal_uid (settings); - - if (gal_uid != NULL) { - server = e_collection_backend_ref_server (collection_backend); - source = e_source_registry_server_ref_source (server, gal_uid); - g_object_unref (server); - - if (source != NULL) { - e_source_set_enabled (source, can_enable); - g_object_unref (source); - return; - } - } - - oal_selected = camel_ews_settings_dup_oal_selected (settings); - - /* This is supposed to be in the form: ID ':' NAME */ - if (oal_selected != NULL) { - gchar *cp = strrchr (oal_selected, ':'); - if (cp != NULL) { - /* skip ':' and any leading backslash in the display name */ - cp++; - while (*cp && *cp == '\\') - cp++; - - display_name = cp; - oal_id = oal_selected; - } else { - g_free (oal_selected); - oal_selected = NULL; - - camel_ews_settings_set_oal_selected (settings, NULL); - } - } - - if (oal_selected == NULL) { - display_name = _("Global Address List"); - oal_id = "global-address-list"; - } - - g_free (backend->priv->oal_selected); - backend->priv->oal_selected = oal_selected; /* takes ownership */ - - source = e_collection_backend_new_child ( - collection_backend, oal_id); - e_source_set_enabled (source, can_enable); - ews_backend_sync_authentication (backend, source); - - e_source_set_display_name (source, display_name); - - /* do not re-setup previously saved ESource, - * that would rewrite user's choice */ - if (!e_source_has_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER)) { - extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK; - backend_extension = e_source_get_extension (source, extension_name); - e_source_backend_set_backend_name (backend_extension, "ews"); - - extension_name = E_SOURCE_EXTENSION_AUTOCOMPLETE; - autocomplete_extension = e_source_get_extension (source, extension_name); - e_source_autocomplete_set_include_me (autocomplete_extension, TRUE); - - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - folder_extension = e_source_get_extension (source, extension_name); - e_source_ews_folder_set_id (folder_extension, oal_id); - - extension_name = E_SOURCE_EXTENSION_OFFLINE; - offline_extension = e_source_get_extension (source, extension_name); - e_source_offline_set_stay_synchronized (offline_extension, TRUE); - } - - server = e_collection_backend_ref_server (collection_backend); - e_source_registry_server_add_source (server, source); - g_object_unref (server); - - uid = e_source_get_uid (source); - camel_ews_settings_set_gal_uid (settings, uid); - - g_object_unref (source); -} - -static void ews_backend_populate (ECollectionBackend *backend); - -static void -ews_backend_source_changed_cb (ESource *source, - EEwsBackend *backend) -{ - if (!e_source_get_enabled (source)) { - backend->priv->need_update_folders = TRUE; - return; - } - - if (!backend->priv->need_update_folders) - return; - - ews_backend_populate (E_COLLECTION_BACKEND (backend)); -} - -static void -add_remote_sources (EEwsBackend *backend) -{ - GList *old_sources, *iter; - ESourceRegistryServer *registry; - const gchar *extension_name; - - registry = e_collection_backend_ref_server ( - E_COLLECTION_BACKEND (backend)); - old_sources = e_collection_backend_claim_all_resources ( - E_COLLECTION_BACKEND (backend)); - - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - - for (iter = old_sources; iter; iter = iter->next) { - ESource *source = iter->data; - ESourceEwsFolder *extension; - - if (!e_source_has_extension (source, extension_name)) - continue; - - /* foreign or public folders are just added */ - extension = e_source_get_extension (source, extension_name); - if (e_source_ews_folder_get_foreign (extension) || - e_source_ews_folder_get_public (extension)) { - e_server_side_source_set_writable ( - E_SERVER_SIDE_SOURCE (source), TRUE); - e_server_side_source_set_remote_deletable ( - E_SERVER_SIDE_SOURCE (source), TRUE); - ews_backend_update_enabled (source, e_backend_get_source (E_BACKEND (backend))); - e_source_registry_server_add_source (registry, source); - } else { - GError *error = NULL; - - if (!e_source_remove_sync (source, NULL, &error)) - g_warning ("%s: Failed to remove old EWS source '%s': %s", G_STRFUNC, e_source_get_uid (source), - error ? error->message : "Unknown error"); - - g_clear_error (&error); - } - } - - g_list_free_full (old_sources, g_object_unref); - g_object_unref (registry); -} - -static gboolean -ews_backend_sync_folders_idle_cb (gpointer user_data) -{ - SyncFoldersClosure *closure = user_data; - - /* FIXME Handle updated folders. */ - - ews_backend_sync_deleted_folders ( - closure->backend, closure->folders_deleted); - ews_backend_sync_created_folders ( - closure->backend, closure->folders_created); - - add_remote_sources (closure->backend); - - return FALSE; -} - -static void -ews_backend_dispose (GObject *object) -{ - EEwsBackendPrivate *priv; - ESource *source; - - priv = E_EWS_BACKEND_GET_PRIVATE (object); - - source = e_backend_get_source (E_BACKEND (object)); - if (source && priv->source_changed_id) { - g_signal_handler_disconnect (source, priv->source_changed_id); - priv->source_changed_id = 0; - } - - g_hash_table_remove_all (priv->folders); - - g_mutex_lock (&priv->connection_lock); - g_clear_object (&priv->connection); - g_mutex_unlock (&priv->connection_lock); - - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (e_ews_backend_parent_class)->dispose (object); -} - -static void -ews_backend_finalize (GObject *object) -{ - EEwsBackendPrivate *priv; - - priv = E_EWS_BACKEND_GET_PRIVATE (object); - - g_hash_table_destroy (priv->folders); - g_mutex_clear (&priv->folders_lock); - - g_free (priv->oal_selected); - - g_free (priv->sync_state); - g_mutex_clear (&priv->sync_state_lock); - - g_mutex_clear (&priv->connection_lock); - - e_named_parameters_free (priv->credentials); - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (e_ews_backend_parent_class)->finalize (object); -} - -static void -ews_backend_constructed (GObject *object) -{ - EBackend *backend; - ESource *source; - ESourceAuthentication *auth_extension; - const gchar *extension_name; - gchar *host = NULL; - guint16 port = 0; - - /* Chain up to parent's constructed() method. */ - G_OBJECT_CLASS (e_ews_backend_parent_class)->constructed (object); - - backend = E_BACKEND (object); - source = e_backend_get_source (backend); - - /* XXX Wondering if we ought to delay this until after folders - * are initially populated, just to remove the possibility - * of weird races with clients trying to create folders. */ - e_server_side_source_set_remote_creatable ( - E_SERVER_SIDE_SOURCE (source), TRUE); - - /* Setup the Authentication extension so - * Camel can determine host reachability. */ - extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; - auth_extension = e_source_get_extension (source, extension_name); - - if (e_backend_get_destination_address (backend, &host, &port)) { - e_source_authentication_set_host (auth_extension, host); - e_source_authentication_set_port (auth_extension, port); - } - - g_free (host); - - /* Reset the connectable, it steals data from Authentication extension, - where is written incorrect address */ - e_backend_set_connectable (backend, NULL); -} - -static void -ews_backend_claim_old_resources (ECollectionBackend *backend) -{ - ESourceRegistryServer *registry; - GList *old_resources, *iter; - - g_return_if_fail (E_IS_COLLECTION_BACKEND (backend)); - - registry = e_collection_backend_ref_server (backend); - old_resources = e_collection_backend_claim_all_resources (backend); - - for (iter = old_resources; iter; iter = g_list_next (iter)) { - ESource *source = iter->data; - - ews_backend_update_enabled (source, e_backend_get_source (E_BACKEND (backend))); - e_source_registry_server_add_source (registry, source); - } - - g_list_free_full (old_resources, g_object_unref); - g_clear_object (®istry); -} - -static void -ews_backend_folders_synced_cb (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - EEwsBackend *ews_backend; - - g_return_if_fail (E_IS_EWS_BACKEND (source)); - - ews_backend = E_EWS_BACKEND (source); - - if (!e_ews_backend_sync_folders_finish (ews_backend, result, NULL)) - ews_backend_claim_old_resources (E_COLLECTION_BACKEND (ews_backend)); -} - -static void -ews_backend_populate (ECollectionBackend *backend) -{ - ESource *source; - EEwsBackend *ews_backend = E_EWS_BACKEND (backend); - - source = e_backend_get_source (E_BACKEND (backend)); - - ews_backend->priv->need_update_folders = TRUE; - - if (!ews_backend->priv->source_changed_id) { - ews_backend->priv->source_changed_id = g_signal_connect ( - source, "changed", - G_CALLBACK (ews_backend_source_changed_cb), ews_backend); - } - - /* do not do anything, if account is disabled */ - if (!e_source_get_enabled (source)) - return; - - ews_backend_add_gal_source (ews_backend); - - if (e_backend_get_online (E_BACKEND (backend))) { - CamelEwsSettings *ews_settings; - - ews_settings = ews_backend_get_settings (ews_backend); - - if (e_ews_connection_utils_get_without_password (ews_settings)) { - e_backend_schedule_authenticate (E_BACKEND (backend), NULL); - } else { - e_backend_credentials_required_sync (E_BACKEND (backend), - E_SOURCE_CREDENTIALS_REASON_REQUIRED, NULL, 0, NULL, - NULL, NULL); - } - } else { - ews_backend_claim_old_resources (backend); - } -} - -static gchar * -ews_backend_dup_resource_id (ECollectionBackend *backend, - ESource *child_source) -{ - ESourceEwsFolder *extension; - const gchar *extension_name; - - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - extension = e_source_get_extension (child_source, extension_name); - - return e_source_ews_folder_dup_id (extension); -} - -static void -ews_backend_child_added (ECollectionBackend *backend, - ESource *child_source) -{ - ESource *collection_source; - const gchar *extension_name; - gboolean is_mail = FALSE; - - collection_source = e_backend_get_source (E_BACKEND (backend)); - - extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT; - is_mail |= e_source_has_extension (child_source, extension_name); - - extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY; - is_mail |= e_source_has_extension (child_source, extension_name); - - extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT; - is_mail |= e_source_has_extension (child_source, extension_name); - - /* Synchronize mail-related user with the collection identity. */ - extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; - if (is_mail && e_source_has_extension (child_source, extension_name)) { - ESourceAuthentication *auth_child_extension; - ESourceCollection *collection_extension; - - extension_name = E_SOURCE_EXTENSION_COLLECTION; - collection_extension = e_source_get_extension ( - collection_source, extension_name); - - extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; - auth_child_extension = e_source_get_extension ( - child_source, extension_name); - - e_binding_bind_property ( - collection_extension, "identity", - auth_child_extension, "user", - G_BINDING_SYNC_CREATE); - } - - /* We track EWS folders in a hash table by folder ID. */ - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - if (e_source_has_extension (child_source, extension_name)) { - ESourceEwsFolder *extension; - gchar *folder_id; - - extension = e_source_get_extension ( - child_source, extension_name); - folder_id = e_source_ews_folder_dup_id (extension); - if (folder_id != NULL) { - ews_backend_folders_insert ( - E_EWS_BACKEND (backend), - folder_id, child_source); - g_free (folder_id); - } - } - - /* Chain up to parent's child_added() method. */ - E_COLLECTION_BACKEND_CLASS (e_ews_backend_parent_class)-> - child_added (backend, child_source); -} - -static void -ews_backend_child_removed (ECollectionBackend *backend, - ESource *child_source) -{ - const gchar *extension_name; - - /* We track EWS folders in a hash table by folder ID. */ - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - if (e_source_has_extension (child_source, extension_name)) { - ESourceEwsFolder *extension; - const gchar *folder_id; - - extension = e_source_get_extension ( - child_source, extension_name); - folder_id = e_source_ews_folder_get_id (extension); - if (folder_id != NULL) - ews_backend_folders_remove ( - E_EWS_BACKEND (backend), folder_id); - } - - /* Chain up to parent's child_removed() method. */ - E_COLLECTION_BACKEND_CLASS (e_ews_backend_parent_class)-> - child_removed (backend, child_source); -} - -static gboolean -ews_backend_create_resource_sync (ECollectionBackend *backend, - ESource *source, - GCancellable *cancellable, - GError **error) -{ - EEwsConnection *connection = NULL; - EwsFolderId *out_folder_id = NULL; - EEwsFolderType folder_type = E_EWS_FOLDER_TYPE_UNKNOWN; - const gchar *extension_name; - const gchar *parent_folder_id = NULL; - gchar *folder_name; - gboolean success = FALSE; - - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - if (e_source_has_extension (source, extension_name)) { - ESourceEwsFolder *extension; - - /* foreign and public folders are just added */ - extension = e_source_get_extension (source, extension_name); - if (e_source_ews_folder_get_foreign (extension) || - e_source_ews_folder_get_public (extension)) - success = TRUE; - } - - if (!success) { - connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (backend), NULL, cancellable, error); - if (connection == NULL) - return FALSE; - - extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK; - if (e_source_has_extension (source, extension_name)) { - folder_type = E_EWS_FOLDER_TYPE_CONTACTS; - parent_folder_id = "contacts"; - } - - extension_name = E_SOURCE_EXTENSION_CALENDAR; - if (e_source_has_extension (source, extension_name)) { - folder_type = E_EWS_FOLDER_TYPE_CALENDAR; - parent_folder_id = "calendar"; - } - - extension_name = E_SOURCE_EXTENSION_TASK_LIST; - if (e_source_has_extension (source, extension_name)) { - folder_type = E_EWS_FOLDER_TYPE_TASKS; - parent_folder_id = "tasks"; - } - - /* FIXME No support for memo lists. */ - - if (parent_folder_id == NULL) { - g_set_error ( - error, G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - _("Could not determine a suitable folder " - "class for a new folder named “%s”"), - e_source_get_display_name (source)); - goto exit; - } - - folder_name = e_source_dup_display_name (source); - - success = e_ews_connection_create_folder_sync ( - connection, EWS_PRIORITY_MEDIUM, - parent_folder_id, TRUE, - folder_name, folder_type, - &out_folder_id, cancellable, error); - - g_free (folder_name); - - /* Sanity check */ - g_warn_if_fail ( - (success && out_folder_id != NULL) || - (!success && out_folder_id == NULL)); - - if (out_folder_id != NULL) { - ESourceEwsFolder *extension; - const gchar *extension_name; - - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - extension = e_source_get_extension (source, extension_name); - e_source_ews_folder_set_id ( - extension, out_folder_id->id); - e_source_ews_folder_set_change_key ( - extension, out_folder_id->change_key); - - e_ews_folder_id_free (out_folder_id); - } - } - - if (success) { - ESourceRegistryServer *server; - ESource *parent_source; - const gchar *cache_dir; - const gchar *parent_uid; - - /* Configure the source as a collection member. */ - parent_source = e_backend_get_source (E_BACKEND (backend)); - parent_uid = e_source_get_uid (parent_source); - e_source_set_parent (source, parent_uid); - - /* Changes should be written back to the cache directory. */ - cache_dir = e_collection_backend_get_cache_dir (backend); - e_server_side_source_set_write_directory ( - E_SERVER_SIDE_SOURCE (source), cache_dir); - - /* Set permissions for clients. */ - e_server_side_source_set_writable (E_SERVER_SIDE_SOURCE (source), TRUE); - e_server_side_source_set_remote_deletable (E_SERVER_SIDE_SOURCE (source), TRUE); - - server = e_collection_backend_ref_server (backend); - e_source_registry_server_add_source (server, source); - g_object_unref (server); - } - - exit: - if (connection) - g_object_unref (connection); - - return success; -} - -static gboolean -ews_backend_delete_resource_sync (ECollectionBackend *backend, - ESource *source, - GCancellable *cancellable, - GError **error) -{ - EEwsConnection *connection; - ESourceEwsFolder *extension; - const gchar *extension_name; - gboolean success = FALSE; - - connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (backend), NULL, cancellable, error); - if (connection == NULL) - return FALSE; - - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - if (!e_source_has_extension (source, extension_name)) { - g_set_error ( - error, G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - _("Data source “%s” does not represent " - "an Exchange Web Services folder"), - e_source_get_display_name (source)); - goto exit; - } - extension = e_source_get_extension (source, extension_name); - - if (e_source_ews_folder_get_foreign (extension) || - e_source_ews_folder_get_public (extension)) { - /* do not delete foreign or public folders, - * just remove them from local store */ - success = TRUE; - } else { - gchar *folder_id; - - folder_id = e_source_ews_folder_dup_id (extension); - - success = e_ews_connection_delete_folder_sync ( - connection, EWS_PRIORITY_MEDIUM, folder_id, - FALSE, "HardDelete", cancellable, error); - - g_free (folder_id); - } - - if (success) - success = e_source_remove_sync (source, cancellable, error); - -exit: - g_object_unref (connection); - - return success; -} - -static gboolean -ews_backend_get_destination_address (EBackend *backend, - gchar **host, - guint16 *port) -{ - CamelEwsSettings *ews_settings; - SoupURI *soup_uri; - gchar *host_url; - gboolean result = FALSE; - - g_return_val_if_fail (port != NULL, FALSE); - g_return_val_if_fail (host != NULL, FALSE); - - ews_settings = ews_backend_get_settings (E_EWS_BACKEND (backend)); - g_return_val_if_fail (ews_settings != NULL, FALSE); - - host_url = camel_ews_settings_dup_hosturl (ews_settings); - g_return_val_if_fail (host_url != NULL, FALSE); - - soup_uri = soup_uri_new (host_url); - if (soup_uri) { - *host = g_strdup (soup_uri_get_host (soup_uri)); - *port = soup_uri_get_port (soup_uri); - - result = *host && **host; - if (!result) { - g_free (*host); - *host = NULL; - } - - soup_uri_free (soup_uri); - } - - g_free (host_url); - - return result; -} - -static ESourceAuthenticationResult -ews_backend_authenticate_sync (EBackend *backend, - const ENamedParameters *credentials, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, - GCancellable *cancellable, - GError **error) -{ - EEwsBackend *ews_backend; - EEwsConnection *connection; - CamelEwsSettings *ews_settings; - ESourceAuthenticationResult result = E_SOURCE_AUTHENTICATION_ERROR; - - g_return_val_if_fail (E_IS_EWS_BACKEND (backend), E_SOURCE_AUTHENTICATION_ERROR); - - ews_backend = E_EWS_BACKEND (backend); - ews_settings = ews_backend_get_settings (ews_backend); - g_return_val_if_fail (ews_settings != NULL, E_SOURCE_AUTHENTICATION_ERROR); - - g_mutex_lock (&ews_backend->priv->connection_lock); - g_clear_object (&ews_backend->priv->connection); - e_named_parameters_free (ews_backend->priv->credentials); - ews_backend->priv->credentials = e_named_parameters_new_clone (credentials); - g_mutex_unlock (&ews_backend->priv->connection_lock); - - connection = e_ews_backend_ref_connection_sync (ews_backend, &result, cancellable, error); - g_clear_object (&connection); - - if (result == E_SOURCE_AUTHENTICATION_ACCEPTED) { - e_collection_backend_authenticate_children (E_COLLECTION_BACKEND (backend), credentials); - - e_ews_backend_sync_folders (ews_backend, NULL, ews_backend_folders_synced_cb, NULL); - } else if (e_ews_connection_utils_get_without_password (ews_settings) && - result == E_SOURCE_AUTHENTICATION_REJECTED && - !e_named_parameters_exists (credentials, E_SOURCE_CREDENTIAL_PASSWORD)) { - e_ews_connection_utils_force_off_ntlm_auth_check (); - result = E_SOURCE_AUTHENTICATION_REQUIRED; - } - - return result; -} - -static void -e_ews_backend_class_init (EEwsBackendClass *class) -{ - GObjectClass *object_class; - EBackendClass *backend_class; - ECollectionBackendClass *collection_backend_class; - - g_type_class_add_private (class, sizeof (EEwsBackendPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->dispose = ews_backend_dispose; - object_class->finalize = ews_backend_finalize; - object_class->constructed = ews_backend_constructed; - - collection_backend_class = E_COLLECTION_BACKEND_CLASS (class); - collection_backend_class->populate = ews_backend_populate; - collection_backend_class->dup_resource_id = ews_backend_dup_resource_id; - collection_backend_class->child_added = ews_backend_child_added; - collection_backend_class->child_removed = ews_backend_child_removed; - collection_backend_class->create_resource_sync = ews_backend_create_resource_sync; - collection_backend_class->delete_resource_sync = ews_backend_delete_resource_sync; - - backend_class = E_BACKEND_CLASS (class); - backend_class->get_destination_address = ews_backend_get_destination_address; - backend_class->authenticate_sync = ews_backend_authenticate_sync; - - /* This generates an ESourceCamel subtype for CamelEwsSettings. */ - e_source_camel_generate_subtype ("ews", CAMEL_TYPE_EWS_SETTINGS); -} - -static void -e_ews_backend_class_finalize (EEwsBackendClass *class) -{ -} - -static void -e_ews_backend_init (EEwsBackend *backend) -{ - backend->priv = E_EWS_BACKEND_GET_PRIVATE (backend); - - backend->priv->folders = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - - g_mutex_init (&backend->priv->folders_lock); - g_mutex_init (&backend->priv->sync_state_lock); - g_mutex_init (&backend->priv->connection_lock); -} - -void -e_ews_backend_type_register (GTypeModule *type_module) -{ - /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration - * function, so we have to wrap it with a public function in - * order to register types from a separate compilation unit. */ - e_ews_backend_register_type (type_module); -} - -static void -ews_backend_ref_connection_thread (GSimpleAsyncResult *simple, - GObject *object, - GCancellable *cancellable) -{ - EEwsConnection *connection; - GError *error = NULL; - - connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (object), NULL, cancellable, &error); - - /* Sanity check. */ - g_return_if_fail ( - ((connection != NULL) && (error == NULL)) || - ((connection == NULL) && (error != NULL))); - - if (connection != NULL) - g_simple_async_result_set_op_res_gpointer ( - simple, connection, g_object_unref); - - if (error != NULL) - g_simple_async_result_take_error (simple, error); -} - -EEwsConnection * -e_ews_backend_ref_connection_sync (EEwsBackend *backend, - ESourceAuthenticationResult *result, - GCancellable *cancellable, - GError **error) -{ - EEwsConnection *connection = NULL; - ESourceAuthenticationResult local_result; - CamelEwsSettings *settings; - gchar *hosturl; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_BACKEND (backend), NULL); - - g_mutex_lock (&backend->priv->connection_lock); - if (backend->priv->connection != NULL) - connection = g_object_ref (backend->priv->connection); - g_mutex_unlock (&backend->priv->connection_lock); - - /* If we already have an authenticated - * connection object, just return that. */ - if (connection != NULL || !backend->priv->credentials) - return connection; - - settings = ews_backend_get_settings (backend); - hosturl = camel_ews_settings_dup_hosturl (settings); - connection = e_ews_connection_new_full (e_backend_get_source (E_BACKEND (backend)), hosturl, settings, FALSE); - g_free (hosturl); - - e_binding_bind_property ( - backend, "proxy-resolver", - connection, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - local_result = e_ews_connection_try_credentials_sync (connection, backend->priv->credentials, cancellable, error); - if (result) - *result = local_result; - - success = local_result == E_SOURCE_AUTHENTICATION_ACCEPTED; - - if (success) { - g_mutex_lock (&backend->priv->connection_lock); - if (backend->priv->connection != NULL) - g_object_unref (backend->priv->connection); - backend->priv->connection = g_object_ref (connection); - g_mutex_unlock (&backend->priv->connection_lock); - } else { - g_object_unref (connection); - connection = NULL; - } - - return connection; -} - -void -e_ews_backend_ref_connection (EEwsBackend *backend, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - - g_return_if_fail (E_IS_EWS_BACKEND (backend)); - - simple = g_simple_async_result_new ( - G_OBJECT (backend), callback, - user_data, e_ews_backend_ref_connection); - - g_simple_async_result_run_in_thread ( - simple, ews_backend_ref_connection_thread, - G_PRIORITY_DEFAULT, cancellable); - - g_object_unref (simple); -} - -EEwsConnection * -e_ews_backend_ref_connection_finish (EEwsBackend *backend, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - EEwsConnection *connection; - - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (backend), - e_ews_backend_ref_connection), NULL); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; - - connection = g_simple_async_result_get_op_res_gpointer (simple); - g_return_val_if_fail (E_IS_EWS_CONNECTION (connection), NULL); - - return g_object_ref (connection); -} - -static void -ews_backend_sync_folders_thread (GSimpleAsyncResult *simple, - GObject *object, - GCancellable *cancellable) -{ - GError *error = NULL; - - e_ews_backend_sync_folders_sync ( - E_EWS_BACKEND (object), cancellable, &error); - - if (error != NULL) - g_simple_async_result_take_error (simple, error); -} - -static void -ews_backend_delete_each_source_cb (gpointer data, - gpointer user_data) -{ - ESource *source = data; - ECollectionBackend *backend = user_data; - - e_collection_backend_delete_resource_sync (backend, source, NULL, NULL); -} - -static void -ews_backend_forget_all_sources (EEwsBackend *backend) -{ - GList *sources; - - g_return_if_fail (E_IS_EWS_BACKEND (backend)); - - sources = e_collection_backend_list_calendar_sources (E_COLLECTION_BACKEND (backend)); - g_list_foreach (sources, ews_backend_delete_each_source_cb, backend); - g_list_free_full (sources, g_object_unref); - - sources = e_collection_backend_list_contacts_sources (E_COLLECTION_BACKEND (backend)); - g_list_foreach (sources, ews_backend_delete_each_source_cb, backend); - g_list_free_full (sources, g_object_unref); - - sources = e_collection_backend_list_mail_sources (E_COLLECTION_BACKEND (backend)); - g_list_foreach (sources, ews_backend_delete_each_source_cb, backend); - g_list_free_full (sources, g_object_unref); -} - -gboolean -e_ews_backend_sync_folders_sync (EEwsBackend *backend, - GCancellable *cancellable, - GError **error) -{ - EEwsConnection *connection; - GSList *folders_created = NULL; - GSList *folders_updated = NULL; - GSList *folders_deleted = NULL; - gboolean includes_last_folder = FALSE; - gchar *old_sync_state, *new_sync_state = NULL; - gboolean success; - GError *local_error = NULL; - - g_return_val_if_fail (E_IS_EWS_BACKEND (backend), FALSE); - - if (!e_backend_get_online (E_BACKEND (backend))) { - SyncFoldersClosure *closure; - - /* This takes ownership of the folder lists. */ - closure = g_slice_new0 (SyncFoldersClosure); - closure->backend = g_object_ref (backend); - - /* Process the results from an idle callback. */ - g_idle_add_full ( - G_PRIORITY_DEFAULT_IDLE, - ews_backend_sync_folders_idle_cb, closure, - (GDestroyNotify) sync_folders_closure_free); - - return TRUE; - } - - connection = e_ews_backend_ref_connection_sync (backend, NULL, cancellable, error); - - if (connection == NULL) { - backend->priv->need_update_folders = TRUE; - return FALSE; - } - - backend->priv->need_update_folders = FALSE; - - g_mutex_lock (&backend->priv->sync_state_lock); - old_sync_state = g_strdup (backend->priv->sync_state); - g_mutex_unlock (&backend->priv->sync_state_lock); - - success = e_ews_connection_sync_folder_hierarchy_sync (connection, EWS_PRIORITY_MEDIUM, old_sync_state, - &new_sync_state, &includes_last_folder, &folders_created, &folders_updated, &folders_deleted, - cancellable, &local_error); - - if (old_sync_state && g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) { - g_clear_error (&local_error); - - g_mutex_lock (&backend->priv->sync_state_lock); - g_free (backend->priv->sync_state); - backend->priv->sync_state = NULL; - g_mutex_unlock (&backend->priv->sync_state_lock); - - ews_backend_forget_all_sources (backend); - - success = e_ews_connection_sync_folder_hierarchy_sync (connection, EWS_PRIORITY_MEDIUM, NULL, - &new_sync_state, &includes_last_folder, &folders_created, &folders_updated, &folders_deleted, - cancellable, &local_error); - } else if (local_error) { - g_propagate_error (error, local_error); - local_error = NULL; - } - - g_free (old_sync_state); - old_sync_state = NULL; - - if (success) { - SyncFoldersClosure *closure; - - /* This takes ownership of the folder lists. */ - closure = g_slice_new0 (SyncFoldersClosure); - closure->backend = g_object_ref (backend); - closure->folders_created = folders_created; - closure->folders_deleted = folders_deleted; - closure->folders_updated = folders_updated; - - /* Process the results from an idle callback. */ - g_idle_add_full ( - G_PRIORITY_DEFAULT_IDLE, - ews_backend_sync_folders_idle_cb, closure, - (GDestroyNotify) sync_folders_closure_free); - - g_mutex_lock (&backend->priv->sync_state_lock); - g_free (backend->priv->sync_state); - backend->priv->sync_state = g_strdup (new_sync_state); - g_mutex_unlock (&backend->priv->sync_state_lock); - - } else { - /* Make sure we're not leaking anything. */ - g_warn_if_fail (folders_created == NULL); - g_warn_if_fail (folders_updated == NULL); - g_warn_if_fail (folders_deleted == NULL); - - backend->priv->need_update_folders = TRUE; - } - - g_free (new_sync_state); - - g_object_unref (connection); - - return success; -} - -void -e_ews_backend_sync_folders (EEwsBackend *backend, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - - g_return_if_fail (E_IS_EWS_BACKEND (backend)); - - simple = g_simple_async_result_new ( - G_OBJECT (backend), callback, - user_data, e_ews_backend_sync_folders); - - g_simple_async_result_run_in_thread ( - simple, ews_backend_sync_folders_thread, - G_PRIORITY_DEFAULT, cancellable); - - g_object_unref (simple); -} - -gboolean -e_ews_backend_sync_folders_finish (EEwsBackend *backend, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (backend), - e_ews_backend_sync_folders), 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/src/collection/e-ews-backend.c.double-collection-backend-populate b/src/collection/e-ews-backend.c.double-collection-backend-populate deleted file mode 100644 index 651694b..0000000 --- a/src/collection/e-ews-backend.c.double-collection-backend-populate +++ /dev/null @@ -1,1527 +0,0 @@ -/* - * e-ews-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 - * - */ - -#include "evolution-ews-config.h" - -#include "e-ews-backend.h" - -#include - -#include "server/e-ews-connection-utils.h" -#include "server/e-source-ews-folder.h" - -#define E_EWS_BACKEND_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_EWS_BACKEND, EEwsBackendPrivate)) - -typedef struct _SyncFoldersClosure SyncFoldersClosure; - -struct _EEwsBackendPrivate { - /* Folder ID -> ESource */ - GHashTable *folders; - GMutex folders_lock; - - ESource *gal_source; - gchar *oal_selected; - - gchar *sync_state; - GMutex sync_state_lock; - - ENamedParameters *credentials; - EEwsConnection *connection; - GMutex connection_lock; - - gboolean need_update_folders; - - gulong notify_online_id; -}; - -struct _SyncFoldersClosure { - EEwsBackend *backend; - GSList *folders_created; - GSList *folders_deleted; - GSList *folders_updated; -}; - -G_DEFINE_DYNAMIC_TYPE ( - EEwsBackend, - e_ews_backend, - E_TYPE_COLLECTION_BACKEND) - -static void -sync_folders_closure_free (SyncFoldersClosure *closure) -{ - g_object_unref (closure->backend); - - /* List of EEwsFolder instances. */ - g_slist_free_full (closure->folders_created, g_object_unref); - - /* List of folder ID strings. */ - g_slist_free_full (closure->folders_deleted, g_free); - - /* List of EEwsFolder instances. */ - g_slist_free_full (closure->folders_updated, g_object_unref); - - g_slice_free (SyncFoldersClosure, closure); -} - -static gboolean -ews_backend_folders_contains (EEwsBackend *backend, - const gchar *folder_id) -{ - gboolean contains; - - g_return_val_if_fail (folder_id != NULL, FALSE); - - g_mutex_lock (&backend->priv->folders_lock); - - contains = g_hash_table_contains (backend->priv->folders, folder_id); - - g_mutex_unlock (&backend->priv->folders_lock); - - return contains; -} - -static void -ews_backend_folders_insert (EEwsBackend *backend, - const gchar *folder_id, - ESource *source) -{ - g_return_if_fail (folder_id != NULL); - g_return_if_fail (E_IS_SOURCE (source)); - - g_mutex_lock (&backend->priv->folders_lock); - - g_hash_table_insert ( - backend->priv->folders, - g_strdup (folder_id), - g_object_ref (source)); - - g_mutex_unlock (&backend->priv->folders_lock); -} - -static ESource * -ews_backend_folders_lookup (EEwsBackend *backend, - const gchar *folder_id) -{ - ESource *source; - - g_return_val_if_fail (folder_id != NULL, NULL); - - g_mutex_lock (&backend->priv->folders_lock); - - source = g_hash_table_lookup (backend->priv->folders, folder_id); - - if (source != NULL) - g_object_ref (source); - - g_mutex_unlock (&backend->priv->folders_lock); - - return source; -} - -static gboolean -ews_backend_folders_remove (EEwsBackend *backend, - const gchar *folder_id) -{ - gboolean removed; - - g_return_val_if_fail (folder_id != NULL, FALSE); - - g_mutex_lock (&backend->priv->folders_lock); - - removed = g_hash_table_remove (backend->priv->folders, folder_id); - - g_mutex_unlock (&backend->priv->folders_lock); - - return removed; -} - -static CamelEwsSettings * -ews_backend_get_settings (EEwsBackend *backend) -{ - ESource *source; - ESourceCamel *extension; - CamelSettings *settings; - const gchar *extension_name; - - source = e_backend_get_source (E_BACKEND (backend)); - extension_name = e_source_camel_get_extension_name ("ews"); - extension = e_source_get_extension (source, extension_name); - settings = e_source_camel_get_settings (extension); - - return CAMEL_EWS_SETTINGS (settings); -} - -static void -ews_backend_update_enabled (ESource *data_source, - ESource *collection_source) -{ - ESourceCollection *collection_extension = NULL; - gboolean part_enabled = TRUE; - - g_return_if_fail (E_IS_SOURCE (data_source)); - - if (!collection_source || !e_source_get_enabled (collection_source)) { - e_source_set_enabled (data_source, FALSE); - return; - } - - if (e_source_has_extension (collection_source, E_SOURCE_EXTENSION_COLLECTION)) - collection_extension = e_source_get_extension (collection_source, E_SOURCE_EXTENSION_COLLECTION); - - if (e_source_has_extension (data_source, E_SOURCE_EXTENSION_CALENDAR) || - e_source_has_extension (data_source, E_SOURCE_EXTENSION_TASK_LIST) || - e_source_has_extension (data_source, E_SOURCE_EXTENSION_MEMO_LIST)) { - part_enabled = !collection_extension || e_source_collection_get_calendar_enabled (collection_extension); - } else if (e_source_has_extension (data_source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) { - part_enabled = !collection_extension || e_source_collection_get_contacts_enabled (collection_extension); - } else if (e_source_has_extension (data_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT) || - e_source_has_extension (data_source, E_SOURCE_EXTENSION_MAIL_IDENTITY) || - e_source_has_extension (data_source, E_SOURCE_EXTENSION_MAIL_TRANSPORT)) { - part_enabled = !collection_extension || e_source_collection_get_mail_enabled (collection_extension); - } - - e_source_set_enabled (data_source, part_enabled); -} - -static void -ews_backend_sync_authentication (EEwsBackend *ews_backend, - ESource *child_source) -{ - ESourceAuthentication *coll_authentication_extension, *child_authentication_extension; - ESource *collection_source; - - g_return_if_fail (E_IS_EWS_BACKEND (ews_backend)); - g_return_if_fail (E_IS_SOURCE (child_source)); - - collection_source = e_backend_get_source (E_BACKEND (ews_backend)); - - coll_authentication_extension = e_source_get_extension (collection_source, E_SOURCE_EXTENSION_AUTHENTICATION); - child_authentication_extension = e_source_get_extension (child_source, E_SOURCE_EXTENSION_AUTHENTICATION); - - e_source_authentication_set_host (child_authentication_extension, - e_source_authentication_get_host (coll_authentication_extension)); - - e_source_authentication_set_user (child_authentication_extension, - e_source_authentication_get_user (coll_authentication_extension)); -} - -static gboolean -ews_backend_is_uuid_like_name (const gchar *name) -{ - const gchar *uuid_mask = "{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"; - const gchar *ptr, *msk_ptr; - gint len; - - if (!name || *name != '{') - return FALSE; - - len = strlen (name); - if (name[len - 1] != '}' || len != strlen (uuid_mask)) - return FALSE; - - for (ptr = name, msk_ptr = uuid_mask; *ptr && *msk_ptr; ptr++, msk_ptr++) { - if (*msk_ptr == 'X') { - if (!g_ascii_isxdigit (*ptr)) - break; - } else if (*msk_ptr != *ptr) { - break; - } - } - - return *msk_ptr == *ptr && !*msk_ptr; -} - -static ESource * -ews_backend_new_child (EEwsBackend *backend, - EEwsFolder *folder) -{ - ECollectionBackend *collection_backend; - ESourceExtension *extension; - ESource *source; - const EwsFolderId *fid; - const gchar *display_name; - const gchar *extension_name; - - fid = e_ews_folder_get_id (folder); - - g_return_val_if_fail (fid != NULL, NULL); - - display_name = e_ews_folder_get_name (folder); - - if (e_ews_folder_get_folder_type (folder) == E_EWS_FOLDER_TYPE_CONTACTS && - ews_backend_is_uuid_like_name (display_name)) { - /* Ignore address books with UUID-like name */ - return NULL; - } - - collection_backend = E_COLLECTION_BACKEND (backend); - source = e_collection_backend_new_child (collection_backend, fid->id); - - e_source_set_display_name (source, display_name); - - switch (e_ews_folder_get_folder_type (folder)) { - case E_EWS_FOLDER_TYPE_CALENDAR: - extension_name = E_SOURCE_EXTENSION_CALENDAR; - break; - case E_EWS_FOLDER_TYPE_TASKS: - extension_name = E_SOURCE_EXTENSION_TASK_LIST; - break; - case E_EWS_FOLDER_TYPE_MEMOS: - extension_name = E_SOURCE_EXTENSION_MEMO_LIST; - break; - case E_EWS_FOLDER_TYPE_CONTACTS: - extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK; - break; - default: - g_object_unref (source); - g_return_val_if_reached (NULL); - } - extension = e_source_get_extension (source, extension_name); - e_source_backend_set_backend_name ( - E_SOURCE_BACKEND (extension), "ews"); - ews_backend_sync_authentication (backend, source); - ews_backend_update_enabled (source, e_backend_get_source (E_BACKEND (backend))); - - if (e_ews_folder_get_folder_type (folder) != E_EWS_FOLDER_TYPE_CONTACTS && - !e_source_has_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER) && - !e_source_has_extension (source, E_SOURCE_EXTENSION_ALARMS)) { - /* a completely new ESource, do not notify with too old reminders */ - ESourceAlarms *alarms; - gchar *today; - GTimeVal today_tv; - GDate dt; - - g_date_clear (&dt, 1); - g_get_current_time (&today_tv); - g_date_set_time_val (&dt, &today_tv); - - /* midnight UTC */ - today = g_strdup_printf ("%04d-%02d-%02dT00:00:00Z", g_date_get_year (&dt), g_date_get_month (&dt), g_date_get_day (&dt)); - - alarms = e_source_get_extension (source, E_SOURCE_EXTENSION_ALARMS); - e_source_alarms_set_last_notified (alarms, today); - - g_free (today); - } - - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - extension = e_source_get_extension (source, extension_name); - e_source_ews_folder_set_id ( - E_SOURCE_EWS_FOLDER (extension), fid->id); - e_source_ews_folder_set_change_key ( - E_SOURCE_EWS_FOLDER (extension), fid->change_key); - - extension_name = E_SOURCE_EXTENSION_OFFLINE; - extension = e_source_get_extension (source, extension_name); - e_source_offline_set_stay_synchronized ( - E_SOURCE_OFFLINE (extension), TRUE); - - e_server_side_source_set_remote_deletable ( - E_SERVER_SIDE_SOURCE (source), TRUE); - - return source; -} - -static ESource * -ews_backend_new_calendar (EEwsBackend *backend, - EEwsFolder *folder) -{ - /* No extra configuration to do. */ - return ews_backend_new_child (backend, folder); -} - -static ESource * -ews_backend_new_task_list (EEwsBackend *backend, - EEwsFolder *folder) -{ - /* No extra configuration to do. */ - return ews_backend_new_child (backend, folder); -} - -static ESource * -ews_backend_new_memo_list (EEwsBackend *backend, - EEwsFolder *folder) -{ - /* No extra configuration to do. */ - return ews_backend_new_child (backend, folder); -} - -static ESource * -ews_backend_new_address_book (EEwsBackend *backend, - EEwsFolder *folder) -{ - /* No extra configuration to do. */ - return ews_backend_new_child (backend, folder); -} - -static void -ews_backend_sync_created_folders (EEwsBackend *backend, - GSList *list) -{ - ECollectionBackend *collection_backend; - ESourceRegistryServer *server; - GSList *link; - - collection_backend = E_COLLECTION_BACKEND (backend); - server = e_collection_backend_ref_server (collection_backend); - - for (link = list; link != NULL; link = g_slist_next (link)) { - EEwsFolder *folder = E_EWS_FOLDER (link->data); - const EwsFolderId *fid; - ESource *source = NULL; - - /* If we already know about this folder, skip it. */ - fid = e_ews_folder_get_id (folder); - if (!fid || !fid->id) - continue; /* not a valid ID anyway */ - if (ews_backend_folders_contains (backend, fid->id)) - continue; - - switch (e_ews_folder_get_folder_type (folder)) { - case E_EWS_FOLDER_TYPE_CALENDAR: - source = ews_backend_new_calendar ( - backend, folder); - break; - case E_EWS_FOLDER_TYPE_TASKS: - source = ews_backend_new_task_list ( - backend, folder); - break; - case E_EWS_FOLDER_TYPE_MEMOS: - source = ews_backend_new_memo_list ( - backend, folder); - break; - case E_EWS_FOLDER_TYPE_CONTACTS: - source = ews_backend_new_address_book ( - backend, folder); - break; - default: - break; - } - - if (source != NULL) { - e_source_registry_server_add_source (server, source); - g_object_unref (source); - } - } - - g_object_unref (server); -} - -static void -ews_backend_sync_deleted_folders (EEwsBackend *backend, - GSList *list) -{ - GSList *link; - - for (link = list; link != NULL; link = g_slist_next (link)) { - const gchar *folder_id = link->data; - ESource *source = NULL; - - if (folder_id != NULL) - source = ews_backend_folders_lookup ( - backend, folder_id); - - if (source == NULL) - continue; - - /* This will trigger a "child-removed" signal and - * our handler will remove the hash table entry. */ - e_source_remove_sync (source, NULL, NULL); - - g_object_unref (source); - } -} - -static void -ews_backend_add_gal_source (EEwsBackend *backend) -{ - ECollectionBackend *collection_backend; - ESourceAutocomplete *autocomplete_extension; - ESourceBackend *backend_extension; - ESourceEwsFolder *folder_extension; - ESourceOffline *offline_extension; - ESourceRegistryServer *server; - ESource *source = NULL; - CamelEwsSettings *settings; - const gchar *display_name; - const gchar *extension_name; - const gchar *gal_uid; - const gchar *oal_id = NULL; - const gchar *uid; - gchar *oal_selected; - gboolean can_enable; - - settings = ews_backend_get_settings (backend); - collection_backend = E_COLLECTION_BACKEND (backend); - source = e_backend_get_source (E_BACKEND (backend)); - if (source) { - ESourceCollection *collection_extension = NULL; - - if (e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION)) - collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION); - - can_enable = !collection_extension || (e_source_get_enabled (source) && - e_source_collection_get_contacts_enabled (collection_extension)); - } else { - can_enable = FALSE; - } - - gal_uid = camel_ews_settings_get_gal_uid (settings); - - if (gal_uid != NULL) { - server = e_collection_backend_ref_server (collection_backend); - source = e_source_registry_server_ref_source (server, gal_uid); - g_object_unref (server); - - if (source != NULL) { - e_source_set_enabled (source, can_enable); - g_object_unref (source); - return; - } - } - - oal_selected = camel_ews_settings_dup_oal_selected (settings); - - /* This is supposed to be in the form: ID ':' NAME */ - if (oal_selected != NULL) { - gchar *cp = strrchr (oal_selected, ':'); - if (cp != NULL) { - /* skip ':' and any leading backslash in the display name */ - cp++; - while (*cp && *cp == '\\') - cp++; - - display_name = cp; - oal_id = oal_selected; - } else { - g_free (oal_selected); - oal_selected = NULL; - - camel_ews_settings_set_oal_selected (settings, NULL); - } - } - - if (oal_selected == NULL) { - display_name = _("Global Address List"); - oal_id = "global-address-list"; - } - - g_free (backend->priv->oal_selected); - backend->priv->oal_selected = oal_selected; /* takes ownership */ - - source = e_collection_backend_new_child ( - collection_backend, oal_id); - e_source_set_enabled (source, can_enable); - ews_backend_sync_authentication (backend, source); - - e_source_set_display_name (source, display_name); - - /* do not re-setup previously saved ESource, - * that would rewrite user's choice */ - if (!e_source_has_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER)) { - extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK; - backend_extension = e_source_get_extension (source, extension_name); - e_source_backend_set_backend_name (backend_extension, "ews"); - - extension_name = E_SOURCE_EXTENSION_AUTOCOMPLETE; - autocomplete_extension = e_source_get_extension (source, extension_name); - e_source_autocomplete_set_include_me (autocomplete_extension, TRUE); - - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - folder_extension = e_source_get_extension (source, extension_name); - e_source_ews_folder_set_id (folder_extension, oal_id); - - extension_name = E_SOURCE_EXTENSION_OFFLINE; - offline_extension = e_source_get_extension (source, extension_name); - e_source_offline_set_stay_synchronized (offline_extension, TRUE); - } - - server = e_collection_backend_ref_server (collection_backend); - e_source_registry_server_add_source (server, source); - g_object_unref (server); - - uid = e_source_get_uid (source); - camel_ews_settings_set_gal_uid (settings, uid); - - g_object_unref (source); -} - -static void ews_backend_populate (ECollectionBackend *backend); - -static void -ews_backend_source_changed_cb (ESource *source, - EEwsBackend *backend) -{ - if (!e_source_get_enabled (source)) { - backend->priv->need_update_folders = TRUE; - return; - } - - if (!backend->priv->need_update_folders) - return; - - ews_backend_populate (E_COLLECTION_BACKEND (backend)); -} - -static void -add_remote_sources (EEwsBackend *backend) -{ - GList *old_sources, *iter; - ESourceRegistryServer *registry; - const gchar *extension_name; - - registry = e_collection_backend_ref_server ( - E_COLLECTION_BACKEND (backend)); - old_sources = e_collection_backend_claim_all_resources ( - E_COLLECTION_BACKEND (backend)); - - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - - for (iter = old_sources; iter; iter = iter->next) { - ESource *source = iter->data; - ESourceEwsFolder *extension; - - if (!e_source_has_extension (source, extension_name)) - continue; - - /* foreign or public folders are just added */ - extension = e_source_get_extension (source, extension_name); - if (e_source_ews_folder_get_foreign (extension) || - e_source_ews_folder_get_public (extension)) { - e_server_side_source_set_writable ( - E_SERVER_SIDE_SOURCE (source), TRUE); - e_server_side_source_set_remote_deletable ( - E_SERVER_SIDE_SOURCE (source), TRUE); - ews_backend_update_enabled (source, e_backend_get_source (E_BACKEND (backend))); - e_source_registry_server_add_source (registry, source); - } else { - GError *error = NULL; - - if (!e_source_remove_sync (source, NULL, &error)) - g_warning ("%s: Failed to remove old EWS source '%s': %s", G_STRFUNC, e_source_get_uid (source), - error ? error->message : "Unknown error"); - - g_clear_error (&error); - } - } - - g_list_free_full (old_sources, g_object_unref); - g_object_unref (registry); -} - -static gboolean -ews_backend_sync_folders_idle_cb (gpointer user_data) -{ - SyncFoldersClosure *closure = user_data; - - /* FIXME Handle updated folders. */ - - ews_backend_sync_deleted_folders ( - closure->backend, closure->folders_deleted); - ews_backend_sync_created_folders ( - closure->backend, closure->folders_created); - - add_remote_sources (closure->backend); - - return FALSE; -} - -static void -ews_backend_dispose (GObject *object) -{ - EEwsBackendPrivate *priv; - - priv = E_EWS_BACKEND_GET_PRIVATE (object); - - g_hash_table_remove_all (priv->folders); - - if (priv->connection != NULL) { - g_object_unref (priv->connection); - priv->connection = NULL; - } - - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (e_ews_backend_parent_class)->dispose (object); -} - -static void -ews_backend_finalize (GObject *object) -{ - EEwsBackendPrivate *priv; - - priv = E_EWS_BACKEND_GET_PRIVATE (object); - - g_hash_table_destroy (priv->folders); - g_mutex_clear (&priv->folders_lock); - - g_free (priv->oal_selected); - - g_free (priv->sync_state); - g_mutex_clear (&priv->sync_state_lock); - - g_mutex_clear (&priv->connection_lock); - - e_named_parameters_free (priv->credentials); - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (e_ews_backend_parent_class)->finalize (object); -} - -static void -ews_backend_constructed (GObject *object) -{ - EBackend *backend; - ESource *source; - ESourceAuthentication *auth_extension; - const gchar *extension_name; - gchar *host = NULL; - guint16 port = 0; - - /* Chain up to parent's constructed() method. */ - G_OBJECT_CLASS (e_ews_backend_parent_class)->constructed (object); - - backend = E_BACKEND (object); - source = e_backend_get_source (backend); - - /* XXX Wondering if we ought to delay this until after folders - * are initially populated, just to remove the possibility - * of weird races with clients trying to create folders. */ - e_server_side_source_set_remote_creatable ( - E_SERVER_SIDE_SOURCE (source), TRUE); - - /* Setup the Authentication extension so - * Camel can determine host reachability. */ - extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; - auth_extension = e_source_get_extension (source, extension_name); - - if (e_backend_get_destination_address (backend, &host, &port)) { - e_source_authentication_set_host (auth_extension, host); - e_source_authentication_set_port (auth_extension, port); - } - - g_free (host); - - /* Reset the connectable, it steals data from Authentication extension, - where is written incorrect address */ - e_backend_set_connectable (backend, NULL); -} - -static void -ews_backend_claim_old_resources (ECollectionBackend *backend) -{ - ESourceRegistryServer *registry; - GList *old_resources, *iter; - - g_return_if_fail (E_IS_COLLECTION_BACKEND (backend)); - - registry = e_collection_backend_ref_server (backend); - old_resources = e_collection_backend_claim_all_resources (backend); - - for (iter = old_resources; iter; iter = g_list_next (iter)) { - ESource *source = iter->data; - - ews_backend_update_enabled (source, e_backend_get_source (E_BACKEND (backend))); - e_source_registry_server_add_source (registry, source); - } - - g_list_free_full (old_resources, g_object_unref); - g_clear_object (®istry); -} - -static void -ews_backend_folders_synced_cb (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - EEwsBackend *ews_backend; - - g_return_if_fail (E_IS_EWS_BACKEND (source)); - - ews_backend = E_EWS_BACKEND (source); - - if (!e_ews_backend_sync_folders_finish (ews_backend, result, NULL)) - ews_backend_claim_old_resources (E_COLLECTION_BACKEND (ews_backend)); -} - -static void -ews_backend_populate (ECollectionBackend *backend) -{ - ESource *source; - EEwsBackend *ews_backend = E_EWS_BACKEND (backend); - - source = e_backend_get_source (E_BACKEND (backend)); - - ews_backend->priv->need_update_folders = TRUE; - - if (!ews_backend->priv->notify_online_id) { - ews_backend->priv->notify_online_id = g_signal_connect ( - backend, "notify::online", - G_CALLBACK (ews_backend_populate), NULL); - - g_signal_connect ( - source, "changed", - G_CALLBACK (ews_backend_source_changed_cb), ews_backend); - } - - /* do not do anything, if account is disabled */ - if (!e_source_get_enabled (source)) - return; - - ews_backend_add_gal_source (ews_backend); - - if (e_backend_get_online (E_BACKEND (backend))) { - CamelEwsSettings *ews_settings; - - ews_settings = ews_backend_get_settings (ews_backend); - - if (e_ews_connection_utils_get_without_password (ews_settings)) { - e_backend_schedule_authenticate (E_BACKEND (backend), NULL); - } else { - e_backend_credentials_required_sync (E_BACKEND (backend), - E_SOURCE_CREDENTIALS_REASON_REQUIRED, NULL, 0, NULL, - NULL, NULL); - } - } else { - ews_backend_claim_old_resources (backend); - } -} - -static gchar * -ews_backend_dup_resource_id (ECollectionBackend *backend, - ESource *child_source) -{ - ESourceEwsFolder *extension; - const gchar *extension_name; - - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - extension = e_source_get_extension (child_source, extension_name); - - return e_source_ews_folder_dup_id (extension); -} - -static void -ews_backend_child_added (ECollectionBackend *backend, - ESource *child_source) -{ - ESource *collection_source; - const gchar *extension_name; - gboolean is_mail = FALSE; - - collection_source = e_backend_get_source (E_BACKEND (backend)); - - extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT; - is_mail |= e_source_has_extension (child_source, extension_name); - - extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY; - is_mail |= e_source_has_extension (child_source, extension_name); - - extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT; - is_mail |= e_source_has_extension (child_source, extension_name); - - /* Synchronize mail-related user with the collection identity. */ - extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; - if (is_mail && e_source_has_extension (child_source, extension_name)) { - ESourceAuthentication *auth_child_extension; - ESourceCollection *collection_extension; - - extension_name = E_SOURCE_EXTENSION_COLLECTION; - collection_extension = e_source_get_extension ( - collection_source, extension_name); - - extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; - auth_child_extension = e_source_get_extension ( - child_source, extension_name); - - e_binding_bind_property ( - collection_extension, "identity", - auth_child_extension, "user", - G_BINDING_SYNC_CREATE); - } - - /* We track EWS folders in a hash table by folder ID. */ - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - if (e_source_has_extension (child_source, extension_name)) { - ESourceEwsFolder *extension; - gchar *folder_id; - - extension = e_source_get_extension ( - child_source, extension_name); - folder_id = e_source_ews_folder_dup_id (extension); - if (folder_id != NULL) { - ews_backend_folders_insert ( - E_EWS_BACKEND (backend), - folder_id, child_source); - g_free (folder_id); - } - } - - /* Chain up to parent's child_added() method. */ - E_COLLECTION_BACKEND_CLASS (e_ews_backend_parent_class)-> - child_added (backend, child_source); -} - -static void -ews_backend_child_removed (ECollectionBackend *backend, - ESource *child_source) -{ - const gchar *extension_name; - - /* We track EWS folders in a hash table by folder ID. */ - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - if (e_source_has_extension (child_source, extension_name)) { - ESourceEwsFolder *extension; - const gchar *folder_id; - - extension = e_source_get_extension ( - child_source, extension_name); - folder_id = e_source_ews_folder_get_id (extension); - if (folder_id != NULL) - ews_backend_folders_remove ( - E_EWS_BACKEND (backend), folder_id); - } - - /* Chain up to parent's child_removed() method. */ - E_COLLECTION_BACKEND_CLASS (e_ews_backend_parent_class)-> - child_removed (backend, child_source); -} - -static gboolean -ews_backend_create_resource_sync (ECollectionBackend *backend, - ESource *source, - GCancellable *cancellable, - GError **error) -{ - EEwsConnection *connection = NULL; - EwsFolderId *out_folder_id = NULL; - EEwsFolderType folder_type = E_EWS_FOLDER_TYPE_UNKNOWN; - const gchar *extension_name; - const gchar *parent_folder_id = NULL; - gchar *folder_name; - gboolean success = FALSE; - - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - if (e_source_has_extension (source, extension_name)) { - ESourceEwsFolder *extension; - - /* foreign and public folders are just added */ - extension = e_source_get_extension (source, extension_name); - if (e_source_ews_folder_get_foreign (extension) || - e_source_ews_folder_get_public (extension)) - success = TRUE; - } - - if (!success) { - connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (backend), NULL, cancellable, error); - if (connection == NULL) - return FALSE; - - extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK; - if (e_source_has_extension (source, extension_name)) { - folder_type = E_EWS_FOLDER_TYPE_CONTACTS; - parent_folder_id = "contacts"; - } - - extension_name = E_SOURCE_EXTENSION_CALENDAR; - if (e_source_has_extension (source, extension_name)) { - folder_type = E_EWS_FOLDER_TYPE_CALENDAR; - parent_folder_id = "calendar"; - } - - extension_name = E_SOURCE_EXTENSION_TASK_LIST; - if (e_source_has_extension (source, extension_name)) { - folder_type = E_EWS_FOLDER_TYPE_TASKS; - parent_folder_id = "tasks"; - } - - /* FIXME No support for memo lists. */ - - if (parent_folder_id == NULL) { - g_set_error ( - error, G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - _("Could not determine a suitable folder " - "class for a new folder named “%s”"), - e_source_get_display_name (source)); - goto exit; - } - - folder_name = e_source_dup_display_name (source); - - success = e_ews_connection_create_folder_sync ( - connection, EWS_PRIORITY_MEDIUM, - parent_folder_id, TRUE, - folder_name, folder_type, - &out_folder_id, cancellable, error); - - g_free (folder_name); - - /* Sanity check */ - g_warn_if_fail ( - (success && out_folder_id != NULL) || - (!success && out_folder_id == NULL)); - - if (out_folder_id != NULL) { - ESourceEwsFolder *extension; - const gchar *extension_name; - - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - extension = e_source_get_extension (source, extension_name); - e_source_ews_folder_set_id ( - extension, out_folder_id->id); - e_source_ews_folder_set_change_key ( - extension, out_folder_id->change_key); - - e_ews_folder_id_free (out_folder_id); - } - } - - if (success) { - ESourceRegistryServer *server; - ESource *parent_source; - const gchar *cache_dir; - const gchar *parent_uid; - - /* Configure the source as a collection member. */ - parent_source = e_backend_get_source (E_BACKEND (backend)); - parent_uid = e_source_get_uid (parent_source); - e_source_set_parent (source, parent_uid); - - /* Changes should be written back to the cache directory. */ - cache_dir = e_collection_backend_get_cache_dir (backend); - e_server_side_source_set_write_directory ( - E_SERVER_SIDE_SOURCE (source), cache_dir); - - /* Set permissions for clients. */ - e_server_side_source_set_writable (E_SERVER_SIDE_SOURCE (source), TRUE); - e_server_side_source_set_remote_deletable (E_SERVER_SIDE_SOURCE (source), TRUE); - - server = e_collection_backend_ref_server (backend); - e_source_registry_server_add_source (server, source); - g_object_unref (server); - } - - exit: - if (connection) - g_object_unref (connection); - - return success; -} - -static gboolean -ews_backend_delete_resource_sync (ECollectionBackend *backend, - ESource *source, - GCancellable *cancellable, - GError **error) -{ - EEwsConnection *connection; - ESourceEwsFolder *extension; - const gchar *extension_name; - gboolean success = FALSE; - - connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (backend), NULL, cancellable, error); - if (connection == NULL) - return FALSE; - - extension_name = E_SOURCE_EXTENSION_EWS_FOLDER; - if (!e_source_has_extension (source, extension_name)) { - g_set_error ( - error, G_IO_ERROR, - G_IO_ERROR_INVALID_ARGUMENT, - _("Data source “%s” does not represent " - "an Exchange Web Services folder"), - e_source_get_display_name (source)); - goto exit; - } - extension = e_source_get_extension (source, extension_name); - - if (e_source_ews_folder_get_foreign (extension) || - e_source_ews_folder_get_public (extension)) { - /* do not delete foreign or public folders, - * just remove them from local store */ - success = TRUE; - } else { - gchar *folder_id; - - folder_id = e_source_ews_folder_dup_id (extension); - - success = e_ews_connection_delete_folder_sync ( - connection, EWS_PRIORITY_MEDIUM, folder_id, - FALSE, "HardDelete", cancellable, error); - - g_free (folder_id); - } - - if (success) - success = e_source_remove_sync (source, cancellable, error); - -exit: - g_object_unref (connection); - - return success; -} - -static gboolean -ews_backend_get_destination_address (EBackend *backend, - gchar **host, - guint16 *port) -{ - CamelEwsSettings *ews_settings; - SoupURI *soup_uri; - gchar *host_url; - gboolean result = FALSE; - - g_return_val_if_fail (port != NULL, FALSE); - g_return_val_if_fail (host != NULL, FALSE); - - ews_settings = ews_backend_get_settings (E_EWS_BACKEND (backend)); - g_return_val_if_fail (ews_settings != NULL, FALSE); - - host_url = camel_ews_settings_dup_hosturl (ews_settings); - g_return_val_if_fail (host_url != NULL, FALSE); - - soup_uri = soup_uri_new (host_url); - if (soup_uri) { - *host = g_strdup (soup_uri_get_host (soup_uri)); - *port = soup_uri_get_port (soup_uri); - - result = *host && **host; - if (!result) { - g_free (*host); - *host = NULL; - } - - soup_uri_free (soup_uri); - } - - g_free (host_url); - - return result; -} - -static ESourceAuthenticationResult -ews_backend_authenticate_sync (EBackend *backend, - const ENamedParameters *credentials, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, - GCancellable *cancellable, - GError **error) -{ - EEwsBackend *ews_backend; - EEwsConnection *connection; - CamelEwsSettings *ews_settings; - ESourceAuthenticationResult result = E_SOURCE_AUTHENTICATION_ERROR; - - g_return_val_if_fail (E_IS_EWS_BACKEND (backend), E_SOURCE_AUTHENTICATION_ERROR); - - ews_backend = E_EWS_BACKEND (backend); - ews_settings = ews_backend_get_settings (ews_backend); - g_return_val_if_fail (ews_settings != NULL, E_SOURCE_AUTHENTICATION_ERROR); - - g_mutex_lock (&ews_backend->priv->connection_lock); - g_clear_object (&ews_backend->priv->connection); - e_named_parameters_free (ews_backend->priv->credentials); - ews_backend->priv->credentials = e_named_parameters_new_clone (credentials); - g_mutex_unlock (&ews_backend->priv->connection_lock); - - connection = e_ews_backend_ref_connection_sync (ews_backend, &result, cancellable, error); - g_clear_object (&connection); - - if (result == E_SOURCE_AUTHENTICATION_ACCEPTED) { - e_collection_backend_authenticate_children (E_COLLECTION_BACKEND (backend), credentials); - - e_ews_backend_sync_folders (ews_backend, NULL, ews_backend_folders_synced_cb, NULL); - } else if (e_ews_connection_utils_get_without_password (ews_settings) && - result == E_SOURCE_AUTHENTICATION_REJECTED && - !e_named_parameters_exists (credentials, E_SOURCE_CREDENTIAL_PASSWORD)) { - e_ews_connection_utils_force_off_ntlm_auth_check (); - result = E_SOURCE_AUTHENTICATION_REQUIRED; - } - - return result; -} - -static void -e_ews_backend_class_init (EEwsBackendClass *class) -{ - GObjectClass *object_class; - EBackendClass *backend_class; - ECollectionBackendClass *collection_backend_class; - - g_type_class_add_private (class, sizeof (EEwsBackendPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->dispose = ews_backend_dispose; - object_class->finalize = ews_backend_finalize; - object_class->constructed = ews_backend_constructed; - - collection_backend_class = E_COLLECTION_BACKEND_CLASS (class); - collection_backend_class->populate = ews_backend_populate; - collection_backend_class->dup_resource_id = ews_backend_dup_resource_id; - collection_backend_class->child_added = ews_backend_child_added; - collection_backend_class->child_removed = ews_backend_child_removed; - collection_backend_class->create_resource_sync = ews_backend_create_resource_sync; - collection_backend_class->delete_resource_sync = ews_backend_delete_resource_sync; - - backend_class = E_BACKEND_CLASS (class); - backend_class->get_destination_address = ews_backend_get_destination_address; - backend_class->authenticate_sync = ews_backend_authenticate_sync; - - /* This generates an ESourceCamel subtype for CamelEwsSettings. */ - e_source_camel_generate_subtype ("ews", CAMEL_TYPE_EWS_SETTINGS); -} - -static void -e_ews_backend_class_finalize (EEwsBackendClass *class) -{ -} - -static void -e_ews_backend_init (EEwsBackend *backend) -{ - backend->priv = E_EWS_BACKEND_GET_PRIVATE (backend); - - backend->priv->folders = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - - g_mutex_init (&backend->priv->folders_lock); - g_mutex_init (&backend->priv->sync_state_lock); - g_mutex_init (&backend->priv->connection_lock); -} - -void -e_ews_backend_type_register (GTypeModule *type_module) -{ - /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration - * function, so we have to wrap it with a public function in - * order to register types from a separate compilation unit. */ - e_ews_backend_register_type (type_module); -} - -static void -ews_backend_ref_connection_thread (GSimpleAsyncResult *simple, - GObject *object, - GCancellable *cancellable) -{ - EEwsConnection *connection; - GError *error = NULL; - - connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (object), NULL, cancellable, &error); - - /* Sanity check. */ - g_return_if_fail ( - ((connection != NULL) && (error == NULL)) || - ((connection == NULL) && (error != NULL))); - - if (connection != NULL) - g_simple_async_result_set_op_res_gpointer ( - simple, connection, g_object_unref); - - if (error != NULL) - g_simple_async_result_take_error (simple, error); -} - -EEwsConnection * -e_ews_backend_ref_connection_sync (EEwsBackend *backend, - ESourceAuthenticationResult *result, - GCancellable *cancellable, - GError **error) -{ - EEwsConnection *connection = NULL; - ESourceAuthenticationResult local_result; - CamelEwsSettings *settings; - gchar *hosturl; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_BACKEND (backend), NULL); - - g_mutex_lock (&backend->priv->connection_lock); - if (backend->priv->connection != NULL) - connection = g_object_ref (backend->priv->connection); - g_mutex_unlock (&backend->priv->connection_lock); - - /* If we already have an authenticated - * connection object, just return that. */ - if (connection != NULL || !backend->priv->credentials) - return connection; - - settings = ews_backend_get_settings (backend); - hosturl = camel_ews_settings_dup_hosturl (settings); - connection = e_ews_connection_new_full (e_backend_get_source (E_BACKEND (backend)), hosturl, settings, FALSE); - g_free (hosturl); - - e_binding_bind_property ( - backend, "proxy-resolver", - connection, "proxy-resolver", - G_BINDING_SYNC_CREATE); - - local_result = e_ews_connection_try_credentials_sync (connection, backend->priv->credentials, cancellable, error); - if (result) - *result = local_result; - - success = local_result == E_SOURCE_AUTHENTICATION_ACCEPTED; - - if (success) { - g_mutex_lock (&backend->priv->connection_lock); - if (backend->priv->connection != NULL) - g_object_unref (backend->priv->connection); - backend->priv->connection = g_object_ref (connection); - g_mutex_unlock (&backend->priv->connection_lock); - } else { - g_object_unref (connection); - connection = NULL; - } - - return connection; -} - -void -e_ews_backend_ref_connection (EEwsBackend *backend, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - - g_return_if_fail (E_IS_EWS_BACKEND (backend)); - - simple = g_simple_async_result_new ( - G_OBJECT (backend), callback, - user_data, e_ews_backend_ref_connection); - - g_simple_async_result_run_in_thread ( - simple, ews_backend_ref_connection_thread, - G_PRIORITY_DEFAULT, cancellable); - - g_object_unref (simple); -} - -EEwsConnection * -e_ews_backend_ref_connection_finish (EEwsBackend *backend, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - EEwsConnection *connection; - - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (backend), - e_ews_backend_ref_connection), NULL); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - if (g_simple_async_result_propagate_error (simple, error)) - return NULL; - - connection = g_simple_async_result_get_op_res_gpointer (simple); - g_return_val_if_fail (E_IS_EWS_CONNECTION (connection), NULL); - - return g_object_ref (connection); -} - -static void -ews_backend_sync_folders_thread (GSimpleAsyncResult *simple, - GObject *object, - GCancellable *cancellable) -{ - GError *error = NULL; - - e_ews_backend_sync_folders_sync ( - E_EWS_BACKEND (object), cancellable, &error); - - if (error != NULL) - g_simple_async_result_take_error (simple, error); -} - -static void -ews_backend_delete_each_source_cb (gpointer data, - gpointer user_data) -{ - ESource *source = data; - ECollectionBackend *backend = user_data; - - e_collection_backend_delete_resource_sync (backend, source, NULL, NULL); -} - -static void -ews_backend_forget_all_sources (EEwsBackend *backend) -{ - GList *sources; - - g_return_if_fail (E_IS_EWS_BACKEND (backend)); - - sources = e_collection_backend_list_calendar_sources (E_COLLECTION_BACKEND (backend)); - g_list_foreach (sources, ews_backend_delete_each_source_cb, backend); - g_list_free_full (sources, g_object_unref); - - sources = e_collection_backend_list_contacts_sources (E_COLLECTION_BACKEND (backend)); - g_list_foreach (sources, ews_backend_delete_each_source_cb, backend); - g_list_free_full (sources, g_object_unref); - - sources = e_collection_backend_list_mail_sources (E_COLLECTION_BACKEND (backend)); - g_list_foreach (sources, ews_backend_delete_each_source_cb, backend); - g_list_free_full (sources, g_object_unref); -} - -gboolean -e_ews_backend_sync_folders_sync (EEwsBackend *backend, - GCancellable *cancellable, - GError **error) -{ - EEwsConnection *connection; - GSList *folders_created = NULL; - GSList *folders_updated = NULL; - GSList *folders_deleted = NULL; - gboolean includes_last_folder = FALSE; - gchar *old_sync_state, *new_sync_state = NULL; - gboolean success; - GError *local_error = NULL; - - g_return_val_if_fail (E_IS_EWS_BACKEND (backend), FALSE); - - if (!e_backend_get_online (E_BACKEND (backend))) { - SyncFoldersClosure *closure; - - /* This takes ownership of the folder lists. */ - closure = g_slice_new0 (SyncFoldersClosure); - closure->backend = g_object_ref (backend); - - /* Process the results from an idle callback. */ - g_idle_add_full ( - G_PRIORITY_DEFAULT_IDLE, - ews_backend_sync_folders_idle_cb, closure, - (GDestroyNotify) sync_folders_closure_free); - - return TRUE; - } - - connection = e_ews_backend_ref_connection_sync (backend, NULL, cancellable, error); - - if (connection == NULL) { - backend->priv->need_update_folders = TRUE; - return FALSE; - } - - backend->priv->need_update_folders = FALSE; - - g_mutex_lock (&backend->priv->sync_state_lock); - old_sync_state = g_strdup (backend->priv->sync_state); - g_mutex_unlock (&backend->priv->sync_state_lock); - - success = e_ews_connection_sync_folder_hierarchy_sync (connection, EWS_PRIORITY_MEDIUM, old_sync_state, - &new_sync_state, &includes_last_folder, &folders_created, &folders_updated, &folders_deleted, - cancellable, &local_error); - - if (old_sync_state && g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) { - g_clear_error (&local_error); - - g_mutex_lock (&backend->priv->sync_state_lock); - g_free (backend->priv->sync_state); - backend->priv->sync_state = NULL; - g_mutex_unlock (&backend->priv->sync_state_lock); - - ews_backend_forget_all_sources (backend); - - success = e_ews_connection_sync_folder_hierarchy_sync (connection, EWS_PRIORITY_MEDIUM, NULL, - &new_sync_state, &includes_last_folder, &folders_created, &folders_updated, &folders_deleted, - cancellable, &local_error); - } else if (local_error) { - g_propagate_error (error, local_error); - local_error = NULL; - } - - g_free (old_sync_state); - old_sync_state = NULL; - - if (success) { - SyncFoldersClosure *closure; - - /* This takes ownership of the folder lists. */ - closure = g_slice_new0 (SyncFoldersClosure); - closure->backend = g_object_ref (backend); - closure->folders_created = folders_created; - closure->folders_deleted = folders_deleted; - closure->folders_updated = folders_updated; - - /* Process the results from an idle callback. */ - g_idle_add_full ( - G_PRIORITY_DEFAULT_IDLE, - ews_backend_sync_folders_idle_cb, closure, - (GDestroyNotify) sync_folders_closure_free); - - g_mutex_lock (&backend->priv->sync_state_lock); - g_free (backend->priv->sync_state); - backend->priv->sync_state = g_strdup (new_sync_state); - g_mutex_unlock (&backend->priv->sync_state_lock); - - } else { - /* Make sure we're not leaking anything. */ - g_warn_if_fail (folders_created == NULL); - g_warn_if_fail (folders_updated == NULL); - g_warn_if_fail (folders_deleted == NULL); - - backend->priv->need_update_folders = TRUE; - } - - g_free (new_sync_state); - - g_object_unref (connection); - - return success; -} - -void -e_ews_backend_sync_folders (EEwsBackend *backend, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - - g_return_if_fail (E_IS_EWS_BACKEND (backend)); - - simple = g_simple_async_result_new ( - G_OBJECT (backend), callback, - user_data, e_ews_backend_sync_folders); - - g_simple_async_result_run_in_thread ( - simple, ews_backend_sync_folders_thread, - G_PRIORITY_DEFAULT, cancellable); - - g_object_unref (simple); -} - -gboolean -e_ews_backend_sync_folders_finish (EEwsBackend *backend, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (backend), - e_ews_backend_sync_folders), 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/src/collection/e-ews-backend.h b/src/collection/e-ews-backend.h index 33d9c73..9e23184 100644 --- a/src/collection/e-ews-backend.h +++ b/src/collection/e-ews-backend.h @@ -63,8 +63,6 @@ EEwsConnection * e_ews_backend_ref_connection_sync (EEwsBackend *backend, ESourceAuthenticationResult *result, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, GCancellable *cancellable, GError **error); void e_ews_backend_ref_connection (EEwsBackend *backend, diff --git a/src/collection/e-ews-backend.h.cve-2019-3890 b/src/collection/e-ews-backend.h.cve-2019-3890 deleted file mode 100644 index 9e23184..0000000 --- a/src/collection/e-ews-backend.h.cve-2019-3890 +++ /dev/null @@ -1,92 +0,0 @@ -/* - * e-ews-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_EWS_BACKEND_H -#define E_EWS_BACKEND_H - -#include - -#include "server/e-ews-connection.h" - -/* Standard GObject macros */ -#define E_TYPE_EWS_BACKEND \ - (e_ews_backend_get_type ()) -#define E_EWS_BACKEND(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), E_TYPE_EWS_BACKEND, EEwsBackend)) -#define E_EWS_BACKEND_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), E_TYPE_EWS_BACKEND, EEwsBackendClass)) -#define E_IS_EWS_BACKEND(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), E_TYPE_EWS_BACKEND)) -#define E_IS_EWS_BACKEND_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), E_TYPE_EWS_BACKEND)) -#define E_EWS_BACKEND_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), E_TYPE_EWS_BACKEND, EEwsBackendClass)) - -G_BEGIN_DECLS - -typedef struct _EEwsBackend EEwsBackend; -typedef struct _EEwsBackendClass EEwsBackendClass; -typedef struct _EEwsBackendPrivate EEwsBackendPrivate; - -struct _EEwsBackend { - ECollectionBackend parent; - EEwsBackendPrivate *priv; -}; - -struct _EEwsBackendClass { - ECollectionBackendClass parent_class; -}; - -GType e_ews_backend_get_type (void) G_GNUC_CONST; -void e_ews_backend_type_register (GTypeModule *type_module); -EEwsConnection * - e_ews_backend_ref_connection_sync - (EEwsBackend *backend, - ESourceAuthenticationResult *result, - GCancellable *cancellable, - GError **error); -void e_ews_backend_ref_connection (EEwsBackend *backend, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -EEwsConnection * - e_ews_backend_ref_connection_finish - (EEwsBackend *backend, - GAsyncResult *result, - GError **error); -gboolean e_ews_backend_sync_folders_sync (EEwsBackend *backend, - GCancellable *cancellable, - GError **error); -void e_ews_backend_sync_folders (EEwsBackend *backend, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_backend_sync_folders_finish - (EEwsBackend *backend, - GAsyncResult *result, - GError **error); - -G_END_DECLS - -#endif /* E_EWS_BACKEND_H */ - diff --git a/src/configuration/e-ews-config-lookup.c b/src/configuration/e-ews-config-lookup.c index b7a437e..c18a7ac 100644 --- a/src/configuration/e-ews-config-lookup.c +++ b/src/configuration/e-ews-config-lookup.c @@ -344,54 +344,9 @@ ews_config_lookup_worker_run (EConfigLookupWorker *lookup_worker, if (password) { const gchar *servers; - gchar *certificate_host = NULL; - gchar *certificate_pem = NULL; - GTlsCertificateFlags certificate_errors = 0; - GError *local_error = NULL; - if (e_named_parameters_exists (params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_PEM) && - e_named_parameters_exists (params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_TRUST) && - e_named_parameters_exists (params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_HOST)) { - GTlsCertificate *certificate; - const gchar *param_certificate_pem; - - param_certificate_pem = e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_PEM); - certificate = g_tls_certificate_new_from_pem (param_certificate_pem, -1, NULL); - - if (certificate) { - ETrustPromptResponse trust_response; - - trust_response = e_config_lookup_decode_certificate_trust ( - e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_TRUST)); - - if (trust_response != E_TRUST_PROMPT_RESPONSE_UNKNOWN) { - ESourceWebdav *webdav_extension; - - webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND); - e_source_webdav_update_ssl_trust (webdav_extension, - e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_HOST), - certificate, trust_response); - } - - g_object_unref (certificate); - } - } - - if (e_ews_autodiscover_ws_url_sync (source, ews_settings, email_address, password, &certificate_pem, &certificate_errors, cancellable, &local_error)) { + if (e_ews_autodiscover_ws_url_sync (source, ews_settings, email_address, password, cancellable, NULL)) { ews_config_lookup_worker_result_from_settings (lookup_worker, config_lookup, email_address, ews_settings, params); - } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) { - const gchar *hosturl; - SoupURI *suri; - - hosturl = camel_ews_settings_get_hosturl (ews_settings); - suri = soup_uri_new (hosturl); - if (suri) { - certificate_host = g_strdup (soup_uri_get_host (suri)); - - soup_uri_free (suri); - } - } else { - g_clear_error (&local_error); } servers = e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_SERVERS); @@ -402,7 +357,7 @@ ews_config_lookup_worker_run (EConfigLookupWorker *lookup_worker, servers_strv = g_strsplit (servers, ";", 0); - for (ii = 0; servers_strv && servers_strv[ii] && !g_cancellable_is_cancelled (cancellable) && !local_error; ii++) { + for (ii = 0; servers_strv && servers_strv[ii] && !g_cancellable_is_cancelled (cancellable); ii++) { const gchar *server = servers_strv[ii]; gchar *tmp = NULL; @@ -413,21 +368,8 @@ ews_config_lookup_worker_run (EConfigLookupWorker *lookup_worker, camel_ews_settings_set_hosturl (ews_settings, server); - if (e_ews_autodiscover_ws_url_sync (source, ews_settings, email_address, password, &certificate_pem, &certificate_errors, cancellable, &local_error)) { + if (e_ews_autodiscover_ws_url_sync (source, ews_settings, email_address, password, cancellable, NULL)) { ews_config_lookup_worker_result_from_settings (lookup_worker, config_lookup, email_address, ews_settings, params); - } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) { - const gchar *hosturl; - SoupURI *suri; - - hosturl = camel_ews_settings_get_hosturl (ews_settings); - suri = soup_uri_new (hosturl); - if (suri) { - certificate_host = g_strdup (soup_uri_get_host (suri)); - - soup_uri_free (suri); - } - } else { - g_clear_error (&local_error); } g_free (tmp); @@ -436,31 +378,7 @@ ews_config_lookup_worker_run (EConfigLookupWorker *lookup_worker, g_strfreev (servers_strv); } - if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) && - certificate_pem && *certificate_pem && certificate_errors) { - gchar *description = e_trust_prompt_describe_certificate_errors (certificate_errors); - - if (description) { - g_set_error_literal (error, E_CONFIG_LOOKUP_WORKER_ERROR, - E_CONFIG_LOOKUP_WORKER_ERROR_CERTIFICATE, description); - - g_free (description); - - if (out_restart_params) { - if (!*out_restart_params) - *out_restart_params = e_named_parameters_new_clone (params); - - e_named_parameters_set (*out_restart_params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_PEM, certificate_pem); - e_named_parameters_set (*out_restart_params, E_CONFIG_LOOKUP_PARAM_CERTIFICATE_HOST, certificate_host); - } - } - } - - g_clear_error (&local_error); - g_free (certificate_host); - g_free (certificate_pem); - - if (out_restart_params && !*out_restart_params) + if (out_restart_params) *out_restart_params = e_named_parameters_new_clone (params); } diff --git a/src/configuration/e-ews-config-lookup.c.cve-2019-3890 b/src/configuration/e-ews-config-lookup.c.cve-2019-3890 deleted file mode 100644 index c18a7ac..0000000 --- a/src/configuration/e-ews-config-lookup.c.cve-2019-3890 +++ /dev/null @@ -1,438 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com) - * - * This library 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. - * - * This library 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 this library. If not, see . - */ - -#include "evolution-ews-config.h" - -#include - -#include - -#include "server/camel-ews-settings.h" -#include "server/e-ews-connection.h" -#include "e-ews-config-lookup.h" - -#define E_TYPE_EWS_CONFIG_LOOKUP_RESULT \ - (e_ews_config_lookup_result_get_type ()) -#define E_EWS_CONFIG_LOOKUP_RESULT(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), E_TYPE_EWS_CONFIG_LOOKUP_RESULT, EEwsConfigLookupResult)) -#define E_IS_EWS_CONFIG_LOOKUP_RESULT(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), E_TYPE_EWS_CONFIG_LOOKUP_RESULT)) - -typedef struct _EEwsConfigLookupResult EEwsConfigLookupResult; -typedef struct _EEwsConfigLookupResultClass EEwsConfigLookupResultClass; - -struct _EEwsConfigLookupResult { - /*< private >*/ - EConfigLookupResultSimple parent; - - gchar *host; -}; - -struct _EEwsConfigLookupResultClass { - /*< private >*/ - EConfigLookupResultSimpleClass parent_class; -}; - -GType e_ews_config_lookup_result_get_type (void) G_GNUC_CONST; - -G_DEFINE_TYPE (EEwsConfigLookupResult, e_ews_config_lookup_result, E_TYPE_CONFIG_LOOKUP_RESULT_SIMPLE) - -static void -ews_config_lookup_result_copy_authentication (ESourceAuthentication *src_authentication_extension, - ESource *des_source) -{ - ESourceAuthentication *des_authentication_extension; - - g_return_if_fail (E_IS_SOURCE_AUTHENTICATION (src_authentication_extension)); - g_return_if_fail (E_IS_SOURCE (des_source)); - - des_authentication_extension = e_source_get_extension (des_source, E_SOURCE_EXTENSION_AUTHENTICATION); - - e_source_authentication_set_host (des_authentication_extension, - e_source_authentication_get_host (src_authentication_extension)); - - e_source_authentication_set_port (des_authentication_extension, - e_source_authentication_get_port (src_authentication_extension)); - - e_source_authentication_set_user (des_authentication_extension, - e_source_authentication_get_user (src_authentication_extension)); -} - -static gboolean -ews_config_lookup_result_configure_source (EConfigLookupResult *lookup_result, - EConfigLookup *config_lookup, - ESource *source) -{ - ESource *other_source; - ESourceAuthentication *src_authentication_extension; - ESourceMailAccount *mail_account_extension; - ESourceMailTransport *mail_transport_extension; - - g_return_val_if_fail (E_IS_EWS_CONFIG_LOOKUP_RESULT (lookup_result), FALSE); - g_return_val_if_fail (E_IS_CONFIG_LOOKUP (config_lookup), FALSE); - - /* Chain up to parent's method. */ - if (!E_CONFIG_LOOKUP_RESULT_SIMPLE_CLASS (e_ews_config_lookup_result_parent_class)->configure_source (lookup_result, config_lookup, source)) - return FALSE; - - src_authentication_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION); - - other_source = e_config_lookup_get_source (config_lookup, E_CONFIG_LOOKUP_SOURCE_MAIL_ACCOUNT); - mail_account_extension = e_source_get_extension (other_source, E_SOURCE_EXTENSION_MAIL_ACCOUNT); - ews_config_lookup_result_copy_authentication (src_authentication_extension, other_source); - e_source_backend_set_backend_name (E_SOURCE_BACKEND (mail_account_extension), "ews"); - - other_source = e_config_lookup_get_source (config_lookup, E_CONFIG_LOOKUP_SOURCE_MAIL_TRANSPORT); - mail_transport_extension = e_source_get_extension (other_source, E_SOURCE_EXTENSION_MAIL_TRANSPORT); - ews_config_lookup_result_copy_authentication (src_authentication_extension, other_source); - e_source_backend_set_backend_name (E_SOURCE_BACKEND (mail_transport_extension), "ews"); - - return TRUE; -} - -static void -e_ews_config_lookup_result_class_init (EEwsConfigLookupResultClass *klass) -{ - EConfigLookupResultSimpleClass *simple_result_class; - - simple_result_class = E_CONFIG_LOOKUP_RESULT_SIMPLE_CLASS (klass); - simple_result_class->configure_source = ews_config_lookup_result_configure_source; -} - -static void -e_ews_config_lookup_result_init (EEwsConfigLookupResult *ews_result) -{ -} - -static EConfigLookupResult * -e_ews_config_lookup_result_new (EConfigLookupResultKind kind, - gint priority, - const gchar *protocol, - const gchar *display_name, - const gchar *description, - const gchar *password) -{ - EEwsConfigLookupResult *ews_result; - - g_return_val_if_fail (protocol != NULL, NULL); - g_return_val_if_fail (display_name != NULL, NULL); - g_return_val_if_fail (description != NULL, NULL); - - ews_result = g_object_new (E_TYPE_EWS_CONFIG_LOOKUP_RESULT, - "kind", kind, - "priority", priority, - "is-complete", TRUE, - "protocol", protocol, - "display-name", display_name, - "description", description, - "password", password, - NULL); - - return E_CONFIG_LOOKUP_RESULT (ews_result); -} - -/* ------------------------------------------------------------------------- */ - -/* Standard GObject macros */ -#define E_TYPE_EWS_CONFIG_LOOKUP \ - (e_ews_config_lookup_get_type ()) -#define E_EWS_CONFIG_LOOKUP(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), E_TYPE_EWS_CONFIG_LOOKUP, EEwsConfigLookup)) -#define E_EWS_CONFIG_LOOKUP_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), E_TYPE_EWS_CONFIG_LOOKUP, EEwsConfigLookupClass)) -#define E_IS_EWS_CONFIG_LOOKUP(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), E_TYPE_EWS_CONFIG_LOOKUP)) -#define E_IS_EWS_CONFIG_LOOKUP_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), E_TYPE_EWS_CONFIG_LOOKUP)) -#define E_EWS_CONFIG_LOOKUP_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), E_TYPE_EWS_CONFIG_LOOKUP, EEwsConfigLookupClass)) - -typedef struct _EEwsConfigLookup EEwsConfigLookup; -typedef struct _EEwsConfigLookupClass EEwsConfigLookupClass; - -struct _EEwsConfigLookup { - EExtension parent; -}; - -struct _EEwsConfigLookupClass { - EExtensionClass parent_class; -}; - -GType e_ews_config_lookup_get_type (void) G_GNUC_CONST; - -static void ews_config_lookup_worker_iface_init (EConfigLookupWorkerInterface *iface); - -G_DEFINE_DYNAMIC_TYPE_EXTENDED (EEwsConfigLookup, e_ews_config_lookup, E_TYPE_EXTENSION, 0, - G_IMPLEMENT_INTERFACE_DYNAMIC (E_TYPE_CONFIG_LOOKUP_WORKER, ews_config_lookup_worker_iface_init)) - -static const gchar * -ews_config_lookup_worker_get_display_name (EConfigLookupWorker *worker) -{ - return _("Exchange Web Services autodiscovery"); -} - -static void -ews_config_lookup_worker_result_from_settings (EConfigLookupWorker *lookup_worker, - EConfigLookup *config_lookup, - const gchar *email_address, - CamelEwsSettings *ews_settings, - const ENamedParameters *params) -{ - const gchar *url; - - g_return_if_fail (E_IS_EWS_CONFIG_LOOKUP (lookup_worker)); - g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup)); - g_return_if_fail (CAMEL_IS_EWS_SETTINGS (ews_settings)); - - url = camel_ews_settings_get_hosturl (ews_settings); - if (url && *url) { - EConfigLookupResult *lookup_result; - GString *description; - gchar *tmp, *ptr, *user; - const gchar *extension_name; - SoupURI *suri; - - extension_name = e_source_camel_get_extension_name ("ews"); - - tmp = g_strdup (email_address); - ptr = tmp ? strchr (tmp, '@') : NULL; - if (ptr) - *ptr = '\0'; - - if (!tmp || !*tmp) { - g_free (tmp); - tmp = NULL; - } - - user = tmp; - - suri = soup_uri_new (url); - - description = g_string_new (""); - - if (user && *user) - g_string_append_printf (description, _("User: %s"), user); - - if (description->len) - g_string_append_c (description, '\n'); - - g_string_append_printf (description, _("Host URL: %s"), url); - - url = camel_ews_settings_get_oaburl (ews_settings); - if (url && *url) { - g_string_append_c (description, '\n'); - g_string_append_printf (description, _("OAB URL: %s"), url); - } - - lookup_result = e_ews_config_lookup_result_new (E_CONFIG_LOOKUP_RESULT_COLLECTION, - E_CONFIG_LOOKUP_RESULT_PRIORITY_IMAP - 100, /* This is better than IMAP */ - "ews", - _("Exchange Web Services"), - description->str, - params && e_named_parameters_exists (params, E_CONFIG_LOOKUP_PARAM_PASSWORD) && - e_named_parameters_exists (params, E_CONFIG_LOOKUP_PARAM_REMEMBER_PASSWORD) ? - e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_PASSWORD) : NULL); - - e_config_lookup_result_simple_add_string (lookup_result, E_SOURCE_EXTENSION_COLLECTION, - "backend-name", "ews"); - - e_config_lookup_result_simple_add_string (lookup_result, extension_name, - "hosturl", camel_ews_settings_get_hosturl (ews_settings)); - - e_config_lookup_result_simple_add_string (lookup_result, extension_name, - "oaburl", camel_ews_settings_get_oaburl (ews_settings)); - - if (user && *user) { - e_config_lookup_result_simple_add_string (lookup_result, - E_SOURCE_EXTENSION_COLLECTION, - "identity", user); - - e_config_lookup_result_simple_add_string (lookup_result, - E_SOURCE_EXTENSION_AUTHENTICATION, - "user", user); - } - - if (suri && suri->host && *suri->host) { - e_config_lookup_result_simple_add_string (lookup_result, - E_SOURCE_EXTENSION_AUTHENTICATION, - "host", suri->host); - } - - if (suri && suri->port) { - e_config_lookup_result_simple_add_uint (lookup_result, - E_SOURCE_EXTENSION_AUTHENTICATION, - "port", suri->port); - } - - e_config_lookup_add_result (config_lookup, lookup_result); - - g_string_free (description, TRUE); - g_free (user); - if (suri) - soup_uri_free (suri); - } -} - -static void -ews_config_lookup_worker_run (EConfigLookupWorker *lookup_worker, - EConfigLookup *config_lookup, - const ENamedParameters *params, - ENamedParameters **out_restart_params, - GCancellable *cancellable, - GError **error) -{ - CamelEwsSettings *ews_settings = NULL; - ESource *source; - const gchar *email_address; - const gchar *password; - const gchar *extension_name; - - g_return_if_fail (E_IS_EWS_CONFIG_LOOKUP (lookup_worker)); - g_return_if_fail (E_IS_CONFIG_LOOKUP (config_lookup)); - g_return_if_fail (params != NULL); - - email_address = e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_EMAIL_ADDRESS); - - if (!email_address || !*email_address) - return; - - if (!e_named_parameters_exists (params, E_CONFIG_LOOKUP_PARAM_PASSWORD)) { - g_set_error (error, E_CONFIG_LOOKUP_WORKER_ERROR, E_CONFIG_LOOKUP_WORKER_ERROR_REQUIRES_PASSWORD, - _("Requires user password to continue")); - return; - } - - ews_settings = g_object_new (CAMEL_TYPE_EWS_SETTINGS, NULL); - camel_ews_settings_set_email (ews_settings, email_address); - - extension_name = e_source_camel_get_extension_name ("ews"); - source = e_config_lookup_get_source (config_lookup, E_CONFIG_LOOKUP_SOURCE_COLLECTION); - if (source && e_source_has_extension (source, extension_name)) { - ESourceCamel *camel_extension; - CamelSettings *settings; - - camel_extension = e_source_get_extension (source, extension_name); - settings = e_source_camel_get_settings (camel_extension); - - /* Copy only the host url */ - if (CAMEL_IS_EWS_SETTINGS (settings)) - camel_ews_settings_set_hosturl (ews_settings, camel_ews_settings_get_hosturl (CAMEL_EWS_SETTINGS (settings))); - } - - password = e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_PASSWORD); - - if (password) { - const gchar *servers; - - if (e_ews_autodiscover_ws_url_sync (source, ews_settings, email_address, password, cancellable, NULL)) { - ews_config_lookup_worker_result_from_settings (lookup_worker, config_lookup, email_address, ews_settings, params); - } - - servers = e_named_parameters_get (params, E_CONFIG_LOOKUP_PARAM_SERVERS); - - if (servers && *servers) { - gchar **servers_strv; - gint ii; - - servers_strv = g_strsplit (servers, ";", 0); - - for (ii = 0; servers_strv && servers_strv[ii] && !g_cancellable_is_cancelled (cancellable); ii++) { - const gchar *server = servers_strv[ii]; - gchar *tmp = NULL; - - if (*server && !strstr (server, "://")) { - tmp = g_strconcat ("https://", server, "/EWS/Exchange.asmx", NULL); - server = tmp; - } - - camel_ews_settings_set_hosturl (ews_settings, server); - - if (e_ews_autodiscover_ws_url_sync (source, ews_settings, email_address, password, cancellable, NULL)) { - ews_config_lookup_worker_result_from_settings (lookup_worker, config_lookup, email_address, ews_settings, params); - } - - g_free (tmp); - } - - g_strfreev (servers_strv); - } - - if (out_restart_params) - *out_restart_params = e_named_parameters_new_clone (params); - } - - g_clear_object (&ews_settings); -} - -static void -ews_config_lookup_constructed (GObject *object) -{ - EConfigLookup *config_lookup; - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_ews_config_lookup_parent_class)->constructed (object); - - config_lookup = E_CONFIG_LOOKUP (e_extension_get_extensible (E_EXTENSION (object))); - - e_config_lookup_register_worker (config_lookup, E_CONFIG_LOOKUP_WORKER (object)); -} - -static void -e_ews_config_lookup_class_init (EEwsConfigLookupClass *class) -{ - GObjectClass *object_class; - EExtensionClass *extension_class; - - object_class = G_OBJECT_CLASS (class); - object_class->constructed = ews_config_lookup_constructed; - - extension_class = E_EXTENSION_CLASS (class); - extension_class->extensible_type = E_TYPE_CONFIG_LOOKUP; -} - -static void -e_ews_config_lookup_class_finalize (EEwsConfigLookupClass *class) -{ -} - -static void -ews_config_lookup_worker_iface_init (EConfigLookupWorkerInterface *iface) -{ - iface->get_display_name = ews_config_lookup_worker_get_display_name; - iface->run = ews_config_lookup_worker_run; -} - -static void -e_ews_config_lookup_init (EEwsConfigLookup *extension) -{ -} - -void -e_ews_config_lookup_type_register (GTypeModule *type_module) -{ - /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration - * function, so we have to wrap it with a public function in - * order to register types from a separate compilation unit. */ - e_ews_config_lookup_register_type (type_module); -} diff --git a/src/configuration/e-ews-config-utils.c b/src/configuration/e-ews-config-utils.c index f5e89cb..c303784 100644 --- a/src/configuration/e-ews-config-utils.c +++ b/src/configuration/e-ews-config-utils.c @@ -317,7 +317,7 @@ ews_config_utils_try_credentials_sync (ECredentialsPrompter *prompter, if (data->try_credentials_func) auth_result = data->try_credentials_func (data->conn, credentials, data->user_data, cancellable, error); else - auth_result = e_ews_connection_try_credentials_sync (data->conn, credentials, NULL, NULL, NULL, cancellable, error); + auth_result = e_ews_connection_try_credentials_sync (data->conn, credentials, cancellable, error); if (auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) { *out_authenticated = TRUE; @@ -377,7 +377,7 @@ e_ews_config_utils_open_connection_for (ESource *source, if (try_credentials_func) result = try_credentials_func (conn, NULL, user_data, cancellable, &local_error); else - result = e_ews_connection_try_credentials_sync (conn, NULL, NULL, NULL, NULL, cancellable, &local_error); + result = e_ews_connection_try_credentials_sync (conn, NULL, cancellable, &local_error); if (result != E_SOURCE_AUTHENTICATION_ACCEPTED) { g_clear_object (&conn); diff --git a/src/configuration/e-ews-config-utils.c.cve-2019-3890 b/src/configuration/e-ews-config-utils.c.cve-2019-3890 deleted file mode 100644 index c303784..0000000 --- a/src/configuration/e-ews-config-utils.c.cve-2019-3890 +++ /dev/null @@ -1,1511 +0,0 @@ -/* - * e-ews-config-utils.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 "evolution-ews-config.h" - -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "server/e-ews-connection.h" -#include "server/e-ews-connection-utils.h" -#include "server/e-source-ews-folder.h" - -#include "e-ews-edit-folder-permissions.h" - -#include "camel/camel-ews-store.h" -#include "camel/camel-ews-store-summary.h" - -#include "e-ews-config-utils.h" -#include "e-ews-search-user.h" -#include "e-ews-subscribe-foreign-folder.h" - -struct RunWithFeedbackData -{ - GtkWindow *parent; - GtkWidget *dialog; - GCancellable *cancellable; - GObject *with_object; - EEwsSetupFunc thread_func; - EEwsSetupFunc idle_func; - EEwsSetupFunc finish_idle_func; - gpointer user_data; - GDestroyNotify free_user_data; - GError *error; - gboolean run_modal; -}; - -static void -free_run_with_feedback_data (gpointer ptr) -{ - struct RunWithFeedbackData *rfd = ptr; - - if (!rfd) - return; - - if (rfd->dialog) - gtk_widget_destroy (rfd->dialog); - - g_object_unref (rfd->cancellable); - g_object_unref (rfd->with_object); - - if (rfd->free_user_data) - rfd->free_user_data (rfd->user_data); - - g_clear_error (&rfd->error); - - g_free (rfd); -} - -static gboolean -run_with_feedback_idle (gpointer user_data) -{ - struct RunWithFeedbackData *rfd = user_data; - gboolean was_cancelled = FALSE; - - g_return_val_if_fail (rfd != NULL, FALSE); - - if (!g_cancellable_is_cancelled (rfd->cancellable)) { - if (rfd->idle_func && !rfd->error) - rfd->idle_func (rfd->with_object, rfd->user_data, rfd->cancellable, &rfd->error); - - was_cancelled = g_cancellable_is_cancelled (rfd->cancellable); - - if (rfd->dialog) { - gtk_widget_destroy (rfd->dialog); - rfd->dialog = NULL; - } - } else { - was_cancelled = TRUE; - } - - if (rfd->finish_idle_func) - rfd->finish_idle_func (rfd->with_object, rfd->user_data, rfd->cancellable, &rfd->error); - - if (!was_cancelled) { - if (rfd->error) { - g_dbus_error_strip_remote_error (rfd->error); - - e_notice (rfd->parent, GTK_MESSAGE_ERROR, "%s", rfd->error->message); - } - } - - free_run_with_feedback_data (rfd); - - return FALSE; -} - -static gpointer -run_with_feedback_thread (gpointer user_data) -{ - struct RunWithFeedbackData *rfd = user_data; - - g_return_val_if_fail (rfd != NULL, NULL); - g_return_val_if_fail (rfd->thread_func != NULL, NULL); - - if (!g_cancellable_is_cancelled (rfd->cancellable)) - rfd->thread_func (rfd->with_object, rfd->user_data, rfd->cancellable, &rfd->error); - - g_idle_add (run_with_feedback_idle, rfd); - - return NULL; -} - -static void -run_with_feedback_response_cb (GtkWidget *dialog, - gint resonse_id, - struct RunWithFeedbackData *rfd) -{ - g_return_if_fail (rfd != NULL); - - rfd->dialog = NULL; - - g_cancellable_cancel (rfd->cancellable); - - gtk_widget_destroy (dialog); -} - -static void -e_ews_config_utils_run_in_thread_with_feedback_general (GtkWindow *parent, - GObject *with_object, - const gchar *description, - EEwsSetupFunc thread_func, - EEwsSetupFunc idle_func, - gpointer user_data, - GDestroyNotify free_user_data, - gboolean run_modal) -{ - GtkWidget *dialog, *label, *content, *spinner, *box; - struct RunWithFeedbackData *rfd; - - g_return_if_fail (with_object != NULL); - g_return_if_fail (description != NULL); - g_return_if_fail (thread_func != NULL); - - dialog = gtk_dialog_new_with_buttons ( - "", - parent, - GTK_DIALOG_MODAL, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - NULL); - - box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6); - - spinner = e_spinner_new (); - e_spinner_start (E_SPINNER (spinner)); - gtk_box_pack_start (GTK_BOX (box), spinner, FALSE, FALSE, 0); - - label = gtk_label_new (description); - gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0); - - gtk_widget_show_all (box); - - content = gtk_dialog_get_content_area (GTK_DIALOG (dialog)); - - gtk_container_add (GTK_CONTAINER (content), box); - gtk_container_set_border_width (GTK_CONTAINER (content), 12); - - rfd = g_new0 (struct RunWithFeedbackData, 1); - rfd->parent = parent; - rfd->dialog = dialog; - rfd->cancellable = g_cancellable_new (); - rfd->with_object = g_object_ref (with_object); - rfd->thread_func = thread_func; - rfd->idle_func = idle_func; - rfd->finish_idle_func = NULL; - rfd->user_data = user_data; - rfd->free_user_data = free_user_data; - rfd->error = NULL; - rfd->run_modal = run_modal; - - g_signal_connect (dialog, "response", G_CALLBACK (run_with_feedback_response_cb), rfd); - - if (run_modal) { - GThread *thread; - GCancellable *cancellable; - - cancellable = g_object_ref (rfd->cancellable); - - thread = g_thread_new (NULL, run_with_feedback_thread, rfd); - g_thread_unref (thread); - - gtk_dialog_run (GTK_DIALOG (dialog)); - - g_cancellable_cancel (cancellable); - g_object_unref (cancellable); - } else { - GThread *thread; - - gtk_widget_show (dialog); - - thread = g_thread_new (NULL, run_with_feedback_thread, rfd); - g_thread_unref (thread); - } -} - -void -e_ews_config_utils_run_in_thread_with_feedback (GtkWindow *parent, - GObject *with_object, - const gchar *description, - EEwsSetupFunc thread_func, - EEwsSetupFunc idle_func, - gpointer user_data, - GDestroyNotify free_user_data) -{ - e_ews_config_utils_run_in_thread_with_feedback_general (parent, with_object, description, thread_func, idle_func, user_data, free_user_data, FALSE); -} - -void -e_ews_config_utils_run_in_thread_with_feedback_modal (GtkWindow *parent, - GObject *with_object, - const gchar *description, - EEwsSetupFunc thread_func, - EEwsSetupFunc idle_func, - gpointer user_data, - GDestroyNotify free_user_data) -{ - e_ews_config_utils_run_in_thread_with_feedback_general (parent, with_object, description, thread_func, idle_func, user_data, free_user_data, TRUE); -} - -void -e_ews_config_utils_run_in_thread (GObject *with_object, - EEwsSetupFunc thread_func, - EEwsSetupFunc idle_func, - gpointer user_data, - GDestroyNotify free_user_data, - GCancellable *cancellable) -{ - struct RunWithFeedbackData *rfd; - GThread *thread; - - g_return_if_fail (with_object != NULL); - g_return_if_fail (thread_func != NULL); - - rfd = g_new0 (struct RunWithFeedbackData, 1); - rfd->parent = NULL; - rfd->dialog = NULL; - rfd->cancellable = cancellable ? g_object_ref (cancellable) : g_cancellable_new (); - rfd->with_object = g_object_ref (with_object); - rfd->thread_func = thread_func; - rfd->idle_func = NULL; - rfd->finish_idle_func = idle_func; - rfd->user_data = user_data; - rfd->free_user_data = free_user_data; - rfd->error = NULL; - rfd->run_modal = FALSE; - - thread = g_thread_new (NULL, run_with_feedback_thread, rfd); - g_thread_unref (thread); -} - -typedef struct _TryCredentialsData { - CamelEwsSettings *ews_settings; - const gchar *connect_url; - EEwsConfigUtilTryCredentialsFunc try_credentials_func; - gpointer user_data; - EEwsConnection *conn; -} TryCredentialsData; - -static gboolean -ews_config_utils_try_credentials_sync (ECredentialsPrompter *prompter, - ESource *source, - const ENamedParameters *credentials, - gboolean *out_authenticated, - gpointer user_data, - GCancellable *cancellable, - GError **error) -{ - TryCredentialsData *data = user_data; - ESourceAuthenticationResult auth_result; - gchar *hosturl; - gboolean res = TRUE; - - hosturl = camel_ews_settings_dup_hosturl (data->ews_settings); - data->conn = e_ews_connection_new (source, data->connect_url ? data->connect_url : hosturl, data->ews_settings); - g_free (hosturl); - - e_ews_connection_update_credentials (data->conn, credentials); - - if (data->try_credentials_func) - auth_result = data->try_credentials_func (data->conn, credentials, data->user_data, cancellable, error); - else - auth_result = e_ews_connection_try_credentials_sync (data->conn, credentials, cancellable, error); - - if (auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) { - *out_authenticated = TRUE; - } else if (auth_result == E_SOURCE_AUTHENTICATION_REJECTED) { - *out_authenticated = FALSE; - g_clear_object (&data->conn); - g_clear_error (error); - } else { - res = FALSE; - g_clear_object (&data->conn); - } - - return res; -} - -EEwsConnection * -e_ews_config_utils_open_connection_for (ESource *source, - CamelEwsSettings *ews_settings, - const gchar *connect_url, - EEwsConfigUtilTryCredentialsFunc try_credentials_func, - gpointer user_data, - GCancellable *cancellable, - GError **perror) -{ - EEwsConnection *conn = NULL; - CamelNetworkSettings *network_settings; - GError *local_error = NULL; - - g_return_val_if_fail (source != NULL, NULL); - g_return_val_if_fail (ews_settings != NULL, NULL); - - network_settings = CAMEL_NETWORK_SETTINGS (ews_settings); - - /* use the one from mailer, if there, otherwise open new */ - conn = e_ews_connection_find ( - connect_url && *connect_url ? connect_url : camel_ews_settings_get_hosturl (ews_settings), - camel_network_settings_get_user (network_settings)); - if (conn) { - if (try_credentials_func && - try_credentials_func (conn, NULL, user_data, cancellable, perror) != E_SOURCE_AUTHENTICATION_ACCEPTED) { - g_clear_object (&conn); - } - return conn; - } - - while (!conn && !g_cancellable_is_cancelled (cancellable) && !local_error) { - if (e_ews_connection_utils_get_without_password (ews_settings)) { - ESourceAuthenticationResult result; - gchar *hosturl; - - hosturl = camel_ews_settings_dup_hosturl (ews_settings); - conn = e_ews_connection_new (source, connect_url && *connect_url ? connect_url : hosturl, ews_settings); - g_free (hosturl); - - e_ews_connection_update_credentials (conn, NULL); - - if (try_credentials_func) - result = try_credentials_func (conn, NULL, user_data, cancellable, &local_error); - else - result = e_ews_connection_try_credentials_sync (conn, NULL, cancellable, &local_error); - - if (result != E_SOURCE_AUTHENTICATION_ACCEPTED) { - g_clear_object (&conn); - if (result != E_SOURCE_AUTHENTICATION_REJECTED || local_error) - break; - } - } - - if (!conn) { - EShell *shell; - TryCredentialsData data; - - e_ews_connection_utils_force_off_ntlm_auth_check (); - g_clear_error (&local_error); - - shell = e_shell_get_default (); - - data.ews_settings = g_object_ref (ews_settings); - data.connect_url = connect_url && *connect_url ? connect_url : NULL; - data.try_credentials_func = try_credentials_func; - data.user_data = user_data; - data.conn = NULL; - - e_credentials_prompter_loop_prompt_sync (e_shell_get_credentials_prompter (shell), - source, E_CREDENTIALS_PROMPTER_PROMPT_FLAG_ALLOW_SOURCE_SAVE, - ews_config_utils_try_credentials_sync, &data, cancellable, &local_error); - - if (data.conn) - conn = g_object_ref (data.conn); - - g_clear_object (&data.ews_settings); - g_clear_object (&data.conn); - } - } - - if (local_error) - g_propagate_error (perror, local_error); - - return conn; -} - -enum { - COL_FOLDER_ICON = 0, /* G_TYPE_STRING */ - COL_FOLDER_NAME, /* G_TYPE_STRING */ - COL_FOLDER_SIZE, /* G_TYPE_STRING */ - COL_FOLDER_FLAGS, /* G_TYPE_UINT */ - N_COLUMNS -}; - -typedef struct -{ - GtkDialog *dialog; - GtkGrid *spinner_grid; - - ESourceRegistry *registry; - ESource *source; - CamelEwsSettings *ews_settings; - CamelEwsStore *ews_store; - - GHashTable *folder_sizes; - GCancellable *cancellable; - GError *error; -} FolderSizeDialogData; - -static gint -folder_tree_model_sort (GtkTreeModel *model, - GtkTreeIter *a, - GtkTreeIter *b, - gpointer unused) -{ - gchar *aname, *bname; - guint32 aflags, bflags; - gint ret = -2; - - gtk_tree_model_get ( - model, a, - COL_FOLDER_NAME, &aname, - COL_FOLDER_FLAGS, &aflags, - -1); - - gtk_tree_model_get ( - model, b, - COL_FOLDER_NAME, &bname, - COL_FOLDER_FLAGS, &bflags, - -1); - - /* Inbox is always first. */ - if ((aflags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX) - ret = -1; - else if ((bflags & CAMEL_FOLDER_TYPE_MASK) == CAMEL_FOLDER_TYPE_INBOX) - ret = 1; - else { - if (aname != NULL && bname != NULL) - ret = g_utf8_collate (aname, bname); - else if (aname == bname) - ret = 0; - else if (aname == NULL) - ret = -1; - else - ret = 1; - } - - g_free (aname); - g_free (bname); - - return ret; -} - -static void -folder_sizes_tree_populate (GtkTreeStore *store, - CamelFolderInfo *folder_info, - GtkTreeIter *parent, - FolderSizeDialogData *fsd) -{ - while (folder_info != NULL) { - GtkTreeIter iter; - const gchar *icon_name; - const gchar *folder_size; - - icon_name = em_folder_utils_get_icon_name (folder_info->flags); - if (g_strcmp0 (icon_name, "folder") == 0) { - CamelFolder *folder; - - folder = camel_store_get_folder_sync ( - CAMEL_STORE (fsd->ews_store), folder_info->full_name, 0, NULL, NULL); - - if (folder != NULL) { - if (em_utils_folder_is_drafts (fsd->registry, folder)) - icon_name = "accessories-text-editor"; - - g_object_unref (folder); - } - } - - folder_size = g_hash_table_lookup (fsd->folder_sizes, folder_info->full_name); - - gtk_tree_store_append (store, &iter, parent); - gtk_tree_store_set (store, &iter, - COL_FOLDER_ICON, icon_name, - COL_FOLDER_NAME, folder_info->display_name, - COL_FOLDER_SIZE, folder_size, - COL_FOLDER_FLAGS, folder_info->flags, - -1); - - if (folder_info->child != NULL) - folder_sizes_tree_populate (store, folder_info->child, &iter, fsd); - - folder_info = folder_info->next; - } -} - -static gboolean -ews_settings_get_folder_sizes_idle (gpointer user_data) -{ - GtkWidget *widget; - GtkCellRenderer *renderer; - GtkTreeStore *tree_store; - GtkBox *content_area; - FolderSizeDialogData *fsd = user_data; - CamelFolderInfo *root; - - g_return_val_if_fail (fsd != NULL, FALSE); - - if (g_cancellable_is_cancelled (fsd->cancellable)) - goto cleanup; - - /* Hide progress bar. Set status */ - gtk_widget_destroy (GTK_WIDGET (fsd->spinner_grid)); - - if (fsd->folder_sizes != NULL) { - GtkWidget *scrolledwindow, *tree_view; - - scrolledwindow = gtk_scrolled_window_new (NULL, NULL); - gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwindow), - GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); - gtk_widget_show (scrolledwindow); - - /* Tree View */ - tree_view = gtk_tree_view_new (); - renderer = gtk_cell_renderer_pixbuf_new (); - gtk_tree_view_insert_column_with_attributes ( - GTK_TREE_VIEW (tree_view), - -1, - NULL, - renderer, - "icon-name", - COL_FOLDER_ICON, - NULL); - - renderer = gtk_cell_renderer_text_new (); - gtk_tree_view_insert_column_with_attributes ( - GTK_TREE_VIEW (tree_view), - -1, - _("Folder"), - renderer, - "text", - COL_FOLDER_NAME, - NULL); - - renderer = gtk_cell_renderer_text_new (); - gtk_tree_view_insert_column_with_attributes ( - GTK_TREE_VIEW (tree_view), - -1, - _("Size"), - renderer, - "text", - COL_FOLDER_SIZE, - NULL); - - /* Model for TreeView */ - tree_store = gtk_tree_store_new ( - N_COLUMNS, - /* COL_FOLDER_ICON */ G_TYPE_STRING, - /* COL_FOLDER_NAME */ G_TYPE_STRING, - /* COL_FOLDER_SIZE */ G_TYPE_STRING, - /* COL_FOLDER_FLAGS */ G_TYPE_UINT); - - gtk_tree_sortable_set_default_sort_func ( - GTK_TREE_SORTABLE (tree_store), - folder_tree_model_sort, NULL, NULL); - - gtk_tree_sortable_set_sort_column_id ( - GTK_TREE_SORTABLE (tree_store), - GTK_TREE_SORTABLE_DEFAULT_SORT_COLUMN_ID, - GTK_SORT_ASCENDING); - - gtk_tree_view_set_model (GTK_TREE_VIEW (tree_view), GTK_TREE_MODEL (tree_store)); - - root = camel_store_get_folder_info_sync ( - CAMEL_STORE (fsd->ews_store), NULL, - CAMEL_STORE_FOLDER_INFO_RECURSIVE, - NULL, NULL); - - folder_sizes_tree_populate (tree_store, root, NULL, fsd); - - camel_folder_info_free (root); - - gtk_tree_view_expand_all (GTK_TREE_VIEW (tree_view)); - gtk_container_add (GTK_CONTAINER (scrolledwindow), tree_view); - widget = scrolledwindow; - } else if (fsd->error) { - gchar *msg = g_strconcat (_("Unable to retrieve folder size information"), "\n", - fsd->error->message, NULL); - widget = gtk_label_new (msg); - g_free (msg); - } else { - widget = gtk_label_new (_("Unable to retrieve folder size information")); - } - - gtk_widget_show_all (widget); - - /* Pack into content_area */ - content_area = GTK_BOX (gtk_dialog_get_content_area (fsd->dialog)); - gtk_box_pack_start (content_area, widget, TRUE, TRUE, 6); - -cleanup: - g_hash_table_destroy (fsd->folder_sizes); - g_object_unref (fsd->registry); - g_object_unref (fsd->source); - g_object_unref (fsd->ews_settings); - g_object_unref (fsd->ews_store); - g_object_unref (fsd->cancellable); - g_clear_error (&fsd->error); - g_free (fsd); - - return FALSE; -} - -static gpointer -ews_settings_get_folder_sizes_thread (gpointer user_data) -{ - FolderSizeDialogData *fsd = user_data; - EEwsConnection *cnc; - - g_return_val_if_fail (fsd != NULL, NULL); - - cnc = e_ews_config_utils_open_connection_for ( - fsd->source, - fsd->ews_settings, - NULL, NULL, NULL, - fsd->cancellable, - &fsd->error); - - if (cnc) { - EEwsAdditionalProps *add_props; - EEwsExtendedFieldURI *ext_uri; - GSList *ids, *l, *folders_ids = NULL, *folders_list = NULL; - - fsd->folder_sizes = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - - /* Use MAPI property to retrieve folder size */ - add_props = e_ews_additional_props_new (); - ext_uri = e_ews_extended_field_uri_new (); - ext_uri->prop_tag = g_strdup_printf ("%d", 0x0e08); /* Folder size property tag */ - ext_uri->prop_type = g_strdup ("Integer"); - add_props->extended_furis = g_slist_prepend (add_props->extended_furis, ext_uri); - - ids = camel_ews_store_summary_get_folders (fsd->ews_store->summary, NULL); - for (l = ids; l != NULL; l = l->next) { - EwsFolderId *fid; - fid = e_ews_folder_id_new (l->data, NULL, FALSE); - folders_ids = g_slist_prepend (folders_ids, fid); - } - folders_ids = g_slist_reverse (folders_ids); - - e_ews_connection_get_folder_sync ( - cnc, EWS_PRIORITY_MEDIUM, "Default", - add_props, folders_ids, &folders_list, - fsd->cancellable, &fsd->error); - - for (l = folders_list; l != NULL; l = l->next) { - const EEwsFolder *folder = l->data; - const EwsFolderId *folder_id; - gchar *folder_full_name; - gchar *folder_size; - - if (!folder || e_ews_folder_is_error (folder)) - continue; - - folder_id = e_ews_folder_get_id (folder); - if (!folder_id) - continue; - - folder_full_name = camel_ews_store_summary_get_folder_full_name ( - fsd->ews_store->summary, folder_id->id, NULL); - folder_size = g_format_size (e_ews_folder_get_size (folder)); - - g_hash_table_insert (fsd->folder_sizes, folder_full_name, folder_size); - } - - g_slist_free_full (folders_list, g_object_unref); - g_slist_free_full (folders_ids, (GDestroyNotify) e_ews_folder_id_free); - g_slist_free_full (ids, g_free); - e_ews_additional_props_free (add_props); - g_object_unref (cnc); - } - - g_idle_add (ews_settings_get_folder_sizes_idle, fsd); - - return NULL; -} - -static void -folder_sizes_dialog_response_cb (GObject *dialog, - gint response_id, - gpointer data) -{ - GCancellable *cancellable = data; - - g_cancellable_cancel (cancellable); - g_object_unref (cancellable); - - gtk_widget_destroy (GTK_WIDGET (dialog)); -} - -void -e_ews_config_utils_run_folder_sizes_dialog (GtkWindow *parent, - ESourceRegistry *registry, - ESource *source, - CamelEwsStore *ews_store) -{ - GtkBox *content_area; - GtkWidget *spinner, *alignment, *dialog; - GtkWidget *spinner_label; - GCancellable *cancellable; - GThread *thread; - FolderSizeDialogData *fsd; - - g_return_if_fail (ews_store != NULL); - - cancellable = g_cancellable_new (); - - dialog = gtk_dialog_new_with_buttons ( - _("Folder Sizes"), - parent, - GTK_DIALOG_DESTROY_WITH_PARENT, - _("_Close"), GTK_RESPONSE_ACCEPT, - NULL); - - g_signal_connect (dialog, "response", G_CALLBACK (folder_sizes_dialog_response_cb), cancellable); - - fsd = g_new0 (FolderSizeDialogData, 1); - fsd->dialog = GTK_DIALOG (dialog); - - gtk_window_set_default_size (GTK_WINDOW (fsd->dialog), 250, 300); - - content_area = GTK_BOX (gtk_dialog_get_content_area (fsd->dialog)); - - spinner = e_spinner_new (); - e_spinner_start (E_SPINNER (spinner)); - spinner_label = gtk_label_new (_("Fetching folder list…")); - - fsd->spinner_grid = GTK_GRID (gtk_grid_new ()); - gtk_grid_set_column_spacing (fsd->spinner_grid, 6); - gtk_grid_set_column_homogeneous (fsd->spinner_grid, FALSE); - gtk_orientable_set_orientation (GTK_ORIENTABLE (fsd->spinner_grid), GTK_ORIENTATION_HORIZONTAL); - - alignment = gtk_alignment_new (1.0, 0.5, 0.0, 1.0); - gtk_container_add (GTK_CONTAINER (alignment), spinner); - gtk_misc_set_alignment (GTK_MISC (spinner_label), 0.0, 0.5); - - gtk_container_add (GTK_CONTAINER (fsd->spinner_grid), alignment); - gtk_container_add (GTK_CONTAINER (fsd->spinner_grid), spinner_label); - - /* Pack the TreeView into dialog's content area */ - gtk_box_pack_start (content_area, GTK_WIDGET (fsd->spinner_grid), TRUE, TRUE, 6); - gtk_widget_show_all (GTK_WIDGET (fsd->dialog)); - - fsd->registry = g_object_ref (registry); - fsd->source = g_object_ref (source); - fsd->ews_store = g_object_ref (ews_store); - fsd->ews_settings = CAMEL_EWS_SETTINGS (camel_service_ref_settings (CAMEL_SERVICE (ews_store))); - fsd->cancellable = g_object_ref (cancellable); - - thread = g_thread_new (NULL, ews_settings_get_folder_sizes_thread, fsd); - g_thread_unref (thread); - - /* Start the dialog */ - gtk_widget_show (GTK_WIDGET (dialog)); -} - -static gboolean -get_ews_store_from_folder_tree (EShellView *shell_view, - gchar **pfolder_path, - CamelStore **pstore) -{ - EShellSidebar *shell_sidebar; - EMFolderTree *folder_tree; - gchar *selected_path = NULL; - CamelStore *selected_store = NULL; - gboolean found = FALSE; - - /* Get hold of Folder Tree */ - shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); - g_object_get (shell_sidebar, "folder-tree", &folder_tree, NULL); - if (em_folder_tree_get_selected (folder_tree, &selected_store, &selected_path) || - em_folder_tree_store_root_selected (folder_tree, &selected_store)) { - if (selected_store) { - CamelProvider *provider = camel_service_get_provider (CAMEL_SERVICE (selected_store)); - - if (provider && g_ascii_strcasecmp (provider->protocol, "ews") == 0) { - found = TRUE; - - if (pstore) - *pstore = g_object_ref (selected_store); - - if (pfolder_path) - *pfolder_path = selected_path; - else - g_free (selected_path); - - selected_path = NULL; - } - - g_object_unref (selected_store); - } - - g_free (selected_path); - } - - g_object_unref (folder_tree); - - return found; -} - -static void -action_folder_sizes_cb (GtkAction *action, - EShellView *shell_view) -{ - GtkWindow *parent; - CamelSession *session; - CamelStore *store = NULL; - ESourceRegistry *registry; - ESource *source; - - if (!get_ews_store_from_folder_tree (shell_view, NULL, &store)) - return; - - g_return_if_fail (store != NULL); - - parent = GTK_WINDOW (e_shell_view_get_shell_window (shell_view)); - - session = camel_service_ref_session (CAMEL_SERVICE (store)); - registry = e_mail_session_get_registry (E_MAIL_SESSION (session)); - source = e_source_registry_ref_source (registry, camel_service_get_uid (CAMEL_SERVICE (store))); - - e_ews_config_utils_run_folder_sizes_dialog (parent, registry, source, CAMEL_EWS_STORE (store)); - - g_object_unref (source); - g_object_unref (session); - g_object_unref (store); -} - -static void -action_subscribe_foreign_folder_cb (GtkAction *action, - EShellView *shell_view) -{ - GtkWindow *parent; - EShell *shell; - EShellBackend *backend; - EClientCache *client_cache; - CamelSession *session = NULL; - CamelStore *store = NULL; - - if (!get_ews_store_from_folder_tree (shell_view, NULL, &store)) - return; - - parent = GTK_WINDOW (e_shell_view_get_shell_window (shell_view)); - backend = e_shell_view_get_shell_backend (shell_view); - g_object_get (G_OBJECT (backend), "session", &session, NULL); - - shell = e_shell_backend_get_shell (backend); - client_cache = e_shell_get_client_cache (shell); - - e_ews_subscribe_foreign_folder (parent, session, store, client_cache); - - g_object_unref (session); - g_object_unref (store); -} - -static void -action_folder_permissions_mail_cb (GtkAction *action, - EShellView *shell_view) -{ - gchar *folder_path = NULL; - EShellWindow *shell_window; - GtkWindow *parent; - CamelStore *store = NULL; - CamelEwsStore *ews_store; - gchar *str_folder_id; - - if (!get_ews_store_from_folder_tree (shell_view, &folder_path, &store)) - return; - - ews_store = CAMEL_EWS_STORE (store); - g_return_if_fail (ews_store != NULL); - g_return_if_fail (folder_path != NULL); - - shell_window = e_shell_view_get_shell_window (shell_view); - parent = GTK_WINDOW (shell_window); - - str_folder_id = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, folder_path); - if (!str_folder_id) { - e_notice (parent, GTK_MESSAGE_ERROR, _("Cannot edit permissions of folder “%s”, choose other folder."), folder_path); - } else { - EShell *shell; - ESource *source; - ESourceRegistry *registry; - CamelService *service; - CamelSettings *settings; - EwsFolderId *folder_id; - gchar *str_change_key; - const gchar *uid; - - shell = e_shell_window_get_shell (shell_window); - registry = e_shell_get_registry (shell); - - service = CAMEL_SERVICE (store); - uid = camel_service_get_uid (service); - source = e_source_registry_ref_source (registry, uid); - g_return_if_fail (source != NULL); - - str_change_key = camel_ews_store_summary_get_change_key ( - ews_store->summary, str_folder_id, NULL); - - folder_id = e_ews_folder_id_new ( - str_folder_id, str_change_key, FALSE); - - settings = camel_service_ref_settings (service); - - e_ews_edit_folder_permissions ( - parent, - registry, - source, - CAMEL_EWS_SETTINGS (settings), - camel_service_get_display_name (service), - folder_path, - folder_id, - E_EWS_FOLDER_TYPE_MAILBOX); - - g_object_unref (settings); - - g_object_unref (source); - g_free (str_folder_id); - g_free (str_change_key); - e_ews_folder_id_free (folder_id); - } - - g_object_unref (store); - g_free (folder_path); -} - -static void -ews_ui_enable_actions (GtkActionGroup *action_group, - const GtkActionEntry *entries, - guint n_entries, - gboolean can_show, - gboolean is_online) -{ - gint ii; - - g_return_if_fail (action_group != NULL); - g_return_if_fail (entries != NULL); - - for (ii = 0; ii < n_entries; ii++) { - GtkAction *action; - - action = gtk_action_group_get_action (action_group, entries[ii].name); - if (!action) - continue; - - gtk_action_set_visible (action, can_show); - if (can_show) - gtk_action_set_sensitive (action, is_online); - } -} - -static GtkActionEntry mail_account_context_entries[] = { - { "mail-ews-folder-sizes", - NULL, - N_("Folder Sizes..."), - NULL, - NULL, /* XXX Add a tooltip! */ - G_CALLBACK (action_folder_sizes_cb) }, - - { "mail-ews-subscribe-foreign-folder", - NULL, - N_("Subscribe to folder of other user..."), - NULL, - NULL, /* XXX Add a tooltip! */ - G_CALLBACK (action_subscribe_foreign_folder_cb) } -}; - -static GtkActionEntry mail_folder_context_entries[] = { - { "mail-ews-folder-permissions", - "folder-new", - N_("Permissions..."), - NULL, - N_("Edit EWS folder permissions"), - G_CALLBACK (action_folder_permissions_mail_cb) } -}; - -static const gchar *ews_ui_mail_def = - "\n" - " \n" - " \n" - " \n" - " \n" - " \n" - "\n"; - -static void -ews_ui_update_actions_mail_cb (EShellView *shell_view, - GtkActionEntry *entries) -{ - EShellWindow *shell_window; - GtkActionGroup *action_group; - GtkUIManager *ui_manager; - EShellSidebar *shell_sidebar; - EMFolderTree *folder_tree; - CamelStore *selected_store = NULL; - gchar *selected_path = NULL; - gboolean account_node = FALSE, folder_node = FALSE; - gboolean online = FALSE; - - shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); - g_object_get (shell_sidebar, "folder-tree", &folder_tree, NULL); - if (em_folder_tree_get_selected (folder_tree, &selected_store, &selected_path) || - em_folder_tree_store_root_selected (folder_tree, &selected_store)) { - if (selected_store) { - CamelProvider *provider = camel_service_get_provider (CAMEL_SERVICE (selected_store)); - - if (provider && g_ascii_strcasecmp (provider->protocol, "ews") == 0) { - account_node = !selected_path || !*selected_path; - folder_node = !account_node; - } - - g_object_unref (selected_store); - } - } - g_object_unref (folder_tree); - - g_free (selected_path); - - shell_window = e_shell_view_get_shell_window (shell_view); - ui_manager = e_shell_window_get_ui_manager (shell_window); - action_group = e_lookup_action_group (ui_manager, "mail"); - - if (account_node || folder_node) { - EShellBackend *backend; - CamelSession *session = NULL; - - backend = e_shell_view_get_shell_backend (shell_view); - g_object_get (G_OBJECT (backend), "session", &session, NULL); - - online = session && camel_session_get_online (session); - - if (session) - g_object_unref (session); - } - - ews_ui_enable_actions (action_group, mail_account_context_entries, G_N_ELEMENTS (mail_account_context_entries), account_node, online); - ews_ui_enable_actions (action_group, mail_folder_context_entries, G_N_ELEMENTS (mail_folder_context_entries), folder_node, online); -} - -static void -ews_ui_init_mail (GtkUIManager *ui_manager, - EShellView *shell_view, - gchar **ui_definition) -{ - EShellWindow *shell_window; - GtkActionGroup *action_group; - - g_return_if_fail (ui_definition != NULL); - - *ui_definition = g_strdup (ews_ui_mail_def); - - shell_window = e_shell_view_get_shell_window (shell_view); - action_group = e_shell_window_get_action_group (shell_window, "mail"); - - /* Add actions to the "mail" action group. */ - e_action_group_add_actions_localized ( - action_group, GETTEXT_PACKAGE, - mail_account_context_entries, G_N_ELEMENTS (mail_account_context_entries), shell_view); - e_action_group_add_actions_localized ( - action_group, GETTEXT_PACKAGE, - mail_folder_context_entries, G_N_ELEMENTS (mail_folder_context_entries), shell_view); - - /* Decide whether we want this option to be visible or not */ - g_signal_connect ( - shell_view, "update-actions", - G_CALLBACK (ews_ui_update_actions_mail_cb), - shell_view); -} - -static gboolean -get_selected_ews_source (EShellView *shell_view, - ESource **selected_source, - ESourceRegistry **registry) -{ - ESource *source; - EShellSidebar *shell_sidebar; - ESourceSelector *selector = NULL; - - g_return_val_if_fail (shell_view != NULL, FALSE); - - shell_sidebar = e_shell_view_get_shell_sidebar (shell_view); - g_return_val_if_fail (shell_sidebar != NULL, FALSE); - - g_object_get (shell_sidebar, "selector", &selector, NULL); - g_return_val_if_fail (selector != NULL, FALSE); - - source = e_source_selector_ref_primary_selection (selector); - if (source) { - ESourceBackend *backend_ext = NULL; - - if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) - backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK); - else if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) - backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_CALENDAR); - else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST)) - backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_MEMO_LIST); - else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) - backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_TASK_LIST); - else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT)) - backend_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT); - - if (!backend_ext || - g_strcmp0 (e_source_backend_get_backend_name (backend_ext), "ews") != 0) { - g_object_unref (source); - source = NULL; - } - } - - if (source && registry) - *registry = g_object_ref (e_source_selector_get_registry (selector)); - - g_object_unref (selector); - - if (selected_source) - *selected_source = source; - else if (source) - g_object_unref (source); - - return source != NULL; -} - -/* how many menu entries are defined; all calendar/tasks/memos/contacts - * actions should have same count */ -#define EWS_ESOURCE_NUM_ENTRIES 1 - -static void -update_ews_source_entries_cb (EShellView *shell_view, - GtkActionEntry *entries) -{ - GtkActionGroup *action_group; - EShell *shell; - EShellWindow *shell_window; - ESource *source = NULL; - const gchar *group; - gboolean is_ews_source, is_online; - - g_return_if_fail (E_IS_SHELL_VIEW (shell_view)); - g_return_if_fail (entries != NULL); - - if (strstr (entries->name, "calendar")) - group = "calendar"; - else if (strstr (entries->name, "tasks")) - group = "tasks"; - else if (strstr (entries->name, "memos")) - group = "memos"; - else if (strstr (entries->name, "contacts")) - group = "contacts"; - else - g_return_if_reached (); - - is_ews_source = get_selected_ews_source (shell_view, &source, NULL); - - if (is_ews_source) { - if (!source || !e_source_has_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER)) - is_ews_source = FALSE; - - if (is_ews_source) { - ESourceEwsFolder *ews_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER); - - /* Require both ChangeKey and folder's Id, but GAL can have a ':' in the Id, - which should be ignored, because it's not a valid folder Id. */ - if (!e_source_ews_folder_get_id (ews_folder) || - g_strcmp0 (e_source_ews_folder_get_id (ews_folder), "") == 0 || - !e_source_ews_folder_get_change_key (ews_folder) || - g_strcmp0 (e_source_ews_folder_get_change_key (ews_folder), "") == 0 || - strchr (e_source_ews_folder_get_id (ews_folder), ':') != NULL) - is_ews_source = FALSE; - } - } - - g_clear_object (&source); - - shell_window = e_shell_view_get_shell_window (shell_view); - shell = e_shell_window_get_shell (shell_window); - - is_online = shell && e_shell_get_online (shell); - action_group = e_shell_window_get_action_group (shell_window, group); - - ews_ui_enable_actions (action_group, entries, EWS_ESOURCE_NUM_ENTRIES, is_ews_source, is_online); -} - -static void -setup_ews_source_actions (EShellView *shell_view, - GtkUIManager *ui_manager, - GtkActionEntry *entries, - guint n_entries) -{ - EShellWindow *shell_window; - const gchar *group; - - g_return_if_fail (shell_view != NULL); - g_return_if_fail (ui_manager != NULL); - g_return_if_fail (entries != NULL); - g_return_if_fail (n_entries > 0); - g_return_if_fail (n_entries == EWS_ESOURCE_NUM_ENTRIES); - - if (strstr (entries->name, "calendar")) - group = "calendar"; - else if (strstr (entries->name, "tasks")) - group = "tasks"; - else if (strstr (entries->name, "memos")) - group = "memos"; - else if (strstr (entries->name, "contacts")) - group = "contacts"; - else - g_return_if_reached (); - - shell_window = e_shell_view_get_shell_window (shell_view); - - e_action_group_add_actions_localized ( - e_shell_window_get_action_group (shell_window, group), GETTEXT_PACKAGE, - entries, EWS_ESOURCE_NUM_ENTRIES, shell_view); - - g_signal_connect (shell_view, "update-actions", G_CALLBACK (update_ews_source_entries_cb), entries); -} - -static void -action_folder_permissions_source_cb (GtkAction *action, - EShellView *shell_view) -{ - ESourceRegistry *registry = NULL; - ESource *source = NULL, *parent_source; - ESourceEwsFolder *folder_ext; - ESourceCamel *extension; - CamelSettings *settings; - const gchar *extension_name; - EwsFolderId *folder_id; - EEwsFolderType folder_type; - - g_return_if_fail (action != NULL); - g_return_if_fail (shell_view != NULL); - g_return_if_fail (get_selected_ews_source (shell_view, &source, ®istry)); - g_return_if_fail (source != NULL); - g_return_if_fail (e_source_has_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER)); - g_return_if_fail (gtk_action_get_name (action) != NULL); - - folder_ext = e_source_get_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER); - folder_id = e_source_ews_folder_dup_folder_id (folder_ext); - g_return_if_fail (folder_id != NULL); - - parent_source = e_source_registry_ref_source (registry, e_source_get_parent (source)); - - extension_name = e_source_camel_get_extension_name ("ews"); - extension = e_source_get_extension (parent_source, extension_name); - settings = e_source_camel_get_settings (extension); - - folder_type = E_EWS_FOLDER_TYPE_MAILBOX; - if (strstr (gtk_action_get_name (action), "calendar") != NULL) - folder_type = E_EWS_FOLDER_TYPE_CALENDAR; - else if (strstr (gtk_action_get_name (action), "contacts") != NULL) - folder_type = E_EWS_FOLDER_TYPE_CONTACTS; - else if (strstr (gtk_action_get_name (action), "tasks") != NULL) - folder_type = E_EWS_FOLDER_TYPE_TASKS; - - e_ews_edit_folder_permissions ( - NULL, - registry, - source, - CAMEL_EWS_SETTINGS (settings), - e_source_get_display_name (parent_source), - e_source_get_display_name (source), - folder_id, - folder_type); - - g_object_unref (source); - g_object_unref (parent_source); - g_object_unref (registry); - e_ews_folder_id_free (folder_id); -} - -static GtkActionEntry calendar_context_entries[] = { - - { "calendar-ews-folder-permissions", - "folder-new", - N_("Permissions..."), - NULL, - N_("Edit EWS calendar permissions"), - G_CALLBACK (action_folder_permissions_source_cb) } -}; - -static const gchar *ews_ui_cal_def = - "\n" - " \n" - " \n" - " \n" - "\n"; - -static void -ews_ui_init_calendar (GtkUIManager *ui_manager, - EShellView *shell_view, - gchar **ui_definition) -{ - g_return_if_fail (ui_definition != NULL); - - *ui_definition = g_strdup (ews_ui_cal_def); - - setup_ews_source_actions ( - shell_view, ui_manager, - calendar_context_entries, G_N_ELEMENTS (calendar_context_entries)); -} - -static GtkActionEntry tasks_context_entries[] = { - - { "tasks-ews-folder-permissions", - "folder-new", - N_("Permissions..."), - NULL, - N_("Edit EWS tasks permissions"), - G_CALLBACK (action_folder_permissions_source_cb) } -}; - -static const gchar *ews_ui_task_def = - "\n" - " \n" - " \n" - " \n" - "\n"; - -static void -ews_ui_init_tasks (GtkUIManager *ui_manager, - EShellView *shell_view, - gchar **ui_definition) -{ - g_return_if_fail (ui_definition != NULL); - - *ui_definition = g_strdup (ews_ui_task_def); - - setup_ews_source_actions ( - shell_view, ui_manager, - tasks_context_entries, G_N_ELEMENTS (tasks_context_entries)); -} - -static GtkActionEntry memos_context_entries[] = { - - { "memos-ews-folder-permissions", - "folder-new", - N_("Permissions..."), - NULL, - N_("Edit EWS memos permissions"), - G_CALLBACK (action_folder_permissions_source_cb) } -}; - -static const gchar *ews_ui_memo_def = - "\n" - " \n" - " \n" - " \n" - "\n"; - -static void -ews_ui_init_memos (GtkUIManager *ui_manager, - EShellView *shell_view, - gchar **ui_definition) -{ - g_return_if_fail (ui_definition != NULL); - - *ui_definition = g_strdup (ews_ui_memo_def); - - setup_ews_source_actions ( - shell_view, ui_manager, - memos_context_entries, G_N_ELEMENTS (memos_context_entries)); -} - -static GtkActionEntry contacts_context_entries[] = { - - { "contacts-ews-folder-permissions", - "folder-new", - N_("Permissions..."), - NULL, - N_("Edit EWS contacts permissions"), - G_CALLBACK (action_folder_permissions_source_cb) } -}; - -static const gchar *ews_ui_book_def = - "\n" - " \n" - " \n" - " \n" - "\n"; - -static void -ews_ui_init_contacts (GtkUIManager *ui_manager, - EShellView *shell_view, - gchar **ui_definition) -{ - g_return_if_fail (ui_definition != NULL); - - *ui_definition = g_strdup (ews_ui_book_def); - - setup_ews_source_actions ( - shell_view, ui_manager, - contacts_context_entries, G_N_ELEMENTS (contacts_context_entries)); -} - -void -e_ews_config_utils_init_ui (EShellView *shell_view, - const gchar *ui_manager_id, - gchar **ui_definition) -{ - EShellWindow *shell_window; - GtkUIManager *ui_manager; - - g_return_if_fail (shell_view != NULL); - g_return_if_fail (ui_manager_id != NULL); - g_return_if_fail (ui_definition != NULL); - - shell_window = e_shell_view_get_shell_window (shell_view); - ui_manager = e_shell_window_get_ui_manager (shell_window); - - if (g_strcmp0 (ui_manager_id, "org.gnome.evolution.mail") == 0) - ews_ui_init_mail (ui_manager, shell_view, ui_definition); - else if (g_strcmp0 (ui_manager_id, "org.gnome.evolution.calendars") == 0) - ews_ui_init_calendar (ui_manager, shell_view, ui_definition); - else if (g_strcmp0 (ui_manager_id, "org.gnome.evolution.tasks") == 0) - ews_ui_init_tasks (ui_manager, shell_view, ui_definition); - else if (g_strcmp0 (ui_manager_id, "org.gnome.evolution.memos") == 0) - ews_ui_init_memos (ui_manager, shell_view, ui_definition); - else if (g_strcmp0 (ui_manager_id, "org.gnome.evolution.contacts") == 0) - ews_ui_init_contacts (ui_manager, shell_view, ui_definition); -} - -gboolean -e_ews_config_utils_is_online (void) -{ - EShell *shell; - - shell = e_shell_get_default (); - - return shell && e_shell_get_online (shell); -} - -GtkWindow * -e_ews_config_utils_get_widget_toplevel_window (GtkWidget *widget) -{ - if (!widget) - return NULL; - - if (!GTK_IS_WINDOW (widget)) - widget = gtk_widget_get_toplevel (widget); - - if (GTK_IS_WINDOW (widget)) - return GTK_WINDOW (widget); - - return NULL; -} - -static gpointer -ews_config_utils_unref_in_thread (gpointer user_data) -{ - g_object_unref (user_data); - - return NULL; -} - -void -e_ews_config_utils_unref_in_thread (GObject *object) -{ - GThread *thread; - - g_return_if_fail (object != NULL); - g_return_if_fail (G_IS_OBJECT (object)); - - thread = g_thread_new (NULL, ews_config_utils_unref_in_thread, object); - g_thread_unref (thread); -} diff --git a/src/configuration/e-mail-config-ews-autodiscover.c b/src/configuration/e-mail-config-ews-autodiscover.c index aae6c46..09f5848 100644 --- a/src/configuration/e-mail-config-ews-autodiscover.c +++ b/src/configuration/e-mail-config-ews-autodiscover.c @@ -45,8 +45,6 @@ struct _AsyncContext { ESource *source; CamelEwsSettings *ews_settings; gchar *email_address; - gchar *certificate_pem; - GTlsCertificateFlags certificate_errors; }; enum { @@ -69,7 +67,6 @@ async_context_free (gpointer ptr) g_clear_object (&async_context->source); g_clear_object (&async_context->ews_settings); g_free (async_context->email_address); - g_free (async_context->certificate_pem); g_slice_free (AsyncContext, async_context); } @@ -90,9 +87,6 @@ mail_config_ews_autodiscover_finish (EMailConfigEwsAutodiscover *autodiscover, } static void -mail_config_ews_autodiscover_run (EMailConfigEwsAutodiscover *autodiscover); - -static void mail_config_ews_autodiscover_run_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) @@ -117,62 +111,17 @@ mail_config_ews_autodiscover_run_cb (GObject *source_object, g_object_thaw_notify (G_OBJECT (settings)); if (e_activity_handle_cancellation (async_context->activity, error)) { - /* Do nothing, just free the error below */ - } else if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) && - async_context->certificate_pem && *async_context->certificate_pem && async_context->certificate_errors) { - ETrustPromptResponse response; - GtkWidget *parent; - const gchar *host; - - parent = gtk_widget_get_toplevel (GTK_WIDGET (autodiscover)); - if (!GTK_IS_WINDOW (parent)) - parent = NULL; - - host = camel_network_settings_get_host (CAMEL_NETWORK_SETTINGS (settings)); - - response = e_trust_prompt_run_modal (parent ? GTK_WINDOW (parent) : NULL, - E_SOURCE_EXTENSION_COLLECTION, _("Exchange Web Services"), - host, async_context->certificate_pem, async_context->certificate_errors, - error->message); - - g_clear_error (&error); - - if (response != E_TRUST_PROMPT_RESPONSE_UNKNOWN) { - GTlsCertificate *certificate; - - certificate = g_tls_certificate_new_from_pem (async_context->certificate_pem, -1, &error); - if (certificate) { - ESourceWebdav *extension_webdav; + g_error_free (error); - extension_webdav = e_source_get_extension (async_context->source, E_SOURCE_EXTENSION_WEBDAV_BACKEND); - - e_source_webdav_update_ssl_trust (extension_webdav, host, certificate, response); - - g_object_unref (certificate); - } - - if (error) { - e_alert_submit ( - alert_sink, - "ews:autodiscovery-error", - error->message, NULL); - } - } - - if (response == E_TRUST_PROMPT_RESPONSE_ACCEPT || - response == E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY) { - mail_config_ews_autodiscover_run (autodiscover); - } } else if (error != NULL) { e_alert_submit ( alert_sink, "ews:autodiscovery-error", error->message, NULL); + g_error_free (error); } gtk_widget_set_sensitive (GTK_WIDGET (autodiscover), TRUE); - - g_clear_error (&error); } static gboolean @@ -192,7 +141,6 @@ mail_config_ews_autodiscover_sync (ECredentialsPrompter *prompter, async_context->ews_settings, async_context->email_address, credentials && e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD) ? e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD) : "", - &async_context->certificate_pem, &async_context->certificate_errors, cancellable, &local_error); if (local_error == NULL) { @@ -225,7 +173,6 @@ mail_config_ews_autodiscover_run_thread (GTask *task, if (without_password) { success = e_ews_autodiscover_ws_url_sync (async_context->source, async_context->ews_settings, async_context->email_address, "", - &async_context->certificate_pem, &async_context->certificate_errors, cancellable, &local_error); } @@ -289,8 +236,6 @@ mail_config_ews_autodiscover_run (EMailConfigEwsAutodiscover *autodiscover) async_context->source = g_object_ref (source); async_context->ews_settings = g_object_ref (settings); async_context->email_address = g_strdup (e_mail_config_service_page_get_email_address (page)); - async_context->certificate_pem = NULL; - async_context->certificate_errors = 0; /* * The GTask will be run in a new thread, which will invoke diff --git a/src/configuration/e-mail-config-ews-autodiscover.c.cve-2019-3890 b/src/configuration/e-mail-config-ews-autodiscover.c.cve-2019-3890 deleted file mode 100644 index 09f5848..0000000 --- a/src/configuration/e-mail-config-ews-autodiscover.c.cve-2019-3890 +++ /dev/null @@ -1,412 +0,0 @@ -/* - * e-mail-config-ews-autodiscover.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 "evolution-ews-config.h" - -#include "e-mail-config-ews-autodiscover.h" - -#include - -#include -#include -#include - -#include "server/e-ews-connection.h" -#include "server/e-ews-connection-utils.h" - -#define E_MAIL_CONFIG_EWS_AUTODISCOVER_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_MAIL_CONFIG_EWS_AUTODISCOVER, EMailConfigEwsAutodiscoverPrivate)) - -typedef struct _AsyncContext AsyncContext; - -struct _EMailConfigEwsAutodiscoverPrivate { - EMailConfigServiceBackend *backend; -}; - -struct _AsyncContext { - EMailConfigEwsAutodiscover *autodiscover; - EActivity *activity; - ESource *source; - CamelEwsSettings *ews_settings; - gchar *email_address; -}; - -enum { - PROP_0, - PROP_BACKEND -}; - -G_DEFINE_DYNAMIC_TYPE (EMailConfigEwsAutodiscover, e_mail_config_ews_autodiscover, GTK_TYPE_BUTTON) - -static void -async_context_free (gpointer ptr) -{ - AsyncContext *async_context = ptr; - - if (!async_context) - return; - - g_clear_object (&async_context->autodiscover); - g_clear_object (&async_context->activity); - g_clear_object (&async_context->source); - g_clear_object (&async_context->ews_settings); - g_free (async_context->email_address); - - g_slice_free (AsyncContext, async_context); -} - -static gboolean -mail_config_ews_autodiscover_finish (EMailConfigEwsAutodiscover *autodiscover, - GAsyncResult *result, - GError **error) -{ - g_return_val_if_fail (E_IS_MAIL_CONFIG_EWS_AUTODISCOVER (autodiscover), FALSE); - g_return_val_if_fail (g_task_is_valid (result, autodiscover), FALSE); - - g_return_val_if_fail ( - g_async_result_is_tagged ( - result, mail_config_ews_autodiscover_finish), FALSE); - - return g_task_propagate_boolean (G_TASK (result), error); -} - -static void -mail_config_ews_autodiscover_run_cb (GObject *source_object, - GAsyncResult *result, - gpointer user_data) -{ - AsyncContext *async_context = user_data; - EMailConfigEwsAutodiscover *autodiscover; - EAlertSink *alert_sink; - GError *error = NULL; - EMailConfigServiceBackend *backend; - CamelSettings *settings; - - autodiscover = async_context->autodiscover; - alert_sink = e_activity_get_alert_sink (async_context->activity); - - mail_config_ews_autodiscover_finish (E_MAIL_CONFIG_EWS_AUTODISCOVER (source_object), result, &error); - - backend = e_mail_config_ews_autodiscover_get_backend (autodiscover); - settings = e_mail_config_service_backend_get_settings (backend); - /* - * And unstop since we are back to the main thread. - */ - g_object_thaw_notify (G_OBJECT (settings)); - - if (e_activity_handle_cancellation (async_context->activity, error)) { - g_error_free (error); - - } else if (error != NULL) { - e_alert_submit ( - alert_sink, - "ews:autodiscovery-error", - error->message, NULL); - g_error_free (error); - } - - gtk_widget_set_sensitive (GTK_WIDGET (autodiscover), TRUE); -} - -static gboolean -mail_config_ews_autodiscover_sync (ECredentialsPrompter *prompter, - ESource *source, - const ENamedParameters *credentials, - gboolean *out_authenticated, - gpointer user_data, - GCancellable *cancellable, - GError **error) -{ - AsyncContext *async_context = user_data; - GError *local_error = NULL; - gboolean res = TRUE; - - e_ews_autodiscover_ws_url_sync (source, - async_context->ews_settings, async_context->email_address, - credentials && e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD) ? - e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD) : "", - cancellable, &local_error); - - if (local_error == NULL) { - *out_authenticated = TRUE; - } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) { - *out_authenticated = FALSE; - g_error_free (local_error); - } else { - res = FALSE; - g_propagate_error (error, local_error); - } - - return res; -} - -static void -mail_config_ews_autodiscover_run_thread (GTask *task, - gpointer source_object, - gpointer task_data, - GCancellable *cancellable) -{ - AsyncContext *async_context = task_data; - GError *local_error = NULL; - gboolean success = FALSE; - - if (!g_cancellable_set_error_if_cancelled (cancellable, &local_error) && !local_error) { - gboolean without_password; - - without_password = e_ews_connection_utils_get_without_password (async_context->ews_settings); - if (without_password) { - success = e_ews_autodiscover_ws_url_sync (async_context->source, - async_context->ews_settings, async_context->email_address, "", - cancellable, &local_error); - } - - if (!without_password || g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) { - EShell *shell; - - e_ews_connection_utils_force_off_ntlm_auth_check (); - g_clear_error (&local_error); - - shell = e_shell_get_default (); - - success = e_credentials_prompter_loop_prompt_sync (e_shell_get_credentials_prompter (shell), - async_context->source, E_CREDENTIALS_PROMPTER_PROMPT_FLAG_ALLOW_SOURCE_SAVE, - mail_config_ews_autodiscover_sync, async_context, cancellable, &local_error); - } - } - - if (local_error != NULL) { - g_task_return_error (task, local_error); - } else { - g_task_return_boolean (task, success); - } -} - -static void -mail_config_ews_autodiscover_run (EMailConfigEwsAutodiscover *autodiscover) -{ - EActivity *activity; - EMailConfigServicePage *page; - EMailConfigServiceBackend *backend; - CamelSettings *settings; - ESource *source; - GCancellable *cancellable; - AsyncContext *async_context; - GTask *task; - - backend = e_mail_config_ews_autodiscover_get_backend (autodiscover); - page = e_mail_config_service_backend_get_page (backend); - source = e_mail_config_service_backend_get_source (backend); - settings = e_mail_config_service_backend_get_settings (backend); - - if (!e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) { - ESource *collection; - - collection = e_mail_config_service_backend_get_collection (backend); - if (collection && e_source_has_extension (collection, E_SOURCE_EXTENSION_AUTHENTICATION)) { - source = collection; - } - } - - activity = e_mail_config_activity_page_new_activity (E_MAIL_CONFIG_ACTIVITY_PAGE (page)); - cancellable = e_activity_get_cancellable (activity); - - e_activity_set_text (activity, _("Querying Autodiscover service")); - - gtk_widget_set_sensitive (GTK_WIDGET (autodiscover), FALSE); - - async_context = g_slice_new0 (AsyncContext); - async_context->autodiscover = g_object_ref (autodiscover); - async_context->activity = activity; /* takes ownership */ - async_context->source = g_object_ref (source); - async_context->ews_settings = g_object_ref (settings); - async_context->email_address = g_strdup (e_mail_config_service_page_get_email_address (page)); - - /* - * The GTask will be run in a new thread, which will invoke - * camel_ews_settings_set_{oaburl,hosturl}(), emiting signals that - * are bound to GTK+ UI signals, causing GTK+ calls in this - * secondary thread and consequently a crash. To avoid this, let's stop - * the property changes notifications while we are not in the main thread. - */ - g_object_freeze_notify (G_OBJECT (settings)); - - task = g_task_new (autodiscover, cancellable, mail_config_ews_autodiscover_run_cb, async_context); - g_task_set_source_tag (task, mail_config_ews_autodiscover_finish); - g_task_set_task_data (task, async_context, async_context_free); - - g_task_run_in_thread (task, mail_config_ews_autodiscover_run_thread); - - g_object_unref (task); -} - -static void -mail_config_ews_autodiscover_set_backend (EMailConfigEwsAutodiscover *autodiscover, - EMailConfigServiceBackend *backend) -{ - g_return_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend)); - g_return_if_fail (autodiscover->priv->backend == NULL); - - autodiscover->priv->backend = g_object_ref (backend); -} - -static void -mail_config_ews_autodiscover_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_BACKEND: - mail_config_ews_autodiscover_set_backend ( - E_MAIL_CONFIG_EWS_AUTODISCOVER (object), - g_value_get_object (value)); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -mail_config_ews_autodiscover_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_BACKEND: - g_value_set_object ( - value, - e_mail_config_ews_autodiscover_get_backend ( - E_MAIL_CONFIG_EWS_AUTODISCOVER (object))); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -mail_config_ews_autodiscover_dispose (GObject *object) -{ - EMailConfigEwsAutodiscoverPrivate *priv; - - priv = E_MAIL_CONFIG_EWS_AUTODISCOVER_GET_PRIVATE (object); - - if (priv->backend != NULL) { - g_object_unref (priv->backend); - priv->backend = NULL; - } - - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (e_mail_config_ews_autodiscover_parent_class)-> - dispose (object); -} - -static void -mail_config_ews_autodiscover_constructed (GObject *object) -{ - GtkButton *button; - - button = GTK_BUTTON (object); - gtk_button_set_label (button, _("Fetch _URL")); - gtk_button_set_use_underline (button, TRUE); - - /* Chain up tp parent's constructed() method. */ - G_OBJECT_CLASS (e_mail_config_ews_autodiscover_parent_class)->constructed (object); -} - -static void -mail_config_ews_autodiscover_clicked (GtkButton *button) -{ - EMailConfigEwsAutodiscover *autodiscover; - - autodiscover = E_MAIL_CONFIG_EWS_AUTODISCOVER (button); - - mail_config_ews_autodiscover_run (autodiscover); -} - -static void -e_mail_config_ews_autodiscover_class_init (EMailConfigEwsAutodiscoverClass *class) -{ - GObjectClass *object_class; - GtkButtonClass *button_class; - - g_type_class_add_private ( - class, sizeof (EMailConfigEwsAutodiscoverPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->set_property = mail_config_ews_autodiscover_set_property; - object_class->get_property = mail_config_ews_autodiscover_get_property; - object_class->dispose = mail_config_ews_autodiscover_dispose; - object_class->constructed = mail_config_ews_autodiscover_constructed; - - button_class = GTK_BUTTON_CLASS (class); - button_class->clicked = mail_config_ews_autodiscover_clicked; - - g_object_class_install_property ( - object_class, - PROP_BACKEND, - g_param_spec_object ( - "backend", - "Backend", - "Mail configuration backend", - E_TYPE_MAIL_CONFIG_SERVICE_BACKEND, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY)); -} - -static void -e_mail_config_ews_autodiscover_class_finalize (EMailConfigEwsAutodiscoverClass *class) -{ -} - -static void -e_mail_config_ews_autodiscover_init (EMailConfigEwsAutodiscover *autodiscover) -{ - autodiscover->priv = - E_MAIL_CONFIG_EWS_AUTODISCOVER_GET_PRIVATE (autodiscover); -} - -void -e_mail_config_ews_autodiscover_type_register (GTypeModule *type_module) -{ - /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration - * function, so we have to wrap it with a public function in - * order to register types from a separate compilation unit. */ - e_mail_config_ews_autodiscover_register_type (type_module); -} - -GtkWidget * -e_mail_config_ews_autodiscover_new (EMailConfigServiceBackend *backend) -{ - g_return_val_if_fail (E_IS_MAIL_CONFIG_SERVICE_BACKEND (backend), NULL); - - return g_object_new ( - E_TYPE_MAIL_CONFIG_EWS_AUTODISCOVER, - "backend", backend, NULL); -} - -EMailConfigServiceBackend * -e_mail_config_ews_autodiscover_get_backend (EMailConfigEwsAutodiscover *autodiscover) -{ - g_return_val_if_fail ( - E_IS_MAIL_CONFIG_EWS_AUTODISCOVER (autodiscover), NULL); - - return autodiscover->priv->backend; -} - diff --git a/src/server/e-ews-connection-utils.c b/src/server/e-ews-connection-utils.c index 360844e..c5c86e2 100644 --- a/src/server/e-ews-connection-utils.c +++ b/src/server/e-ews-connection-utils.c @@ -522,13 +522,8 @@ e_ews_connection_utils_prepare_message (EEwsConnection *cnc, GCancellable *cancellable) { ESoupAuthBearer *using_bearer_auth; - ESource *source; GError *local_error = NULL; - source = e_ews_connection_get_source (cnc); - if (source) - e_soup_ssl_trust_connect (message, source); - if (!ews_connection_utils_maybe_prepare_bearer_auth (cnc, message, cancellable)) return FALSE; diff --git a/src/server/e-ews-connection-utils.c.cve-2019-3890 b/src/server/e-ews-connection-utils.c.cve-2019-3890 deleted file mode 100644 index c5c86e2..0000000 --- a/src/server/e-ews-connection-utils.c.cve-2019-3890 +++ /dev/null @@ -1,550 +0,0 @@ -/* - * e-ews-connection-utils.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 "evolution-ews-config.h" - -#include - -#include -#include -#include - -#include "e-ews-connection-utils.h" -#include "e-soup-auth-negotiate.h" -#include "camel-ews-settings.h" - -static gpointer -ews_unref_in_thread_func (gpointer data) -{ - g_object_unref (G_OBJECT (data)); - - return NULL; -} - -void -e_ews_connection_utils_unref_in_thread (gpointer object) -{ - GThread *thread; - - g_return_if_fail (G_IS_OBJECT (object)); - - thread = g_thread_new (NULL, ews_unref_in_thread_func, object); - g_thread_unref (thread); -} - -/* Do not call this directly; use E_EWS_CONNECTION_UTILS_CHECK_ELEMENT macro instead. */ -gboolean -e_ews_connection_utils_check_element (const gchar *function_name, - const gchar *element_name, - const gchar *expected_name) -{ - g_return_val_if_fail (function_name != NULL, FALSE); - g_return_val_if_fail (element_name != NULL, FALSE); - g_return_val_if_fail (expected_name != NULL, FALSE); - - if (!g_str_equal (element_name, expected_name)) { - g_warning ( - "%s: Expected <%s> but got <%s>", - function_name, expected_name, element_name); - return FALSE; - } - - return TRUE; -} - -static gboolean force_off_ntlm_auth_check = FALSE; - -static gboolean -ews_connect_check_ntlm_available (void) -{ -#ifndef G_OS_WIN32 - const gchar *helper; - CamelStream *stream; - const gchar *cp; - const gchar *user; - gchar buf[1024]; - gsize s; - gchar *command; - gint ret; - - if (force_off_ntlm_auth_check) - return FALSE; - - /* We are attempting to predict what libsoup will do. */ - helper = g_getenv ("SOUP_NTLM_AUTH_DEBUG"); - if (!helper) - helper = "/usr/bin/ntlm_auth"; - else if (!helper[0]) - return FALSE; - - if (g_access (helper, X_OK)) - return FALSE; - - user = g_getenv ("NTLMUSER"); - if (!user) - user = g_get_user_name(); - - cp = strpbrk (user, "\\/"); - if (cp != NULL) { - command = g_strdup_printf ( - "%s --helper-protocol ntlmssp-client-1 " - "--use-cached-creds --username '%s' " - "--domain '%.*s'", helper, - cp + 1, (gint)(cp - user), user); - } else { - command = g_strdup_printf ( - "%s --helper-protocol ntlmssp-client-1 " - "--use-cached-creds --username '%s'", - helper, user); - } - - stream = camel_stream_process_new (); - - ret = camel_stream_process_connect (CAMEL_STREAM_PROCESS (stream), - command, NULL, NULL); - - g_free (command); - - if (ret) { - g_object_unref (stream); - return FALSE; - } - - if (camel_stream_write_string (stream, "YR\n", NULL, NULL) < 0) { - g_object_unref (stream); - return FALSE; - } - - s = camel_stream_read (stream, buf, sizeof (buf), NULL, NULL); - if (s < 4) { - g_object_unref (stream); - return FALSE; - } - - if (buf[0] != 'Y' || buf[1] != 'R' || buf[2] != ' ' || buf[s - 1] != '\n') { - g_object_unref (stream); - return FALSE; - } - - g_object_unref (stream); - - return TRUE; -#else - /* Win32 should be able to use SSPI here. */ - return FALSE; -#endif -} - -void -e_ews_connection_utils_force_off_ntlm_auth_check (void) -{ - force_off_ntlm_auth_check = TRUE; -} - -/* Should we bother to attempt a connection without a password? Remember, - * this is *purely* an optimisation to avoid that extra round-trip if we - * *KNOW* it's going to fail. So if unsure, return TRUE to avoid pestering - * the user for a password which might not even get used. - * - * We *have* to handle the case where the passwordless attempt fails - * and we have to fall back to asking for a password anyway. */ -gboolean -e_ews_connection_utils_get_without_password (CamelEwsSettings *ews_settings) -{ - switch (camel_ews_settings_get_auth_mechanism (ews_settings)) { - case EWS_AUTH_TYPE_GSSAPI: - case EWS_AUTH_TYPE_OAUTH2: - return TRUE; - - case EWS_AUTH_TYPE_NTLM: - return ews_connect_check_ntlm_available (); - - case EWS_AUTH_TYPE_BASIC: - return FALSE; - - /* No default: case (which should never be used anyway). That - * means the compiler will warn if we ever add a new mechanism - * to the enum and don't handle it here. */ - } - - return FALSE; -} - -void -e_ews_connection_utils_expired_password_to_error (const gchar *service_url, - GError **error) -{ - if (!error) - return; - - if (service_url) { - g_set_error (error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_PASSWORDEXPIRED, - _("Password expired. Change password at “%s”."), service_url); - } else { - g_set_error_literal (error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_PASSWORDEXPIRED, - _("Password expired.")); - } -} - -gboolean -e_ews_connection_utils_check_x_ms_credential_headers (SoupMessage *message, - gint *out_expire_in_days, - gboolean *out_expired, - gchar **out_service_url) -{ - gboolean any_found = FALSE; - const gchar *header; - - if (!message || !message->response_headers) - return FALSE; - - header = soup_message_headers_get_list (message->response_headers, "X-MS-Credential-Service-CredExpired"); - if (header && g_ascii_strcasecmp (header, "true") == 0) { - any_found = TRUE; - - if (out_expired) - *out_expired = TRUE; - } - - header = soup_message_headers_get_list (message->response_headers, "X-MS-Credentials-Expire"); - if (header) { - gint in_days; - - in_days = g_ascii_strtoll (header, NULL, 10); - if (in_days <= 30 && in_days >= 0) { - any_found = TRUE; - - if (out_expire_in_days) - *out_expire_in_days = in_days; - } - } - - if (any_found && out_service_url) { - header = soup_message_headers_get_list (message->response_headers, "X-MS-Credential-Service-Url"); - - *out_service_url = g_strdup (header); - } - - return any_found; -} - -void -e_ews_connection_utils_prepare_auth_method (SoupSession *soup_session, - EwsAuthType auth_method) -{ - /* We used to disable Basic auth to avoid it getting in the way of - * our GSSAPI hacks. But leave it enabled in the case where NTLM is - * enabled, which is the default configuration. It's a useful fallback - * which people may be relying on. */ - if (auth_method == EWS_AUTH_TYPE_GSSAPI) { - soup_session_add_feature_by_type (soup_session, E_SOUP_TYPE_AUTH_NEGOTIATE); - soup_session_remove_feature_by_type (soup_session, SOUP_TYPE_AUTH_BASIC); - } else if (auth_method == EWS_AUTH_TYPE_OAUTH2) { - soup_session_add_feature_by_type (soup_session, E_TYPE_SOUP_AUTH_BEARER); - soup_session_remove_feature_by_type (soup_session, SOUP_TYPE_AUTH_BASIC); - } else if (auth_method == EWS_AUTH_TYPE_NTLM) { - soup_session_add_feature_by_type (soup_session, SOUP_TYPE_AUTH_NTLM); - } -} - -static void -ews_connection_utils_ensure_bearer_auth_usage (SoupSession *session, - SoupMessage *message, - ESoupAuthBearer *bearer) -{ - SoupSessionFeature *feature; - SoupURI *soup_uri; - - g_return_if_fail (SOUP_IS_SESSION (session)); - - /* Preload the SoupAuthManager with a valid "Bearer" token - * when using OAuth 2.0. This avoids an extra unauthorized - * HTTP round-trip, which apparently Google doesn't like. */ - - feature = soup_session_get_feature (SOUP_SESSION (session), SOUP_TYPE_AUTH_MANAGER); - - if (!soup_session_feature_has_feature (feature, E_TYPE_SOUP_AUTH_BEARER)) { - /* Add the "Bearer" auth type to support OAuth 2.0. */ - soup_session_feature_add_feature (feature, E_TYPE_SOUP_AUTH_BEARER); - } - - soup_uri = message ? soup_message_get_uri (message) : NULL; - if (soup_uri && soup_uri->host && *soup_uri->host) { - soup_uri = soup_uri_copy_host (soup_uri); - } else { - soup_uri = NULL; - } - - g_return_if_fail (soup_uri != NULL); - - soup_auth_manager_use_auth ( - SOUP_AUTH_MANAGER (feature), - soup_uri, SOUP_AUTH (bearer)); - - soup_uri_free (soup_uri); -} - -static gboolean -ews_connection_utils_setup_bearer_auth (EEwsConnection *cnc, - SoupMessage *message, - gboolean is_in_authenticate_handler, - ESoupAuthBearer *bearer, - GCancellable *cancellable, - GError **error) -{ - ESource *source; - gchar *access_token = NULL; - gint expires_in_seconds = -1; - gboolean success = FALSE; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (E_IS_SOUP_AUTH_BEARER (bearer), FALSE); - - source = e_ews_connection_get_source (cnc); - - success = e_source_get_oauth2_access_token_sync (source, cancellable, - &access_token, &expires_in_seconds, error); - - if (success) { - e_soup_auth_bearer_set_access_token (bearer, access_token, expires_in_seconds); - - if (!is_in_authenticate_handler) { - SoupSession *session; - - session = e_ews_connection_ref_soup_session (cnc); - - ews_connection_utils_ensure_bearer_auth_usage (session, message, bearer); - - g_clear_object (&session); - } - } - - g_free (access_token); - - return success; -} - -static gboolean -ews_connection_utils_maybe_prepare_bearer_auth (EEwsConnection *cnc, - SoupMessage *message, - GCancellable *cancellable) -{ - ESource *source; - ESoupAuthBearer *using_bearer_auth; - gchar *auth_method = NULL; - gboolean success; - GError *local_error = NULL; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - - source = e_ews_connection_get_source (cnc); - if (!source) - return TRUE; - - if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) { - ESourceAuthentication *extension; - - extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION); - auth_method = e_source_authentication_dup_method (extension); - } else { - CamelEwsSettings *ews_settings; - - ews_settings = e_ews_connection_ref_settings (cnc); - if (ews_settings) { - if (camel_ews_settings_get_auth_mechanism (ews_settings) == EWS_AUTH_TYPE_OAUTH2) - auth_method = g_strdup ("OAuth2"); - - g_object_unref (ews_settings); - } - - if (!auth_method) - return TRUE; - } - - if (g_strcmp0 (auth_method, "OAuth2") != 0 && - g_strcmp0 (auth_method, "Office365") != 0 && - !e_oauth2_services_is_oauth2_alias_static (auth_method)) { - g_free (auth_method); - return TRUE; - } - - g_free (auth_method); - - using_bearer_auth = e_ews_connection_ref_bearer_auth (cnc); - if (using_bearer_auth) { - success = ews_connection_utils_setup_bearer_auth (cnc, message, FALSE, using_bearer_auth, cancellable, &local_error); - g_clear_object (&using_bearer_auth); - } else { - SoupAuth *soup_auth; - SoupURI *soup_uri; - - soup_uri = message ? soup_message_get_uri (message) : NULL; - if (soup_uri && soup_uri->host && *soup_uri->host) { - soup_uri = soup_uri_copy_host (soup_uri); - } else { - soup_uri = NULL; - } - - g_warn_if_fail (soup_uri != NULL); - - if (!soup_uri) { - soup_message_set_status_full (message, SOUP_STATUS_MALFORMED, "Cannot get host from message"); - return FALSE; - } - - soup_auth = g_object_new (E_TYPE_SOUP_AUTH_BEARER, SOUP_AUTH_HOST, soup_uri->host, NULL); - - success = ews_connection_utils_setup_bearer_auth (cnc, message, FALSE, E_SOUP_AUTH_BEARER (soup_auth), cancellable, &local_error); - if (success) - e_ews_connection_set_bearer_auth (cnc, E_SOUP_AUTH_BEARER (soup_auth)); - - g_object_unref (soup_auth); - soup_uri_free (soup_uri); - } - - if (!success) { - if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - soup_message_set_status (message, SOUP_STATUS_CANCELLED); - else if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED) || - g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) - soup_message_set_status_full (message, SOUP_STATUS_UNAUTHORIZED, local_error->message); - else - soup_message_set_status_full (message, SOUP_STATUS_MALFORMED, local_error ? local_error->message : _("Unknown error")); - } - - g_clear_error (&local_error); - - return success; -} - -/* Callback implementation for SoupSession::authenticate */ -void -e_ews_connection_utils_authenticate (EEwsConnection *cnc, - SoupSession *session, - SoupMessage *msg, - SoupAuth *auth, - gboolean retrying) -{ - CamelNetworkSettings *network_settings; - ESoupAuthBearer *using_bearer_auth; - gchar *user, *password, *service_url = NULL; - gboolean expired = FALSE; - - g_return_if_fail (cnc != NULL); - - using_bearer_auth = e_ews_connection_ref_bearer_auth (cnc); - - if (E_IS_SOUP_AUTH_BEARER (auth)) { - g_object_ref (auth); - g_warn_if_fail ((gpointer) using_bearer_auth == (gpointer) auth); - - g_clear_object (&using_bearer_auth); - using_bearer_auth = E_SOUP_AUTH_BEARER (auth); - - e_ews_connection_set_bearer_auth (cnc, using_bearer_auth); - } - - if (retrying) - e_ews_connection_set_password (cnc, NULL); - - if (using_bearer_auth) { - GError *local_error = NULL; - - ews_connection_utils_setup_bearer_auth (cnc, msg, TRUE, E_SOUP_AUTH_BEARER (auth), NULL, &local_error); - - if (local_error) - soup_message_set_status_full (msg, SOUP_STATUS_IO_ERROR, local_error->message); - - g_object_unref (using_bearer_auth); - g_clear_error (&local_error); - - return; - } - - if (e_ews_connection_utils_check_x_ms_credential_headers (msg, NULL, &expired, &service_url) && expired) { - GError *local_error = NULL; - - e_ews_connection_utils_expired_password_to_error (service_url, &local_error); - - if (local_error) - soup_message_set_status_full (msg, SOUP_STATUS_IO_ERROR, local_error->message); - - g_clear_error (&local_error); - g_free (service_url); - - return; - } - - g_free (service_url); - - network_settings = CAMEL_NETWORK_SETTINGS (e_ews_connection_ref_settings (cnc)); - user = camel_network_settings_dup_user (network_settings); - - password = e_ews_connection_dup_password (cnc); - if (password != NULL) { - soup_auth_authenticate (auth, user, password); - } else { - /* The NTLM implementation in libsoup doesn't cope very well - * with recovering from authentication failures (bug 703181). - * So cancel the message now while it's in-flight, and we'll - * get a shiny new connection for the next attempt. */ - const gchar *scheme = soup_auth_get_scheme_name (auth); - - if (!g_ascii_strcasecmp(scheme, "NTLM")) { - soup_session_cancel_message (session, msg, SOUP_STATUS_UNAUTHORIZED); - } - } - - g_clear_object (&network_settings); - g_free (password); - g_free (user); -} - -/* Returns whether succeeded */ -gboolean -e_ews_connection_utils_prepare_message (EEwsConnection *cnc, - SoupMessage *message, - GCancellable *cancellable) -{ - ESoupAuthBearer *using_bearer_auth; - GError *local_error = NULL; - - if (!ews_connection_utils_maybe_prepare_bearer_auth (cnc, message, cancellable)) - return FALSE; - - using_bearer_auth = e_ews_connection_ref_bearer_auth (cnc); - - if (using_bearer_auth && - e_soup_auth_bearer_is_expired (using_bearer_auth) && - !ews_connection_utils_setup_bearer_auth (cnc, message, FALSE, using_bearer_auth, cancellable, &local_error)) { - if (local_error) { - soup_message_set_status_full (message, SOUP_STATUS_BAD_REQUEST, local_error->message); - g_clear_error (&local_error); - } else { - soup_message_set_status (message, SOUP_STATUS_BAD_REQUEST); - } - - g_object_unref (using_bearer_auth); - - return FALSE; - } - - g_clear_object (&using_bearer_auth); - - return TRUE; -} diff --git a/src/server/e-ews-connection.c b/src/server/e-ews-connection.c index f495f6f..267d270 100644 --- a/src/server/e-ews-connection.c +++ b/src/server/e-ews-connection.c @@ -111,10 +111,6 @@ struct _EEwsConnectionPrivate { /* Set to TRUE when this connection had been disconnected and cannot be used anymore */ gboolean disconnected_flag; - - gboolean ssl_info_set; - gchar *ssl_certificate_pem; - GTlsCertificateFlags ssl_certificate_errors; }; enum { @@ -155,7 +151,7 @@ struct _EwsAsyncData { EwsDelegateDeliver deliver_to; EEwsFolderType folder_type; EEwsConnection *cnc; - gchar *custom_data; /* Can be re-used by operations, will be freed with g_free() */ + gchar *user_photo; /* base64-encoded, as GetUserPhoto result */ }; struct _EwsNode { @@ -200,7 +196,7 @@ ews_connection_error_quark (void) static void async_data_free (EwsAsyncData *async_data) { - g_free (async_data->custom_data); + g_free (async_data->user_photo); g_free (async_data); } @@ -840,37 +836,6 @@ ews_connection_credentials_failed (EEwsConnection *connection, return expired; } -static void -ews_connection_check_ssl_error (EEwsConnection *connection, - SoupMessage *message) -{ - g_return_if_fail (E_IS_EWS_CONNECTION (connection)); - g_return_if_fail (SOUP_IS_MESSAGE (message)); - - if (message->status_code == SOUP_STATUS_SSL_FAILED) { - GTlsCertificate *certificate = NULL; - - g_mutex_lock (&connection->priv->property_lock); - - g_clear_pointer (&connection->priv->ssl_certificate_pem, g_free); - connection->priv->ssl_info_set = FALSE; - - g_object_get (G_OBJECT (message), - "tls-certificate", &certificate, - "tls-errors", &connection->priv->ssl_certificate_errors, - NULL); - - if (certificate) { - g_object_get (certificate, "certificate-pem", &connection->priv->ssl_certificate_pem, NULL); - connection->priv->ssl_info_set = TRUE; - - g_object_unref (certificate); - } - - g_mutex_unlock (&connection->priv->property_lock); - } -} - /* Response callbacks */ static void @@ -887,15 +852,8 @@ ews_response_cb (SoupSession *session, if (g_cancellable_is_cancelled (enode->cancellable)) goto exit; - ews_connection_check_ssl_error (enode->cnc, msg); - if (ews_connection_credentials_failed (enode->cnc, msg, enode->simple)) { goto exit; - } else if (msg->status_code == SOUP_STATUS_SSL_FAILED) { - g_simple_async_result_set_error ( - enode->simple, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED, - "%s", msg->reason_phrase); - goto exit; } else if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) { if (msg->response_headers) { const gchar *diagnostics; @@ -1897,9 +1855,6 @@ ews_connection_constructed (GObject *object) cnc->priv->soup_thread = g_thread_new (NULL, e_ews_soup_thread, cnc); cnc->priv->soup_session = soup_session_async_new_with_options ( - SOUP_SESSION_TIMEOUT, 90, - SOUP_SESSION_SSL_STRICT, TRUE, - SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE, SOUP_SESSION_ASYNC_CONTEXT, cnc->priv->soup_context, NULL); @@ -2016,7 +1971,6 @@ ews_connection_finalize (GObject *object) g_free (priv->email); g_free (priv->hash_key); g_free (priv->impersonate_user); - g_free (priv->ssl_certificate_pem); g_clear_object (&priv->bearer_auth); @@ -2603,15 +2557,10 @@ e_ews_connection_update_credentials (EEwsConnection *cnc, ESourceAuthenticationResult e_ews_connection_try_credentials_sync (EEwsConnection *cnc, const ENamedParameters *credentials, - ESource *use_source, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, GCancellable *cancellable, GError **error) { ESourceAuthenticationResult result; - ESource *source; - gboolean de_set_source; EwsFolderId *fid = NULL; GSList *ids = NULL; GError *local_error = NULL; @@ -2625,31 +2574,14 @@ e_ews_connection_try_credentials_sync (EEwsConnection *cnc, fid->is_distinguished_id = TRUE; ids = g_slist_append (ids, fid); - source = e_ews_connection_get_source (cnc); - if (use_source && use_source != source) { - cnc->priv->source = g_object_ref (use_source); - de_set_source = TRUE; - } else { - source = NULL; - de_set_source = FALSE; - } - e_ews_connection_get_folder_sync ( cnc, EWS_PRIORITY_MEDIUM, "Default", NULL, ids, NULL, cancellable, &local_error); - if (de_set_source) { - g_clear_object (&cnc->priv->source); - cnc->priv->source = source; - } - g_slist_free_full (ids, (GDestroyNotify) e_ews_folder_id_free); if (local_error == NULL) { result = E_SOURCE_AUTHENTICATION_ACCEPTED; - } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) && - e_ews_connection_get_ssl_error_details (cnc, out_certificate_pem, out_certificate_errors)) { - result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED; } else { gboolean auth_failed; @@ -2686,29 +2618,6 @@ e_ews_connection_get_source (EEwsConnection *cnc) return cnc->priv->source; } -gboolean -e_ews_connection_get_ssl_error_details (EEwsConnection *cnc, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (out_certificate_pem != NULL, FALSE); - g_return_val_if_fail (out_certificate_errors != NULL, FALSE); - - g_mutex_lock (&cnc->priv->property_lock); - if (!cnc->priv->ssl_info_set) { - g_mutex_unlock (&cnc->priv->property_lock); - return FALSE; - } - - *out_certificate_pem = g_strdup (cnc->priv->ssl_certificate_pem); - *out_certificate_errors = cnc->priv->ssl_certificate_errors; - - g_mutex_unlock (&cnc->priv->property_lock); - - return TRUE; -} - const gchar * e_ews_connection_get_uri (EEwsConnection *cnc) { @@ -2997,9 +2906,6 @@ autodiscover_response_cb (SoupSession *session, g_set_error ( &error, SOUP_HTTP_ERROR, status, "%d %s", status, msg->reason_phrase); - - if (status == SOUP_STATUS_SSL_FAILED) - ews_connection_check_ssl_error (ad->cnc, msg); } g_free (service_url); @@ -3150,8 +3056,7 @@ static void post_restarted (SoupMessage *msg, gpointer data) } static SoupMessage * -e_ews_get_msg_for_url (EEwsConnection *cnc, - CamelEwsSettings *settings, +e_ews_get_msg_for_url (CamelEwsSettings *settings, const gchar *url, xmlOutputBuffer *buf, GError **error) @@ -3173,9 +3078,6 @@ e_ews_get_msg_for_url (EEwsConnection *cnc, return NULL; } - if (cnc->priv->source) - e_soup_ssl_trust_connect (msg, cnc->priv->source); - e_ews_message_attach_chunk_allocator (msg); e_ews_message_set_user_agent_header (msg, settings); @@ -3205,8 +3107,6 @@ e_ews_autodiscover_ws_url_sync (ESource *source, CamelEwsSettings *settings, const gchar *email_address, const gchar *password, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, GCancellable *cancellable, GError **error) { @@ -3225,7 +3125,7 @@ e_ews_autodiscover_ws_url_sync (ESource *source, result = e_async_closure_wait (closure); - success = e_ews_autodiscover_ws_url_finish (settings, result, out_certificate_pem, out_certificate_errors, error); + success = e_ews_autodiscover_ws_url_finish (settings, result, error); e_async_closure_free (closure); @@ -3336,11 +3236,11 @@ e_ews_autodiscover_ws_url (ESource *source, simple, ad, (GDestroyNotify) autodiscover_data_free); /* Passing a NULL URL string returns NULL. */ - ad->msgs[0] = e_ews_get_msg_for_url (cnc, settings, url1, buf, &error); - ad->msgs[1] = e_ews_get_msg_for_url (cnc, settings, url2, buf, NULL); - ad->msgs[2] = e_ews_get_msg_for_url (cnc, settings, url3, buf, NULL); - ad->msgs[3] = e_ews_get_msg_for_url (cnc, settings, url4, buf, NULL); - ad->msgs[4] = e_ews_get_msg_for_url (cnc, settings, url5, buf, NULL); + ad->msgs[0] = e_ews_get_msg_for_url (settings, url1, buf, &error); + ad->msgs[1] = e_ews_get_msg_for_url (settings, url2, buf, NULL); + ad->msgs[2] = e_ews_get_msg_for_url (settings, url3, buf, NULL); + ad->msgs[3] = e_ews_get_msg_for_url (settings, url4, buf, NULL); + ad->msgs[4] = e_ews_get_msg_for_url (settings, url5, buf, NULL); /* These have to be submitted only after they're both set in ad->msgs[] * or there will be races with fast completion */ @@ -3400,13 +3300,10 @@ has_suffix_icmp (const gchar *text, gboolean e_ews_autodiscover_ws_url_finish (CamelEwsSettings *settings, GAsyncResult *result, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, GError **error) { GSimpleAsyncResult *simple; struct _autodiscover_data *ad; - GError *local_error = NULL; g_return_val_if_fail ( g_simple_async_result_is_valid ( @@ -3416,20 +3313,8 @@ e_ews_autodiscover_ws_url_finish (CamelEwsSettings *settings, simple = G_SIMPLE_ASYNC_RESULT (result); ad = g_simple_async_result_get_op_res_gpointer (simple); - if (g_simple_async_result_propagate_error (simple, &local_error)) { - if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) { - if (!e_ews_connection_get_ssl_error_details (ad->cnc, out_certificate_pem, out_certificate_errors)) { - if (out_certificate_pem) - *out_certificate_pem = NULL; - if (out_certificate_errors) - *out_certificate_errors = 0; - } - } - - g_propagate_error (error, local_error); - + if (g_simple_async_result_propagate_error (simple, error)) return FALSE; - } g_warn_if_fail (ad->as_url != NULL); g_warn_if_fail (ad->oab_url != NULL); @@ -3588,8 +3473,6 @@ oal_response_cb (SoupSession *soup_session, simple = G_SIMPLE_ASYNC_RESULT (user_data); data = g_simple_async_result_get_op_res_gpointer (simple); - ews_connection_check_ssl_error (data->cnc, soup_message); - if (ews_connection_credentials_failed (data->cnc, soup_message, simple)) { goto exit; } else if (soup_message->status_code != 200) { @@ -3735,7 +3618,7 @@ e_ews_connection_get_oal_list (EEwsConnection *cnc, g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - soup_message = e_ews_get_msg_for_url (cnc, cnc->priv->settings, cnc->priv->uri, NULL, &error); + soup_message = e_ews_get_msg_for_url (cnc->priv->settings, cnc->priv->uri, NULL, &error); simple = g_simple_async_result_new ( G_OBJECT (cnc), callback, user_data, @@ -3856,7 +3739,7 @@ e_ews_connection_get_oal_detail (EEwsConnection *cnc, g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - soup_message = e_ews_get_msg_for_url (cnc, cnc->priv->settings, cnc->priv->uri, NULL, &error); + soup_message = e_ews_get_msg_for_url (cnc->priv->settings, cnc->priv->uri, NULL, &error); simple = g_simple_async_result_new ( G_OBJECT (cnc), callback, user_data, @@ -3943,8 +3826,6 @@ oal_download_response_cb (SoupSession *soup_session, simple = G_SIMPLE_ASYNC_RESULT (user_data); data = g_simple_async_result_get_op_res_gpointer (simple); - ews_connection_check_ssl_error (data->cnc, soup_message); - if (ews_connection_credentials_failed (data->cnc, soup_message, simple)) { g_unlink (data->cache_filename); } else if (soup_message->status_code != 200) { @@ -4073,7 +3954,7 @@ e_ews_connection_download_oal_file (EEwsConnection *cnc, g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - soup_message = e_ews_get_msg_for_url (cnc, cnc->priv->settings, cnc->priv->uri, NULL, &error); + soup_message = e_ews_get_msg_for_url (cnc->priv->settings, cnc->priv->uri, NULL, &error); simple = g_simple_async_result_new ( G_OBJECT (cnc), callback, user_data, @@ -10830,10 +10711,10 @@ get_user_photo_response_cb (ESoapResponse *response, return; } - async_data->custom_data = e_soap_parameter_get_string_value (param); - if (async_data->custom_data && !*async_data->custom_data) { - g_free (async_data->custom_data); - async_data->custom_data = NULL; + async_data->user_photo = e_soap_parameter_get_string_value (param); + if (async_data->user_photo && !*async_data->user_photo) { + g_free (async_data->user_photo); + async_data->user_photo = NULL; } } @@ -10918,11 +10799,11 @@ e_ews_connection_get_user_photo_finish (EEwsConnection *cnc, if (g_simple_async_result_propagate_error (simple, error)) return FALSE; - if (!async_data->custom_data) + if (!async_data->user_photo) return FALSE; - *out_picture_data = async_data->custom_data; - async_data->custom_data = NULL; + *out_picture_data = async_data->user_photo; + async_data->user_photo = NULL; return TRUE; } @@ -10955,254 +10836,3 @@ e_ews_connection_get_user_photo_sync (EEwsConnection *cnc, return success; } - -static void -get_user_configuration_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param, *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name (response, "ResponseMessages", &error); - - if (param) { - param = e_soap_parameter_get_first_child_by_name (param, "GetUserConfigurationResponseMessage"); - if (!param) { - g_set_error (&error, - SOUP_HTTP_ERROR, SOUP_STATUS_MALFORMED, - "Missing <%s> in SOAP response", "GetUserConfigurationResponseMessage"); - } - } - - if (param) { - param = e_soap_parameter_get_first_child_by_name (param, "UserConfiguration"); - if (!param) { - g_set_error (&error, - SOUP_HTTP_ERROR, SOUP_STATUS_MALFORMED, - "Missing <%s> in SOAP response", "UserConfiguration"); - } - } - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child_by_name (param, "ItemId"); - if (subparam) { - gchar *id, *changekey; - - id = e_soap_parameter_get_property (subparam, "Id"); - changekey = e_soap_parameter_get_property (subparam, "ChangeKey"); - - /* Encoded as: Id + "\n" + ChangeKey */ - async_data->custom_data = g_strconcat (id ? id : "", "\n", changekey, NULL); - - g_free (changekey); - g_free (id); - } - - if (!subparam) { - subparam = e_soap_parameter_get_first_child_by_name (param, "Dictionary"); - if (subparam) - async_data->custom_data = e_soap_response_dump_parameter (response, subparam); - } - - if (!subparam) { - subparam = e_soap_parameter_get_first_child_by_name (param, "XmlData"); - if (subparam) { - async_data->custom_data = e_soap_parameter_get_string_value (subparam); - } - } - - if (!subparam) { - subparam = e_soap_parameter_get_first_child_by_name (param, "BinaryData"); - if (subparam) { - async_data->custom_data = e_soap_parameter_get_string_value (subparam); - } - } - - if (async_data->custom_data && !*async_data->custom_data) { - g_free (async_data->custom_data); - async_data->custom_data = NULL; - } -} - -static void -e_ews_folder_id_append_to_msg (ESoapMessage *msg, - const gchar *email, - const EwsFolderId *fid) -{ - g_return_if_fail (msg != NULL); - g_return_if_fail (fid != NULL); - - if (fid->is_distinguished_id) - e_soap_message_start_element (msg, "DistinguishedFolderId", NULL, NULL); - else - e_soap_message_start_element (msg, "FolderId", NULL, NULL); - - e_soap_message_add_attribute (msg, "Id", fid->id, NULL, NULL); - if (fid->change_key) - e_soap_message_add_attribute (msg, "ChangeKey", fid->change_key, NULL, NULL); - - if (fid->is_distinguished_id && email) { - e_soap_message_start_element (msg, "Mailbox", NULL, NULL); - e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, email); - e_soap_message_end_element (msg); - } - - e_soap_message_end_element (msg); -} - -void -e_ews_connection_get_user_configuration (EEwsConnection *cnc, - gint pri, - const EwsFolderId *fid, - const gchar *config_name, - EEwsUserConfigurationProperties props, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - EwsFolderId local_fid; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (cnc->priv != NULL); - g_return_if_fail (fid != NULL); - g_return_if_fail (config_name != NULL); - - simple = g_simple_async_result_new (G_OBJECT (cnc), callback, user_data, e_ews_connection_get_user_configuration); - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer (simple, async_data, (GDestroyNotify) async_data_free); - - /* EWS server version earlier than 2010 doesn't support it. */ - if (!e_ews_connection_satisfies_server_version (cnc, E_EWS_EXCHANGE_2010)) { - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - return; - } - - local_fid = *fid; - local_fid.change_key = NULL; - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetUserConfiguration", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2010, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "UserConfigurationName", "messages", NULL); - e_soap_message_add_attribute (msg, "Name", config_name, NULL, NULL); - - e_ews_folder_id_append_to_msg (msg, cnc->priv->email, &local_fid); - - e_soap_message_end_element (msg); /* UserConfigurationName */ - - e_soap_message_start_element (msg, "UserConfigurationProperties", "messages", NULL); - - switch (props) { - case E_EWS_USER_CONFIGURATION_PROPERTIES_ID: - e_soap_message_write_string (msg, "Id"); - break; - case E_EWS_USER_CONFIGURATION_PROPERTIES_DICTIONARY: - e_soap_message_write_string (msg, "Dictionary"); - break; - case E_EWS_USER_CONFIGURATION_PROPERTIES_XMLDATA: - e_soap_message_write_string (msg, "XmlData"); - break; - case E_EWS_USER_CONFIGURATION_PROPERTIES_BINARYDATA: - e_soap_message_write_string (msg, "BinaryData"); - break; - /* case E_EWS_USER_CONFIGURATION_PROPERTIES_ALL: - e_soap_message_write_string (msg, "All"); - break; */ - default: - e_soap_message_write_string (msg, "Unknown"); - break; - } - - e_soap_message_end_element (msg); /* UserConfigurationProperties */ - - e_ews_message_write_footer (msg); - - e_ews_connection_queue_request (cnc, msg, get_user_configuration_response_cb, pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_user_configuration_finish (EEwsConnection *cnc, - GAsyncResult *result, - gchar **out_properties, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid (result, G_OBJECT (cnc), e_ews_connection_get_user_configuration), - FALSE); - g_return_val_if_fail (out_properties != NULL, FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (!async_data->custom_data) - return FALSE; - - *out_properties = async_data->custom_data; - async_data->custom_data = NULL; - - return TRUE; -} - -gboolean -e_ews_connection_get_user_configuration_sync (EEwsConnection *cnc, - gint pri, - const EwsFolderId *fid, - const gchar *config_name, - EEwsUserConfigurationProperties props, - gchar **out_properties, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_user_configuration ( - cnc, pri, fid, config_name, props, cancellable, e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_user_configuration_finish (cnc, result, out_properties, error); - - e_async_closure_free (closure); - - return success; -} diff --git a/src/server/e-ews-connection.c.cve-2019-3890 b/src/server/e-ews-connection.c.cve-2019-3890 deleted file mode 100644 index 267d270..0000000 --- a/src/server/e-ews-connection.c.cve-2019-3890 +++ /dev/null @@ -1,10838 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Authors : - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - * 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. - * - * 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "e-ews-connection.h" -#include "e-ews-connection-utils.h" -#include "e-ews-message.h" -#include "e-ews-item-change.h" -#include "e-ews-debug.h" -#include "e-ews-notification.h" - -#define d(x) x - -#define E_EWS_CONNECTION_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_EWS_CONNECTION, EEwsConnectionPrivate)) - -/* For the number of connections */ -#define EWS_CONNECTION_MAX_REQUESTS 1 - -/* A chunk size limit when moving items in chunks. */ -#define EWS_MOVE_ITEMS_CHUNK_SIZE 500 - -#define QUEUE_LOCK(x) (g_rec_mutex_lock(&(x)->priv->queue_lock)) -#define QUEUE_UNLOCK(x) (g_rec_mutex_unlock(&(x)->priv->queue_lock)) - -#define NOTIFICATION_LOCK(x) (g_mutex_lock(&(x)->priv->notification_lock)) -#define NOTIFICATION_UNLOCK(x) (g_mutex_unlock(&(x)->priv->notification_lock)) - -struct _EwsNode; -static GMutex connecting; -static GHashTable *loaded_connections_permissions = NULL; -static gint comp_func (gconstpointer a, gconstpointer b); - -static void ews_response_cb (SoupSession *session, SoupMessage *msg, gpointer data); - -static void ews_connection_authenticate (SoupSession *sess, - SoupMessage *msg, - SoupAuth *auth, - gboolean retrying, - gpointer data); - -/* Connection APIS */ - -struct _EEwsConnectionPrivate { - ESource *source; - ESoupAuthBearer *bearer_auth; - SoupSession *soup_session; - GThread *soup_thread; - GMainLoop *soup_loop; - GMainContext *soup_context; - GProxyResolver *proxy_resolver; - EEwsNotification *notification; - - CamelEwsSettings *settings; - GMutex property_lock; - - /* Hash key for the loaded_connections_permissions table. */ - gchar *hash_key; - - gchar *uri; - gchar *password; - gchar *email; - gchar *impersonate_user; - - GSList *jobs; - GSList *active_job_queue; - GRecMutex queue_lock; - GMutex notification_lock; - - GHashTable *subscriptions; - GSList *subscribed_folders; - - EEwsServerVersion version; - - /* Set to TRUE when this connection had been disconnected and cannot be used anymore */ - gboolean disconnected_flag; -}; - -enum { - PROP_0, - PROP_PASSWORD, - PROP_PROXY_RESOLVER, - PROP_SETTINGS, - PROP_SOURCE -}; - -enum { - SERVER_NOTIFICATION, - PASSWORD_WILL_EXPIRE, - LAST_SIGNAL, -}; - -static guint signals[LAST_SIGNAL]; - -static guint notification_key = 1; - -typedef struct _EwsNode EwsNode; -typedef struct _EwsAsyncData EwsAsyncData; -typedef struct _EwsEventsAsyncData EwsEventsAsyncData; -typedef struct _EwsUrls EwsUrls; - -struct _EwsAsyncData { - GSList *items_created; - GSList *items_updated; - GSList *items_deleted; - GSList *tzds; /* EEwsCalendarTimeZoneDefinition */ - - gint total_items; - const gchar *directory; - GSList *items; - EwsPhotoAttachmentInfo *photo; - gchar *sync_state; - gboolean includes_last_item; - EwsDelegateDeliver deliver_to; - EEwsFolderType folder_type; - EEwsConnection *cnc; - gchar *user_photo; /* base64-encoded, as GetUserPhoto result */ -}; - -struct _EwsNode { - ESoapMessage *msg; - EEwsConnection *cnc; - GSimpleAsyncResult *simple; - - gint pri; /* the command priority */ - EEwsResponseCallback cb; - - GCancellable *cancellable; - gulong cancel_handler_id; -}; - -struct _EwsUrls { - xmlChar *as_url; - xmlChar *oab_url; - - /* all the below variables are for future use */ - xmlChar *oof_url; - gpointer future1; - gpointer future2; -}; - -G_DEFINE_TYPE (EEwsConnection, e_ews_connection, G_TYPE_OBJECT) - -/* Static Functions */ - -GQuark -ews_connection_error_quark (void) -{ - static GQuark quark = 0; - - if (G_UNLIKELY (quark == 0)) { - const gchar *string = "ews-connection-error-quark"; - quark = g_quark_from_static_string (string); - } - - return quark; -} - -static void -async_data_free (EwsAsyncData *async_data) -{ - g_free (async_data->user_photo); - g_free (async_data); -} - -EEwsNotificationEvent * -e_ews_notification_event_new (void) -{ - return g_new0 (EEwsNotificationEvent, 1); -} - -void -e_ews_notification_event_free (EEwsNotificationEvent *event) -{ - if (event != NULL) { - g_free (event->folder_id); - g_free (event->old_folder_id); - g_free (event); - } -} - -EEwsCalendarTo * -e_ews_calendar_to_new (void) -{ - return g_new0 (EEwsCalendarTo, 1); -} - -void -e_ews_calendar_to_free (EEwsCalendarTo *to) { - if (to != NULL) { - g_free (to->kind); - g_free (to->value); - } -} - -EEwsCalendarAbsoluteDateTransition * -e_ews_calendar_absolute_date_transition_new (void) -{ - return g_new0 (EEwsCalendarAbsoluteDateTransition, 1); -} - -void -e_ews_calendar_absolute_date_transition_free (EEwsCalendarAbsoluteDateTransition *adt) -{ - if (adt != NULL) { - e_ews_calendar_to_free (adt->to); - g_free (adt->date_time); - g_free (adt); - } -} - -EEwsCalendarRecurringDayTransition * -e_ews_calendar_recurring_day_transition_new (void) -{ - return g_new0 (EEwsCalendarRecurringDayTransition, 1); -} - -void -e_ews_calendar_recurring_day_transition_free (EEwsCalendarRecurringDayTransition *rdayt) -{ - if (rdayt != NULL) { - e_ews_calendar_to_free (rdayt->to); - g_free (rdayt->time_offset); - g_free (rdayt->month); - g_free (rdayt->day_of_week); - g_free (rdayt->occurrence); - g_free (rdayt); - } -} - -EEwsCalendarRecurringDateTransition * -e_ews_calendar_recurring_date_transition_new (void) -{ - return g_new0 (EEwsCalendarRecurringDateTransition, 1); -} - -void -e_ews_calendar_recurring_date_transition_free (EEwsCalendarRecurringDateTransition *rdatet) -{ - if (rdatet != NULL) { - e_ews_calendar_to_free (rdatet->to); - g_free (rdatet->time_offset); - g_free (rdatet->month); - g_free (rdatet->day); - g_free (rdatet); - } -} - -EEwsCalendarPeriod * -e_ews_calendar_period_new (void) -{ - return g_new0 (EEwsCalendarPeriod, 1); -} - -void -e_ews_calendar_period_free (EEwsCalendarPeriod *period) -{ - if (period != NULL) { - g_free (period->bias); - g_free (period->name); - g_free (period->id); - g_free (period); - } -} - -EEwsCalendarTransitionsGroup * -e_ews_calendar_transitions_group_new (void) -{ - return g_new0 (EEwsCalendarTransitionsGroup, 1); -} - -void -e_ews_calendar_transitions_group_free (EEwsCalendarTransitionsGroup *tg) -{ - if (tg != NULL) { - g_free (tg->id); - e_ews_calendar_to_free (tg->transition); - g_slist_free_full ( - tg->absolute_date_transitions, - (GDestroyNotify) e_ews_calendar_absolute_date_transition_free); - g_slist_free_full ( - tg->recurring_day_transitions, - (GDestroyNotify) e_ews_calendar_recurring_day_transition_free); - g_slist_free_full ( - tg->recurring_date_transitions, - (GDestroyNotify) e_ews_calendar_recurring_date_transition_free); - g_free (tg); - } -} - -EEwsCalendarTransitions * -e_ews_calendar_transitions_new (void) -{ - return g_new0 (EEwsCalendarTransitions, 1); -} - -void -e_ews_calendar_transitions_free (EEwsCalendarTransitions *transitions) -{ - if (transitions != NULL) { - e_ews_calendar_to_free (transitions->transition); - g_slist_free_full ( - transitions->absolute_date_transitions, - (GDestroyNotify) e_ews_calendar_absolute_date_transition_free); - g_slist_free_full ( - transitions->recurring_day_transitions, - (GDestroyNotify) e_ews_calendar_recurring_day_transition_free); - g_slist_free_full ( - transitions->recurring_date_transitions, - (GDestroyNotify) e_ews_calendar_recurring_date_transition_free); - g_free (transitions); - } -} - -EEwsCalendarTimeZoneDefinition * -e_ews_calendar_time_zone_definition_new (void) -{ - return g_new0 (EEwsCalendarTimeZoneDefinition, 1); -} - -void -e_ews_calendar_time_zone_definition_free (EEwsCalendarTimeZoneDefinition *tzd) -{ - if (tzd != NULL) { - g_free (tzd->name); - g_free (tzd->id); - g_slist_free_full (tzd->periods, (GDestroyNotify) e_ews_calendar_period_free); - g_slist_free_full (tzd->transitions_groups, (GDestroyNotify) e_ews_calendar_transitions_group_free); - e_ews_calendar_transitions_free (tzd->transitions); - g_free (tzd); - } -} - -EEwsExtendedFieldURI * -e_ews_extended_field_uri_new (void) -{ - return g_new0 (EEwsExtendedFieldURI, 1); -} - -void -e_ews_extended_field_uri_free (EEwsExtendedFieldURI *ex_field_uri) -{ - if (ex_field_uri != NULL) { - g_free (ex_field_uri->distinguished_prop_set_id); - g_free (ex_field_uri->prop_set_id); - g_free (ex_field_uri->prop_tag); - g_free (ex_field_uri->prop_name); - g_free (ex_field_uri->prop_id); - g_free (ex_field_uri->prop_type); - g_free (ex_field_uri); - } -} - -EEwsIndexedFieldURI * -e_ews_indexed_field_uri_new (const gchar *uri, - const gchar *index) -{ - EEwsIndexedFieldURI *furi; - - furi = g_new0 (EEwsIndexedFieldURI, 1); - furi->field_uri = g_strdup (uri); - furi->field_index = g_strdup (index); - - return furi; -} - -void -e_ews_indexed_field_uri_free (EEwsIndexedFieldURI *id_field_uri) -{ - if (id_field_uri != NULL) { - g_free (id_field_uri->field_uri); - g_free (id_field_uri->field_index); - g_free (id_field_uri); - } -} - -EEwsAdditionalProps * -e_ews_additional_props_new (void) -{ - return g_new0 (EEwsAdditionalProps, 1); -} - -void -e_ews_additional_props_free (EEwsAdditionalProps *add_props) -{ - if (add_props != NULL) { - g_free (add_props->field_uri); - g_slist_free_full (add_props->extended_furis, (GDestroyNotify) e_ews_extended_field_uri_free); - g_slist_free_full (add_props->indexed_furis, (GDestroyNotify) e_ews_indexed_field_uri_free); - g_free (add_props); - } -} - -static EwsNode * -ews_node_new () -{ - EwsNode *node; - - node = g_new0 (EwsNode, 1); - return node; -} - -static gboolean -autodiscover_parse_protocol (xmlNode *node, - EwsUrls *urls) -{ - for (node = node->children; node; node = node->next) { - if (node->type == XML_ELEMENT_NODE && - !strcmp ((gchar *) node->name, "ASUrl")) { - if (urls->as_url != NULL) - xmlFree (urls->as_url); - - urls->as_url = xmlNodeGetContent (node); - } else if (node->type == XML_ELEMENT_NODE && - !strcmp ((gchar *) node->name, "OABUrl")) { - if (urls->oab_url != NULL) - xmlFree (urls->oab_url); - - urls->oab_url = xmlNodeGetContent (node); - } - - /* Once we find both, we can stop looking for the URLs */ - if (urls->as_url && urls->oab_url) - return TRUE; - } - - return FALSE; -} - -static gint -comp_func (gconstpointer a, - gconstpointer b) -{ - EwsNode *node1 = (EwsNode *) a; - EwsNode *node2 = (EwsNode *) b; - if (node1->pri > node2->pri) - return -1; - else if (node1->pri < node2->pri) - return 1; - else - return 0; -} - -typedef enum _EwsScheduleOp { - EWS_SCHEDULE_OP_QUEUE_MESSAGE, - EWS_SCHEDULE_OP_CANCEL, - EWS_SCHEDULE_OP_ABORT -} EwsScheduleOp; - -typedef struct _EwsScheduleData -{ - EEwsConnection *cnc; - SoupMessage *message; - - EwsScheduleOp op; - - SoupSessionCallback queue_callback; - gpointer queue_user_data; -} EwsScheduleData; - -/* this is run in priv->soup_thread */ -static gboolean -ews_connection_scheduled_cb (gpointer user_data) -{ - EwsScheduleData *sd = user_data; - - g_return_val_if_fail (sd != NULL, FALSE); - - switch (sd->op) { - case EWS_SCHEDULE_OP_QUEUE_MESSAGE: - if (!e_ews_connection_utils_prepare_message (sd->cnc, sd->message, NULL)) { - e_ews_debug_dump_raw_soup_request (sd->message); - - if (sd->queue_callback) { - sd->queue_callback (sd->cnc->priv->soup_session, sd->message, sd->queue_user_data); - } else { - /* This should not happen */ - g_warn_if_reached (); - - soup_session_queue_message ( - sd->cnc->priv->soup_session, sd->message, - sd->queue_callback, sd->queue_user_data); - soup_session_cancel_message (sd->cnc->priv->soup_session, sd->message, sd->message->status_code); - } - } else { - e_ews_debug_dump_raw_soup_request (sd->message); - - soup_session_queue_message ( - sd->cnc->priv->soup_session, sd->message, - sd->queue_callback, sd->queue_user_data); - } - break; - case EWS_SCHEDULE_OP_CANCEL: - soup_session_cancel_message (sd->cnc->priv->soup_session, sd->message, SOUP_STATUS_CANCELLED); - break; - case EWS_SCHEDULE_OP_ABORT: - soup_session_abort (sd->cnc->priv->soup_session); - break; - } - - if (sd->message) - g_object_unref (sd->message); - /* in case this is the last reference */ - e_ews_connection_utils_unref_in_thread (sd->cnc); - g_free (sd); - - return FALSE; -} - -static void -ews_connection_schedule_queue_message (EEwsConnection *cnc, - SoupMessage *message, - SoupSessionCallback callback, - gpointer user_data) -{ - EwsScheduleData *sd; - GSource *source; - - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - g_return_if_fail (SOUP_IS_MESSAGE (message)); - - sd = g_new0 (EwsScheduleData, 1); - sd->cnc = g_object_ref (cnc); - sd->message = g_object_ref (message); - sd->op = EWS_SCHEDULE_OP_QUEUE_MESSAGE; - sd->queue_callback = callback; - sd->queue_user_data = user_data; - - source = g_idle_source_new (); - g_source_set_priority (source, G_PRIORITY_DEFAULT); - g_source_set_callback (source, ews_connection_scheduled_cb, sd, NULL); - g_source_attach (source, cnc->priv->soup_context); - g_source_unref (source); -} - -static void -ews_connection_schedule_cancel_message (EEwsConnection *cnc, - SoupMessage *message) -{ - EwsScheduleData *sd; - GSource *source; - - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - g_return_if_fail (SOUP_IS_MESSAGE (message)); - - sd = g_new0 (EwsScheduleData, 1); - sd->cnc = g_object_ref (cnc); - sd->message = g_object_ref (message); - sd->op = EWS_SCHEDULE_OP_CANCEL; - - source = g_idle_source_new (); - g_source_set_priority (source, G_PRIORITY_DEFAULT); - g_source_set_callback (source, ews_connection_scheduled_cb, sd, NULL); - g_source_attach (source, cnc->priv->soup_context); - g_source_unref (source); -} - -static void -ews_connection_schedule_abort (EEwsConnection *cnc) -{ - EwsScheduleData *sd; - GSource *source; - - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - - sd = g_new0 (EwsScheduleData, 1); - sd->cnc = g_object_ref (cnc); - sd->op = EWS_SCHEDULE_OP_ABORT; - - source = g_idle_source_new (); - g_source_set_priority (source, G_PRIORITY_DEFAULT); - g_source_set_callback (source, ews_connection_scheduled_cb, sd, NULL); - g_source_attach (source, cnc->priv->soup_context); - g_source_unref (source); -} - -static void ews_cancel_request (GCancellable *cancellable, gpointer user_data); - -static void -ews_discover_server_version (EEwsConnection *cnc, - ESoapResponse *response) -{ - ESoapParameter *param; - gchar *version; - - g_return_if_fail (cnc != NULL); - - if (cnc->priv->version != E_EWS_EXCHANGE_UNKNOWN) - return; - - param = e_soap_response_get_first_parameter_by_name ( - response, "ServerVersionInfo", NULL); - if (!param) - return; - - version = e_soap_parameter_get_property (param, "Version"); - - e_ews_connection_set_server_version_from_string (cnc, version); - - g_free (version); -} - -/* this is run in priv->soup_thread */ -static gboolean -ews_next_request (gpointer _cnc) -{ - EEwsConnection *cnc = _cnc; - GSList *l; - EwsNode *node; - - QUEUE_LOCK (cnc); - - l = cnc->priv->jobs; - - if (!l || g_slist_length (cnc->priv->active_job_queue) >= EWS_CONNECTION_MAX_REQUESTS) { - QUEUE_UNLOCK (cnc); - return FALSE; - } - - node = (EwsNode *) l->data; - - /* Remove the node from the priority queue */ - cnc->priv->jobs = g_slist_remove (cnc->priv->jobs, (gconstpointer *) node); - - /* Add to active job queue */ - cnc->priv->active_job_queue = g_slist_append (cnc->priv->active_job_queue, node); - - if (cnc->priv->soup_session) { - SoupMessage *msg = SOUP_MESSAGE (node->msg); - - if (!e_ews_connection_utils_prepare_message (cnc, msg, node->cancellable)) { - e_ews_debug_dump_raw_soup_request (msg); - QUEUE_UNLOCK (cnc); - - ews_response_cb (cnc->priv->soup_session, msg, node); - } else { - e_ews_debug_dump_raw_soup_request (msg); - soup_session_queue_message (cnc->priv->soup_session, msg, ews_response_cb, node); - QUEUE_UNLOCK (cnc); - } - } else { - QUEUE_UNLOCK (cnc); - - ews_cancel_request (NULL, node); - } - - return FALSE; -} - -static void -ews_trigger_next_request (EEwsConnection *cnc) -{ - GSource *source; - - g_return_if_fail (cnc != NULL); - - if (cnc->priv->soup_session) { - source = g_idle_source_new (); - g_source_set_priority (source, G_PRIORITY_DEFAULT); - g_source_set_callback (source, ews_next_request, cnc, NULL); - g_source_attach (source, cnc->priv->soup_context); - g_source_unref (source); - } else { - ews_next_request (cnc); - } -} - -/** - * ews_active_job_done - * @cnc: - * @msg: - * Removes the node from active Queue and free's the node - * - * Returns: - **/ -static void -ews_active_job_done (EEwsConnection *cnc, - EwsNode *ews_node) -{ - g_return_if_fail (cnc != NULL); - g_return_if_fail (ews_node != NULL); - - QUEUE_LOCK (cnc); - - cnc->priv->active_job_queue = g_slist_remove (cnc->priv->active_job_queue, ews_node); - if (ews_node->cancellable && ews_node->cancel_handler_id) - g_signal_handler_disconnect (ews_node->cancellable, ews_node->cancel_handler_id); - - QUEUE_UNLOCK (cnc); - - ews_trigger_next_request (cnc); - - if (ews_node->cancellable) - g_object_unref (ews_node->cancellable); - - if (ews_node->simple) { - /* the 'simple' holds reference on 'cnc' and this function - * is called in a dedicated thread, which 'cnc' joins on dispose, - * thus to avoid race condition, unref the object in its own thread */ - e_ews_connection_utils_unref_in_thread (ews_node->simple); - } - - g_free (ews_node); -} - -static void -ews_cancel_request (GCancellable *cancellable, - gpointer user_data) -{ - EwsNode *node = user_data; - EEwsConnection *cnc = node->cnc; - GSimpleAsyncResult *simple = node->simple; - ESoapMessage *msg = node->msg; - GSList *found; - - QUEUE_LOCK (cnc); - found = g_slist_find (cnc->priv->active_job_queue, node); - cnc->priv->jobs = g_slist_remove (cnc->priv->jobs, node); - QUEUE_UNLOCK (cnc); - - g_simple_async_result_set_error ( - simple, - G_IO_ERROR, - G_IO_ERROR_CANCELLED, - _("Operation Cancelled")); - if (found) { - ews_connection_schedule_cancel_message (cnc, SOUP_MESSAGE (msg)); - } else { - ews_response_cb (cnc->priv->soup_session, SOUP_MESSAGE (msg), node); - } -} - -void -e_ews_connection_queue_request (EEwsConnection *cnc, - ESoapMessage *msg, - EEwsResponseCallback cb, - gint pri, - GCancellable *cancellable, - GSimpleAsyncResult *simple) -{ - EwsNode *node; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (cb != NULL); - g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple)); - - node = ews_node_new (); - node->msg = msg; - node->pri = pri; - node->cb = cb; - node->cnc = cnc; - node->simple = g_object_ref (simple); - - QUEUE_LOCK (cnc); - cnc->priv->jobs = g_slist_insert_sorted (cnc->priv->jobs, (gpointer *) node, (GCompareFunc) comp_func); - QUEUE_UNLOCK (cnc); - - if (cancellable) { - node->cancellable = g_object_ref (cancellable); - if (g_cancellable_is_cancelled (cancellable)) - ews_cancel_request (cancellable, node); - else - node->cancel_handler_id = g_cancellable_connect ( - cancellable, - G_CALLBACK (ews_cancel_request), - (gpointer) node, NULL); - } - - ews_trigger_next_request (cnc); -} - -static gboolean -ews_connection_credentials_failed (EEwsConnection *connection, - SoupMessage *message, - GSimpleAsyncResult *simple) -{ - gint expire_in_days = 0; - gboolean expired = FALSE; - gchar *service_url = NULL; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (connection), FALSE); - g_return_val_if_fail (SOUP_IS_MESSAGE (message), FALSE); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple), FALSE); - - if (!e_ews_connection_utils_check_x_ms_credential_headers (message, &expire_in_days, &expired, &service_url)) - return FALSE; - - if (expired) { - GError *error = NULL; - - e_ews_connection_utils_expired_password_to_error (service_url, &error); - g_simple_async_result_take_error (simple, error); - } else if (expire_in_days > 0) { - g_signal_emit (connection, signals[PASSWORD_WILL_EXPIRE], 0, expire_in_days, service_url); - } - - g_free (service_url); - - return expired; -} - -/* Response callbacks */ - -static void -ews_response_cb (SoupSession *session, - SoupMessage *msg, - gpointer data) -{ - EwsNode *enode = (EwsNode *) data; - ESoapResponse *response; - ESoapParameter *param; - gint log_level; - gint wait_ms = 0; - - if (g_cancellable_is_cancelled (enode->cancellable)) - goto exit; - - if (ews_connection_credentials_failed (enode->cnc, msg, enode->simple)) { - goto exit; - } else if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) { - if (msg->response_headers) { - const gchar *diagnostics; - - diagnostics = soup_message_headers_get_list (msg->response_headers, "X-MS-DIAGNOSTICS"); - if (diagnostics && strstr (diagnostics, "invalid_grant")) { - g_simple_async_result_set_error ( - enode->simple, - EWS_CONNECTION_ERROR, - EWS_CONNECTION_ERROR_ACCESSDENIED, - "%s", diagnostics); - goto exit; - } else if (diagnostics && *diagnostics) { - g_simple_async_result_set_error ( - enode->simple, - EWS_CONNECTION_ERROR, - EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED, - "%s", diagnostics); - goto exit; - } - } - - g_simple_async_result_set_error ( - enode->simple, - EWS_CONNECTION_ERROR, - EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED, - _("Authentication failed")); - goto exit; - } else if (msg->status_code == SOUP_STATUS_CANT_RESOLVE || - msg->status_code == SOUP_STATUS_CANT_RESOLVE_PROXY || - msg->status_code == SOUP_STATUS_CANT_CONNECT || - msg->status_code == SOUP_STATUS_CANT_CONNECT_PROXY || - msg->status_code == SOUP_STATUS_IO_ERROR) { - g_simple_async_result_set_error ( - enode->simple, - EWS_CONNECTION_ERROR, - EWS_CONNECTION_ERROR_UNAVAILABLE, - "%s", msg->reason_phrase); - goto exit; - } - - response = e_soap_message_parse_response ((ESoapMessage *) msg); - - if (response == NULL) { - g_simple_async_result_set_error ( - enode->simple, - EWS_CONNECTION_ERROR, - EWS_CONNECTION_ERROR_NORESPONSE, - _("No response: %s"), msg->reason_phrase); - goto exit; - } - - /* TODO: The stdout can be replaced with Evolution's - * Logging framework also */ - - log_level = e_ews_debug_get_log_level (); - if (log_level >= 1 && log_level < 3) { - /* This will dump only the headers, since we stole the body. - * And only if EWS_DEBUG=1, since higher levels will have dumped - * it directly from libsoup anyway. */ - e_ews_debug_dump_raw_soup_response (msg); - /* And this will dump the body... */ - e_soap_response_dump_response (response, stdout); - } - - param = e_soap_response_get_first_parameter_by_name (response, "detail", NULL); - if (param) - param = e_soap_parameter_get_first_child_by_name (param, "ResponseCode"); - if (param) { - gchar *value; - - value = e_soap_parameter_get_string_value (param); - if (value && ews_get_error_code (value) == EWS_CONNECTION_ERROR_SERVERBUSY) { - param = e_soap_response_get_first_parameter_by_name (response, "detail", NULL); - if (param) - param = e_soap_parameter_get_first_child_by_name (param, "MessageXml"); - if (param) { - param = e_soap_parameter_get_first_child_by_name (param, "Value"); - if (param) { - g_free (value); - - value = e_soap_parameter_get_property (param, "Name"); - if (g_strcmp0 (value, "BackOffMilliseconds") == 0) { - wait_ms = e_soap_parameter_get_int_value (param); - } - } - } - } - - g_free (value); - } - - if (wait_ms > 0) { - GCancellable *cancellable = enode->cancellable; - EFlag *flag; - - if (cancellable) - g_object_ref (cancellable); - g_object_ref (msg); - - flag = e_flag_new (); - while (wait_ms > 0 && !g_cancellable_is_cancelled (cancellable) && msg->status_code != SOUP_STATUS_CANCELLED) { - gint64 now = g_get_monotonic_time (); - gint left_minutes, left_seconds; - - left_minutes = wait_ms / 60000; - left_seconds = (wait_ms / 1000) % 60; - - if (left_minutes > 0) { - camel_operation_push_message (cancellable, - g_dngettext (GETTEXT_PACKAGE, - "Exchange server is busy, waiting to retry (%d:%02d minute)", - "Exchange server is busy, waiting to retry (%d:%02d minutes)", left_minutes), - left_minutes, left_seconds); - } else { - camel_operation_push_message (cancellable, - g_dngettext (GETTEXT_PACKAGE, - "Exchange server is busy, waiting to retry (%d second)", - "Exchange server is busy, waiting to retry (%d seconds)", left_seconds), - left_seconds); - } - - e_flag_wait_until (flag, now + (G_TIME_SPAN_MILLISECOND * (wait_ms > 1000 ? 1000 : wait_ms))); - - now = g_get_monotonic_time () - now; - now = now / G_TIME_SPAN_MILLISECOND; - - if (now >= wait_ms) - wait_ms = 0; - wait_ms -= now; - - camel_operation_pop_message (cancellable); - } - - e_flag_free (flag); - - g_object_unref (response); - - if (g_cancellable_is_cancelled (cancellable) || - msg->status_code == SOUP_STATUS_CANCELLED) { - g_clear_object (&cancellable); - g_object_unref (msg); - } else { - EwsNode *new_node; - - new_node = ews_node_new (); - new_node->msg = E_SOAP_MESSAGE (msg); /* takes ownership */ - new_node->pri = enode->pri; - new_node->cb = enode->cb; - new_node->cnc = enode->cnc; - new_node->simple = enode->simple; - - enode->simple = NULL; - - QUEUE_LOCK (enode->cnc); - enode->cnc->priv->jobs = g_slist_prepend (enode->cnc->priv->jobs, new_node); - QUEUE_UNLOCK (enode->cnc); - - if (cancellable) { - new_node->cancellable = g_object_ref (cancellable); - new_node->cancel_handler_id = g_cancellable_connect ( - cancellable, G_CALLBACK (ews_cancel_request), new_node, NULL); - } - - g_clear_object (&cancellable); - } - - goto exit; - } - - if (enode->cb != NULL) - enode->cb (response, enode->simple); - - g_object_unref (response); - -exit: - if (enode->simple) - g_simple_async_result_complete_in_idle (enode->simple); - - ews_active_job_done (enode->cnc, enode); -} - -typedef gpointer (*ItemParser) (ESoapParameter *param); - -static void -sync_xxx_response_cb (ESoapParameter *subparam, - EwsAsyncData *async_data, - ItemParser parser, - const gchar *last_tag, - const gchar *delete_id_tag) -{ - ESoapParameter *node; - gchar *new_sync_state = NULL, *value, *last; - GSList *items_created = NULL, *items_updated = NULL, *items_deleted = NULL; - gboolean includes_last_item; - - node = e_soap_parameter_get_first_child_by_name (subparam, "SyncState"); - new_sync_state = e_soap_parameter_get_string_value (node); - - node = e_soap_parameter_get_first_child_by_name (subparam, last_tag); - last = e_soap_parameter_get_string_value (node); - /* - * Set the includes_last_item to TRUE as default. - * It can avoid an infinite loop in caller, when, for some reason, - * we don't receive the last_tag property from the server. - */ - includes_last_item = g_strcmp0 (last, "false") != 0; - g_free (last); - - node = e_soap_parameter_get_first_child_by_name (subparam, "Changes"); - - if (node) { - ESoapParameter *subparam1; - for (subparam1 = e_soap_parameter_get_first_child_by_name (node, "Create"); - subparam1 != NULL; - subparam1 = e_soap_parameter_get_next_child_by_name (subparam1, "Create")) { - EEwsFolder *folder; - - folder = parser (subparam1); - if (folder) - items_created = g_slist_append (items_created, folder); - } - - for (subparam1 = e_soap_parameter_get_first_child_by_name (node, "Update"); - subparam1 != NULL; - subparam1 = e_soap_parameter_get_next_child_by_name (subparam1, "Update")) { - EEwsFolder *folder; - - folder = parser (subparam1); - if (folder) - items_updated = g_slist_append (items_updated, folder); - } - /* Exchange 2007SP1 introduced which is basically identical - * to ; no idea why they thought it was a good idea. */ - for (subparam1 = e_soap_parameter_get_first_child_by_name (node, "ReadFlagChange"); - subparam1 != NULL; - subparam1 = e_soap_parameter_get_next_child_by_name (subparam1, "ReadFlagChange")) { - EEwsFolder *folder; - - folder = parser (subparam1); - if (folder) - items_updated = g_slist_append (items_updated, folder); - } - - for (subparam1 = e_soap_parameter_get_first_child_by_name (node, "Delete"); - subparam1 != NULL; - subparam1 = e_soap_parameter_get_next_child_by_name (subparam1, "Delete")) { - ESoapParameter *folder_param; - - folder_param = e_soap_parameter_get_first_child_by_name (subparam1, delete_id_tag); - value = e_soap_parameter_get_property (folder_param, "Id"); - items_deleted = g_slist_append (items_deleted, value); - } - } - - async_data->items_created = items_created; - async_data->items_updated = items_updated; - async_data->items_deleted = items_deleted; - async_data->sync_state = new_sync_state; - async_data->includes_last_item = includes_last_item; -} - -static void -sync_hierarchy_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - /* - * During the first connection, we are able to get the current version of the Exchange server. - * For Addressbook/Calendar backends, we are ensuring it happens during the - * e_ews_connection_try_credentials_sync(), that calls e_e_ews_connection_get_folder_sync() and then - * we are able to get the current version of the server from this first response. - * - * For Camel, the first connection is done calling e_ews_connection_sync_folder_hierarchy_sync(). - */ - ews_discover_server_version (async_data->cnc, response); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "SyncFolderHierarchyResponseMessage")) - sync_xxx_response_cb ( - subparam, async_data, - (ItemParser) e_ews_folder_new_from_soap_parameter, - "IncludesLastFolderInRange", "FolderId"); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -static void -sync_folder_items_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "SyncFolderItemsResponseMessage")) - sync_xxx_response_cb ( - subparam, async_data, - (ItemParser) e_ews_item_new_from_soap_parameter, - "IncludesLastItemInRange", "ItemId"); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -static void -ews_handle_folders_param (ESoapParameter *subparam, - EwsAsyncData *async_data) -{ - ESoapParameter *node; - EEwsFolder *folder; - - for (node = e_soap_parameter_get_first_child_by_name (subparam, "Folders"); - node; node = e_soap_parameter_get_next_child_by_name (subparam, "Folders")) { - folder = e_ews_folder_new_from_soap_parameter (node); - if (!folder) continue; - async_data->items = g_slist_append (async_data->items, folder); - } -} - -static void -get_folder_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - /* - * During the first connection, we are able to get the current version of the Exchange server. - * For Addressbook/Calendar backends, we are ensuring it happens during the - * e_ews_connection_try_credentials_sync(), that calls e_e_ews_connection_get_folder_sync() and then - * we are able to get the current version of the server from this first response. - * - * For Camel, the first connection is done calling e_ews_connection_sync_folder_hierarchy_sync(). - */ - ews_discover_server_version (async_data->cnc, response); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - if (g_strcmp0 (name, "GetFolderResponseMessage") == 0) { - async_data->items = g_slist_append (async_data->items, e_ews_folder_new_from_error (error)); - g_clear_error (&error); - } else { - g_simple_async_result_take_error (simple, error); - return; - } - } else if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "GetFolderResponseMessage")) - ews_handle_folders_param (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -static void -ews_handle_root_folder_param_items (ESoapParameter *subparam, - EwsAsyncData *async_data) -{ - ESoapParameter *node, *subparam1; - gchar *last, *total; - gint total_items; - EEwsItem *item; - gboolean includes_last_item; - - node = e_soap_parameter_get_first_child_by_name (subparam, "RootFolder"); - total = e_soap_parameter_get_property (node, "TotalItemsInView"); - total_items = atoi (total); - g_free (total); - last = e_soap_parameter_get_property (node, "IncludesLastItemInRange"); - /* - * Set the includes_last_item to TRUE as default. - * It can avoid an infinite loop in caller, when, for some reason, - * we don't receive the last_tag property from the server. - */ - includes_last_item = g_strcmp0 (last, "false") != 0; - g_free (last); - - node = e_soap_parameter_get_first_child_by_name (node, "Items"); - for (subparam1 = e_soap_parameter_get_first_child (node); - subparam1; subparam1 = e_soap_parameter_get_next_child (subparam1)) { - item = e_ews_item_new_from_soap_parameter (subparam1); - if (!item) continue; - async_data->items = g_slist_append (async_data->items, item); - } - async_data->total_items = total_items; - async_data->includes_last_item = includes_last_item; -} - -static void -find_folder_items_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "FindItemResponseMessage")) - ews_handle_root_folder_param_items (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -/* Used for CreateItems and GetItems */ -static void -ews_handle_items_param (ESoapParameter *subparam, - EwsAsyncData *async_data, - const GError *error) -{ - ESoapParameter *node; - EEwsItem *item; - - for (node = e_soap_parameter_get_first_child_by_name (subparam, "Items"); - node; node = e_soap_parameter_get_next_child_by_name (subparam, "Items")) { - if (node->children) - item = e_ews_item_new_from_soap_parameter (node); - else - item = NULL; - if (!item && error != NULL) - item = e_ews_item_new_from_error (error); - if (!item) continue; - async_data->items = g_slist_append (async_data->items, item); - } -} - -static void -handle_get_items_response_cb (EwsAsyncData *async_data, ESoapParameter *param) -{ - ESoapParameter *subparam; - GError *error = NULL; - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (g_str_has_suffix (name, "ResponseMessage")) { - if (ews_get_response_status (subparam, &error)) - error = NULL; - - ews_handle_items_param (subparam, async_data, error); - } else { - g_warning ( - "%s: Unexpected element <%s>", - G_STRFUNC, name); - } - - /* Do not stop on errors. */ - if (error != NULL) - g_clear_error (&error); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -static void -get_items_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - handle_get_items_response_cb (async_data, param); -} - -static void -ews_handle_resolution_set_param (ESoapParameter *subparam, - EwsAsyncData *async_data) -{ - ESoapParameter *node; - gchar *prop; - gboolean includes_last_item; - GSList *mailboxes = NULL, *contact_items = NULL; - - subparam = e_soap_parameter_get_first_child_by_name (subparam, "ResolutionSet"); - prop = e_soap_parameter_get_property (subparam, "IncludesLastItemInRange"); - /* - * Set the includes_last_item to TRUE as default. - * It can avoid an infinite loop in caller, when, for some reason, - * we don't receive the last_tag property from the server. - */ - includes_last_item = g_strcmp0 (prop, "false") != 0; - g_free (prop); - - for (subparam = e_soap_parameter_get_first_child_by_name (subparam, "Resolution"); - subparam != NULL; - subparam = e_soap_parameter_get_next_child_by_name (subparam, "Resolution")) { - EwsMailbox *mb; - - node = e_soap_parameter_get_first_child_by_name (subparam, "Mailbox"); - mb = e_ews_item_mailbox_from_soap_param (node); - if (mb) { - EEwsItem *contact_item; - - mailboxes = g_slist_prepend (mailboxes, mb); - - /* 'mailboxes' and 'contact_items' match 1:1, but if the contact information - * wasn't found, then NULL is stored in the corresponding position */ - node = e_soap_parameter_get_first_child_by_name (subparam, "Contact"); - if (node) { - contact_item = e_ews_item_new_from_soap_parameter (node); - contact_items = g_slist_prepend (contact_items, contact_item); - } else { - contact_items = g_slist_prepend (contact_items, NULL); - } - } - } - - /* Reuse existing variables */ - async_data->items = g_slist_reverse (mailboxes); - async_data->includes_last_item = includes_last_item; - async_data->items_created = g_slist_reverse (contact_items); -} - -static void -resolve_names_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "ResolveNamesResponseMessage")) - ews_handle_resolution_set_param (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -static void -ews_handle_dl_expansion_param (ESoapParameter *subparam, - EwsAsyncData *async_data) -{ - gboolean includes_last_item; - GSList *mailboxes = NULL; - gchar *prop; - - subparam = e_soap_parameter_get_first_child_by_name (subparam, "DLExpansion"); - prop = e_soap_parameter_get_property (subparam, "IncludesLastItemInRange"); - /* - * Set the includes_last_item to TRUE as default. - * It can avoid an infinite loop in caller, when, for some reason, - * we don't receive the last_tag property from the server. - */ - includes_last_item = g_strcmp0 (prop, "false") != 0; - g_free (prop); - - for (subparam = e_soap_parameter_get_first_child_by_name (subparam, "Mailbox"); - subparam != NULL; - subparam = e_soap_parameter_get_next_child_by_name (subparam, "Mailbox")) { - EwsMailbox *mb; - - mb = e_ews_item_mailbox_from_soap_param (subparam); - if (mb) - mailboxes = g_slist_append (mailboxes, mb); - } - - /* Reuse existing variables */ - async_data->items = mailboxes; - async_data->includes_last_item = includes_last_item; -} - -static void -expand_dl_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "ExpandDLResponseMessage")) - ews_handle_dl_expansion_param (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -/* TODO scan all folders if we support creating multiple folders in the request */ -static void -ews_handle_create_folders_param (ESoapParameter *soapparam, - EwsAsyncData *async_data) -{ - ESoapParameter *param, *node; - EwsFolderId *fid = NULL; - GSList *fids = NULL; - const gchar *folder_element; - - switch (async_data->folder_type) { - case E_EWS_FOLDER_TYPE_MAILBOX: - folder_element = "Folder"; - break; - case E_EWS_FOLDER_TYPE_CALENDAR: - folder_element = "CalendarFolder"; - break; - case E_EWS_FOLDER_TYPE_CONTACTS: - folder_element = "ContactsFolder"; - break; - case E_EWS_FOLDER_TYPE_SEARCH: - folder_element = "SearchFolder"; - break; - case E_EWS_FOLDER_TYPE_TASKS: - folder_element = "TasksFolder"; - break; - default: - g_warn_if_reached (); - folder_element = "Folder"; - break; - } - - node = e_soap_parameter_get_first_child_by_name (soapparam, "Folders"); - node = e_soap_parameter_get_first_child_by_name (node, folder_element); - param = e_soap_parameter_get_first_child_by_name (node, "FolderId"); - - fid = g_new0 (EwsFolderId, 1); - fid->id = e_soap_parameter_get_property (param, "Id"); - fid->change_key = e_soap_parameter_get_property (param, "ChangeKey"); - fids = g_slist_append (fids, fid); - - async_data->items_created = fids; -} - -static void -create_folder_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "CreateFolderResponseMessage")) - ews_handle_create_folders_param (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -static gpointer -e_ews_soup_thread (gpointer user_data) -{ - EEwsConnection *cnc = user_data; - - g_main_context_push_thread_default (cnc->priv->soup_context); - g_main_loop_run (cnc->priv->soup_loop); - g_main_context_pop_thread_default (cnc->priv->soup_context); - - /* abort any pending operations */ - soup_session_abort (cnc->priv->soup_session); - - g_object_unref (cnc->priv->soup_session); - cnc->priv->soup_session = NULL; - - return NULL; -} - -/* - * e_ews_debug_handler: - * - * GLib debug message handler, which is passed all messages from g_debug() calls, - * and decides whether to print them. - */ -static void -e_ews_debug_handler (const gchar *log_domain, - GLogLevelFlags log_level, - const gchar *message, - gpointer user_data) -{ - if (e_ews_debug_get_log_level () >= 3) - g_log_default_handler (log_domain, log_level, message, NULL); -} - -/* - * e_ews_soup_log_print: - * - * Log printer for the libsoup logging functionality, which just marshal all soup log - * output to the standard GLib logging framework (and thus to debug_handler(), above). - */ -static void -e_ews_soup_log_printer (SoupLogger *logger, - SoupLoggerLogLevel level, - char direction, - const gchar *data, - gpointer user_data) -{ - const gchar *filtered_data = NULL; - - if (e_ews_debug_get_log_level () >= 3) { - if (direction == '>' && g_ascii_strncasecmp (data, "Host:", 5) == 0) - filtered_data = "Host: "; - else if (direction == '>' && g_ascii_strncasecmp (data, "Authorization:", 14) == 0) - filtered_data = "Authorization: "; - else if (direction == '<' && g_ascii_strncasecmp (data, "Set-Cookie:", 11) == 0) - filtered_data = "Set-Cookie: "; - else - filtered_data = data; - } - - g_debug ("%c %s", direction, filtered_data ? filtered_data : data); -} - -static void -ews_connection_set_settings (EEwsConnection *connection, - CamelEwsSettings *settings) -{ - g_return_if_fail (CAMEL_IS_EWS_SETTINGS (settings)); - g_return_if_fail (connection->priv->settings == NULL); - - connection->priv->settings = g_object_ref (settings); -} - -static void -ews_connection_set_source (EEwsConnection *connection, - ESource *source) -{ - if (source) - g_return_if_fail (E_IS_SOURCE (source)); - g_return_if_fail (connection->priv->source == NULL); - - connection->priv->source = source ? g_object_ref (source) : NULL; -} - -static void -ews_connection_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_PASSWORD: - e_ews_connection_set_password ( - E_EWS_CONNECTION (object), - g_value_get_string (value)); - return; - - case PROP_PROXY_RESOLVER: - e_ews_connection_set_proxy_resolver ( - E_EWS_CONNECTION (object), - g_value_get_object (value)); - return; - - case PROP_SETTINGS: - ews_connection_set_settings ( - E_EWS_CONNECTION (object), - g_value_get_object (value)); - return; - - case PROP_SOURCE: - ews_connection_set_source ( - E_EWS_CONNECTION (object), - g_value_get_object (value)); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -ews_connection_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_PASSWORD: - g_value_take_string ( - value, - e_ews_connection_dup_password ( - E_EWS_CONNECTION (object))); - return; - - case PROP_PROXY_RESOLVER: - g_value_take_object ( - value, - e_ews_connection_ref_proxy_resolver ( - E_EWS_CONNECTION (object))); - return; - - case PROP_SETTINGS: - g_value_take_object ( - value, - e_ews_connection_ref_settings ( - E_EWS_CONNECTION (object))); - return; - - case PROP_SOURCE: - g_value_set_object ( - value, - e_ews_connection_get_source ( - E_EWS_CONNECTION (object))); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -ews_connection_constructed (GObject *object) -{ - EEwsConnection *cnc = E_EWS_CONNECTION (object); - gint log_level; - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_ews_connection_parent_class)->constructed (object); - - cnc->priv->soup_thread = g_thread_new (NULL, e_ews_soup_thread, cnc); - - cnc->priv->soup_session = soup_session_async_new_with_options ( - SOUP_SESSION_ASYNC_CONTEXT, cnc->priv->soup_context, - NULL); - - /* Do not use G_BINDING_SYNC_CREATE because the property_lock is - * not initialized and we don't have a GProxyResolver yet anyway. */ - e_binding_bind_property ( - cnc, "proxy-resolver", - cnc->priv->soup_session, "proxy-resolver", - G_BINDING_DEFAULT); - - cnc->priv->version = E_EWS_EXCHANGE_UNKNOWN; - - log_level = e_ews_debug_get_log_level (); - - if (log_level >= 2) { - SoupLogger *logger; - logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1); - - if (log_level >= 3) { - soup_logger_set_printer (logger, e_ews_soup_log_printer, NULL, NULL); - g_log_set_handler ( - G_LOG_DOMAIN, - G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING | - G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO, - e_ews_debug_handler, cnc); - } - - soup_session_add_feature ( - cnc->priv->soup_session, - SOUP_SESSION_FEATURE (logger)); - g_object_unref (logger); - } - - soup_session_add_feature_by_type (cnc->priv->soup_session, - SOUP_TYPE_COOKIE_JAR); - - g_signal_connect ( - cnc->priv->soup_session, "authenticate", - G_CALLBACK (ews_connection_authenticate), cnc); - - e_ews_connection_utils_prepare_auth_method (cnc->priv->soup_session, - camel_ews_settings_get_auth_mechanism (cnc->priv->settings)); -} - -static void -ews_connection_dispose (GObject *object) -{ - EEwsConnectionPrivate *priv; - - priv = E_EWS_CONNECTION_GET_PRIVATE (object); - - g_mutex_lock (&connecting); - - /* remove the connection from the hash table */ - if (loaded_connections_permissions != NULL && - g_hash_table_lookup (loaded_connections_permissions, priv->hash_key) == (gpointer) object) { - g_hash_table_remove (loaded_connections_permissions, priv->hash_key); - if (g_hash_table_size (loaded_connections_permissions) == 0) { - g_hash_table_destroy (loaded_connections_permissions); - loaded_connections_permissions = NULL; - } - } - - g_mutex_unlock (&connecting); - - if (priv->soup_session) { - g_signal_handlers_disconnect_by_func ( - priv->soup_session, - ews_connection_authenticate, object); - - g_main_loop_quit (priv->soup_loop); - g_thread_join (priv->soup_thread); - priv->soup_thread = NULL; - - g_main_loop_unref (priv->soup_loop); - priv->soup_loop = NULL; - g_main_context_unref (priv->soup_context); - priv->soup_context = NULL; - } - - g_clear_object (&priv->proxy_resolver); - g_clear_object (&priv->source); - g_clear_object (&priv->settings); - - e_ews_connection_set_password (E_EWS_CONNECTION (object), NULL); - - g_slist_free (priv->jobs); - priv->jobs = NULL; - - g_slist_free (priv->active_job_queue); - priv->active_job_queue = NULL; - - g_slist_free_full (priv->subscribed_folders, g_free); - priv->subscribed_folders = NULL; - - if (priv->subscriptions != NULL) { - g_hash_table_destroy (priv->subscriptions); - priv->subscriptions = NULL; - } - - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (e_ews_connection_parent_class)->dispose (object); -} - -static void -ews_connection_finalize (GObject *object) -{ - EEwsConnectionPrivate *priv; - - priv = E_EWS_CONNECTION_GET_PRIVATE (object); - - g_free (priv->uri); - g_free (priv->password); - g_free (priv->email); - g_free (priv->hash_key); - g_free (priv->impersonate_user); - - g_clear_object (&priv->bearer_auth); - - g_mutex_clear (&priv->property_lock); - g_rec_mutex_clear (&priv->queue_lock); - g_mutex_clear (&priv->notification_lock); - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (e_ews_connection_parent_class)->finalize (object); -} - -static void -e_ews_connection_class_init (EEwsConnectionClass *class) -{ - GObjectClass *object_class; - - g_type_class_add_private (class, sizeof (EEwsConnectionPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->set_property = ews_connection_set_property; - object_class->get_property = ews_connection_get_property; - object_class->constructed = ews_connection_constructed; - object_class->dispose = ews_connection_dispose; - object_class->finalize = ews_connection_finalize; - - g_object_class_install_property ( - object_class, - PROP_PASSWORD, - g_param_spec_string ( - "password", - "Password", - "Authentication password", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property ( - object_class, - PROP_PROXY_RESOLVER, - g_param_spec_object ( - "proxy-resolver", - "Proxy Resolver", - "The proxy resolver for this backend", - G_TYPE_PROXY_RESOLVER, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property ( - object_class, - PROP_SETTINGS, - g_param_spec_object ( - "settings", - "Settings", - "Connection settings", - CAMEL_TYPE_EWS_SETTINGS, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property ( - object_class, - PROP_SOURCE, - g_param_spec_object ( - "source", - "Source", - "Corresponding ESource", - E_TYPE_SOURCE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - signals[SERVER_NOTIFICATION] = g_signal_new ( - "server-notification", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED | G_SIGNAL_ACTION, - 0, NULL, NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, 1, - G_TYPE_POINTER); - - signals[PASSWORD_WILL_EXPIRE] = g_signal_new ( - "password-will-expire", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (EEwsConnectionClass, password_will_expire), - NULL, NULL, NULL, - G_TYPE_NONE, 2, - G_TYPE_INT, - G_TYPE_STRING); -} - -static void -e_ews_connection_folders_list_free (gpointer data) -{ - g_slist_free_full ((GSList *) data, g_free); -} - -static void -e_ews_connection_init (EEwsConnection *cnc) -{ - cnc->priv = E_EWS_CONNECTION_GET_PRIVATE (cnc); - - cnc->priv->soup_context = g_main_context_new (); - cnc->priv->soup_loop = g_main_loop_new (cnc->priv->soup_context, FALSE); - cnc->priv->disconnected_flag = FALSE; - - cnc->priv->subscriptions = g_hash_table_new_full ( - g_direct_hash, g_direct_equal, - NULL, e_ews_connection_folders_list_free); - - g_mutex_init (&cnc->priv->property_lock); - g_rec_mutex_init (&cnc->priv->queue_lock); - g_mutex_init (&cnc->priv->notification_lock); -} - -static void -ews_connection_authenticate (SoupSession *sess, - SoupMessage *msg, - SoupAuth *auth, - gboolean retrying, - gpointer data) -{ - EEwsConnection *cnc = data; - - g_return_if_fail (cnc != NULL); - - e_ews_connection_utils_authenticate (cnc, sess, msg, auth, retrying); -} - -void -ews_oal_free (EwsOAL *oal) -{ - if (oal != NULL) { - g_free (oal->id); - g_free (oal->dn); - g_free (oal->name); - g_free (oal); - } -} - -void -ews_oal_details_free (EwsOALDetails *details) -{ - if (details != NULL) { - g_free (details->type); - g_free (details->sha); - g_free (details->filename); - g_free (details); - } -} - -void -ews_user_id_free (EwsUserId *id) -{ - if (id) { - g_free (id->sid); - g_free (id->primary_smtp); - g_free (id->display_name); - g_free (id->distinguished_user); - g_free (id->external_user); - g_free (id); - } -} - -void -ews_delegate_info_free (EwsDelegateInfo *info) -{ - if (!info) - return; - - ews_user_id_free (info->user_id); - g_free (info); -} - -EEwsAttachmentInfo * -e_ews_attachment_info_new (EEwsAttachmentInfoType type) -{ - EEwsAttachmentInfo *info; - info = g_new0 (EEwsAttachmentInfo, 1); - - info->type = type; - return info; -} - -void -e_ews_attachment_info_free (EEwsAttachmentInfo *info) -{ - if (!info) - return; - - switch (info->type) { - case E_EWS_ATTACHMENT_INFO_TYPE_INLINED: - g_free (info->data.inlined.filename); - g_free (info->data.inlined.mime_type); - g_free (info->data.inlined.data); - break; - case E_EWS_ATTACHMENT_INFO_TYPE_URI: - g_free (info->data.uri); - break; - default: - g_warning ("Unknown EEwsAttachmentInfoType %d", info->type); - break; - } - - g_free (info->prefer_filename); - g_free (info->id); - g_free (info); -} - -EEwsAttachmentInfoType -e_ews_attachment_info_get_type (EEwsAttachmentInfo *info) -{ - return info->type; -} - -const gchar * -e_ews_attachment_info_get_prefer_filename (EEwsAttachmentInfo *info) -{ - g_return_val_if_fail (info != NULL, NULL); - - return info->prefer_filename; -} - -void -e_ews_attachment_info_set_prefer_filename (EEwsAttachmentInfo *info, - const gchar *prefer_filename) -{ - g_return_if_fail (info != NULL); - - if (info->prefer_filename == prefer_filename) - return; - - g_free (info->prefer_filename); - info->prefer_filename = g_strdup (prefer_filename); -} - -const gchar * -e_ews_attachment_info_get_inlined_data (EEwsAttachmentInfo *info, - gsize *len) -{ - g_return_val_if_fail (info != NULL, NULL); - g_return_val_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_INLINED, NULL); - - *len = info->data.inlined.length; - return info->data.inlined.data; -} - -void -e_ews_attachment_info_set_inlined_data (EEwsAttachmentInfo *info, - const guchar *data, - gsize len) -{ - g_return_if_fail (info != NULL); - g_return_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_INLINED); - - info->data.inlined.data = g_malloc (len); - memcpy (info->data.inlined.data, data, len); - info->data.inlined.length = len; -} - -const gchar * -e_ews_attachment_info_get_mime_type (EEwsAttachmentInfo *info) -{ - g_return_val_if_fail (info != NULL, NULL); - g_return_val_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_INLINED, NULL); - - return info->data.inlined.mime_type; -} - -void -e_ews_attachment_info_set_mime_type (EEwsAttachmentInfo *info, - const gchar *mime_type) -{ - g_return_if_fail (info != NULL); - g_return_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_INLINED); - - g_free (info->data.inlined.mime_type); - info->data.inlined.mime_type = g_strdup (mime_type); -} - -const gchar * -e_ews_attachment_info_get_filename (EEwsAttachmentInfo *info) -{ - g_return_val_if_fail (info != NULL, NULL); - g_return_val_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_INLINED, NULL); - - return info->data.inlined.filename; -} - -void -e_ews_attachment_info_set_filename (EEwsAttachmentInfo *info, - const gchar *filename) -{ - g_return_if_fail (info != NULL); - g_return_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_INLINED); - - g_free (info->data.inlined.filename); - info->data.inlined.filename = g_strdup (filename); -} - -const gchar * -e_ews_attachment_info_get_uri (EEwsAttachmentInfo *info) -{ - g_return_val_if_fail (info != NULL, NULL); - g_return_val_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_URI, NULL); - - return info->data.uri; -} - -void -e_ews_attachment_info_set_uri (EEwsAttachmentInfo *info, - const gchar *uri) -{ - g_return_if_fail (info != NULL); - g_return_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_URI); - - g_free (info->data.uri); - info->data.uri = g_strdup (uri); -} - -const gchar * -e_ews_attachment_info_get_id (EEwsAttachmentInfo *info) -{ - g_return_val_if_fail (info != NULL, NULL); - - return info->id; -} - -void -e_ews_attachment_info_set_id (EEwsAttachmentInfo *info, - const gchar *id) -{ - g_return_if_fail (info != NULL); - - if (info->id != id) { - g_free (info->id); - info->id = g_strdup (id); - } -} - -/* Connection APIS */ - -/** - * e_ews_connection_find - * @uri: Exchange server uri - * @username: - * - * Find an existing connection for this user/uri, if it exists. - * - * Returns: EEwsConnection - **/ -EEwsConnection * -e_ews_connection_find (const gchar *uri, - const gchar *username) -{ - EEwsConnection *cnc; - gchar *hash_key; - - g_mutex_lock (&connecting); - - /* search the connection in our hash table */ - if (loaded_connections_permissions != NULL) { - hash_key = g_strdup_printf ( - "%s@%s", - username ? username : "", - uri); - cnc = g_hash_table_lookup ( - loaded_connections_permissions, hash_key); - g_free (hash_key); - - if (E_IS_EWS_CONNECTION (cnc) && - !e_ews_connection_get_disconnected_flag (cnc)) { - g_object_ref (cnc); - g_mutex_unlock (&connecting); - return cnc; - } - } - - g_mutex_unlock (&connecting); - - return NULL; -} - -/** - * e_ews_connection_list_existing: - * - * Returns: (transfer full) (element-type EEwsConnection): a new #GSList of all currently - * opened connections to all servers. Free the returned #GSList with - * g_slist_free_full (connections, g_object_unref); - * when no longer needed. - **/ -GSList * /* EEwsConnection * */ -e_ews_connection_list_existing (void) -{ - GSList *connections = NULL; - - g_mutex_lock (&connecting); - - /* search the connection in our hash table */ - if (loaded_connections_permissions != NULL) { - GHashTableIter iter; - gpointer value; - - g_hash_table_iter_init (&iter, loaded_connections_permissions); - while (g_hash_table_iter_next (&iter, NULL, &value)) { - if (value && !e_ews_connection_get_disconnected_flag (value)) - connections = g_slist_prepend (connections, g_object_ref (value)); - } - } - - g_mutex_unlock (&connecting); - - return connections; -} - -/** - * e_ews_connection_new_full - * @source: corresponding #ESource - * @uri: Exchange server uri - * @settings: a #CamelEwsSettings - * @allow_connection_reuse: whether can return already created connection - * - * This does not authenticate to the server. It merely stores the username and password. - * Authentication happens when a request is made to the server. - * - * Returns: EEwsConnection - **/ -EEwsConnection * -e_ews_connection_new_full (ESource *source, - const gchar *uri, - CamelEwsSettings *settings, - gboolean allow_connection_reuse) -{ - CamelNetworkSettings *network_settings; - EEwsConnection *cnc; - gchar *hash_key; - gchar *user; - - if (source) - g_return_val_if_fail (E_IS_SOURCE (source), NULL); - g_return_val_if_fail (uri != NULL, NULL); - g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), NULL); - - network_settings = CAMEL_NETWORK_SETTINGS (settings); - user = camel_network_settings_dup_user (network_settings); - hash_key = g_strdup_printf ("%s@%s", user, uri); - g_free (user); - - g_mutex_lock (&connecting); - - /* search the connection in our hash table */ - if (allow_connection_reuse && loaded_connections_permissions != NULL) { - cnc = g_hash_table_lookup ( - loaded_connections_permissions, hash_key); - - if (E_IS_EWS_CONNECTION (cnc) && - !e_ews_connection_get_disconnected_flag (cnc)) { - g_object_ref (cnc); - - g_free (hash_key); - - g_mutex_unlock (&connecting); - return cnc; - } - } - - /* not found, so create a new connection */ - cnc = g_object_new (E_TYPE_EWS_CONNECTION, - "settings", settings, - "source", source, - NULL); - - cnc->priv->uri = g_strdup (uri); - cnc->priv->hash_key = hash_key; /* takes ownership */ - - g_free (cnc->priv->impersonate_user); - if (camel_ews_settings_get_use_impersonation (settings)) { - cnc->priv->impersonate_user = camel_ews_settings_dup_impersonate_user (settings); - if (cnc->priv->impersonate_user && !*cnc->priv->impersonate_user) { - g_free (cnc->priv->impersonate_user); - cnc->priv->impersonate_user = NULL; - } - } else { - cnc->priv->impersonate_user = NULL; - } - - e_binding_bind_property ( - settings, "timeout", - cnc->priv->soup_session, "timeout", - G_BINDING_SYNC_CREATE); - - if (allow_connection_reuse) { - /* add the connection to the loaded_connections_permissions hash table */ - if (loaded_connections_permissions == NULL) - loaded_connections_permissions = g_hash_table_new_full ( - g_str_hash, g_str_equal, - g_free, NULL); - g_hash_table_insert ( - loaded_connections_permissions, - g_strdup (cnc->priv->hash_key), cnc); - } - - /* free memory */ - g_mutex_unlock (&connecting); - return cnc; - -} - -EEwsConnection * -e_ews_connection_new (ESource *source, - const gchar *uri, - CamelEwsSettings *settings) -{ - return e_ews_connection_new_full (source, uri, settings, TRUE); -} - -EEwsConnection * -e_ews_connection_new_for_backend (EBackend *backend, - ESourceRegistry *registry, - const gchar *uri, - CamelEwsSettings *settings) -{ - ESource *source; - EEwsConnection *cnc; - - g_return_val_if_fail (E_IS_BACKEND (backend), NULL); - g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); - - source = e_backend_get_source (backend); - if (!source) - return e_ews_connection_new (source, uri, settings); - - g_object_ref (source); - - while (source && !e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION) && - e_source_get_parent (source)) { - ESource *parent; - - parent = e_source_registry_ref_source (registry, e_source_get_parent (source)); - if (!parent) { - g_clear_object (&source); - break; - } - - g_object_unref (source); - source = parent; - } - - if (source) - cnc = e_ews_connection_new (source, uri, settings); - else - cnc = e_ews_connection_new (e_backend_get_source (backend), uri, settings); - - g_clear_object (&source); - - return cnc; -} - -void -e_ews_connection_update_credentials (EEwsConnection *cnc, - const ENamedParameters *credentials) -{ - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - - if (credentials) { - const gchar *password; - - /* Update password only if it's provided, otherwise keep the previously set, if any */ - password = e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD); - if (password && *password) - e_ews_connection_set_password (cnc, password); - - if (e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME)) { - CamelNetworkSettings *network_settings; - - network_settings = CAMEL_NETWORK_SETTINGS (cnc->priv->settings); - camel_network_settings_set_user (network_settings, e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME)); - } - } else { - e_ews_connection_set_password (cnc, NULL); - } -} - -ESourceAuthenticationResult -e_ews_connection_try_credentials_sync (EEwsConnection *cnc, - const ENamedParameters *credentials, - GCancellable *cancellable, - GError **error) -{ - ESourceAuthenticationResult result; - EwsFolderId *fid = NULL; - GSList *ids = NULL; - GError *local_error = NULL; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), E_SOURCE_AUTHENTICATION_ERROR); - - e_ews_connection_update_credentials (cnc, credentials); - - fid = g_new0 (EwsFolderId, 1); - fid->id = g_strdup ("inbox"); - fid->is_distinguished_id = TRUE; - ids = g_slist_append (ids, fid); - - e_ews_connection_get_folder_sync ( - cnc, EWS_PRIORITY_MEDIUM, "Default", - NULL, ids, NULL, cancellable, &local_error); - - g_slist_free_full (ids, (GDestroyNotify) e_ews_folder_id_free); - - if (local_error == NULL) { - result = E_SOURCE_AUTHENTICATION_ACCEPTED; - } else { - gboolean auth_failed; - - auth_failed = g_error_matches ( - local_error, EWS_CONNECTION_ERROR, - EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED); - - if (auth_failed) { - g_clear_error (&local_error); - - if (camel_ews_settings_get_auth_mechanism (cnc->priv->settings) != EWS_AUTH_TYPE_GSSAPI && - camel_ews_settings_get_auth_mechanism (cnc->priv->settings) != EWS_AUTH_TYPE_OAUTH2 && - (!credentials || !e_named_parameters_exists (credentials, E_SOURCE_CREDENTIAL_PASSWORD))) { - result = E_SOURCE_AUTHENTICATION_REQUIRED; - } else { - result = E_SOURCE_AUTHENTICATION_REJECTED; - } - } else { - g_propagate_error (error, local_error); - result = E_SOURCE_AUTHENTICATION_ERROR; - } - - e_ews_connection_set_password (cnc, NULL); - } - - return result; -} - -ESource * -e_ews_connection_get_source (EEwsConnection *cnc) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - return cnc->priv->source; -} - -const gchar * -e_ews_connection_get_uri (EEwsConnection *cnc) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - return cnc->priv->uri; -} - -ESoupAuthBearer * -e_ews_connection_ref_bearer_auth (EEwsConnection *cnc) -{ - ESoupAuthBearer *bearer_auth; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - g_mutex_lock (&cnc->priv->property_lock); - bearer_auth = cnc->priv->bearer_auth; - if (bearer_auth) - g_object_ref (bearer_auth); - g_mutex_unlock (&cnc->priv->property_lock); - - return bearer_auth; -} - -void -e_ews_connection_set_bearer_auth (EEwsConnection *cnc, - ESoupAuthBearer *bearer_auth) -{ - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - if (bearer_auth) - g_return_if_fail (E_IS_SOUP_AUTH_BEARER (bearer_auth)); - - g_mutex_lock (&cnc->priv->property_lock); - - if (bearer_auth != cnc->priv->bearer_auth) { - g_clear_object (&cnc->priv->bearer_auth); - cnc->priv->bearer_auth = bearer_auth; - - if (cnc->priv->bearer_auth) - g_object_ref (cnc->priv->bearer_auth); - } - - g_mutex_unlock (&cnc->priv->property_lock); -} - -const gchar * -e_ews_connection_get_password (EEwsConnection *cnc) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - return cnc->priv->password; -} - -gchar * -e_ews_connection_dup_password (EEwsConnection *cnc) -{ - const gchar *protected; - gchar *duplicate; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - g_mutex_lock (&cnc->priv->property_lock); - - protected = e_ews_connection_get_password (cnc); - duplicate = g_strdup (protected); - - g_mutex_unlock (&cnc->priv->property_lock); - - return duplicate; -} - -void -e_ews_connection_set_password (EEwsConnection *cnc, - const gchar *password) -{ - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - - g_mutex_lock (&cnc->priv->property_lock); - - /* Zero-fill the old password before freeing it. */ - if (cnc->priv->password != NULL && *cnc->priv->password != '\0') - memset (cnc->priv->password, 0, strlen (cnc->priv->password)); - - g_free (cnc->priv->password); - cnc->priv->password = g_strdup (password); - - g_mutex_unlock (&cnc->priv->property_lock); - - g_object_notify (G_OBJECT (cnc), "password"); -} - -const gchar * -e_ews_connection_get_impersonate_user (EEwsConnection *cnc) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - return cnc->priv->impersonate_user; -} - -GProxyResolver * -e_ews_connection_ref_proxy_resolver (EEwsConnection *cnc) -{ - GProxyResolver *proxy_resolver = NULL; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - g_mutex_lock (&cnc->priv->property_lock); - - if (cnc->priv->proxy_resolver != NULL) - proxy_resolver = g_object_ref (cnc->priv->proxy_resolver); - - g_mutex_unlock (&cnc->priv->property_lock); - - return proxy_resolver; -} - -void -e_ews_connection_set_proxy_resolver (EEwsConnection *cnc, - GProxyResolver *proxy_resolver) -{ - gboolean notify = FALSE; - - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - - g_mutex_lock (&cnc->priv->property_lock); - - /* Emitting a "notify" signal unnecessarily might have - * unwanted side effects like cancelling a SoupMessage. - * Only emit if we now have a different GProxyResolver. */ - - if (proxy_resolver != cnc->priv->proxy_resolver) { - g_clear_object (&cnc->priv->proxy_resolver); - cnc->priv->proxy_resolver = proxy_resolver; - - if (proxy_resolver != NULL) - g_object_ref (proxy_resolver); - - notify = TRUE; - } - - g_mutex_unlock (&cnc->priv->property_lock); - - if (notify) - g_object_notify (G_OBJECT (cnc), "proxy-resolver"); -} - -CamelEwsSettings * -e_ews_connection_ref_settings (EEwsConnection *cnc) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - return g_object_ref (cnc->priv->settings); -} - -SoupSession * -e_ews_connection_ref_soup_session (EEwsConnection *cnc) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - return g_object_ref (cnc->priv->soup_session); -} - -gboolean -e_ews_connection_get_disconnected_flag (EEwsConnection *cnc) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - - return cnc->priv->disconnected_flag; -} - -void -e_ews_connection_set_disconnected_flag (EEwsConnection *cnc, - gboolean disconnected_flag) -{ - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - - cnc->priv->disconnected_flag = disconnected_flag; -} - -static xmlDoc * -e_ews_autodiscover_ws_xml (const gchar *email_address) -{ - xmlDoc *doc; - xmlNode *node; - xmlNs *ns; - - doc = xmlNewDoc ((xmlChar *) "1.0"); - node = xmlNewDocNode (doc, NULL, (xmlChar *)"Autodiscover", NULL); - xmlDocSetRootElement (doc, node); - ns = xmlNewNs ( - node, - (xmlChar *)"http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006", NULL); - - node = xmlNewChild (node, ns, (xmlChar *)"Request", NULL); - xmlNewChild ( - node, ns, (xmlChar *)"EMailAddress", - (xmlChar *) email_address); - xmlNewChild ( - node, ns, (xmlChar *)"AcceptableResponseSchema", - (xmlChar *)"http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a"); - - return doc; -} - -struct _autodiscover_data { - EEwsConnection *cnc; - xmlOutputBuffer *buf; - SoupMessage *msgs[5]; - - GCancellable *cancellable; - gulong cancel_id; - - /* Results */ - gchar *as_url; - gchar *oab_url; -}; - -static void -autodiscover_data_free (struct _autodiscover_data *ad) -{ - xmlOutputBufferClose (ad->buf); - - if (ad->cancellable != NULL) { - g_cancellable_disconnect (ad->cancellable, ad->cancel_id); - g_object_unref (ad->cancellable); - } - - /* Unref the connection after the cancellable is disconnected, - to avoid race condition when the connection is freed inside - the g_cancellable_disconnect() function, which holds the - cancellable lock and blocks all other threads, while at - the same time the connection can wait for the finish of - its worker thread. */ - g_object_unref (ad->cnc); - - g_free (ad->as_url); - g_free (ad->oab_url); - - g_slice_free (struct _autodiscover_data, ad); -} - -static void -autodiscover_cancelled_cb (GCancellable *cancellable, - EEwsConnection *cnc) -{ - ews_connection_schedule_abort (cnc); -} - -/* Called when each soup message completes */ -static void -autodiscover_response_cb (SoupSession *session, - SoupMessage *msg, - gpointer data) - -{ - GSimpleAsyncResult *simple = data; - struct _autodiscover_data *ad; - EwsUrls *urls = NULL; - guint status = msg->status_code; - xmlDoc *doc; - xmlNode *node; - gint idx; - gboolean success = FALSE; - GError *error = NULL; - - ad = g_simple_async_result_get_op_res_gpointer (simple); - - for (idx = 0; idx < 5; idx++) { - if (ad->msgs[idx] == msg) - break; - } - if (idx == 5) { - /* We already got removed (cancelled). Do nothing */ - goto unref; - } - - ad->msgs[idx] = NULL; - - if (status != 200) { - gboolean expired = FALSE; - gchar *service_url = NULL; - - if (e_ews_connection_utils_check_x_ms_credential_headers (msg, NULL, &expired, &service_url) && expired) { - e_ews_connection_utils_expired_password_to_error (service_url, &error); - } else { - g_set_error ( - &error, SOUP_HTTP_ERROR, status, - "%d %s", status, msg->reason_phrase); - } - - g_free (service_url); - - goto failed; - } - - e_ews_debug_dump_raw_soup_response (msg); - doc = xmlReadMemory ( - msg->response_body->data, - msg->response_body->length, - "autodiscover.xml", NULL, 0); - if (!doc) { - g_set_error ( - &error, EWS_CONNECTION_ERROR, -1, - _("Failed to parse autodiscover response XML")); - goto failed; - } - node = xmlDocGetRootElement (doc); - if (strcmp ((gchar *) node->name, "Autodiscover")) { - g_set_error ( - &error, EWS_CONNECTION_ERROR, -1, - _("Failed to find element")); - goto failed; - } - for (node = node->children; node; node = node->next) { - if (node->type == XML_ELEMENT_NODE && - !strcmp ((gchar *) node->name, "Response")) - break; - } - if (!node) { - g_set_error ( - &error, EWS_CONNECTION_ERROR, -1, - _("Failed to find element")); - goto failed; - } - for (node = node->children; node; node = node->next) { - if (node->type == XML_ELEMENT_NODE && - !strcmp ((gchar *) node->name, "Account")) - break; - } - if (!node) { - g_set_error ( - &error, EWS_CONNECTION_ERROR, -1, - _("Failed to find element")); - goto failed; - } - - urls = g_new0 (EwsUrls, 1); - for (node = node->children; node; node = node->next) { - if (node->type == XML_ELEMENT_NODE && - !strcmp ((gchar *) node->name, "Protocol")) { - success = autodiscover_parse_protocol (node, urls); - /* Since the server may send back multiple nodes - * don't break unless we found the both URLs. - */ - if (success) - break; - } - } - - if (!success) { - if (urls->as_url != NULL) - xmlFree (urls->as_url); - if (urls->oab_url != NULL) - xmlFree (urls->oab_url); - g_free (urls); - g_set_error ( - &error, EWS_CONNECTION_ERROR, -1, - _("Failed to find and in autodiscover response")); - goto failed; - } - - /* We have a good response; cancel all the others */ - for (idx = 0; idx < 5; idx++) { - if (ad->msgs[idx]) { - SoupMessage *m = ad->msgs[idx]; - ad->msgs[idx] = NULL; - ews_connection_schedule_cancel_message (ad->cnc, m); - } - } - - if (urls->as_url != NULL) { - ad->as_url = g_strdup ((gchar *) urls->as_url); - xmlFree (urls->as_url); - } - - if (urls->oab_url != NULL) { - ad->oab_url = g_strdup ((gchar *) urls->oab_url); - xmlFree (urls->oab_url); - } - - g_free (urls); - - goto exit; - - failed: - for (idx = 0; idx < 5; idx++) { - if (ad->msgs[idx]) { - /* There's another request outstanding. - * Hope that it has better luck. */ - g_clear_error (&error); - goto unref; - } - } - - /* FIXME: We're actually returning the *last* error here, - * and in some cases (stupid firewalls causing timeouts) - * that's going to be the least interesting one. We probably - * want the *first* error */ - g_simple_async_result_take_error (simple, error); - - exit: - g_simple_async_result_complete_in_idle (simple); - - unref: - /* This function is processed within e_ews_soup_thread() and the 'simple' - * holds reference to EEwsConnection. For cases when this is the last - * reference to 'simple' the unref would cause crash, because of g_thread_join() - * in connection's dispose, trying to wait on the end of itself, thus it's - * safer to unref the 'simple' in a dedicated thread. - */ - e_ews_connection_utils_unref_in_thread (simple); -} - -static void post_restarted (SoupMessage *msg, gpointer data) -{ - xmlOutputBuffer *buf = data; - - /* Not all restarts are due to a redirect; some are for auth */ - if (msg->status_code == 401) - return; - - /* In violation of RFC2616, libsoup will change a POST request to - * a GET on receiving a 302 redirect. */ - printf ("Working around libsoup bug with redirect\n"); - g_object_set (msg, SOUP_MESSAGE_METHOD, "POST", NULL); - - soup_message_set_request ( - msg, "text/xml; charset=utf-8", SOUP_MEMORY_COPY, - (gchar *) - #ifdef LIBXML2_NEW_BUFFER - xmlOutputBufferGetContent (buf), xmlOutputBufferGetSize (buf) - #else - buf->buffer->content, buf->buffer->use - #endif - ); -} - -static SoupMessage * -e_ews_get_msg_for_url (CamelEwsSettings *settings, - const gchar *url, - xmlOutputBuffer *buf, - GError **error) -{ - SoupMessage *msg; - - if (url == NULL) { - g_set_error_literal ( - error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("URL cannot be NULL")); - return NULL; - } - - msg = soup_message_new (buf != NULL ? "POST" : "GET", url); - if (!msg) { - g_set_error ( - error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("URL “%s” is not valid"), url); - return NULL; - } - - e_ews_message_attach_chunk_allocator (msg); - - e_ews_message_set_user_agent_header (msg, settings); - - if (buf != NULL) { - soup_message_set_request ( - msg, "text/xml; charset=utf-8", SOUP_MEMORY_COPY, - (gchar *) - #ifdef LIBXML2_NEW_BUFFER - xmlOutputBufferGetContent (buf), xmlOutputBufferGetSize (buf) - #else - buf->buffer->content, buf->buffer->use - #endif - ); - g_signal_connect ( - msg, "restarted", - G_CALLBACK (post_restarted), buf); - } - - e_ews_debug_dump_raw_soup_request (msg); - - return msg; -} - -gboolean -e_ews_autodiscover_ws_url_sync (ESource *source, - CamelEwsSettings *settings, - const gchar *email_address, - const gchar *password, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), FALSE); - g_return_val_if_fail (email_address != NULL, FALSE); - g_return_val_if_fail (password != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_autodiscover_ws_url (source, settings, email_address, password, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_autodiscover_ws_url_finish (settings, result, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_autodiscover_ws_url (ESource *source, - CamelEwsSettings *settings, - const gchar *email_address, - const gchar *password, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - struct _autodiscover_data *ad; - xmlOutputBuffer *buf; - gchar *url1, *url2, *url3, *url4, *url5; - gchar *domain; - xmlDoc *doc; - EEwsConnection *cnc; - SoupURI *soup_uri = NULL; - gboolean use_secure = TRUE; - const gchar *host_url; - GError *error = NULL; - - g_return_if_fail (CAMEL_IS_EWS_SETTINGS (settings)); - g_return_if_fail (email_address != NULL); - g_return_if_fail (password != NULL); - - simple = g_simple_async_result_new ( - G_OBJECT (settings), callback, - user_data, e_ews_autodiscover_ws_url); - - domain = strchr (email_address, '@'); - if (domain == NULL || *domain == '\0') { - g_simple_async_result_set_error ( - simple, EWS_CONNECTION_ERROR, -1, - "%s", _("Email address is missing a domain part")); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - return; - } - domain++; - - doc = e_ews_autodiscover_ws_xml (email_address); - buf = xmlAllocOutputBuffer (NULL); - xmlNodeDumpOutput (buf, doc, xmlDocGetRootElement (doc), 0, 1, NULL); - xmlOutputBufferFlush (buf); - - url1 = NULL; - url2 = NULL; - url3 = NULL; - url4 = NULL; - url5 = NULL; - - host_url = camel_ews_settings_get_hosturl (settings); - if (host_url != NULL) - soup_uri = soup_uri_new (host_url); - - if (soup_uri != NULL) { - const gchar *host = soup_uri_get_host (soup_uri); - const gchar *scheme = soup_uri_get_scheme (soup_uri); - - use_secure = g_strcmp0 (scheme, "https") == 0; - - url1 = g_strdup_printf ("http%s://%s/autodiscover/autodiscover.xml", use_secure ? "s" : "", host); - url2 = g_strdup_printf ("http%s://autodiscover.%s/autodiscover/autodiscover.xml", use_secure ? "s" : "", host); - - /* outlook.office365.com has its autodiscovery at outlook.com */ - if (host && g_ascii_strcasecmp (host, "outlook.office365.com") == 0 && - domain && g_ascii_strcasecmp (host, "outlook.com") != 0) { - url5 = g_strdup_printf ("https://outlook.com/autodiscover/autodiscover.xml"); - } - - soup_uri_free (soup_uri); - } - - url3 = g_strdup_printf ("http%s://%s/autodiscover/autodiscover.xml", use_secure ? "s" : "", domain); - url4 = g_strdup_printf ("http%s://autodiscover.%s/autodiscover/autodiscover.xml", use_secure ? "s" : "", domain); - - cnc = e_ews_connection_new (source, url3, settings); - e_ews_connection_set_password (cnc, password); - - /* - * http://msdn.microsoft.com/en-us/library/ee332364.aspx says we are - * supposed to try $domain and then autodiscover.$domain. But some - * people have broken firewalls on the former which drop packets - * instead of rejecting connections, and make the request take ages - * to time out. So run both queries in parallel and let the fastest - * (successful) one win. - */ - ad = g_slice_new0 (struct _autodiscover_data); - ad->cnc = cnc; /* takes ownership */ - ad->buf = buf; /* takes ownership */ - - if (G_IS_CANCELLABLE (cancellable)) { - ad->cancellable = g_object_ref (cancellable); - ad->cancel_id = g_cancellable_connect ( - ad->cancellable, - G_CALLBACK (autodiscover_cancelled_cb), - g_object_ref (cnc), - g_object_unref); - } - - g_simple_async_result_set_op_res_gpointer ( - simple, ad, (GDestroyNotify) autodiscover_data_free); - - /* Passing a NULL URL string returns NULL. */ - ad->msgs[0] = e_ews_get_msg_for_url (settings, url1, buf, &error); - ad->msgs[1] = e_ews_get_msg_for_url (settings, url2, buf, NULL); - ad->msgs[2] = e_ews_get_msg_for_url (settings, url3, buf, NULL); - ad->msgs[3] = e_ews_get_msg_for_url (settings, url4, buf, NULL); - ad->msgs[4] = e_ews_get_msg_for_url (settings, url5, buf, NULL); - - /* These have to be submitted only after they're both set in ad->msgs[] - * or there will be races with fast completion */ - if (ad->msgs[0] != NULL) - ews_connection_schedule_queue_message (cnc, ad->msgs[0], autodiscover_response_cb, g_object_ref (simple)); - if (ad->msgs[1] != NULL) - ews_connection_schedule_queue_message (cnc, ad->msgs[1], autodiscover_response_cb, g_object_ref (simple)); - if (ad->msgs[2] != NULL) - ews_connection_schedule_queue_message (cnc, ad->msgs[2], autodiscover_response_cb, g_object_ref (simple)); - if (ad->msgs[3] != NULL) - ews_connection_schedule_queue_message (cnc, ad->msgs[3], autodiscover_response_cb, g_object_ref (simple)); - if (ad->msgs[4] != NULL) - ews_connection_schedule_queue_message (cnc, ad->msgs[4], autodiscover_response_cb, g_object_ref (simple)); - - xmlFreeDoc (doc); - g_free (url1); - g_free (url2); - g_free (url3); - g_free (url4); - - if (error && !ad->msgs[0] && !ad->msgs[1] && !ad->msgs[2] && !ad->msgs[3] && !ad->msgs[4]) { - g_simple_async_result_take_error (simple, error); - g_simple_async_result_complete_in_idle (simple); - } else { - g_clear_error (&error); - - /* each request holds a reference to 'simple', - * thus remove one, to have it actually freed */ - g_object_unref (simple); - } -} - -static gboolean -has_suffix_icmp (const gchar *text, - const gchar *suffix) -{ - gint ii, tlen, slen; - - g_return_val_if_fail (text != NULL, FALSE); - g_return_val_if_fail (suffix != NULL, FALSE); - - tlen = strlen (text); - slen = strlen (suffix); - - if (!*text || !*suffix || tlen < slen) - return FALSE; - - for (ii = 0; ii < slen; ii++) { - if (g_ascii_tolower (text[tlen - ii - 1]) != - g_ascii_tolower (suffix[slen - ii - 1])) - break; - } - - return ii == slen; -} - -gboolean -e_ews_autodiscover_ws_url_finish (CamelEwsSettings *settings, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - struct _autodiscover_data *ad; - - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (settings), - e_ews_autodiscover_ws_url), FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - ad = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - g_warn_if_fail (ad->as_url != NULL); - g_warn_if_fail (ad->oab_url != NULL); - - camel_ews_settings_set_hosturl (settings, ad->as_url); - - if (!has_suffix_icmp (ad->oab_url, "oab.xml")) { - gchar *tmp; - - if (g_str_has_suffix (ad->oab_url, "/")) - tmp = g_strconcat (ad->oab_url, "oab.xml", NULL); - else - tmp = g_strconcat (ad->oab_url, "/", "oab.xml", NULL); - - camel_ews_settings_set_oaburl (settings, tmp); - g_free (tmp); - } else { - camel_ews_settings_set_oaburl (settings, ad->oab_url); - } - - return TRUE; -} - -struct _oal_req_data { - EEwsConnection *cnc; - SoupMessage *soup_message; - gchar *oal_id; - gchar *oal_element; - - GSList *oals; - GSList *elements; - gchar *etag; - - GCancellable *cancellable; - gulong cancel_id; - - /* for dowloading oal file */ - gchar *cache_filename; - GError *error; - EwsProgressFn progress_fn; - gpointer progress_data; - gsize response_size; - gsize received_size; -}; - -static void -oal_req_data_free (struct _oal_req_data *data) -{ - /* The SoupMessage is owned by the SoupSession. */ - g_object_unref (data->cnc); - - g_free (data->oal_id); - g_free (data->oal_element); - g_free (data->etag); - - g_slist_free_full (data->oals, (GDestroyNotify) ews_oal_free); - g_slist_free_full (data->elements, (GDestroyNotify) ews_oal_details_free); - - if (data->cancellable != NULL) { - g_cancellable_disconnect (data->cancellable, data->cancel_id); - g_object_unref (data->cancellable); - } - - g_free (data->cache_filename); - - g_slice_free (struct _oal_req_data, data); -} - -static gchar * -get_property (xmlNodePtr node_ptr, - const gchar *name) -{ - xmlChar *xml_s; - gchar *s; - - xml_s = xmlGetProp (node_ptr, (const xmlChar *) name); - s = g_strdup ((gchar *) xml_s); - xmlFree (xml_s); - - return s; -} - -static guint32 -get_property_as_uint32 (xmlNodePtr node_ptr, - const gchar *name) -{ - gchar *s; - guint32 val = -1; - - s = get_property (node_ptr, name); - if (s) - sscanf (s,"%"G_GUINT32_FORMAT, &val); - g_free (s); - - return val; -} - -static gchar * -get_content (xmlNodePtr node_ptr) -{ - xmlChar *xml_s; - gchar *s; - - xml_s = xmlNodeGetContent (node_ptr); - s = g_strdup ((gchar *) xml_s); - xmlFree (xml_s); - - return s; -} - -static GSList * -parse_oal_full_details (xmlNode *node, - const gchar *element) -{ - GSList *elements = NULL; - - for (node = node->children; node; node = node->next) { - EwsOALDetails *det; - if (node->type != XML_ELEMENT_NODE) - continue; - if (element && strcmp ((gchar *) node->name, element)) - continue; - if (!element && strcmp ((gchar *) node->name, "Full") && - strcmp((gchar *) node->name, "Diff")) - continue; - - det = g_new0 (EwsOALDetails, 1); - det->type = g_strdup((gchar *) node->name); - det->seq = get_property_as_uint32 (node, "seq"); - det->ver = get_property_as_uint32 (node, "ver"); - det->size = get_property_as_uint32 (node, "size"); - det->uncompressed_size = get_property_as_uint32 (node, "uncompressedsize"); - det->sha = get_property (node, "uncompressedsize"); - det->filename = g_strstrip (get_content (node)); - - elements = g_slist_prepend (elements, det); - if (element && !strcmp (element, "Full")) - break; - } - - return elements; -} - -/* this is run in cnc->priv->soup_thread */ -static void -oal_response_cb (SoupSession *soup_session, - SoupMessage *soup_message, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - struct _oal_req_data *data; - const gchar *etag; - xmlDoc *doc; - xmlNode *node; - - simple = G_SIMPLE_ASYNC_RESULT (user_data); - data = g_simple_async_result_get_op_res_gpointer (simple); - - if (ews_connection_credentials_failed (data->cnc, soup_message, simple)) { - goto exit; - } else if (soup_message->status_code != 200) { - if (soup_message->status_code == SOUP_STATUS_UNAUTHORIZED && - soup_message->response_headers) { - const gchar *diagnostics; - - diagnostics = soup_message_headers_get_list (soup_message->response_headers, "X-MS-DIAGNOSTICS"); - if (diagnostics && strstr (diagnostics, "invalid_grant")) { - g_simple_async_result_set_error ( - simple, - EWS_CONNECTION_ERROR, - EWS_CONNECTION_ERROR_ACCESSDENIED, - "%s", diagnostics); - goto exit; - } else if (diagnostics && *diagnostics) { - g_simple_async_result_set_error ( - simple, - EWS_CONNECTION_ERROR, - EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED, - "%s", diagnostics); - goto exit; - } - } - g_simple_async_result_set_error ( - simple, SOUP_HTTP_ERROR, - soup_message->status_code, - "%d %s", - soup_message->status_code, - soup_message->reason_phrase); - goto exit; - } - - etag = soup_message_headers_get_one(soup_message->response_headers, - "ETag"); - if (etag) - data->etag = g_strdup(etag); - - e_ews_debug_dump_raw_soup_response (soup_message); - - doc = xmlReadMemory ( - soup_message->response_body->data, - soup_message->response_body->length, - "oab.xml", NULL, 0); - if (doc == NULL) { - g_simple_async_result_set_error ( - simple, EWS_CONNECTION_ERROR, -1, - "%s", _("Failed to parse oab XML")); - goto exit; - } - - node = xmlDocGetRootElement (doc); - if (strcmp ((gchar *) node->name, "OAB") != 0) { - g_simple_async_result_set_error ( - simple, EWS_CONNECTION_ERROR, -1, - "%s", _("Failed to find element\n")); - goto exit_doc; - } - - for (node = node->children; node; node = node->next) { - if (node->type == XML_ELEMENT_NODE && strcmp ((gchar *) node->name, "OAL") == 0) { - if (data->oal_id == NULL) { - EwsOAL *oal = g_new0 (EwsOAL, 1); - - oal->id = get_property (node, "id"); - oal->dn = get_property (node, "dn"); - oal->name = get_property (node, "name"); - - data->oals = g_slist_prepend (data->oals, oal); - } else { - gchar *id = get_property (node, "id"); - - if (strcmp (id, data->oal_id) == 0) { - /* parse details of full_details file */ - data->elements = parse_oal_full_details (node, data->oal_element); - - g_free (id); - break; - } - - g_free (id); - } - } - } - - data->oals = g_slist_reverse (data->oals); - - exit_doc: - xmlFreeDoc (doc); - exit: - g_simple_async_result_complete_in_idle (simple); - /* This is run in cnc->priv->soup_thread, and the cnc is held by simple, thus - * for cases when the complete_in_idle is finished before the unref call, when - * the cnc will be left with the last reference and thus cannot join the soup_thread - * while still in it, the unref is done in a dedicated thread. */ - e_ews_connection_utils_unref_in_thread (simple); -} - -static void -ews_cancel_msg (GCancellable *cancellable, - struct _oal_req_data *data) -{ - ews_connection_schedule_cancel_message (data->cnc, data->soup_message); -} - -gboolean -e_ews_connection_get_oal_list_sync (EEwsConnection *cnc, - GSList **oals, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_oal_list ( - cnc, cancellable, e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_oal_list_finish ( - cnc, result, oals, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_get_oal_list (EEwsConnection *cnc, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - SoupMessage *soup_message; - struct _oal_req_data *data; - GError *error = NULL; - - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - - soup_message = e_ews_get_msg_for_url (cnc->priv->settings, cnc->priv->uri, NULL, &error); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_oal_list); - - if (!soup_message) { - g_simple_async_result_take_error (simple, error); - g_simple_async_result_complete_in_idle (simple); - return; - } - - data = g_slice_new0 (struct _oal_req_data); - data->cnc = g_object_ref (cnc); - data->soup_message = soup_message; /* the session owns this */ - - if (G_IS_CANCELLABLE (cancellable)) { - data->cancellable = g_object_ref (cancellable); - data->cancel_id = g_cancellable_connect ( - data->cancellable, - G_CALLBACK (ews_cancel_msg), - data, (GDestroyNotify) NULL); - } - - g_simple_async_result_set_op_res_gpointer ( - simple, data, (GDestroyNotify) oal_req_data_free); - - ews_connection_schedule_queue_message (cnc, soup_message, oal_response_cb, simple); -} - -gboolean -e_ews_connection_get_oal_list_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **oals, - GError **error) -{ - GSimpleAsyncResult *simple; - struct _oal_req_data *data; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_oal_list), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (oals != NULL) { - *oals = data->oals; - data->oals = NULL; - } - - return TRUE; -} - -/** - * e_ews_connection_get_oal_detail - * @cnc: - * @oal_id: - * @oal_element: - * @elements: "Full" "Diff" "Template" are the possible values. - * @cancellable: - * @error: - * - * - * Returns: - **/ -gboolean -e_ews_connection_get_oal_detail_sync (EEwsConnection *cnc, - const gchar *oal_id, - const gchar *oal_element, - const gchar *old_etag, - GSList **elements, - gchar **etag, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_oal_detail ( - cnc, oal_id, oal_element, old_etag, - cancellable, e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_oal_detail_finish ( - cnc, result, elements, etag, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_get_oal_detail (EEwsConnection *cnc, - const gchar *oal_id, - const gchar *oal_element, - const gchar *etag, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - SoupMessage *soup_message; - struct _oal_req_data *data; - gchar *sep; - GError *error = NULL; - - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - - soup_message = e_ews_get_msg_for_url (cnc->priv->settings, cnc->priv->uri, NULL, &error); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_oal_detail); - - if (!soup_message) { - g_simple_async_result_take_error (simple, error); - g_simple_async_result_complete_in_idle (simple); - return; - } - - if (etag && *etag) - soup_message_headers_append (soup_message->request_headers, - "If-None-Match", etag); - - data = g_slice_new0 (struct _oal_req_data); - data->cnc = g_object_ref (cnc); - data->soup_message = soup_message; /* the session owns this */ - data->oal_id = g_strdup (oal_id); - data->oal_element = g_strdup (oal_element); - - /* oal_id can be of form "GUID:name", but here is compared only GUID */ - sep = strchr (data->oal_id, ':'); - if (sep) - *sep = '\0'; - - if (G_IS_CANCELLABLE (cancellable)) { - data->cancellable = g_object_ref (cancellable); - data->cancel_id = g_cancellable_connect ( - data->cancellable, - G_CALLBACK (ews_cancel_msg), - data, (GDestroyNotify) NULL); - } - - g_simple_async_result_set_op_res_gpointer ( - simple, data, (GDestroyNotify) oal_req_data_free); - - ews_connection_schedule_queue_message (cnc, soup_message, oal_response_cb, simple); -} - -gboolean -e_ews_connection_get_oal_detail_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **elements, - gchar **etag, - GError **error) -{ - GSimpleAsyncResult *simple; - struct _oal_req_data *data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_oal_detail), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (elements != NULL) { - *elements = data->elements; - data->elements = NULL; - } - if (etag != NULL) { - *etag = data->etag; - data->etag = NULL; - } - - return TRUE; - -} - -static void -oal_download_response_cb (SoupSession *soup_session, - SoupMessage *soup_message, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - struct _oal_req_data *data; - - simple = G_SIMPLE_ASYNC_RESULT (user_data); - data = g_simple_async_result_get_op_res_gpointer (simple); - - if (ews_connection_credentials_failed (data->cnc, soup_message, simple)) { - g_unlink (data->cache_filename); - } else if (soup_message->status_code != 200) { - g_simple_async_result_set_error ( - simple, SOUP_HTTP_ERROR, - soup_message->status_code, - "%d %s", - soup_message->status_code, - soup_message->reason_phrase); - g_unlink (data->cache_filename); - - } else if (data->error != NULL) { - g_simple_async_result_take_error (simple, data->error); - data->error = NULL; - g_unlink (data->cache_filename); - } - - e_ews_debug_dump_raw_soup_response (soup_message); - - g_simple_async_result_complete_in_idle (simple); - e_ews_connection_utils_unref_in_thread (simple); -} - -static void -ews_soup_got_headers (SoupMessage *msg, - gpointer user_data) -{ - struct _oal_req_data *data = (struct _oal_req_data *) user_data; - const gchar *size; - - size = soup_message_headers_get_one ( - msg->response_headers, - "Content-Length"); - - if (size) - data->response_size = strtol (size, NULL, 10); -} - -static void -ews_soup_restarted (SoupMessage *msg, - gpointer user_data) -{ - struct _oal_req_data *data = (struct _oal_req_data *) user_data; - - data->response_size = 0; - data->received_size = 0; -} - -static void -ews_soup_got_chunk (SoupMessage *msg, - SoupBuffer *chunk, - gpointer user_data) -{ - struct _oal_req_data *data = (struct _oal_req_data *) user_data; - gint fd; - - if (msg->status_code != 200) - return; - - data->received_size += chunk->length; - - if (data->response_size && data->progress_fn) { - gint pc = data->received_size * 100 / data->response_size; - data->progress_fn (data->progress_data, pc); - } - - fd = g_open (data->cache_filename, O_RDONLY | O_WRONLY | O_APPEND | O_CREAT, 0600); - if (fd != -1) { - if (write (fd, (const gchar *) chunk->data, chunk->length) != chunk->length) { - g_set_error ( - &data->error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_UNKNOWN, - "Failed to write streaming data to file '%s': %s", data->cache_filename, g_strerror (errno)); - } - close (fd); - } else { - g_set_error ( - &data->error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_UNKNOWN, - "Failed to open the cache file '%s': %s", data->cache_filename, g_strerror (errno)); - } -} - -gboolean -e_ews_connection_download_oal_file_sync (EEwsConnection *cnc, - const gchar *cache_filename, - EwsProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_download_oal_file ( - cnc, cache_filename, - progress_fn, progress_data, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_download_oal_file_finish ( - cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_download_oal_file (EEwsConnection *cnc, - const gchar *cache_filename, - EwsProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - SoupMessage *soup_message; - struct _oal_req_data *data; - GError *error = NULL; - - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - - soup_message = e_ews_get_msg_for_url (cnc->priv->settings, cnc->priv->uri, NULL, &error); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_download_oal_file); - - if (!soup_message) { - g_simple_async_result_take_error (simple, error); - g_simple_async_result_complete_in_idle (simple); - return; - } - - data = g_slice_new0 (struct _oal_req_data); - data->cnc = g_object_ref (cnc); - data->soup_message = soup_message; /* the session owns this */ - data->cache_filename = g_strdup (cache_filename); - data->progress_fn = progress_fn; - data->progress_data = progress_data; - - if (G_IS_CANCELLABLE (cancellable)) { - data->cancellable = g_object_ref (cancellable); - data->cancel_id = g_cancellable_connect ( - data->cancellable, - G_CALLBACK (ews_cancel_msg), - data, (GDestroyNotify) NULL); - } - - g_simple_async_result_set_op_res_gpointer ( - simple, data, (GDestroyNotify) oal_req_data_free); - - /* - * Don't use streaming-based messages when we are loggin the traffic - * to generate trace files for tests - */ - if (e_ews_debug_get_log_level () <= 2) - soup_message_body_set_accumulate (soup_message->response_body, FALSE); - - g_signal_connect ( - soup_message, "got-headers", - G_CALLBACK (ews_soup_got_headers), data); - g_signal_connect ( - soup_message, "got-chunk", - G_CALLBACK (ews_soup_got_chunk), data); - g_signal_connect ( - soup_message, "restarted", - G_CALLBACK (ews_soup_restarted), data); - - ews_connection_schedule_queue_message (cnc, soup_message, oal_download_response_cb, simple); -} - -gboolean -e_ews_connection_download_oal_file_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), - e_ews_connection_download_oal_file), FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - /* Assume success unless a GError is set. */ - return !g_simple_async_result_propagate_error (simple, error); -} - -const gchar * -e_ews_connection_get_mailbox (EEwsConnection *cnc) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - if (!cnc->priv->email || !*cnc->priv->email) - return camel_ews_settings_get_email (cnc->priv->settings); - - return cnc->priv->email; -} - -void -e_ews_connection_set_mailbox (EEwsConnection *cnc, - const gchar *email) -{ - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - g_return_if_fail (email != NULL); - - g_free (cnc->priv->email); - cnc->priv->email = g_strdup (email); -} - -static void -ews_append_additional_props_to_msg (ESoapMessage *msg, - const EEwsAdditionalProps *add_props) -{ - GSList *l; - - if (!add_props) - return; - - e_soap_message_start_element (msg, "AdditionalProperties", NULL, NULL); - - if (add_props->field_uri) { - gchar **prop = g_strsplit (add_props->field_uri, " ", 0); - gint i = 0; - - while (prop[i]) { - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", prop[i]); - i++; - } - - g_strfreev (prop); - } - - if (add_props->extended_furis) { - for (l = add_props->extended_furis; l != NULL; l = g_slist_next (l)) { - EEwsExtendedFieldURI *ex_furi = l->data; - - e_soap_message_start_element (msg, "ExtendedFieldURI", NULL, NULL); - - if (ex_furi->distinguished_prop_set_id) - e_soap_message_add_attribute (msg, "DistinguishedPropertySetId", ex_furi->distinguished_prop_set_id, NULL, NULL); - - if (ex_furi->prop_tag) - e_soap_message_add_attribute (msg, "PropertyTag", ex_furi->prop_tag, NULL, NULL); - - if (ex_furi->prop_set_id) - e_soap_message_add_attribute (msg, "PropertySetId", ex_furi->prop_set_id, NULL, NULL); - - if (ex_furi->prop_name) - e_soap_message_add_attribute (msg, "PropertyName", ex_furi->prop_name, NULL, NULL); - - if (ex_furi->prop_id) - e_soap_message_add_attribute (msg, "PropertyId", ex_furi->prop_id, NULL, NULL); - - if (ex_furi->prop_type) - e_soap_message_add_attribute (msg, "PropertyType", ex_furi->prop_type, NULL, NULL); - - e_soap_message_end_element (msg); - } - } - - if (add_props->indexed_furis) { - for (l = add_props->indexed_furis; l != NULL; l = g_slist_next (l)) { - EEwsIndexedFieldURI *in_furi = l->data; - - e_soap_message_start_element (msg, "IndexedFieldURI", NULL, NULL); - - e_soap_message_add_attribute (msg, "FieldURI", in_furi->field_uri, NULL, NULL); - e_soap_message_add_attribute (msg, "FieldIndex", in_furi->field_index, NULL, NULL); - - e_soap_message_end_element (msg); - } - } - - e_soap_message_end_element (msg); -} - -static void -ews_write_sort_order_to_msg (ESoapMessage *msg, - EwsSortOrder *sort_order) -{ - if (!sort_order) - return; - - e_soap_message_start_element (msg, "SortOrder", NULL, NULL); - e_soap_message_start_element (msg, "FieldOrder", NULL, NULL); - e_soap_message_add_attribute (msg, "Order", sort_order->order, NULL, NULL); - - if (sort_order->uri_type == NORMAL_FIELD_URI) - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", (gchar *) sort_order->field_uri); - else if (sort_order->uri_type == INDEXED_FIELD_URI) { - EEwsIndexedFieldURI *in_furi = sort_order->field_uri; - - e_soap_message_start_element (msg, "IndexedFieldURI", NULL, NULL); - e_soap_message_add_attribute (msg, "FieldURI", in_furi->field_uri, NULL, NULL); - e_soap_message_add_attribute (msg, "FieldIndex", in_furi->field_index, NULL, NULL); - e_soap_message_end_element (msg); - } else if (sort_order->uri_type == EXTENDED_FIELD_URI) { - EEwsExtendedFieldURI *ex_furi = sort_order->field_uri; - - e_soap_message_start_element (msg, "ExtendedFieldURI", NULL, NULL); - - if (ex_furi->distinguished_prop_set_id) - e_soap_message_add_attribute (msg, "DistinguishedPropertySetId", ex_furi->distinguished_prop_set_id, NULL, NULL); - if (ex_furi->prop_set_id) - e_soap_message_add_attribute (msg, "PropertySetId", ex_furi->prop_set_id, NULL, NULL); - if (ex_furi->prop_name) - e_soap_message_add_attribute (msg, "PropertyName", ex_furi->prop_name, NULL, NULL); - if (ex_furi->prop_id) - e_soap_message_add_attribute (msg, "PropertyId", ex_furi->prop_id, NULL, NULL); - if (ex_furi->prop_type) - e_soap_message_add_attribute (msg, "PropertyType", ex_furi->prop_type, NULL, NULL); - - e_soap_message_end_element (msg); - } - - e_soap_message_end_element (msg); - e_soap_message_end_element (msg); -} - -/** - * e_ews_connection_sync_folder_items: - * @cnc: The EWS Connection - * @pri: The priority associated with the request - * @last_sync_state: To sync with the previous requests - * @folder_id: The folder to which the items belong - * @default_props: Can take one of the values: IdOnly,Default or AllProperties - * @additional_props: Specify any additional properties to be fetched - * @max_entries: Maximum number of items to be returned - * @cancellable: a GCancellable to monitor cancelled operations - * @callback: Responses are parsed and returned to this callback - * @user_data: user data passed to callback - **/ -void -e_ews_connection_sync_folder_items (EEwsConnection *cnc, - gint pri, - const gchar *last_sync_state, - const gchar *fid, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - guint max_entries, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "SyncFolderItems", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - e_soap_message_start_element (msg, "ItemShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, default_props); - - ews_append_additional_props_to_msg (msg, add_props); - - e_soap_message_end_element (msg); - - e_soap_message_start_element (msg, "SyncFolderId", "messages", NULL); - e_ews_message_write_string_parameter_with_attribute (msg, "FolderId", NULL, NULL, "Id", fid); - e_soap_message_end_element (msg); - - if (last_sync_state) - e_ews_message_write_string_parameter (msg, "SyncState", "messages", last_sync_state); - - /* Max changes requested */ - e_ews_message_write_int_parameter (msg, "MaxChangesReturned", "messages", max_entries); - - /* Complete the footer and print the request */ - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_sync_folder_items); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, sync_folder_items_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_sync_folder_items_finish (EEwsConnection *cnc, - GAsyncResult *result, - gchar **new_sync_state, - gboolean *includes_last_item, - GSList **items_created, - GSList **items_updated, - GSList **items_deleted, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_sync_folder_items), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *new_sync_state = async_data->sync_state; - *includes_last_item = async_data->includes_last_item; - *items_created = async_data->items_created; - *items_updated = async_data->items_updated; - *items_deleted = async_data->items_deleted; - - return TRUE; -} - -gboolean -e_ews_connection_sync_folder_items_sync (EEwsConnection *cnc, - gint pri, - const gchar *old_sync_state, - const gchar *fid, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - guint max_entries, - gchar **new_sync_state, - gboolean *includes_last_item, - GSList **items_created, - GSList **items_updated, - GSList **items_deleted, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_sync_folder_items ( - cnc, pri, old_sync_state, fid, default_props, - add_props, max_entries, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_sync_folder_items_finish ( - cnc, result, new_sync_state, includes_last_item, - items_created, items_updated, items_deleted, error); - - e_async_closure_free (closure); - - return success; -} - -static void -ews_append_folder_id_to_msg (ESoapMessage *msg, - const gchar *email, - const EwsFolderId *fid) -{ - g_return_if_fail (msg != NULL); - g_return_if_fail (fid != NULL); - - if (fid->is_distinguished_id) - e_soap_message_start_element (msg, "DistinguishedFolderId", NULL, NULL); - else - e_soap_message_start_element (msg, "FolderId", NULL, NULL); - - e_soap_message_add_attribute (msg, "Id", fid->id, NULL, NULL); - if (fid->change_key) - e_soap_message_add_attribute (msg, "ChangeKey", fid->change_key, NULL, NULL); - - if (fid->is_distinguished_id && email) { - e_soap_message_start_element (msg, "Mailbox", NULL, NULL); - e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, email); - e_soap_message_end_element (msg); - } - - e_soap_message_end_element (msg); -} - -static void -ews_append_folder_ids_to_msg (ESoapMessage *msg, - const gchar *email, - GSList *folder_ids) -{ - GSList *l; - - for (l = folder_ids; l != NULL; l = g_slist_next (l)) { - const EwsFolderId *fid = l->data; - - ews_append_folder_id_to_msg (msg, email, fid); - } -} - -static void -ews_connection_write_only_ids_restriction (ESoapMessage *msg, - GPtrArray *only_ids) -{ - guint ii; - - g_return_if_fail (E_IS_SOAP_MESSAGE (msg)); - g_return_if_fail (only_ids && only_ids->len); - - for (ii = 0; ii < only_ids->len; ii++) { - const gchar *itemid = g_ptr_array_index (only_ids, ii); - - e_soap_message_start_element (msg, "IsEqualTo", NULL, NULL); - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", "item:ItemId"); - e_soap_message_start_element (msg, "FieldURIOrConstant", NULL, NULL); - e_ews_message_write_string_parameter_with_attribute (msg, "Constant", NULL, NULL, "Value", itemid); - e_soap_message_end_element (msg); /* FieldURIOrConstant */ - e_soap_message_end_element (msg); /* IsEqualTo */ - } -} - -/** - * e_ews_connection_find_folder_items: - * @cnc: The EWS Connection - * @pri: The priority associated with the request - * @fid: The folder id to which the items belong - * @default_props: Can take one of the values: IdOnly,Default or AllProperties - * @add_props: Specify any additional properties to be fetched - * @sort_order: Specific sorting order for items - * @query: evo query based on which items will be fetched - * @only_ids: (element-type utf8) (nullable): a gchar * with item IDs, to check with only; can be %NULL - * @type: type of folder - * @convert_query_cb: a callback method to convert query to ews restiction - * @cancellable: a GCancellable to monitor cancelled operations - * @callback: Responses are parsed and returned to this callback - * @user_data: user data passed to callback - **/ -void -e_ews_connection_find_folder_items (EEwsConnection *cnc, - gint pri, - EwsFolderId *fid, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - EwsSortOrder *sort_order, - const gchar *query, - GPtrArray *only_ids, /* element-type utf8 */ - EEwsFolderType type, - EwsConvertQueryCallback convert_query_cb, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "FindItem", - "Traversal", - "Shallow", - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - e_soap_message_start_element (msg, "ItemShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, default_props); - - ews_append_additional_props_to_msg (msg, add_props); - - e_soap_message_end_element (msg); - - /*write restriction message based on query*/ - if (convert_query_cb) { - e_soap_message_start_element (msg, "Restriction", "messages", NULL); - - if (only_ids && only_ids->len) { - e_soap_message_start_element (msg, "And", "messages", NULL); - e_soap_message_start_element (msg, "Or", "messages", NULL); - ews_connection_write_only_ids_restriction (msg, only_ids); - e_soap_message_end_element (msg); /* Or */ - } - - convert_query_cb (msg, query, type); - - if (only_ids && only_ids->len) - e_soap_message_end_element (msg); /* And */ - - e_soap_message_end_element (msg); /* Restriction */ - } else if (only_ids && only_ids->len) { - e_soap_message_start_element (msg, "Restriction", "messages", NULL); - ews_connection_write_only_ids_restriction (msg, only_ids); - e_soap_message_end_element (msg); - } - - if (sort_order) - ews_write_sort_order_to_msg (msg, sort_order); - - e_soap_message_start_element (msg, "ParentFolderIds", "messages", NULL); - - if (fid->is_distinguished_id) - e_ews_message_write_string_parameter_with_attribute (msg, "DistinguishedFolderId", NULL, NULL, "Id", fid->id); - else - e_ews_message_write_string_parameter_with_attribute (msg, "FolderId", NULL, NULL, "Id", fid->id); - - e_soap_message_end_element (msg); - - /* Complete the footer and print the request */ - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_find_folder_items); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, find_folder_items_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_find_folder_items_finish (EEwsConnection *cnc, - GAsyncResult *result, - gboolean *includes_last_item, - GSList **items, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_find_folder_items), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *includes_last_item = async_data->includes_last_item; - *items = async_data->items; - - return TRUE; -} - -gboolean -e_ews_connection_find_folder_items_sync (EEwsConnection *cnc, - gint pri, - EwsFolderId *fid, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - EwsSortOrder *sort_order, - const gchar *query, - GPtrArray *only_ids, /* element-type utf8 */ - EEwsFolderType type, - gboolean *includes_last_item, - GSList **items, - EwsConvertQueryCallback convert_query_cb, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_find_folder_items ( - cnc, pri, fid, default_props, - add_props, sort_order, query, - only_ids, type, convert_query_cb, NULL, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_find_folder_items_finish ( - cnc, result, includes_last_item, items, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_sync_folder_hierarchy (EEwsConnection *cnc, - gint pri, - const gchar *sync_state, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "SyncFolderHierarchy", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - e_soap_message_start_element (msg, "FolderShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, "AllProperties"); - e_soap_message_end_element (msg); - - if (sync_state) - e_ews_message_write_string_parameter (msg, "SyncState", "messages", sync_state); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_sync_folder_hierarchy); - - async_data = g_new0 (EwsAsyncData, 1); - async_data->cnc = cnc; - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, sync_hierarchy_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_sync_folder_hierarchy_finish (EEwsConnection *cnc, - GAsyncResult *result, - gchar **sync_state, - gboolean *includes_last_folder, - GSList **folders_created, - GSList **folders_updated, - GSList **folders_deleted, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_sync_folder_hierarchy), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *sync_state = async_data->sync_state; - *includes_last_folder = async_data->includes_last_item; - *folders_created = async_data->items_created; - *folders_updated = async_data->items_updated; - *folders_deleted = async_data->items_deleted; - - return TRUE; -} - -gboolean -e_ews_connection_sync_folder_hierarchy_sync (EEwsConnection *cnc, - gint pri, - const gchar *old_sync_state, - gchar **new_sync_state, - gboolean *includes_last_folder, - GSList **folders_created, - GSList **folders_updated, - GSList **folders_deleted, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_sync_folder_hierarchy ( - cnc, pri, old_sync_state, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_sync_folder_hierarchy_finish ( - cnc, result, new_sync_state, - includes_last_folder, - folders_created, - folders_updated, - folders_deleted, - error); - - e_async_closure_free (closure); - - return success; -} - -EEwsServerVersion -e_ews_connection_get_server_version (EEwsConnection *cnc) -{ - g_return_val_if_fail (cnc != NULL, E_EWS_EXCHANGE_UNKNOWN); - g_return_val_if_fail (cnc->priv != NULL, E_EWS_EXCHANGE_UNKNOWN); - - return cnc->priv->version; -} - -void -e_ews_connection_set_server_version (EEwsConnection *cnc, - EEwsServerVersion version) -{ - g_return_if_fail (cnc != NULL); - g_return_if_fail (cnc->priv != NULL); - - if (cnc->priv->version != version) - cnc->priv->version = version; -} - -void -e_ews_connection_set_server_version_from_string (EEwsConnection *cnc, - const gchar *version) -{ - if (!version) - cnc->priv->version = E_EWS_EXCHANGE_UNKNOWN; - else if (g_strcmp0 (version, "Exchange2007") == 0) - cnc->priv->version = E_EWS_EXCHANGE_2007; - else if (g_strcmp0 (version, "Exchange2007_SP1") == 0 || - g_str_has_prefix (version, "Exchange2007")) - cnc->priv->version = E_EWS_EXCHANGE_2007_SP1; - else if (g_strcmp0 (version, "Exchange2010") == 0) - cnc->priv->version = E_EWS_EXCHANGE_2010; - else if (g_strcmp0 (version, "Exchange2010_SP1") == 0) - cnc->priv->version = E_EWS_EXCHANGE_2010_SP1; - else if (g_strcmp0 (version, "Exchange2010_SP2") == 0 || - g_str_has_prefix (version, "Exchange2010")) - cnc->priv->version = E_EWS_EXCHANGE_2010_SP2; - else - cnc->priv->version = E_EWS_EXCHANGE_FUTURE; -} - -gboolean -e_ews_connection_satisfies_server_version (EEwsConnection *cnc, - EEwsServerVersion version) -{ - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail (cnc->priv != NULL, FALSE); - - /* - * This test always will fail if, for some reason, we were not able to get the server version. - * It occurrs intentionally because we don't want to call any function that expects an EWS - * Server version higher than 2007 SP1 without be sure we using an EWS Server with version - * 2007 SP1 or later. - */ - return cnc->priv->version >= version; -} - -void -e_ews_connection_get_items (EEwsConnection *cnc, - gint pri, - const GSList *ids, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - gboolean include_mime, - const gchar *mime_directory, - EEwsBodyType body_type, - ESoapProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *l; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetItem", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - if (progress_fn && progress_data) - e_soap_message_set_progress_fn (msg, progress_fn, progress_data); - - e_soap_message_start_element (msg, "ItemShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, default_props); - - if (include_mime) - e_ews_message_write_string_parameter (msg, "IncludeMimeContent", NULL, "true"); - else - e_ews_message_write_string_parameter (msg, "IncludeMimeContent", NULL, "false"); - if (mime_directory) - e_soap_message_store_node_data (msg, "MimeContent", mime_directory, TRUE); - - switch (body_type) { - case E_EWS_BODY_TYPE_BEST: - e_ews_message_write_string_parameter (msg, "BodyType", NULL, "Best"); - break; - case E_EWS_BODY_TYPE_HTML: - e_ews_message_write_string_parameter (msg, "BodyType", NULL, "HTML"); - break; - case E_EWS_BODY_TYPE_TEXT: - e_ews_message_write_string_parameter (msg, "BodyType", NULL, "Text"); - break; - case E_EWS_BODY_TYPE_ANY: - break; - } - - ews_append_additional_props_to_msg (msg, add_props); - - e_soap_message_end_element (msg); - - e_soap_message_start_element (msg, "ItemIds", "messages", NULL); - - for (l = ids; l != NULL; l = g_slist_next (l)) - e_ews_message_write_string_parameter_with_attribute (msg, "ItemId", NULL, NULL, "Id", l->data); - - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_items); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_items_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_items_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **items, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_items), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (!async_data->items) { - g_set_error_literal (error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_ITEMNOTFOUND, _("No items found")); - return FALSE; - } - - *items = async_data->items; - - return TRUE; -} - -gboolean -e_ews_connection_get_items_sync (EEwsConnection *cnc, - gint pri, - const GSList *ids, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - gboolean include_mime, - const gchar *mime_directory, - EEwsBodyType body_type, - GSList **items, - ESoapProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_items ( - cnc, pri,ids, default_props, - add_props, include_mime, - mime_directory, body_type, progress_fn, - progress_data, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_items_finish ( - cnc, result, items, error); - - e_async_closure_free (closure); - - return success; -} - -static const gchar * -ews_delete_type_to_str (EwsDeleteType delete_type) -{ - switch (delete_type) { - case EWS_HARD_DELETE: - return "HardDelete"; - case EWS_SOFT_DELETE: - return "SoftDelete"; - case EWS_MOVE_TO_DELETED_ITEMS: - return "MoveToDeletedItems"; - } - return NULL; -} - -static const gchar * -ews_send_cancels_to_str (EwsSendMeetingCancellationsType send_cancels) -{ - switch (send_cancels) { - case EWS_SEND_TO_NONE: - return "SendToNone"; - case EWS_SEND_ONLY_TO_ALL: - return "SendOnlyToAll"; - case EWS_SEND_TO_ALL_AND_SAVE_COPY: - return "SendToAllAndSaveCopy"; - } - return NULL; -} - -static const gchar * -ews_affected_tasks_to_str (EwsAffectedTaskOccurrencesType affected_tasks) -{ - switch (affected_tasks) { - case EWS_NONE_OCCURRENCES: - return NULL; - case EWS_ALL_OCCURRENCES: - return "AllOccurrences"; - case EWS_SPECIFIED_OCCURRENCE_ONLY: - return "SpecifiedOccurrenceOnly"; - } - return NULL; -} - -static void -delete_item_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_delete_items (EEwsConnection *cnc, - gint pri, - const GSList *ids, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *iter; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "DeleteItem", - "DeleteType", - ews_delete_type_to_str (delete_type), - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - if (send_cancels) - e_soap_message_add_attribute ( - msg, "SendMeetingCancellations", - ews_send_cancels_to_str (send_cancels), NULL, NULL); - - if (affected_tasks) - e_soap_message_add_attribute ( - msg, "AffectedTaskOccurrences", - ews_affected_tasks_to_str (affected_tasks), NULL, NULL); - - e_soap_message_start_element (msg, "ItemIds", "messages", NULL); - - for (iter = ids; iter != NULL; iter = g_slist_next (iter)) - e_ews_message_write_string_parameter_with_attribute (msg, "ItemId", NULL, NULL, "Id", iter->data); - - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_delete_items); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, delete_item_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -void -e_ews_connection_delete_item (EEwsConnection *cnc, - gint pri, - EwsId *item_id, - guint index, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - gchar buffer[32]; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "DeleteItem", - "DeleteType", - ews_delete_type_to_str (delete_type), - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - if (send_cancels) - e_soap_message_add_attribute ( - msg, "SendMeetingCancellations", - ews_send_cancels_to_str (send_cancels), NULL, NULL); - - if (affected_tasks != EWS_NONE_OCCURRENCES) - e_soap_message_add_attribute ( - msg, "AffectedTaskOccurrences", - ews_affected_tasks_to_str (affected_tasks), NULL, NULL); - - e_soap_message_start_element (msg, "ItemIds", "messages", NULL); - - if (index) { - e_soap_message_start_element (msg, "OccurrenceItemId", NULL, NULL); - e_soap_message_add_attribute (msg, "RecurringMasterId", item_id->id, NULL, NULL); - if (item_id->change_key) - e_soap_message_add_attribute (msg, "ChangeKey", item_id->change_key, NULL, NULL); - snprintf (buffer, 32, "%u", index); - e_soap_message_add_attribute (msg, "InstanceIndex", buffer, NULL, NULL); - e_soap_message_end_element (msg); - } else { - e_soap_message_start_element (msg, "ItemId", NULL, NULL); - e_soap_message_add_attribute (msg, "Id", item_id->id, NULL, NULL); - if (item_id->change_key) - e_soap_message_add_attribute (msg, "ChangeKey", item_id->change_key, NULL, NULL); - e_soap_message_end_element (msg); - } - - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_delete_items); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, delete_item_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_delete_items_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_delete_items), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - return TRUE; -} - -gboolean -e_ews_connection_delete_items_sync (EEwsConnection *cnc, - gint pri, - const GSList *ids, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_delete_items ( - cnc, pri, ids, delete_type, - send_cancels, affected_tasks, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_delete_items_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -gboolean -e_ews_connection_delete_item_sync (EEwsConnection *cnc, - gint pri, - EwsId *id, - guint index, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_delete_item ( - cnc, pri, id, index, delete_type, - send_cancels, affected_tasks, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_delete_items_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -gboolean -e_ews_connection_delete_items_in_chunks_sync (EEwsConnection *cnc, - gint pri, - const GSList *ids, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GError **error) -{ - const GSList *iter; - guint total_ids = 0, done_ids = 0; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - - g_object_ref (cnc); - - iter = ids; - - while (success && iter) { - guint n_ids; - const GSList *tmp_iter; - - for (tmp_iter = iter, n_ids = 0; tmp_iter && n_ids < EWS_MOVE_ITEMS_CHUNK_SIZE; tmp_iter = g_slist_next (tmp_iter), n_ids++) { - /* Only check bounds first, to avoid unnecessary allocations */ - } - - if (tmp_iter) { - GSList *shorter = NULL; - - if (total_ids == 0) - total_ids = g_slist_length ((GSList *) ids); - - for (n_ids = 0; iter && n_ids < EWS_MOVE_ITEMS_CHUNK_SIZE; iter = g_slist_next (iter), n_ids++) { - shorter = g_slist_prepend (shorter, iter->data); - } - - shorter = g_slist_reverse (shorter); - - success = e_ews_connection_delete_items_sync (cnc, pri, shorter, delete_type, send_cancels, - affected_tasks, cancellable, error); - - g_slist_free (shorter); - - done_ids += n_ids; - } else { - success = e_ews_connection_delete_items_sync (cnc, pri, iter, delete_type, send_cancels, - affected_tasks, cancellable, error); - - iter = NULL; - done_ids = total_ids; - } - - if (total_ids > 0) - camel_operation_progress (cancellable, 100 * (gdouble) done_ids / (gdouble) total_ids); - } - - g_object_unref (cnc); - - return success; -} - -static xmlXPathObjectPtr -xpath_eval (xmlXPathContextPtr ctx, - const gchar *format, - ...) -{ - xmlXPathObjectPtr result; - va_list args; - gchar *expr; - - if (ctx == NULL) - return NULL; - - va_start (args, format); - expr = g_strdup_vprintf (format, args); - va_end (args); - - result = xmlXPathEvalExpression ((xmlChar *) expr, ctx); - g_free (expr); - - if (result == NULL) - return NULL; - - if (result->type == XPATH_NODESET && xmlXPathNodeSetIsEmpty (result->nodesetval)) { - xmlXPathFreeObject (result); - return NULL; - } - - return result; -} - -static gboolean -element_has_child (ESoapMessage *message, - const gchar *path) -{ - xmlDocPtr doc; - xmlXPathContextPtr xpctx; - xmlXPathObjectPtr result; - xmlNodeSetPtr nodeset; - xmlNodePtr node; - gboolean ret = FALSE; - - doc = e_soap_message_get_xml_doc (message); - xpctx = xmlXPathNewContext (doc); - - xmlXPathRegisterNs ( - xpctx, - (xmlChar *) "s", - (xmlChar *) "http://schemas.xmlsoap.org/soap/envelope/"); - - xmlXPathRegisterNs ( - xpctx, - (xmlChar *) "m", - (xmlChar *) "http://schemas.microsoft.com/exchange/services/2006/messages"); - - xmlXPathRegisterNs ( - xpctx, - (xmlChar *) "t", - (xmlChar *) "http://schemas.microsoft.com/exchange/services/2006/types"); - - result = xpath_eval (xpctx, path); - - if (result == NULL) - goto exit; - - if (!xmlXPathNodeSetGetLength (result->nodesetval)) - goto exit; - - nodeset = result->nodesetval; - node = nodeset->nodeTab[0]; - if (!node->children) - goto exit; - - ret = TRUE; - -exit: - xmlXPathFreeObject (result); - xmlXPathFreeContext (xpctx); - return ret; -} - -void -e_ews_connection_update_items (EEwsConnection *cnc, - gint pri, - const gchar *conflict_res, - const gchar *msg_disposition, - const gchar *send_invites, - const gchar *folder_id, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "UpdateItem", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - if (conflict_res) - e_soap_message_add_attribute ( - msg, "ConflictResolution", - conflict_res, NULL, NULL); - if (msg_disposition) - e_soap_message_add_attribute ( - msg, "MessageDisposition", - msg_disposition, NULL, NULL); - if (send_invites) - e_soap_message_add_attribute ( - msg, "SendMeetingInvitationsOrCancellations", - send_invites, NULL, NULL); - - if (folder_id) { - e_soap_message_start_element (msg, "SavedItemFolderId", "messages", NULL); - e_ews_message_write_string_parameter_with_attribute ( - msg, "FolderId", - NULL, NULL, "Id", folder_id); - e_soap_message_end_element (msg); - } - - e_soap_message_start_element (msg, "ItemChanges", "messages", NULL); - - create_cb (msg, create_user_data); - - e_soap_message_end_element (msg); /* ItemChanges */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_update_items); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - /* - * We need to check for both namespaces, because, the message is being wrote without use the types - * namespace. Maybe it is wrong, but the server doesn't complain about that. But this is the reason - * for the first check. The second one, is related to "how it should be" accord with EWS specifications. - */ - if (!element_has_child (msg, "/s:Envelope/s:Body/m:UpdateItem/m:ItemChanges/ItemChange/Updates") && - !element_has_child (msg, "/s:Envelope/s:Body/m:UpdateItem/m:ItemChanges/t:ItemChange/t:Updates")) - g_simple_async_result_complete_in_idle (simple); - else - e_ews_connection_queue_request ( - cnc, msg, get_items_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_update_items_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **ids, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_update_items), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - /* if there is only one item, then check whether it's an error */ - if (async_data->items && !async_data->items->next) { - EEwsItem *item = async_data->items->data; - - if (item && e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) { - if (error) - *error = g_error_copy (e_ews_item_get_error (item)); - - g_slist_free_full (async_data->items, g_object_unref); - async_data->items = NULL; - - return FALSE; - } - } - - if (ids) - *ids = async_data->items; - else - g_slist_free_full (async_data->items, g_object_unref); - - return TRUE; -} - -gboolean -e_ews_connection_update_items_sync (EEwsConnection *cnc, - gint pri, - const gchar *conflict_res, - const gchar *msg_disposition, - const gchar *send_invites, - const gchar *folder_id, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GSList **ids, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_update_items ( - cnc, pri, conflict_res, - msg_disposition, send_invites, - folder_id, create_cb, - create_user_data, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_update_items_finish ( - cnc, result, ids, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_create_items (EEwsConnection *cnc, - gint pri, - const gchar *msg_disposition, - const gchar *send_invites, - const EwsFolderId *fid, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "CreateItem", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - if (msg_disposition) - e_soap_message_add_attribute ( - msg, "MessageDisposition", - msg_disposition, NULL, NULL); - if (send_invites) - e_soap_message_add_attribute ( - msg, "SendMeetingInvitations", - send_invites, NULL, NULL); - - if (fid) { - e_soap_message_start_element (msg, "SavedItemFolderId", "messages", NULL); - ews_append_folder_id_to_msg (msg, cnc->priv->email, fid); - e_soap_message_end_element (msg); - } - - e_soap_message_start_element (msg, "Items", "messages", NULL); - - create_cb (msg, create_user_data); - - e_soap_message_end_element (msg); /* Items */ - - e_ews_message_write_footer (msg); /* CreateItem */ - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_create_items); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_items_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_create_items_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **ids, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_create_items), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - /* if there is only one item, then check whether it's an error */ - if (async_data->items && !async_data->items->next) { - EEwsItem *item = async_data->items->data; - - if (item && e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) { - if (error) - *error = g_error_copy (e_ews_item_get_error (item)); - - g_slist_free_full (async_data->items, g_object_unref); - async_data->items = NULL; - - return FALSE; - } - } - - *ids = async_data->items; - - return TRUE; -} - -gboolean -e_ews_connection_create_items_sync (EEwsConnection *cnc, - gint pri, - const gchar *msg_disposition, - const gchar *send_invites, - const EwsFolderId *fid, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GSList **ids, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_create_items ( - cnc, pri, msg_disposition, - send_invites, fid, - create_cb, create_user_data, - cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_create_items_finish ( - cnc, result, ids, error); - - e_async_closure_free (closure); - - return success; -} - -static const gchar * -get_search_scope_str (EwsContactsSearchScope scope) -{ - switch (scope) { - case EWS_SEARCH_AD: - return "ActiveDirectory"; - case EWS_SEARCH_AD_CONTACTS: - return "ActiveDirectoryContacts"; - case EWS_SEARCH_CONTACTS: - return "Contacts"; - case EWS_SEARCH_CONTACTS_AD: - return "ContactsActiveDirectory"; - default: - g_warn_if_reached (); - return NULL; - - } -} - -void -e_ews_connection_resolve_names (EEwsConnection *cnc, - gint pri, - const gchar *resolve_name, - EwsContactsSearchScope scope, - GSList *parent_folder_ids, - gboolean fetch_contact_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "ResolveNames", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_add_attribute (msg, "SearchScope", get_search_scope_str (scope), NULL, NULL); - - if (fetch_contact_data) - e_soap_message_add_attribute (msg, "ReturnFullContactData", "true", NULL, NULL); - else - e_soap_message_add_attribute (msg, "ReturnFullContactData", "false", NULL, NULL); - - if (parent_folder_ids) { - e_soap_message_start_element (msg, "ParentFolderIds", "messages", NULL); - ews_append_folder_ids_to_msg (msg, cnc->priv->email, parent_folder_ids); - e_soap_message_end_element (msg); - } - - e_ews_message_write_string_parameter (msg, "UnresolvedEntry", "messages", resolve_name); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_resolve_names); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, resolve_names_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_resolve_names_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **mailboxes, - GSList **contact_items, - gboolean *includes_last_item, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_resolve_names), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *includes_last_item = async_data->includes_last_item; - - if (contact_items) - *contact_items = async_data->items_created; - else - e_util_free_nullable_object_slist (async_data->items_created); - *mailboxes = async_data->items; - - return TRUE; -} - -gboolean -e_ews_connection_resolve_names_sync (EEwsConnection *cnc, - gint pri, - const gchar *resolve_name, - EwsContactsSearchScope scope, - GSList *parent_folder_ids, - gboolean fetch_contact_data, - GSList **mailboxes, - GSList **contact_items, - gboolean *includes_last_item, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_resolve_names ( - cnc, pri, resolve_name, - scope, parent_folder_ids, - fetch_contact_data, - cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_resolve_names_finish ( - cnc, result, - mailboxes, contact_items, - includes_last_item, error); - - e_async_closure_free (closure); - - return success; -} - -static void -ews_connection_resolve_by_name (EEwsConnection *cnc, - gint pri, - const gchar *usename, - gboolean is_user_name, - gchar **smtp_address, - GCancellable *cancellable) -{ - GSList *mailboxes = NULL; - GSList *contacts = NULL; - gboolean includes_last_item = FALSE; - GSList *miter; - gint len; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (usename != NULL); - g_return_if_fail (smtp_address != NULL); - - if (!*usename) - return; - - len = strlen (usename); - mailboxes = NULL; - contacts = NULL; - - /* use the first error, not the guess-part error */ - e_ews_connection_resolve_names_sync ( - cnc, pri, usename, - EWS_SEARCH_AD_CONTACTS, NULL, TRUE, &mailboxes, &contacts, - &includes_last_item, cancellable, NULL); - - for (miter = mailboxes; miter; miter = miter->next) { - const EwsMailbox *mailbox = miter->data; - if (mailbox->email && *mailbox->email && g_strcmp0 (mailbox->routing_type, "EX") != 0 - && ((!is_user_name && g_str_has_prefix (mailbox->email, usename) && mailbox->email[len] == '@') || - (is_user_name && g_str_equal (usename, mailbox->name)))) { - *smtp_address = g_strdup (mailbox->email); - break; - } else if (contacts && !contacts->next && contacts->data && - e_ews_item_get_item_type (contacts->data) == E_EWS_ITEM_TYPE_CONTACT) { - EEwsItem *contact_item = contacts->data; - GHashTable *addresses_hash = e_ews_item_get_email_addresses (contact_item); - GList *emails = addresses_hash ? g_hash_table_get_values (addresses_hash) : NULL, *iter; - const gchar *display_name; - gboolean found = FALSE; - - display_name = e_ews_item_get_display_name (contact_item); - if (!display_name || !*display_name) - display_name = e_ews_item_get_fileas (contact_item); - - for (iter = emails; iter && !found; iter = iter->next) { - const gchar *it_email = iter->data; - - if (it_email && g_str_has_prefix (it_email, "SMTP:") - && ((!is_user_name && g_str_has_prefix (it_email, usename) && it_email[len] == '@') || - (is_user_name && display_name && g_str_equal (usename, display_name)))) { - found = TRUE; - break; - } - } - - g_list_free (emails); - - if (found) { - gint ii; - - for (ii = 0; ii < g_hash_table_size (addresses_hash); ii++) { - gchar *key, *value; - - key = g_strdup_printf ("EmailAddress%d", ii + 1); - value = g_hash_table_lookup (addresses_hash, key); - g_free (key); - - if (value && g_str_has_prefix (value, "SMTP:")) { - /* pick the first available SMTP address */ - *smtp_address = g_strdup (value + 5); - break; - } - } - break; - } - } - } - - g_slist_free_full (mailboxes, (GDestroyNotify) e_ews_mailbox_free); - e_util_free_nullable_object_slist (contacts); -} - -gboolean -e_ews_connection_ex_to_smtp_sync (EEwsConnection *cnc, - gint pri, - const gchar *name, - const gchar *ex_address, - gchar **smtp_address, - GCancellable *cancellable, - GError **error) -{ - GSList *mailboxes = NULL; - GSList *contacts = NULL; - gboolean includes_last_item = FALSE; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail (ex_address != NULL, FALSE); - g_return_val_if_fail (smtp_address != NULL, FALSE); - - *smtp_address = NULL; - - e_ews_connection_resolve_names_sync ( - cnc, pri, ex_address, - EWS_SEARCH_AD_CONTACTS, NULL, TRUE, &mailboxes, &contacts, - &includes_last_item, cancellable, error); - - /* only one mailbox matches */ - if (mailboxes && !mailboxes->next && mailboxes->data) { - const EwsMailbox *mailbox = mailboxes->data; - if (mailbox->email && *mailbox->email && g_strcmp0 (mailbox->routing_type, "EX") != 0) { - *smtp_address = g_strdup (mailbox->email); - } else if (contacts && !contacts->next && contacts->data && - e_ews_item_get_item_type (contacts->data) == E_EWS_ITEM_TYPE_CONTACT) { - EEwsItem *contact_item = contacts->data; - GHashTable *addresses = e_ews_item_get_email_addresses (contact_item); - gint ii; - - for (ii = 0; ii < (addresses ? g_hash_table_size (addresses) : 0); ii++) { - gchar *key, *value; - - key = g_strdup_printf ("EmailAddress%d", ii + 1); - value = g_hash_table_lookup (addresses, key); - g_free (key); - - if (value && g_str_has_prefix (value, "SMTP:")) { - /* pick the first available SMTP address */ - *smtp_address = g_strdup (value + 5); - break; - } - } - } - } - - g_slist_free_full (mailboxes, (GDestroyNotify) e_ews_mailbox_free); - e_util_free_nullable_object_slist (contacts); - - if (!*smtp_address) { - const gchar *usename; - - usename = strrchr (ex_address, '/'); - if (usename && g_ascii_strncasecmp (usename, "/cn=", 4) == 0) { - usename += 4; - - /* try to guess from common name of the EX address */ - ews_connection_resolve_by_name (cnc, pri, usename, FALSE, smtp_address, cancellable); - } - - if (!*smtp_address && name && *name) { - /* try to guess from mailbox name */ - ews_connection_resolve_by_name (cnc, pri, name, TRUE, smtp_address, cancellable); - } - } - - if (*smtp_address) - g_clear_error (error); - - return *smtp_address != NULL; -} - -void -e_ews_connection_expand_dl (EEwsConnection *cnc, - gint pri, - const EwsMailbox *mb, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "ExpandDL", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "Mailbox", "messages", NULL); - - if (mb->item_id) { - e_soap_message_start_element (msg, "ItemId", NULL, NULL); - - e_soap_message_add_attribute (msg, "Id", mb->item_id->id, NULL, NULL); - e_soap_message_add_attribute (msg, "ChangeKey", mb->item_id->change_key, NULL, NULL); - - e_soap_message_end_element (msg); /* Mailbox */ - - } else if (mb->email) - e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, mb->email); - - e_soap_message_end_element (msg); /* Mailbox */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_expand_dl); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, expand_dl_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -/* includes_last_item does not make sense as expand_dl does not support recursive - * fetch, wierd */ -gboolean -e_ews_connection_expand_dl_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **mailboxes, - gboolean *includes_last_item, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_expand_dl), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *includes_last_item = async_data->includes_last_item; - *mailboxes = async_data->items; - - return TRUE; - -} - -gboolean -e_ews_connection_expand_dl_sync (EEwsConnection *cnc, - gint pri, - const EwsMailbox *mb, - GSList **mailboxes, - gboolean *includes_last_item, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_expand_dl ( - cnc, pri, mb, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_expand_dl_finish ( - cnc, result, mailboxes, includes_last_item, error); - - e_async_closure_free (closure); - - return success; -} - -static void -update_folder_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_next_child (param); - } -} - -void -e_ews_connection_update_folder (EEwsConnection *cnc, - gint pri, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "UpdateFolder", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "FolderChanges", "messages", NULL); - - create_cb (msg, create_user_data); - - e_soap_message_end_element (msg); /* FolderChanges */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_update_folder); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, update_folder_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_update_folder_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_update_folder), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - return TRUE; -} - -gboolean -e_ews_connection_update_folder_sync (EEwsConnection *cnc, - gint pri, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_update_folder ( - cnc, pri, create_cb, create_user_data, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_update_folder_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -static void -move_folder_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_move_folder (EEwsConnection *cnc, - gint pri, - const gchar *to_folder, - const gchar *folder, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "MoveFolder", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "ToFolderId", "messages", NULL); - if (to_folder) - e_ews_message_write_string_parameter_with_attribute ( - msg, "FolderId", NULL, - NULL, "Id", to_folder); - else - e_ews_message_write_string_parameter_with_attribute ( - msg, "DistinguishedFolderId", NULL, - NULL, "Id", "msgfolderroot"); - - e_soap_message_end_element (msg); - - e_soap_message_start_element (msg, "FolderIds", "messages", NULL); - e_ews_message_write_string_parameter_with_attribute ( - msg, "FolderId", NULL, - NULL, "Id", folder); - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_move_folder); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, move_folder_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_move_folder_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_move_folder), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - return TRUE; -} - -gboolean -e_ews_connection_move_folder_sync (EEwsConnection *cnc, - gint pri, - const gchar *to_folder, - const gchar *folder, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_move_folder ( - cnc, pri, to_folder, folder, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_move_folder_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_get_folder (EEwsConnection *cnc, - gint pri, - const gchar *folder_shape, - const EEwsAdditionalProps *add_props, - GSList *folder_ids, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetFolder", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - TRUE, - TRUE); - - e_soap_message_start_element (msg, "FolderShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, folder_shape); - - ews_append_additional_props_to_msg (msg, add_props); - e_soap_message_end_element (msg); - - if (folder_ids) { - e_soap_message_start_element (msg, "FolderIds", "messages", NULL); - ews_append_folder_ids_to_msg (msg, cnc->priv->email, folder_ids); - e_soap_message_end_element (msg); - } - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_folder); - - async_data = g_new0 (EwsAsyncData, 1); - async_data->cnc = cnc; - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_folder_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_folder_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **folders, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_folder), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (folders != NULL) - *folders = async_data->items; - else - g_slist_free_full (async_data->items, g_object_unref); - - return TRUE; -} - -gboolean -e_ews_connection_get_folder_sync (EEwsConnection *cnc, - gint pri, - const gchar *folder_shape, - const EEwsAdditionalProps *add_props, - GSList *folder_ids, - GSList **folders, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_folder ( - cnc, pri, folder_shape, add_props, - folder_ids, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_folder_finish ( - cnc, result, folders, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_create_folder (EEwsConnection *cnc, - gint pri, - const gchar *parent_folder_id, - gboolean is_distinguished_id, - const gchar *folder_name, - EEwsFolderType folder_type, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const gchar *folder_element; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "CreateFolder", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "ParentFolderId", "messages", NULL); - - /* If NULL passed for parent_folder_id, use "msgfolderroot" */ - if (is_distinguished_id || !parent_folder_id) { - e_soap_message_start_element (msg, "DistinguishedFolderId", NULL, NULL); - e_soap_message_add_attribute ( - msg, "Id", parent_folder_id ? parent_folder_id : "msgfolderroot", NULL, NULL); - if (is_distinguished_id && cnc->priv->email) { - e_soap_message_start_element (msg, "Mailbox", NULL, NULL); - e_ews_message_write_string_parameter( - msg, "EmailAddress", NULL, cnc->priv->email); - e_soap_message_end_element (msg); - } - e_soap_message_end_element (msg); - } else { - e_ews_message_write_string_parameter_with_attribute (msg, "FolderId", NULL, NULL, "Id", parent_folder_id); - } - - e_soap_message_end_element (msg); - - switch (folder_type) { - case E_EWS_FOLDER_TYPE_MAILBOX: - folder_element = "Folder"; - break; - case E_EWS_FOLDER_TYPE_CALENDAR: - folder_element = "CalendarFolder"; - break; - case E_EWS_FOLDER_TYPE_CONTACTS: - folder_element = "ContactsFolder"; - break; - case E_EWS_FOLDER_TYPE_SEARCH: - folder_element = "SearchFolder"; - break; - case E_EWS_FOLDER_TYPE_TASKS: - folder_element = "TasksFolder"; - break; - default: - g_warn_if_reached (); - folder_element = "Folder"; - break; - } - - e_soap_message_start_element (msg, "Folders", "messages", NULL); - e_soap_message_start_element (msg, folder_element, NULL, NULL); - e_ews_message_write_string_parameter (msg, "DisplayName", NULL, folder_name); - - e_soap_message_end_element (msg); - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_create_folder); - - async_data = g_new0 (EwsAsyncData, 1); - async_data->folder_type = folder_type; - - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, create_folder_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_create_folder_finish (EEwsConnection *cnc, - GAsyncResult *result, - EwsFolderId **fid, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_create_folder), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *fid = (EwsFolderId *) async_data->items_created->data; - g_slist_free (async_data->items_created); - - return TRUE; -} - -gboolean -e_ews_connection_create_folder_sync (EEwsConnection *cnc, - gint pri, - const gchar *parent_folder_id, - gboolean is_distinguished_id, - const gchar *folder_name, - EEwsFolderType folder_type, - EwsFolderId **folder_id, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_create_folder ( - cnc, pri, parent_folder_id, - is_distinguished_id, folder_name, - folder_type, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_create_folder_finish ( - cnc, result, folder_id, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_move_items (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean docopy, - const GSList *ids, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *iter; - - g_return_if_fail (cnc != NULL); - - if (docopy) - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "CopyItem", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - else - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "MoveItem", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "ToFolderId", "messages", NULL); - e_soap_message_start_element (msg, "FolderId", NULL, NULL); - e_soap_message_add_attribute (msg, "Id", folder_id, NULL, NULL); - e_soap_message_end_element (msg); /* FolderId */ - e_soap_message_end_element (msg); /* ToFolderId */ - - e_soap_message_start_element (msg, "ItemIds", "messages", NULL); - for (iter = ids; iter != NULL; iter = g_slist_next (iter)) - e_ews_message_write_string_parameter_with_attribute (msg, "ItemId", NULL, NULL, "Id", iter->data); - e_soap_message_end_element (msg); /* ItemIds */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_move_items); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_items_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_move_items_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **items, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_move_items), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - /* if there is only one item, then check whether it's an error */ - if (async_data->items && !async_data->items->next) { - EEwsItem *item = async_data->items->data; - - if (item && e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) { - if (error) - *error = g_error_copy (e_ews_item_get_error (item)); - - g_slist_free_full (async_data->items, g_object_unref); - async_data->items = NULL; - - return FALSE; - } - } - - *items = async_data->items; - - return TRUE; -} - -gboolean -e_ews_connection_move_items_sync (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean docopy, - const GSList *ids, - GSList **items, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_move_items ( - cnc, pri, folder_id, docopy, ids, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_move_items_finish ( - cnc, result, items, error); - - e_async_closure_free (closure); - - return success; -} - -gboolean -e_ews_connection_move_items_in_chunks_sync (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean docopy, - const GSList *ids, - GSList **items, - GCancellable *cancellable, - GError **error) -{ - const GSList *iter; - guint total_ids = 0, done_ids = 0; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (items != NULL, FALSE); - - g_object_ref (cnc); - - *items = NULL; - iter = ids; - - while (success && iter) { - guint n_ids; - const GSList *tmp_iter; - GSList *processed_items = NULL; - - for (tmp_iter = iter, n_ids = 0; tmp_iter && n_ids < EWS_MOVE_ITEMS_CHUNK_SIZE; tmp_iter = g_slist_next (tmp_iter), n_ids++) { - /* Only check bounds first, to avoid unnecessary allocations */ - } - - if (tmp_iter) { - GSList *shorter = NULL; - - if (total_ids == 0) - total_ids = g_slist_length ((GSList *) ids); - - for (n_ids = 0; iter && n_ids < EWS_MOVE_ITEMS_CHUNK_SIZE; iter = g_slist_next (iter), n_ids++) { - shorter = g_slist_prepend (shorter, iter->data); - } - - shorter = g_slist_reverse (shorter); - - success = e_ews_connection_move_items_sync (cnc, pri, folder_id, docopy, - shorter, &processed_items, cancellable, error); - - g_slist_free (shorter); - - done_ids += n_ids; - } else { - success = e_ews_connection_move_items_sync (cnc, pri, folder_id, docopy, - iter, &processed_items, cancellable, error); - - iter = NULL; - done_ids = total_ids; - } - - if (processed_items) - *items = g_slist_concat (*items, processed_items); - - if (total_ids > 0) - camel_operation_progress (cancellable, 100 * (gdouble) done_ids / (gdouble) total_ids); - } - - g_object_unref (cnc); - - return success; -} - -static void -delete_folder_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -/** - * e_ews_connection_delete_folder: - * @cnc: - * @pri: - * @folder_id: folder to be deleted - * @is_distinguished_id: - * @delete_type: "HardDelete", "SoftDelete", "MoveToDeletedItems" - * @cancellable: - * @callback: - * @user_data: - **/ -void -e_ews_connection_delete_folder (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean is_distinguished_id, - const gchar *delete_type, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "DeleteFolder", - "DeleteType", - delete_type, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "FolderIds", "messages", NULL); - - e_soap_message_start_element ( - msg, - is_distinguished_id ? "DistinguishedFolderId" : "FolderId", - NULL, - NULL); - e_soap_message_add_attribute (msg, "Id", folder_id, NULL, NULL); - - /* This element is required for delegate access */ - if (is_distinguished_id && cnc->priv->email) { - e_soap_message_start_element (msg, "Mailbox", NULL, NULL); - e_ews_message_write_string_parameter( - msg, "EmailAddress", NULL, cnc->priv->email); - e_soap_message_end_element (msg); - } - - e_soap_message_end_element (msg); /* || */ - - e_soap_message_end_element (msg); /* */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_delete_folder); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, delete_folder_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_delete_folder_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_delete_folder), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - return TRUE; -} - -/** - * e_ews_connection_delete_folder_sync: - * @cnc: - * @pri: - * @folder_id: folder to be deleted - * @is_distinguished_id: - * @delete_type: "HardDelete", "SoftDelete", "MoveToDeletedItems" - * @cancellable: - * @error: - **/ -gboolean -e_ews_connection_delete_folder_sync (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean is_distinguished_id, - const gchar *delete_type, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_delete_folder ( - cnc, pri, folder_id, - is_distinguished_id, - delete_type, - cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_delete_folder_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -static void -empty_folder_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_empty_folder (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean is_distinguished_id, - const gchar *delete_type, - gboolean delete_subfolders, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "EmptyFolder", - "DeleteType", - delete_type, - cnc->priv->version, - E_EWS_EXCHANGE_2010, - FALSE, - TRUE); - - e_soap_message_add_attribute (msg, "DeleteSubFolders", delete_subfolders ? "true" : "false", NULL, NULL); - - e_soap_message_start_element (msg, "FolderIds", "messages", NULL); - - e_soap_message_start_element ( - msg, - is_distinguished_id ? "DistinguishedFolderId" : "FolderId", - NULL, - NULL); - e_soap_message_add_attribute (msg, "Id", folder_id, NULL, NULL); - - /* This element is required for delegate access */ - if (is_distinguished_id && cnc->priv->email) { - e_soap_message_start_element (msg, "Mailbox", NULL, NULL); - e_ews_message_write_string_parameter( - msg, "EmailAddress", NULL, cnc->priv->email); - e_soap_message_end_element (msg); - } - - e_soap_message_end_element (msg); /* || */ - - e_soap_message_end_element (msg); /* */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_empty_folder); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, empty_folder_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_empty_folder_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_empty_folder), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - return TRUE; -} - -gboolean -e_ews_connection_empty_folder_sync (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean is_distinguished_id, - const gchar *delete_type, - gboolean delete_subfolder, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_empty_folder ( - cnc, pri, folder_id, - is_distinguished_id, - delete_type, - delete_subfolder, - cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_empty_folder_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -static void -ews_handle_create_attachments_param (ESoapParameter *param, - EwsAsyncData *async_data) -{ - /* http://msdn.microsoft.com/en-us/library/aa565877%28v=EXCHG.80%29.aspx */ - ESoapParameter *subparam, *attspara, *last_relevant = NULL, *attparam; - - attspara = e_soap_parameter_get_first_child_by_name (param, "Attachments"); - - for (subparam = e_soap_parameter_get_first_child (attspara); subparam != NULL; subparam = e_soap_parameter_get_next_child (subparam)) { - if (!g_ascii_strcasecmp (e_soap_parameter_get_name (subparam), "FileAttachment")) { - attparam = e_soap_parameter_get_first_child (subparam); - last_relevant = attparam; - - async_data->items = g_slist_append (async_data->items, e_soap_parameter_get_property (attparam, "Id")); - } - } - - if (last_relevant != NULL) { - async_data->sync_state = e_soap_parameter_get_property (last_relevant, "RootItemChangeKey"); - } -} - -static void -create_attachments_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "CreateAttachmentResponseMessage")) - ews_handle_create_attachments_param (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -static gboolean -e_ews_connection_attach_file (ESoapMessage *msg, - EEwsAttachmentInfo *info, - gboolean contact_photo, - GError **error) -{ - EEwsAttachmentInfoType type = e_ews_attachment_info_get_type (info); - gchar *filename = NULL, *buffer = NULL; - const gchar *content = NULL, *prefer_filename; - gsize length; - - switch (type) { - case E_EWS_ATTACHMENT_INFO_TYPE_URI: { - /* TODO - handle a situation where the file isnt accessible/other problem with it */ - /* TODO - This is a naive implementation that just uploads the whole content into */ - /* memory, ie very inefficient */ - const gchar *uri; - gchar *filepath; - GError *local_error = NULL; - - uri = e_ews_attachment_info_get_uri (info); - - /* convert uri to actual file path */ - filepath = g_filename_from_uri (uri, NULL, &local_error); - if (local_error != NULL) { - g_propagate_error (error, local_error); - return FALSE; - } - - g_file_get_contents (filepath, &buffer, &length, &local_error); - if (local_error != NULL) { - g_free (filepath); - g_propagate_error (error, local_error); - return FALSE; - } - - content = buffer; - - filename = strrchr (filepath, G_DIR_SEPARATOR); - filename = filename ? g_strdup (++filename) : g_strdup (filepath); - - g_free (filepath); - break; - } - case E_EWS_ATTACHMENT_INFO_TYPE_INLINED: - content = e_ews_attachment_info_get_inlined_data (info, &length); - filename = g_strdup (e_ews_attachment_info_get_filename (info)); - break; - default: - g_warning ("Unknown EwsAttachmentInfoType %d", type); - return FALSE; - } - - e_soap_message_start_element (msg, "FileAttachment", NULL, NULL); - - prefer_filename = e_ews_attachment_info_get_prefer_filename (info); - e_ews_message_write_string_parameter (msg, "Name", NULL, prefer_filename ? prefer_filename : filename); - if (contact_photo) - e_ews_message_write_string_parameter (msg, "IsContactPhoto", NULL, "true"); - e_soap_message_start_element (msg, "Content", NULL, NULL); - e_soap_message_write_base64 (msg, content, length); - e_soap_message_end_element (msg); /* "Content" */ - e_soap_message_end_element (msg); /* "FileAttachment" */ - - g_free (filename); - g_free (buffer); - - return TRUE; -} - -void -e_ews_connection_create_attachments (EEwsConnection *cnc, - gint pri, - const EwsId *parent, - const GSList *files, - gboolean is_contact_photo, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *l; - GError *local_error = NULL; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (parent != NULL); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_create_attachments); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "CreateAttachment", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "ParentItemId", "messages", NULL); - e_soap_message_add_attribute (msg, "Id", parent->id, NULL, NULL); - if (parent->change_key) - e_soap_message_add_attribute (msg, "ChangeKey", parent->change_key, NULL, NULL); - e_soap_message_end_element (msg); - - /* start interation over all items to get the attachemnts */ - e_soap_message_start_element (msg, "Attachments", "messages", NULL); - - for (l = files; l != NULL; l = g_slist_next (l)) - if (!e_ews_connection_attach_file (msg, l->data, is_contact_photo, &local_error)) { - if (local_error != NULL) - g_simple_async_result_take_error (simple, local_error); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - - return; - } - - e_soap_message_end_element (msg); /* "Attachments" */ - - e_ews_message_write_footer (msg); - - e_ews_connection_queue_request ( - cnc, msg, create_attachments_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_create_attachments_finish (EEwsConnection *cnc, - gchar **change_key, - GSList **attachments_ids, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_create_attachments), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (attachments_ids) - *attachments_ids = async_data->items; - else - g_slist_free_full (async_data->items, g_free); - - if (change_key) - *change_key = async_data->sync_state; - else - g_free (async_data->sync_state); - - return TRUE; -} - -gboolean -e_ews_connection_create_attachments_sync (EEwsConnection *cnc, - gint pri, - const EwsId *parent, - const GSList *files, - gboolean is_contact_photo, - gchar **change_key, - GSList **attachments_ids, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean ret; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail (parent != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_create_attachments ( - cnc, pri, parent, files, is_contact_photo, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - ret = e_ews_connection_create_attachments_finish ( - cnc, change_key, attachments_ids, result, error); - - e_async_closure_free (closure); - - return ret; -} - -/* Delete attachemnts */ -static void -ews_handle_root_item_id_param (ESoapParameter *subparam, - EwsAsyncData *async_data) -{ - /* http://msdn.microsoft.com/en-us/library/aa580782%28v=EXCHG.80%29.aspx */ - ESoapParameter *attspara; - - attspara = e_soap_parameter_get_first_child_by_name ( - subparam, "RootItemId"); - - if (attspara == NULL) - return; - - async_data->sync_state = e_soap_parameter_get_property (attspara, "RootItemChangeKey"); -} - -static void -delete_attachments_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "DeleteAttachmentResponseMessage")) - ews_handle_root_item_id_param (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_delete_attachments (EEwsConnection *cnc, - gint pri, - const GSList *attachments_ids, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *l; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "DeleteAttachment", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - /* start interation over all items to get the attachemnts */ - e_soap_message_start_element (msg, "AttachmentIds", "messages", NULL); - - for (l = attachments_ids; l != NULL; l = l->next) { - e_ews_message_write_string_parameter_with_attribute (msg, "AttachmentId", NULL, NULL, "Id", l->data); - } - - e_soap_message_end_element (msg); /* "AttachmentIds" */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_delete_attachments); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, delete_attachments_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_delete_attachments_finish (EEwsConnection *cnc, - GAsyncResult *result, - gchar **new_change_key, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_delete_attachments), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (new_change_key) - *new_change_key = async_data->sync_state; - else - g_free (async_data->sync_state); - - return TRUE; -} - -gboolean -e_ews_connection_delete_attachments_sync (EEwsConnection *cnc, - gint pri, - const GSList *attachments_ids, - gchar **new_change_key, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean ret; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_delete_attachments ( - cnc, pri, attachments_ids, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - ret = e_ews_connection_delete_attachments_finish ( - cnc, result, new_change_key, error); - - e_async_closure_free (closure); - - return ret; -} - -static void -ews_handle_attachments_param (ESoapParameter *param, - EwsAsyncData *async_data) -{ - ESoapParameter *subparam, *attspara; - EEwsAttachmentInfo *info = NULL; - EEwsItem *item; - const gchar *name; - - attspara = e_soap_parameter_get_first_child_by_name (param, "Attachments"); - - for (subparam = e_soap_parameter_get_first_child (attspara); subparam != NULL; subparam = e_soap_parameter_get_next_child (subparam)) { - name = e_soap_parameter_get_name (subparam); - - if (!g_ascii_strcasecmp (name, "ItemAttachment")) { - item = e_ews_item_new_from_soap_parameter (subparam); - info = e_ews_item_dump_mime_content (item, async_data->directory); - g_clear_object (&item); - - } else if (!g_ascii_strcasecmp (name, "FileAttachment")) { - info = e_ews_dump_file_attachment_from_soap_parameter ( - subparam, - async_data->directory, - async_data->sync_state); - } - - if (info) - async_data->items = g_slist_append (async_data->items, info); - - info = NULL; - } -} - -static void -get_attachments_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "GetAttachmentResponseMessage")) - ews_handle_attachments_param (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_get_attachments (EEwsConnection *cnc, - gint pri, - const gchar *uid, - const GSList *ids, - const gchar *cache, - gboolean include_mime, - ESoapProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *l; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetAttachment", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - /* not sure why I need it, need to check */ - if (progress_fn && progress_data) - e_soap_message_set_progress_fn (msg, progress_fn, progress_data); - - if (cache) - e_soap_message_store_node_data (msg, "MimeContent Content", cache, TRUE); - - /* wrtie empty attachments shape, need to discover maybe usefull in some cases*/ - e_soap_message_start_element (msg, "AttachmentShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "IncludeMimeContent", NULL, "true"); - e_soap_message_end_element (msg); - - /* start interation over all items to get the attachemnts */ - e_soap_message_start_element (msg, "AttachmentIds", "messages", NULL); - - for (l = ids; l != NULL; l = g_slist_next (l)) - e_ews_message_write_string_parameter_with_attribute (msg, "AttachmentId", NULL, NULL, "Id", l->data); - - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_attachments); - - async_data = g_new0 (EwsAsyncData, 1); - async_data->directory = cache; - async_data->sync_state = (gchar *) uid; - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_attachments_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_attachments_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **items, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_attachments), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (items) - *items = async_data->items; - else - g_slist_free_full (async_data->items, (GDestroyNotify) e_ews_attachment_info_free); - - return TRUE; -} - -gboolean -e_ews_connection_get_attachments_sync (EEwsConnection *cnc, - gint pri, - const gchar *uid, - const GSList *ids, - const gchar *cache, - gboolean include_mime, - GSList **items, - ESoapProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean ret; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_attachments ( - cnc, pri, uid, ids, cache, include_mime, - progress_fn, progress_data, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - ret = e_ews_connection_get_attachments_finish ( - cnc, result, items, error); - - e_async_closure_free (closure); - - return ret; -} - -static void -ews_handle_free_busy_view (ESoapParameter *param, - EwsAsyncData *async_data) -{ - /*parse the response to create a free_busy data - http://msdn.microsoft.com / en - us / library / aa564001 % 28v = EXCHG.140 % 29.aspx */ - icalcomponent *vfb; - icalproperty *icalprop = NULL; - struct icalperiodtype ipt; - ESoapParameter *viewparam, *eventarray, *event_param, *subparam; - GTimeVal t_val; - const gchar *name; - gchar *value, *new_val = NULL, *summary = NULL, *location = NULL, *id = NULL; - - viewparam = e_soap_parameter_get_first_child_by_name (param, "FreeBusyView"); - if (!viewparam) return; - vfb = icalcomponent_new_vfreebusy (); - eventarray = e_soap_parameter_get_first_child_by_name (viewparam, "CalendarEventArray"); - for (event_param = eventarray ? e_soap_parameter_get_first_child (eventarray) : NULL; - event_param != NULL; - event_param = e_soap_parameter_get_next_child (event_param), icalprop = NULL) { - for (subparam = e_soap_parameter_get_first_child (event_param); subparam != NULL; subparam = e_soap_parameter_get_next_child (subparam)) { - name = e_soap_parameter_get_name (subparam); - - if (!g_ascii_strcasecmp (name, "StartTime")) { - value = e_soap_parameter_get_string_value (subparam); - /*We are sending UTC timezone and expect server to return in same*/ - - /*Remove leading and trailing whitespace*/ - g_strstrip (value); - - if (g_utf8_strlen (value, -1) == 19) { - /*If server returns time without zone add Z to treat it in UTC*/ - new_val = g_strdup_printf ("%sZ", value); - g_free (value); - } else - new_val = value; - - g_time_val_from_iso8601 (new_val, &t_val); - g_free (new_val); - - ipt.start = icaltime_from_timet_with_zone (t_val.tv_sec, 0, NULL); - - } else if (!g_ascii_strcasecmp (name, "EndTime")) { - value = e_soap_parameter_get_string_value (subparam); - /*We are sending UTC timezone and expect server to return in same*/ - - /*Remove leading and trailing whitespace*/ - g_strstrip (value); - - if (g_utf8_strlen (value, -1) == 19) { - /*If server returns time without zone add Z to treat it in UTC*/ - new_val = g_strdup_printf ("%sZ", value); - g_free (value); - } else - new_val = value; - - g_time_val_from_iso8601 (new_val, &t_val); - g_free (new_val); - - ipt.end = icaltime_from_timet_with_zone (t_val.tv_sec, 0, NULL); - - icalprop = icalproperty_new_freebusy (ipt); - } else if (!g_ascii_strcasecmp (name, "BusyType")) { - value = e_soap_parameter_get_string_value (subparam); - if (!strcmp (value, "Busy")) - icalproperty_set_parameter_from_string (icalprop, "FBTYPE", "BUSY"); - else if (!strcmp (value, "Tentative")) - icalproperty_set_parameter_from_string (icalprop, "FBTYPE", "BUSY-TENTATIVE"); - else if (!strcmp (value, "OOF")) - icalproperty_set_parameter_from_string (icalprop, "FBTYPE", "BUSY-UNAVAILABLE"); - else if (!strcmp (value, "Free")) - icalproperty_set_parameter_from_string (icalprop, "FBTYPE", "FREE"); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "CalendarEventDetails")) { - ESoapParameter *dparam; - - dparam = e_soap_parameter_get_first_child_by_name (subparam, "ID"); - if (dparam) - id = e_soap_parameter_get_string_value (dparam); - - dparam = e_soap_parameter_get_first_child_by_name (subparam, "Subject"); - if (dparam) - summary = e_soap_parameter_get_string_value (dparam); - - dparam = e_soap_parameter_get_first_child_by_name (subparam, "Location"); - if (dparam) - location = e_soap_parameter_get_string_value (dparam); - } - } - if (icalprop != NULL) { - if (id) - icalproperty_set_parameter_from_string (icalprop, "X-EWS-ID", id); - if (summary) - icalproperty_set_parameter_from_string (icalprop, "X-SUMMARY", summary); - if (location) - icalproperty_set_parameter_from_string (icalprop, "X-LOCATION", location); - icalcomponent_add_property (vfb, icalprop); - } - - g_clear_pointer (&summary, g_free); - g_clear_pointer (&location, g_free); - g_clear_pointer (&id, g_free); - } - - async_data->items = g_slist_append (async_data->items, vfb); -} - -static void -get_free_busy_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "FreeBusyResponseArray", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - ESoapParameter *subsubparam; - - subsubparam = e_soap_parameter_get_first_child_by_name ( - subparam, "ResponseMessage"); - - if (subsubparam) { - if (!ews_get_response_status (subsubparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - ews_handle_free_busy_view (subparam, async_data); - } - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_get_free_busy (EEwsConnection *cnc, - gint pri, - EEwsRequestCreationCallback free_busy_cb, - gpointer free_busy_user_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetUserAvailabilityRequest", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - free_busy_cb (msg, free_busy_user_data); - - e_ews_message_write_footer (msg); /*GetUserAvailabilityRequest */ - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_free_busy); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_free_busy_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_free_busy_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **free_busy, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_free_busy), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - *free_busy = async_data->items; - - return TRUE; -} - -gboolean -e_ews_connection_get_free_busy_sync (EEwsConnection *cnc, - gint pri, - EEwsRequestCreationCallback free_busy_cb, - gpointer free_busy_user_data, - GSList **free_busy, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_free_busy ( - cnc, pri, free_busy_cb, - free_busy_user_data, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_free_busy_finish ( - cnc, result, free_busy, error); - - e_async_closure_free (closure); - - return success; -} - -static EwsPermissionLevel -get_permission_from_string (const gchar *permission) -{ - g_return_val_if_fail (permission != NULL, EwsPermissionLevel_Unknown); - - if (!g_ascii_strcasecmp (permission, "Editor")) - return EwsPermissionLevel_Editor; - else if (!g_ascii_strcasecmp (permission, "Author")) - return EwsPermissionLevel_Author; - else if (!g_ascii_strcasecmp (permission, "Reviewer")) - return EwsPermissionLevel_Reviewer; - else if (!g_ascii_strcasecmp (permission, "Custom")) - return EwsPermissionLevel_Custom; - else - return EwsPermissionLevel_None; - -} - -static void -ews_handle_delegate_user_param (ESoapParameter *param, - EwsAsyncData *async_data) -{ - ESoapParameter *subparam, *node, *child; - EwsDelegateInfo *data; - gchar *value; - - node = e_soap_parameter_get_first_child_by_name (param, "DelegateUser"); - if (!node) - return; - - subparam = e_soap_parameter_get_first_child_by_name (node, "UserId"); - if (!subparam) - return; - - data = g_new0 (EwsDelegateInfo, 1); - data->user_id = g_new0 (EwsUserId, 1); - - /*Parse User Id*/ - - child = e_soap_parameter_get_first_child_by_name (subparam, "SID"); - data->user_id->sid = e_soap_parameter_get_string_value (child); - - child = e_soap_parameter_get_first_child_by_name (subparam, "PrimarySmtpAddress"); - data->user_id->primary_smtp = e_soap_parameter_get_string_value (child); - - child = e_soap_parameter_get_first_child_by_name (subparam, "DisplayName"); - data->user_id->display_name = e_soap_parameter_get_string_value (child); - - subparam = e_soap_parameter_get_first_child_by_name (node, "DelegatePermissions"); - - /*Parse Delegate Permissions*/ - child = e_soap_parameter_get_first_child_by_name (subparam, "CalendarFolderPermissionLevel"); - if (child) { - value = e_soap_parameter_get_string_value (child); - data->calendar = get_permission_from_string (value); - g_free (value); - } - - child = e_soap_parameter_get_first_child_by_name (subparam, "ContactsFolderPermissionLevel"); - if (child) { - value = e_soap_parameter_get_string_value (child); - data->contacts = get_permission_from_string (value); - g_free (value); - } - - child = e_soap_parameter_get_first_child_by_name (subparam, "InboxFolderPermissionLevel"); - if (child) { - value = e_soap_parameter_get_string_value (child); - data->inbox = get_permission_from_string (value); - g_free (value); - } - - child = e_soap_parameter_get_first_child_by_name (subparam, "TasksFolderPermissionLevel"); - if (child) { - value = e_soap_parameter_get_string_value (child); - data->tasks = get_permission_from_string (value); - g_free (value); - } - - child = e_soap_parameter_get_first_child_by_name (subparam, "NotesFolderPermissionLevel"); - if (child) { - value = e_soap_parameter_get_string_value (child); - data->notes = get_permission_from_string (value); - g_free (value); - } - - child = e_soap_parameter_get_first_child_by_name (subparam, "JournalFolderPermissionLevel"); - if (child) { - value = e_soap_parameter_get_string_value (child); - data->journal = get_permission_from_string (value); - g_free (value); - } - - subparam = e_soap_parameter_get_first_child_by_name (node, "ReceiveCopiesOfMeetingMessages"); - if (subparam) { - value = e_soap_parameter_get_string_value (subparam); - data->meetingcopies = g_strcmp0 (value, "true") == 0; - g_free (value); - } - - subparam = e_soap_parameter_get_first_child_by_name (node, "ViewPrivateItems"); - if (subparam) { - value = e_soap_parameter_get_string_value (subparam); - data->view_priv_items = g_strcmp0 (value, "true") == 0; - g_free (value); - } - - async_data->items = g_slist_append (async_data->items, data); -} - -static void -get_delegate_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - gchar *value; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (ews_get_response_status (e_soap_response_get_parameter (response), &error)) - param = e_soap_response_get_first_parameter_by_name ( - response, "DeliverMeetingRequests", &error); - else - param = NULL; - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - value = e_soap_parameter_get_string_value (param); - if (g_strcmp0 (value, "DelegatesOnly") == 0) - async_data->deliver_to = EwsDelegateDeliver_DelegatesOnly; - else if (g_strcmp0 (value, "DelegatesAndMe") == 0) - async_data->deliver_to = EwsDelegateDeliver_DelegatesAndMe; - else if (g_strcmp0 (value, "DelegatesAndSendInformationToMe") == 0) - async_data->deliver_to = EwsDelegateDeliver_DelegatesAndSendInformationToMe; - else { - g_message ("%s: Unknown deliver-to value '%s'", G_STRFUNC, value ? value : "[null]"); - async_data->deliver_to = EwsDelegateDeliver_DelegatesAndSendInformationToMe; - } - g_free (value); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", NULL); - /* it's OK to not have set any delegate */ - if (!param) - return; - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "DelegateUserResponseMessageType")) - ews_handle_delegate_user_param (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_get_delegate (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - gboolean include_permissions, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetDelegate", - "IncludePermissions", - include_permissions ? "true" : "false", - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "Mailbox", "messages", NULL); - - e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, mail_id ? mail_id : cnc->priv->email); - - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_delegate); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_delegate_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_delegate_finish (EEwsConnection *cnc, - GAsyncResult *result, - EwsDelegateDeliver *deliver_to, - GSList **delegates, /* EwsDelegateInfo * */ - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail (delegates != NULL, FALSE); - g_return_val_if_fail (deliver_to != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_delegate), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *deliver_to = async_data->deliver_to; - *delegates = async_data->items; - async_data->items = NULL; - - return TRUE; -} - -gboolean -e_ews_connection_get_delegate_sync (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - gboolean include_permissions, - EwsDelegateDeliver *deliver_to, - GSList **delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (deliver_to != NULL, FALSE); - g_return_val_if_fail (delegates != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_delegate ( - cnc, pri, mail_id, - include_permissions, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_delegate_finish ( - cnc, result, deliver_to, delegates, error); - - e_async_closure_free (closure); - - return success; -} - -static void -update_delegate_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - if (ews_get_response_status (e_soap_response_get_parameter (response), &error)) { - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", NULL); - /* that's OK to not receive any ResponseMessages here */ - if (!param) - return; - } else - param = NULL; - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_next_child (param); - } -} - -static void -set_delegate_permission (ESoapMessage *msg, - const gchar *elem_name, - EwsPermissionLevel perm_level) -{ - const gchar *level_name = NULL; - - if (perm_level == EwsPermissionLevel_None) - level_name = "None"; - else if (perm_level == EwsPermissionLevel_Reviewer) - level_name = "Reviewer"; - else if (perm_level == EwsPermissionLevel_Author) - level_name = "Author"; - else if (perm_level == EwsPermissionLevel_Editor) - level_name = "Editor"; - - if (!level_name) - return; - - e_ews_message_write_string_parameter (msg, elem_name, NULL, level_name); -} - -void -e_ews_connection_add_delegate (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const GSList *delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *iter; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (delegates != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "AddDelegate", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "Mailbox", "messages", NULL); - e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, mail_id ? mail_id : cnc->priv->email); - e_soap_message_end_element (msg); - - e_soap_message_start_element (msg, "DelegateUsers", "messages", NULL); - for (iter = delegates; iter; iter = iter->next) { - const EwsDelegateInfo *di = iter->data; - - if (!di) - continue; - - e_soap_message_start_element (msg, "DelegateUser", NULL, NULL); - - e_soap_message_start_element (msg, "UserId", NULL, NULL); - e_ews_message_write_string_parameter (msg, "PrimarySmtpAddress", NULL, di->user_id->primary_smtp); - e_soap_message_end_element (msg); /* UserId */ - - e_soap_message_start_element (msg, "DelegatePermissions", NULL, NULL); - set_delegate_permission (msg, "CalendarFolderPermissionLevel", di->calendar); - set_delegate_permission (msg, "TasksFolderPermissionLevel", di->tasks); - set_delegate_permission (msg, "InboxFolderPermissionLevel", di->inbox); - set_delegate_permission (msg, "ContactsFolderPermissionLevel", di->contacts); - set_delegate_permission (msg, "NotesFolderPermissionLevel", di->notes); - set_delegate_permission (msg, "JournalFolderPermissionLevel", di->journal); - e_soap_message_end_element (msg); /* DelegatePermissions */ - - e_ews_message_write_string_parameter ( - msg, "ReceiveCopiesOfMeetingMessages", NULL, - di->meetingcopies ? "true" : "false"); - e_ews_message_write_string_parameter ( - msg, "ViewPrivateItems", NULL, - di->view_priv_items ? "true" : "false"); - - e_soap_message_end_element (msg); /* DelegateUser */ - } - - e_soap_message_end_element (msg); /* DelegateUsers */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_add_delegate); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, update_delegate_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_add_delegate_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_add_delegate), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - return !g_simple_async_result_propagate_error (simple, error); -} - -gboolean -e_ews_connection_add_delegate_sync (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const GSList *delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (delegates != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_add_delegate ( - cnc, pri, mail_id, delegates, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_add_delegate_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_remove_delegate (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const GSList *delegate_ids, /* EwsUserId * */ - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *iter; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (delegate_ids != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "RemoveDelegate", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "Mailbox", "messages", NULL); - e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, mail_id ? mail_id : cnc->priv->email); - e_soap_message_end_element (msg); - - e_soap_message_start_element (msg, "UserIds", "messages", NULL); - for (iter = delegate_ids; iter; iter = iter->next) { - const EwsUserId *user_id = iter->data; - - if (!user_id) - continue; - - e_soap_message_start_element (msg, "UserId", NULL, NULL); - e_ews_message_write_string_parameter (msg, "PrimarySmtpAddress", NULL, user_id->primary_smtp); - e_soap_message_end_element (msg); /* UserId */ - } - - e_soap_message_end_element (msg); /* UserIds */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_remove_delegate); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, update_delegate_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_remove_delegate_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_remove_delegate), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - return !g_simple_async_result_propagate_error (simple, error); -} - -gboolean -e_ews_connection_remove_delegate_sync (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const GSList *delegate_ids, /* EwsUserId * */ - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (delegate_ids != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_remove_delegate ( - cnc, pri, mail_id, delegate_ids, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_remove_delegate_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_update_delegate (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - EwsDelegateDeliver deliver_to, - const GSList *delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *iter; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "UpdateDelegate", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "Mailbox", "messages", NULL); - e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, mail_id ? mail_id : cnc->priv->email); - e_soap_message_end_element (msg); - - if (delegates) { - e_soap_message_start_element (msg, "DelegateUsers", "messages", NULL); - for (iter = delegates; iter; iter = iter->next) { - const EwsDelegateInfo *di = iter->data; - - if (!di) - continue; - - e_soap_message_start_element (msg, "DelegateUser", NULL, NULL); - - e_soap_message_start_element (msg, "UserId", NULL, NULL); - e_ews_message_write_string_parameter (msg, "PrimarySmtpAddress", NULL, di->user_id->primary_smtp); - e_soap_message_end_element (msg); /* UserId */ - - e_soap_message_start_element (msg, "DelegatePermissions", NULL, NULL); - set_delegate_permission (msg, "CalendarFolderPermissionLevel", di->calendar); - set_delegate_permission (msg, "TasksFolderPermissionLevel", di->tasks); - set_delegate_permission (msg, "InboxFolderPermissionLevel", di->inbox); - set_delegate_permission (msg, "ContactsFolderPermissionLevel", di->contacts); - set_delegate_permission (msg, "NotesFolderPermissionLevel", di->notes); - set_delegate_permission (msg, "JournalFolderPermissionLevel", di->journal); - e_soap_message_end_element (msg); /* DelegatePermissions */ - - e_ews_message_write_string_parameter ( - msg, "ReceiveCopiesOfMeetingMessages", NULL, - di->meetingcopies ? "true" : "false"); - e_ews_message_write_string_parameter ( - msg, "ViewPrivateItems", NULL, - di->view_priv_items ? "true" : "false"); - - e_soap_message_end_element (msg); /* DelegateUser */ - } - - e_soap_message_end_element (msg); /* DelegateUsers */ - } - - e_ews_message_write_string_parameter ( - msg, "DeliverMeetingRequests", "messages", - deliver_to == EwsDelegateDeliver_DelegatesOnly ? "DelegatesOnly" : - deliver_to == EwsDelegateDeliver_DelegatesAndMe ? "DelegatesAndMe" : - "DelegatesAndSendInformationToMe"); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_update_delegate); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, update_delegate_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_update_delegate_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_update_delegate), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - return !g_simple_async_result_propagate_error (simple, error); -} - -gboolean -e_ews_connection_update_delegate_sync (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - EwsDelegateDeliver deliver_to, - const GSList *delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_update_delegate ( - cnc, pri, mail_id, deliver_to, delegates, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_update_delegate_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -static void -get_folder_permissions_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "GetFolderResponseMessage")) { - ESoapParameter *node; - - node = e_soap_parameter_get_first_child_by_name (subparam, "Folders"); - if (node) { - subparam = node; - - node = e_soap_parameter_get_first_child (subparam); - if (node && node->name && g_str_has_suffix ((const gchar *) node->name, "Folder")) { - node = e_soap_parameter_get_first_child_by_name (node, "PermissionSet"); - if (node) { - async_data->items = e_ews_permissions_from_soap_param (node); - } - } - } - - break; - } - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_get_folder_permissions (EEwsConnection *cnc, - gint pri, - EwsFolderId *folder_id, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (folder_id != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetFolder", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "FolderShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, "IdOnly"); - e_soap_message_start_element (msg, "AdditionalProperties", NULL, NULL); - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", "folder:PermissionSet"); - e_soap_message_end_element (msg); /* AdditionalProperties */ - e_soap_message_end_element (msg); /* FolderShape */ - - e_soap_message_start_element (msg, "FolderIds", "messages", NULL); - ews_append_folder_id_to_msg (msg, cnc->priv->email, folder_id); - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_folder_permissions); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_folder_permissions_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -/* free permissions with e_ews_permissions_free() */ -gboolean -e_ews_connection_get_folder_permissions_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **permissions, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail (permissions != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_folder_permissions), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *permissions = async_data->items; - - return TRUE; -} - -/* free permissions with e_ews_permissions_free() */ -gboolean -e_ews_connection_get_folder_permissions_sync (EEwsConnection *cnc, - gint pri, - EwsFolderId *folder_id, - GSList **permissions, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (folder_id != NULL, FALSE); - g_return_val_if_fail (permissions != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_folder_permissions ( - cnc, pri, folder_id, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_folder_permissions_finish ( - cnc, result, permissions, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_set_folder_permissions (EEwsConnection *cnc, - gint pri, - EwsFolderId *folder_id, - EEwsFolderType folder_type, - const GSList *permissions, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *iter; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (folder_id != NULL); - g_return_if_fail (permissions != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "UpdateFolder", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "FolderChanges", "messages", NULL); - e_ews_message_start_item_change ( - msg, E_EWS_ITEMCHANGE_TYPE_FOLDER, - folder_id->id, folder_id->change_key, 0); - - e_soap_message_start_element (msg, "SetFolderField", NULL, NULL); - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", "folder:PermissionSet"); - - switch (folder_type) { - default: - case E_EWS_FOLDER_TYPE_MAILBOX: - e_soap_message_start_element (msg, "Folder", NULL, NULL); - break; - case E_EWS_FOLDER_TYPE_CALENDAR: - e_soap_message_start_element (msg, "CalendarFolder", NULL, NULL); - break; - case E_EWS_FOLDER_TYPE_CONTACTS: - e_soap_message_start_element (msg, "ContactsFolder", NULL, NULL); - break; - case E_EWS_FOLDER_TYPE_SEARCH: - e_soap_message_start_element (msg, "SearchFolder", NULL, NULL); - break; - case E_EWS_FOLDER_TYPE_TASKS: - e_soap_message_start_element (msg, "TasksFolder", NULL, NULL); - break; - } - - e_soap_message_start_element (msg, "PermissionSet", NULL, NULL); - if (folder_type == E_EWS_FOLDER_TYPE_CALENDAR) - e_soap_message_start_element (msg, "CalendarPermissions", NULL, NULL); - else - e_soap_message_start_element (msg, "Permissions", NULL, NULL); - - for (iter = permissions; iter; iter = iter->next) { - EEwsPermission *perm = iter->data; - const gchar *perm_level_name; - - if (!perm) - continue; - - if (folder_type == E_EWS_FOLDER_TYPE_CALENDAR) - e_soap_message_start_element (msg, "CalendarPermission", NULL, NULL); - else - e_soap_message_start_element (msg, "Permission", NULL, NULL); - - e_soap_message_start_element (msg, "UserId", NULL, NULL); - - switch (perm->user_type) { - case E_EWS_PERMISSION_USER_TYPE_NONE: - g_return_if_reached (); - break; - case E_EWS_PERMISSION_USER_TYPE_ANONYMOUS: - e_ews_message_write_string_parameter (msg, "DistinguishedUser", NULL, "Anonymous"); - break; - case E_EWS_PERMISSION_USER_TYPE_DEFAULT: - e_ews_message_write_string_parameter (msg, "DistinguishedUser", NULL, "Default"); - break; - case E_EWS_PERMISSION_USER_TYPE_REGULAR: - e_ews_message_write_string_parameter (msg, "PrimarySmtpAddress", NULL, perm->primary_smtp); - break; - } - - e_soap_message_end_element (msg); /* UserId */ - - e_ews_permission_rights_to_level_name (perm->rights); - - perm_level_name = e_ews_permission_rights_to_level_name (perm->rights); - - if (g_strcmp0 (perm_level_name, "Custom") == 0) { - e_ews_message_write_string_parameter ( - msg, "CanCreateItems", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_CREATE) != 0 ? "true" : "false"); - e_ews_message_write_string_parameter ( - msg, "CanCreateSubFolders", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_CREATE_SUBFOLDER) != 0 ? "true" : "false"); - e_ews_message_write_string_parameter ( - msg, "IsFolderOwner", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_FOLDER_OWNER) != 0 ? "true" : "false"); - e_ews_message_write_string_parameter ( - msg, "IsFolderVisible", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_FOLDER_VISIBLE) != 0 ? "true" : "false"); - e_ews_message_write_string_parameter ( - msg, "IsFolderContact", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_FOLDER_CONTACT) != 0 ? "true" : "false"); - e_ews_message_write_string_parameter ( - msg, "EditItems", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_EDIT_ANY) != 0 ? "All" : - (perm->rights & E_EWS_PERMISSION_BIT_EDIT_OWNED) != 0 ? "Owned" : "None"); - e_ews_message_write_string_parameter ( - msg, "DeleteItems", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_DELETE_ANY) != 0 ? "All" : - (perm->rights & E_EWS_PERMISSION_BIT_DELETE_OWNED) != 0 ? "Owned" : "None"); - if (folder_type == E_EWS_FOLDER_TYPE_CALENDAR) - e_ews_message_write_string_parameter ( - msg, "ReadItems", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_READ_ANY) != 0 ? "FullDetails" : - (perm->rights & E_EWS_PERMISSION_BIT_FREE_BUSY_DETAILED) != 0 ? "TimeAndSubjectAndLocation" : - (perm->rights & E_EWS_PERMISSION_BIT_FREE_BUSY_SIMPLE) != 0 ? "TimeOnly" : "None"); - else - e_ews_message_write_string_parameter ( - msg, "ReadItems", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_READ_ANY) != 0 ? "FullDetails" : "None"); - } - - e_ews_message_write_string_parameter ( - msg, - folder_type == E_EWS_FOLDER_TYPE_CALENDAR ? "CalendarPermissionLevel" : "PermissionLevel", NULL, - perm_level_name); - - e_soap_message_end_element (msg); /* Permission/CalendarPermission */ - } - - e_soap_message_end_element (msg); /* Permissions */ - e_soap_message_end_element (msg); /* PermissionSet */ - e_soap_message_end_element (msg); /* Folder/CalendarFolder/... */ - e_soap_message_end_element (msg); /* SetFolderField */ - - e_ews_message_end_item_change (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_set_folder_permissions); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, update_folder_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_set_folder_permissions_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_set_folder_permissions), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - return !g_simple_async_result_propagate_error (simple, error); -} - -gboolean -e_ews_connection_set_folder_permissions_sync (EEwsConnection *cnc, - gint pri, - EwsFolderId *folder_id, - EEwsFolderType folder_type, - const GSList *permissions, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (folder_id != NULL, FALSE); - g_return_val_if_fail (permissions != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_set_folder_permissions ( - cnc, pri, folder_id, folder_type, permissions, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_set_folder_permissions_finish ( - cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -static void -get_password_expiration_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - gchar *exp_date; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "PasswordExpirationDate", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - exp_date = e_soap_parameter_get_string_value (param); - - async_data->items = g_slist_append (async_data->items, exp_date); -} - -/** - * e_ews_connection_get_password_expiration - * @cnc: - * @pri: - * @mail_id: mail is for which password expiration is requested - * @cb: - * @cancellable: - * @user_data: - **/ -void -e_ews_connection_get_password_expiration (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetPasswordExpirationDate", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2010_SP2, - FALSE, - TRUE); - e_ews_message_write_string_parameter (msg, "MailboxSmtpAddress", NULL, mail_id ? mail_id : cnc->priv->email); - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_password_expiration); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_password_expiration_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_password_expiration_finish (EEwsConnection *cnc, - GAsyncResult *result, - gchar **exp_date, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (exp_date != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_password_expiration), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = 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_data->items != NULL, FALSE); - - *exp_date = async_data->items->data; - g_slist_free (async_data->items); - - return TRUE; -} - -/** - * e_ews_connection_get_password_expiration_sync - * @cnc: - * @pri: - * @mail_id: mail id for which password expiration is requested - * @cancellable: - * @error: - **/ -gboolean -e_ews_connection_get_password_expiration_sync (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - gchar **exp_date, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (exp_date != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_password_expiration ( - cnc, pri, mail_id, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_password_expiration_finish ( - cnc, result, exp_date, error); - - e_async_closure_free (closure); - - return success; -} - -static void -get_folder_info_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "GetFolderResponseMessage")) { - ESoapParameter *node; - - node = e_soap_parameter_get_first_child_by_name (subparam, "Folders"); - if (node) { - EEwsFolder *folder = e_ews_folder_new_from_soap_parameter (node); - - if (folder) - async_data->items = g_slist_prepend (NULL, folder); - } - - break; - } - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_get_folder_info (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const EwsFolderId *folder_id, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (folder_id != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetFolder", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "FolderShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, "Default"); - e_soap_message_start_element (msg, "AdditionalProperties", NULL, NULL); - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", "folder:FolderClass"); - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", "folder:ParentFolderId"); - e_soap_message_end_element (msg); /* AdditionalProperties */ - e_soap_message_end_element (msg); /* FolderShape */ - - e_soap_message_start_element (msg, "FolderIds", "messages", NULL); - ews_append_folder_id_to_msg (msg, mail_id, folder_id); - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_folder_info); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_folder_info_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_folder_info_finish (EEwsConnection *cnc, - GAsyncResult *result, - EEwsFolder **folder, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail (folder != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_folder_info), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (!async_data->items) - return FALSE; - - *folder = async_data->items ? async_data->items->data : NULL; - - g_slist_free (async_data->items); - async_data->items = NULL; - - return TRUE; -} - -gboolean -e_ews_connection_get_folder_info_sync (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const EwsFolderId *folder_id, - EEwsFolder **folder, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (folder_id != NULL, FALSE); - g_return_val_if_fail (folder != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_folder_info ( - cnc, pri, mail_id, folder_id, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_folder_info_finish ( - cnc, result, folder, error); - - e_async_closure_free (closure); - - return success; -} - -static void -ews_handle_root_folder_param_folders (ESoapParameter *subparam, - EwsAsyncData *async_data) -{ - ESoapParameter *node, *subparam1; - gchar *last, *total; - gint total_items; - EEwsFolder *folder; - gboolean includes_last_item; - - node = e_soap_parameter_get_first_child_by_name (subparam, "RootFolder"); - total = e_soap_parameter_get_property (node, "TotalItemsInView"); - total_items = atoi (total); - g_free (total); - last = e_soap_parameter_get_property (node, "IncludesLastItemInRange"); - /* - * Set the includes_last_item to TRUE as default. - * It can avoid an infinite loop in caller, when, for some reason, - * we don't receive the last_tag property from the server. - */ - includes_last_item = g_strcmp0 (last, "false") != 0; - g_free (last); - - node = e_soap_parameter_get_first_child_by_name (node, "Folders"); - for (subparam1 = e_soap_parameter_get_first_child (node); - subparam1; subparam1 = e_soap_parameter_get_next_child (subparam1)) { - folder = e_ews_folder_new_from_soap_parameter (subparam1); - if (!folder) continue; - async_data->items = g_slist_append (async_data->items, folder); - } - async_data->total_items = total_items; - async_data->includes_last_item = includes_last_item; -} - -static void -find_folder_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "FindFolderResponseMessage")) - ews_handle_root_folder_param_folders (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_find_folder (EEwsConnection *cnc, - gint pri, - const EwsFolderId *fid, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "FindFolder", - "Traversal", - "Shallow", - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - e_soap_message_start_element (msg, "FolderShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, "Default"); - e_soap_message_start_element (msg, "AdditionalProperties", NULL, NULL); - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", "folder:FolderClass"); - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", "folder:ChildFolderCount"); - e_soap_message_end_element (msg); /* AdditionalProperties */ - e_soap_message_end_element (msg); - - e_soap_message_start_element (msg, "ParentFolderIds", "messages", NULL); - - if (fid->is_distinguished_id) - e_ews_message_write_string_parameter_with_attribute (msg, "DistinguishedFolderId", NULL, NULL, "Id", fid->id); - else - e_ews_message_write_string_parameter_with_attribute (msg, "FolderId", NULL, NULL, "Id", fid->id); - - e_soap_message_end_element (msg); - - /* Complete the footer and print the request */ - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_find_folder); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, find_folder_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_find_folder_finish (EEwsConnection *cnc, - GAsyncResult *result, - gboolean *includes_last_item, - GSList **folders, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_find_folder), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *includes_last_item = async_data->includes_last_item; - *folders = async_data->items; - - return TRUE; -} - -gboolean -e_ews_connection_find_folder_sync (EEwsConnection *cnc, - gint pri, - const EwsFolderId *fid, - gboolean *includes_last_item, - GSList **folders, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_find_folder (cnc, pri, fid, cancellable, e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_find_folder_finish ( - cnc, result, includes_last_item, folders, error); - - e_async_closure_free (closure); - - return success; -} - -#define EWS_OBJECT_KEY_AUTHS_GATHERED "ews-auths-gathered" - -static void -query_auth_methods_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - ESoapParameter *param; - GError *error = NULL; - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - /* nothing to read, it should not get this far anyway */ -} - -static void -ews_connection_gather_auth_methods_cb (SoupMessage *message, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - const gchar *auths_lst; - gboolean has_bearer = FALSE; - gchar **auths; - gint ii; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - g_return_if_fail (async_data != NULL); - - auths_lst = soup_message_headers_get_list (message->response_headers, "WWW-Authenticate"); - if (!auths_lst) - return; - - auths = g_strsplit (auths_lst, ",", -1); - for (ii = 0; auths && auths[ii]; ii++) { - gchar *auth, *space; - - auth = g_strstrip (g_strdup (auths[ii])); - if (auth && *auth) { - space = strchr (auth, ' '); - if (space) - *space = '\0'; - - has_bearer = has_bearer || g_ascii_strcasecmp (auth, "Bearer") == 0; - async_data->items = g_slist_prepend (async_data->items, auth); - } else { - g_free (auth); - } - } - - g_strfreev (auths); - - if (!has_bearer) { - /* Special-case Office365 OAuth2, because outlook.office365.com doesn't advertise Bearer */ - SoupURI *suri; - - suri = soup_message_get_uri (message); - if (suri && soup_uri_get_host (suri) && - g_ascii_strcasecmp (soup_uri_get_host (suri), "outlook.office365.com") == 0) { - async_data->items = g_slist_prepend (async_data->items, g_strdup ("Bearer")); - } - } - - g_object_set_data (G_OBJECT (simple), EWS_OBJECT_KEY_AUTHS_GATHERED, GINT_TO_POINTER (1)); - - soup_message_set_status_full (message, SOUP_STATUS_CANCELLED, "EWS auths gathered"); -} - -/* Note: This only works if the connection is not connected yet */ -void -e_ews_connection_query_auth_methods (EEwsConnection *cnc, - gint pri, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - /* use some simple operation to get WWW-Authenticate headers from the server */ - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetFolder", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - TRUE, - TRUE); - - e_soap_message_start_element (msg, "FolderShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, "IdOnly"); - e_soap_message_end_element (msg); - - e_soap_message_start_element (msg, "FolderIds", "messages", NULL); - e_ews_message_write_string_parameter_with_attribute (msg, "DistinguishedFolderId", NULL, NULL, "Id", "inbox"); - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_query_auth_methods); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - soup_message_add_header_handler (SOUP_MESSAGE (msg), "got-headers", "WWW-Authenticate", - G_CALLBACK (ews_connection_gather_auth_methods_cb), simple); - - e_ews_connection_queue_request ( - cnc, msg, query_auth_methods_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_query_auth_methods_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **auth_methods, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail (auth_methods != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_query_auth_methods), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (simple), EWS_OBJECT_KEY_AUTHS_GATHERED)) != 1 && - g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *auth_methods = g_slist_reverse (async_data->items); - - return TRUE; -} - -/* Note: This only works if the connection is not connected yet */ -gboolean -e_ews_connection_query_auth_methods_sync (EEwsConnection *cnc, - gint pri, - GSList **auth_methods, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_query_auth_methods (cnc, pri, cancellable, e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_query_auth_methods_finish ( - cnc, result, auth_methods, error); - - e_async_closure_free (closure); - - return success; -} - -static void -ews_connection_build_subscribed_folders_list (gpointer key, - gpointer value, - gpointer user_data) -{ - GSList *folders = value, *l; - EEwsConnection *cnc = user_data; - - for (l = folders; l != NULL; l = l->next) { - if (g_slist_find_custom (cnc->priv->subscribed_folders, l->data, (GCompareFunc) g_strcmp0) == NULL) { - cnc->priv->subscribed_folders = - g_slist_prepend (cnc->priv->subscribed_folders, g_strdup (l->data)); - } - } -} - -/* - * Enables server notification on a folder (or a set of folders). - * The events we are listen for notifications are: Copied, Created, Deleted, Modified and Moved. - * - * As we have only one subscription per connection, for every enable_notifications_sync() call, - * we do: - * - Check if we already are subscribed - * - If we are already subscribed: - * - Stop to send events regards to the already subscribed folders' list - * - Unsubscribe the already subscribed folders' list - * - Add the user folders' lst to the hash table if subscribed folders - * - Create a new list of the folders to subscribe for, based in the hash table of subscribed folders - * - Subscribed to listen notifications for the folders - * - Start to send events regards to the newly subscribed folders' list - * - * Pair function for this one is e_ews_connection_disable_notifications_sync() and we do - * something really similar for every disable_notifications_sync() call. - * - * The notification is received to the caller with the "server-notification" signal. - * Note that the signal is used for each notification, without distinction on the - * enabled object. - */ -void -e_ews_connection_enable_notifications_sync (EEwsConnection *cnc, - GSList *folders, - guint *subscription_key) -{ - GSList *new_folders = NULL, *l; - gint subscriptions_size; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (cnc->priv != NULL); - g_return_if_fail (cnc->priv->version >= E_EWS_EXCHANGE_2010_SP1); - g_return_if_fail (folders != NULL); - - NOTIFICATION_LOCK (cnc); - subscriptions_size = g_hash_table_size (cnc->priv->subscriptions); - - if (subscriptions_size == G_MAXUINT - 1) - goto exit; - - if (subscriptions_size > 0) { - e_ews_notification_stop_listening_sync (cnc->priv->notification); - - g_clear_object (&cnc->priv->notification); - - g_slist_free_full (cnc->priv->subscribed_folders, g_free); - cnc->priv->subscribed_folders = NULL; - } - - while (g_hash_table_contains (cnc->priv->subscriptions, GINT_TO_POINTER (notification_key))) { - notification_key++; - if (notification_key == 0) - notification_key++; - } - - for (l = folders; l != NULL; l = l->next) - new_folders = g_slist_prepend (new_folders, g_strdup (l->data)); - - g_hash_table_insert (cnc->priv->subscriptions, GINT_TO_POINTER (notification_key), new_folders); - new_folders = NULL; - - g_hash_table_foreach (cnc->priv->subscriptions, ews_connection_build_subscribed_folders_list, cnc); - - cnc->priv->notification = e_ews_notification_new (cnc); - - e_ews_notification_start_listening_sync (cnc->priv->notification, cnc->priv->subscribed_folders); - -exit: - *subscription_key = notification_key; - notification_key++; - if (notification_key == 0) - notification_key++; - - NOTIFICATION_UNLOCK (cnc); -} - -void -e_ews_connection_disable_notifications_sync (EEwsConnection *cnc, - guint subscription_key) -{ - g_return_if_fail (cnc != NULL); - g_return_if_fail (cnc->priv != NULL); - - NOTIFICATION_LOCK (cnc); - if (cnc->priv->notification == NULL) - goto exit; - - if (!g_hash_table_remove (cnc->priv->subscriptions, GINT_TO_POINTER (subscription_key))) - goto exit; - - e_ews_notification_stop_listening_sync (cnc->priv->notification); - - g_slist_free_full (cnc->priv->subscribed_folders, g_free); - cnc->priv->subscribed_folders = NULL; - - g_hash_table_foreach (cnc->priv->subscriptions, ews_connection_build_subscribed_folders_list, cnc); - if (cnc->priv->subscribed_folders != NULL) { - e_ews_notification_start_listening_sync (cnc->priv->notification, cnc->priv->subscribed_folders); - } else { - g_clear_object (&cnc->priv->notification); - } - -exit: - NOTIFICATION_UNLOCK (cnc); -} - -static EEwsCalendarTo * -ews_get_to (ESoapParameter *node) -{ - EEwsCalendarTo *to = NULL; - ESoapParameter *param; - gchar *kind = NULL; - gchar *value = NULL; - gboolean success = FALSE; - - param = e_soap_parameter_get_first_child_by_name (node, "To"); - if (param == NULL) - goto exit; - - kind = e_soap_parameter_get_property (param, "Kind"); - if (kind == NULL) - goto exit; - - value = e_soap_parameter_get_string_value (param); - if (value == NULL) - goto exit; - - success = TRUE; - -exit: - if (success) { - to = e_ews_calendar_to_new (); - to->kind = kind; - to->value = value; - } else { - g_free (kind); - g_free (value); - } - - return to; -} - -static EEwsCalendarPeriod * -ews_get_period (ESoapParameter *node) -{ - EEwsCalendarPeriod *period = NULL; - gchar *bias = NULL; - gchar *name = NULL; - gchar *id = NULL; - - bias = e_soap_parameter_get_property (node, "Bias"); - name = e_soap_parameter_get_property (node, "Name"); - id = e_soap_parameter_get_property (node, "Id"); - - if (bias == NULL || name == NULL || id == NULL) { - g_free (bias); - g_free (name); - g_free (id); - - return NULL; - } - - period = e_ews_calendar_period_new (); - period->bias = bias; - period->name = name; - period->id = id; - - return period; -} - -static GSList * -ews_get_periods_list (ESoapParameter *node) -{ - ESoapParameter *param; - GSList *periods = NULL; - - for (param = e_soap_parameter_get_first_child_by_name (node, "Period"); - param != NULL; - param = e_soap_parameter_get_next_child_by_name (param, "Period")) { - EEwsCalendarPeriod *period; - - period = ews_get_period (param); - if (period != NULL) { - periods = g_slist_prepend (periods, period); - } else { - g_slist_free_full (periods, (GDestroyNotify) e_ews_calendar_period_free); - return NULL; - } - } - - periods = g_slist_reverse (periods); - return periods; -} - -static EEwsCalendarAbsoluteDateTransition * -ews_get_absolute_date_transition (ESoapParameter *node) -{ - ESoapParameter *param; - EEwsCalendarAbsoluteDateTransition *absolute_date_transition = NULL; - EEwsCalendarTo *to = NULL; - gchar *date_time = NULL; - gboolean success = FALSE; - - param = e_soap_parameter_get_first_child_by_name (node, "To"); - if (param != NULL) - to = ews_get_to (param); - - if (to == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "DateTime"); - if (param != NULL) - date_time = e_soap_parameter_get_string_value (param); - - if (date_time == NULL) - goto exit; - - success = TRUE; - -exit: - if (success) { - absolute_date_transition = e_ews_calendar_absolute_date_transition_new (); - absolute_date_transition->to = to; - absolute_date_transition->date_time = date_time; - } else { - e_ews_calendar_to_free (to); - g_free (date_time); - } - - return absolute_date_transition; -} - -static EEwsCalendarRecurringDateTransition * -ews_get_recurring_date_transition (ESoapParameter *node) -{ - ESoapParameter *param; - EEwsCalendarRecurringDateTransition *recurring_date_transition = NULL; - EEwsCalendarTo *to = NULL; - gchar *time_offset = NULL; - gchar *month = NULL; - gchar *day = NULL; - gboolean success = FALSE; - - to = ews_get_to (node); - if (to == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "TimeOffset"); - if (param != NULL) - time_offset = e_soap_parameter_get_string_value (param); - - if (time_offset == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "Month"); - if (param != NULL) - month = e_soap_parameter_get_string_value (param); - - if (month == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "Day"); - if (param != NULL) - day = e_soap_parameter_get_string_value (param); - - if (day == NULL) - goto exit; - - success = TRUE; - -exit: - if (success) { - recurring_date_transition = e_ews_calendar_recurring_date_transition_new (); - recurring_date_transition->to = to; - recurring_date_transition->time_offset = time_offset; - recurring_date_transition->month = month; - recurring_date_transition->day = day; - } else { - e_ews_calendar_to_free (to); - g_free (time_offset); - g_free (month); - g_free (day); - } - - return recurring_date_transition; -} - -static EEwsCalendarRecurringDayTransition * -ews_get_recurring_day_transition (ESoapParameter *node) -{ - ESoapParameter *param; - EEwsCalendarRecurringDayTransition *recurring_day_transition = NULL; - EEwsCalendarTo *to = NULL; - gchar *time_offset = NULL; - gchar *month = NULL; - gchar *day_of_week = NULL; - gchar *occurrence = NULL; - gboolean success = FALSE; - - to = ews_get_to (node); - if (to == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "TimeOffset"); - if (param != NULL) - time_offset = e_soap_parameter_get_string_value (param); - - if (time_offset == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "Month"); - if (param != NULL) - month = e_soap_parameter_get_string_value (param); - - if (month == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "DayOfWeek"); - if (param != NULL) - day_of_week = e_soap_parameter_get_string_value (param); - - if (day_of_week == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "Occurrence"); - if (param != NULL) - occurrence = e_soap_parameter_get_string_value (param); - - if (occurrence == NULL) - goto exit; - - success = TRUE; - -exit: - if (success) { - recurring_day_transition = e_ews_calendar_recurring_day_transition_new (); - recurring_day_transition->to = to; - recurring_day_transition->time_offset = time_offset; - recurring_day_transition->month = month; - recurring_day_transition->day_of_week = day_of_week; - recurring_day_transition->occurrence = occurrence; - } else { - e_ews_calendar_to_free (to); - g_free (time_offset); - g_free (month); - g_free (day_of_week); - g_free (occurrence); - } - - return recurring_day_transition; -} - -static GSList * -ews_get_absolute_date_transitions_list (ESoapParameter *node) -{ - ESoapParameter *param; - GSList *absolute_date_transitions = NULL; - - for (param = e_soap_parameter_get_first_child_by_name (node, "AbsoluteDateTransition"); - param != NULL; - param = e_soap_parameter_get_next_child_by_name (param, "AbsoluteDateTransition")) { - EEwsCalendarAbsoluteDateTransition *absolute_date_transition; - - absolute_date_transition = ews_get_absolute_date_transition (param); - if (absolute_date_transition != NULL) { - absolute_date_transitions = - g_slist_prepend (absolute_date_transitions, absolute_date_transition); - } else { - g_slist_free_full ( - absolute_date_transitions, - (GDestroyNotify) e_ews_calendar_absolute_date_transition_free); - return NULL; - } - } - - absolute_date_transitions = g_slist_reverse (absolute_date_transitions); - return absolute_date_transitions; -} - -static GSList * -ews_get_recurring_day_transitions_list (ESoapParameter *node) -{ - ESoapParameter *param; - GSList *recurring_day_transitions = NULL; - - for (param = e_soap_parameter_get_first_child_by_name (node, "RecurringDayTransition"); - param != NULL; - param = e_soap_parameter_get_next_child_by_name (param, "RecurringDayTransition")) { - EEwsCalendarRecurringDayTransition *recurring_day_transition; - - recurring_day_transition = ews_get_recurring_day_transition (param); - if (recurring_day_transition != NULL) { - recurring_day_transitions = - g_slist_prepend (recurring_day_transitions, recurring_day_transition); - } else { - g_slist_free_full ( - recurring_day_transitions, - (GDestroyNotify) e_ews_calendar_recurring_day_transition_free); - return NULL; - } - } - - recurring_day_transitions = g_slist_reverse (recurring_day_transitions); - return recurring_day_transitions; -} - -static GSList * -ews_get_recurring_date_transitions_list (ESoapParameter *node) -{ - ESoapParameter *param; - GSList *recurring_date_transitions = NULL; - - for (param = e_soap_parameter_get_first_child_by_name (node, "RecurringDateTransition"); - param != NULL; - param = e_soap_parameter_get_next_child_by_name (param, "RecurringDateTransition")) { - EEwsCalendarRecurringDateTransition *recurring_date_transition; - - recurring_date_transition = ews_get_recurring_date_transition (param); - if (recurring_date_transition != NULL) { - recurring_date_transitions = - g_slist_prepend (recurring_date_transitions, recurring_date_transition); - } else { - g_slist_free_full ( - recurring_date_transitions, - (GDestroyNotify) e_ews_calendar_recurring_date_transition_free); - return NULL; - } - } - - recurring_date_transitions = g_slist_reverse (recurring_date_transitions); - return recurring_date_transitions; -} - -static EEwsCalendarTransitionsGroup * -ews_get_transitions_group (ESoapParameter *node) -{ - EEwsCalendarTransitionsGroup *tg = NULL; - EEwsCalendarTo *transition = NULL; - ESoapParameter *param = NULL; - gchar *id = NULL; - GSList *absolute_date_transitions = NULL; - GSList *recurring_date_transitions = NULL; - GSList *recurring_day_transitions = NULL; - - id = e_soap_parameter_get_property (node, "Id"); - if (id == NULL) - return NULL; - - param = e_soap_parameter_get_first_child_by_name (node, "Transition"); - if (param != NULL) - transition = ews_get_to (param); - - absolute_date_transitions = ews_get_absolute_date_transitions_list (node); - recurring_date_transitions = ews_get_recurring_date_transitions_list (node); - recurring_day_transitions = ews_get_recurring_day_transitions_list (node); - - tg = e_ews_calendar_transitions_group_new (); - tg->id = id; - tg->transition = transition; - tg->absolute_date_transitions = absolute_date_transitions; - tg->recurring_date_transitions = recurring_date_transitions; - tg->recurring_day_transitions = recurring_day_transitions; - - return tg; -} - -static GSList * -ews_get_transitions_groups_list (ESoapParameter *node) -{ - ESoapParameter *param; - GSList *transitions_groups = NULL; - - for (param = e_soap_parameter_get_first_child_by_name (node, "TransitionsGroup"); - param != NULL; - param = e_soap_parameter_get_next_child_by_name (param, "TransitionsGroup")) { - EEwsCalendarTransitionsGroup *tg; - - tg = ews_get_transitions_group (param); - if (tg != NULL) { - transitions_groups = g_slist_prepend (transitions_groups, tg); - } else { - g_slist_free_full (transitions_groups, (GDestroyNotify) e_ews_calendar_transitions_group_free); - return NULL; - } - } - - transitions_groups = g_slist_reverse (transitions_groups); - return transitions_groups; -} - -static EEwsCalendarTransitions * -ews_get_transitions (ESoapParameter *node) -{ - ESoapParameter *param; - EEwsCalendarTransitions *transitions = NULL; - EEwsCalendarTo *transition = NULL; - GSList *absolute_date_transitions = NULL; - GSList *recurring_date_transitions = NULL; - GSList *recurring_day_transitions = NULL; - - param = e_soap_parameter_get_first_child_by_name (node, "Transition"); - if (param != NULL) - transition = ews_get_to (param); - - if (transition == NULL) - return NULL; - - absolute_date_transitions = ews_get_absolute_date_transitions_list (node); - recurring_day_transitions = ews_get_recurring_day_transitions_list (node); - recurring_date_transitions = ews_get_recurring_date_transitions_list (node); - - transitions = e_ews_calendar_transitions_new (); - transitions->transition = transition; - transitions->absolute_date_transitions = absolute_date_transitions; - transitions->recurring_day_transitions = recurring_day_transitions; - transitions->recurring_date_transitions = recurring_date_transitions; - - return transitions; -} - -static EEwsCalendarTimeZoneDefinition * -ews_get_time_zone_definition (ESoapParameter *node) -{ - ESoapParameter *param; - gchar *name = NULL; - gchar *id = NULL; - GSList *periods = NULL; - GSList *transitions_groups = NULL; - EEwsCalendarTransitions *transitions = NULL; - EEwsCalendarTimeZoneDefinition *tzd = NULL; - gboolean success = FALSE; - - name = e_soap_parameter_get_property (node, "Name"); - if (name == NULL) - goto exit; - - id = e_soap_parameter_get_property (node, "Id"); - if (id == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "Periods"); - if (param != NULL) - periods = ews_get_periods_list (param); - if (periods == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "TransitionsGroups"); - if (param != NULL) - transitions_groups = ews_get_transitions_groups_list (param); - if (transitions_groups == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "Transitions"); - if (param != NULL) - transitions = ews_get_transitions (param); - if (transitions == NULL) - goto exit; - - success = TRUE; - -exit: - if (success) { - tzd = e_ews_calendar_time_zone_definition_new (); - tzd->name = name; - tzd->id = id; - tzd->periods = periods; - tzd->transitions_groups = transitions_groups; - tzd->transitions = transitions; - } else { - g_free (name); - g_free (id); - g_slist_free_full (periods, (GDestroyNotify) e_ews_calendar_period_free); - g_slist_free_full (transitions_groups, (GDestroyNotify) e_ews_calendar_transitions_group_free); - e_ews_calendar_transitions_free (transitions); - } - - return tzd; -} - -static void -get_server_time_zones_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "GetServerTimeZonesResponseMessage")) { - ESoapParameter *node, *node2; - - node = e_soap_parameter_get_first_child_by_name (subparam, "TimeZoneDefinitions"); - if (node != NULL) { - node2 = e_soap_parameter_get_first_child_by_name (node, "TimeZoneDefinition"); - if (node2 != NULL) { - EEwsCalendarTimeZoneDefinition *tzd; - - tzd = ews_get_time_zone_definition (node2); - if (tzd != NULL) - async_data->tzds = g_slist_prepend (async_data->tzds, tzd); - } - } - } - - subparam = e_soap_parameter_get_next_child (subparam); - } - - async_data->tzds = g_slist_reverse (async_data->tzds); -} - -void -e_ews_connection_get_server_time_zones (EEwsConnection *cnc, - gint pri, - GSList *msdn_locations, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - GSList *l; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (cnc->priv != NULL); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, e_ews_connection_get_server_time_zones); - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer (simple, async_data, (GDestroyNotify) async_data_free); - - /* - * EWS server version earlier than 2010 doesn't have support to "GetServerTimeZones". - * So, if the API is called with an older Exchange's version, let's just fail silently. - */ - if (!e_ews_connection_satisfies_server_version (cnc, E_EWS_EXCHANGE_2010_SP1)) { - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - return; - } - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetServerTimeZones", - "ReturnFullTimeZoneData", - "true", - cnc->priv->version, - E_EWS_EXCHANGE_2010, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "Ids", "messages", NULL); - for (l = msdn_locations; l != NULL; l = l->next) - e_ews_message_write_string_parameter_with_attribute (msg, "Id", NULL, l->data, NULL, NULL); - e_soap_message_end_element (msg); /* Ids */ - - e_ews_message_write_footer (msg); /* Complete the footer and print the request */ - - e_ews_connection_queue_request (cnc, msg, get_server_time_zones_response_cb, pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_server_time_zones_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **tzds, /*EEwsCalendarTimeZoneDefinition */ - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid (result, G_OBJECT (cnc), e_ews_connection_get_server_time_zones), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (async_data->tzds == NULL) - return FALSE; - - if (tzds != NULL) - *tzds = async_data->tzds; - else - g_slist_free_full ( - async_data->tzds, - (GDestroyNotify) e_ews_calendar_time_zone_definition_free); - - return TRUE; -} - -gboolean -e_ews_connection_get_server_time_zones_sync (EEwsConnection *cnc, - gint pri, - GSList *msdn_locations, - GSList **tzds, /* EEwsCalendarTimeZoneDefinition */ - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_server_time_zones ( - cnc, pri, msdn_locations, cancellable, e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_server_time_zones_finish (cnc, result, tzds, error); - - e_async_closure_free (closure); - - return success; -} - -static void -get_user_photo_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "PictureData", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - async_data->user_photo = e_soap_parameter_get_string_value (param); - if (async_data->user_photo && !*async_data->user_photo) { - g_free (async_data->user_photo); - async_data->user_photo = NULL; - } -} - -void -e_ews_connection_get_user_photo (EEwsConnection *cnc, - gint pri, - const gchar *email, - EEwsSizeRequested size_requested, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - gchar *tmp; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (cnc->priv != NULL); - g_return_if_fail (email != NULL); - - simple = g_simple_async_result_new (G_OBJECT (cnc), callback, user_data, e_ews_connection_get_user_photo); - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer (simple, async_data, (GDestroyNotify) async_data_free); - - /* - * EWS server version earlier than 2013 doesn't have support to "GetUserPhoto". - */ - if (!e_ews_connection_satisfies_server_version (cnc, E_EWS_EXCHANGE_2013)) { - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - return; - } - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetUserPhoto", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2013, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "Email", "messages", NULL); - e_soap_message_write_string (msg, email); - e_soap_message_end_element (msg); /* Email */ - - e_soap_message_start_element (msg, "SizeRequested", "messages", NULL); - tmp = g_strdup_printf ("HR%dx%d", (gint) size_requested, size_requested); - e_soap_message_write_string (msg, tmp); - g_free (tmp); - e_soap_message_end_element (msg); /* SizeRequested */ - - e_ews_message_write_footer (msg); /* Complete the footer and print the request */ - - e_ews_connection_queue_request (cnc, msg, get_user_photo_response_cb, pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_user_photo_finish (EEwsConnection *cnc, - GAsyncResult *result, - gchar **out_picture_data, /* base64-encoded */ - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid (result, G_OBJECT (cnc), e_ews_connection_get_user_photo), - FALSE); - g_return_val_if_fail (out_picture_data != NULL, FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (!async_data->user_photo) - return FALSE; - - *out_picture_data = async_data->user_photo; - async_data->user_photo = NULL; - - return TRUE; -} - -gboolean -e_ews_connection_get_user_photo_sync (EEwsConnection *cnc, - gint pri, - const gchar *email, - EEwsSizeRequested size_requested, - gchar **out_picture_data, /* base64-encoded */ - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_user_photo ( - cnc, pri, email, size_requested, cancellable, e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_user_photo_finish (cnc, result, out_picture_data, error); - - e_async_closure_free (closure); - - return success; -} diff --git a/src/server/e-ews-connection.c.sync-category-list b/src/server/e-ews-connection.c.sync-category-list deleted file mode 100644 index 28f7f6f..0000000 --- a/src/server/e-ews-connection.c.sync-category-list +++ /dev/null @@ -1,10957 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Authors : - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - * 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. - * - * 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "e-ews-connection.h" -#include "e-ews-connection-utils.h" -#include "e-ews-message.h" -#include "e-ews-item-change.h" -#include "e-ews-debug.h" -#include "e-ews-notification.h" - -#define d(x) x - -#define E_EWS_CONNECTION_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_EWS_CONNECTION, EEwsConnectionPrivate)) - -/* For the number of connections */ -#define EWS_CONNECTION_MAX_REQUESTS 1 - -/* A chunk size limit when moving items in chunks. */ -#define EWS_MOVE_ITEMS_CHUNK_SIZE 500 - -#define QUEUE_LOCK(x) (g_rec_mutex_lock(&(x)->priv->queue_lock)) -#define QUEUE_UNLOCK(x) (g_rec_mutex_unlock(&(x)->priv->queue_lock)) - -#define NOTIFICATION_LOCK(x) (g_mutex_lock(&(x)->priv->notification_lock)) -#define NOTIFICATION_UNLOCK(x) (g_mutex_unlock(&(x)->priv->notification_lock)) - -struct _EwsNode; -static GMutex connecting; -static GHashTable *loaded_connections_permissions = NULL; -static gint comp_func (gconstpointer a, gconstpointer b); - -static void ews_response_cb (SoupSession *session, SoupMessage *msg, gpointer data); - -static void ews_connection_authenticate (SoupSession *sess, - SoupMessage *msg, - SoupAuth *auth, - gboolean retrying, - gpointer data); - -/* Connection APIS */ - -struct _EEwsConnectionPrivate { - ESource *source; - ESoupAuthBearer *bearer_auth; - SoupSession *soup_session; - GThread *soup_thread; - GMainLoop *soup_loop; - GMainContext *soup_context; - GProxyResolver *proxy_resolver; - EEwsNotification *notification; - - CamelEwsSettings *settings; - GMutex property_lock; - - /* Hash key for the loaded_connections_permissions table. */ - gchar *hash_key; - - gchar *uri; - gchar *password; - gchar *email; - gchar *impersonate_user; - - GSList *jobs; - GSList *active_job_queue; - GRecMutex queue_lock; - GMutex notification_lock; - - GHashTable *subscriptions; - GSList *subscribed_folders; - - EEwsServerVersion version; - - /* Set to TRUE when this connection had been disconnected and cannot be used anymore */ - gboolean disconnected_flag; - - gboolean ssl_info_set; - gchar *ssl_certificate_pem; - GTlsCertificateFlags ssl_certificate_errors; -}; - -enum { - PROP_0, - PROP_PASSWORD, - PROP_PROXY_RESOLVER, - PROP_SETTINGS, - PROP_SOURCE -}; - -enum { - SERVER_NOTIFICATION, - PASSWORD_WILL_EXPIRE, - LAST_SIGNAL, -}; - -static guint signals[LAST_SIGNAL]; - -static guint notification_key = 1; - -typedef struct _EwsNode EwsNode; -typedef struct _EwsAsyncData EwsAsyncData; -typedef struct _EwsEventsAsyncData EwsEventsAsyncData; -typedef struct _EwsUrls EwsUrls; - -struct _EwsAsyncData { - GSList *items_created; - GSList *items_updated; - GSList *items_deleted; - GSList *tzds; /* EEwsCalendarTimeZoneDefinition */ - - gint total_items; - const gchar *directory; - GSList *items; - EwsPhotoAttachmentInfo *photo; - gchar *sync_state; - gboolean includes_last_item; - EwsDelegateDeliver deliver_to; - EEwsFolderType folder_type; - EEwsConnection *cnc; - gchar *user_photo; /* base64-encoded, as GetUserPhoto result */ -}; - -struct _EwsNode { - ESoapMessage *msg; - EEwsConnection *cnc; - GSimpleAsyncResult *simple; - - gint pri; /* the command priority */ - EEwsResponseCallback cb; - - GCancellable *cancellable; - gulong cancel_handler_id; -}; - -struct _EwsUrls { - xmlChar *as_url; - xmlChar *oab_url; - - /* all the below variables are for future use */ - xmlChar *oof_url; - gpointer future1; - gpointer future2; -}; - -G_DEFINE_TYPE (EEwsConnection, e_ews_connection, G_TYPE_OBJECT) - -/* Static Functions */ - -GQuark -ews_connection_error_quark (void) -{ - static GQuark quark = 0; - - if (G_UNLIKELY (quark == 0)) { - const gchar *string = "ews-connection-error-quark"; - quark = g_quark_from_static_string (string); - } - - return quark; -} - -static void -async_data_free (EwsAsyncData *async_data) -{ - g_free (async_data->user_photo); - g_free (async_data); -} - -EEwsNotificationEvent * -e_ews_notification_event_new (void) -{ - return g_new0 (EEwsNotificationEvent, 1); -} - -void -e_ews_notification_event_free (EEwsNotificationEvent *event) -{ - if (event != NULL) { - g_free (event->folder_id); - g_free (event->old_folder_id); - g_free (event); - } -} - -EEwsCalendarTo * -e_ews_calendar_to_new (void) -{ - return g_new0 (EEwsCalendarTo, 1); -} - -void -e_ews_calendar_to_free (EEwsCalendarTo *to) { - if (to != NULL) { - g_free (to->kind); - g_free (to->value); - } -} - -EEwsCalendarAbsoluteDateTransition * -e_ews_calendar_absolute_date_transition_new (void) -{ - return g_new0 (EEwsCalendarAbsoluteDateTransition, 1); -} - -void -e_ews_calendar_absolute_date_transition_free (EEwsCalendarAbsoluteDateTransition *adt) -{ - if (adt != NULL) { - e_ews_calendar_to_free (adt->to); - g_free (adt->date_time); - g_free (adt); - } -} - -EEwsCalendarRecurringDayTransition * -e_ews_calendar_recurring_day_transition_new (void) -{ - return g_new0 (EEwsCalendarRecurringDayTransition, 1); -} - -void -e_ews_calendar_recurring_day_transition_free (EEwsCalendarRecurringDayTransition *rdayt) -{ - if (rdayt != NULL) { - e_ews_calendar_to_free (rdayt->to); - g_free (rdayt->time_offset); - g_free (rdayt->month); - g_free (rdayt->day_of_week); - g_free (rdayt->occurrence); - g_free (rdayt); - } -} - -EEwsCalendarRecurringDateTransition * -e_ews_calendar_recurring_date_transition_new (void) -{ - return g_new0 (EEwsCalendarRecurringDateTransition, 1); -} - -void -e_ews_calendar_recurring_date_transition_free (EEwsCalendarRecurringDateTransition *rdatet) -{ - if (rdatet != NULL) { - e_ews_calendar_to_free (rdatet->to); - g_free (rdatet->time_offset); - g_free (rdatet->month); - g_free (rdatet->day); - g_free (rdatet); - } -} - -EEwsCalendarPeriod * -e_ews_calendar_period_new (void) -{ - return g_new0 (EEwsCalendarPeriod, 1); -} - -void -e_ews_calendar_period_free (EEwsCalendarPeriod *period) -{ - if (period != NULL) { - g_free (period->bias); - g_free (period->name); - g_free (period->id); - g_free (period); - } -} - -EEwsCalendarTransitionsGroup * -e_ews_calendar_transitions_group_new (void) -{ - return g_new0 (EEwsCalendarTransitionsGroup, 1); -} - -void -e_ews_calendar_transitions_group_free (EEwsCalendarTransitionsGroup *tg) -{ - if (tg != NULL) { - g_free (tg->id); - e_ews_calendar_to_free (tg->transition); - g_slist_free_full ( - tg->absolute_date_transitions, - (GDestroyNotify) e_ews_calendar_absolute_date_transition_free); - g_slist_free_full ( - tg->recurring_day_transitions, - (GDestroyNotify) e_ews_calendar_recurring_day_transition_free); - g_slist_free_full ( - tg->recurring_date_transitions, - (GDestroyNotify) e_ews_calendar_recurring_date_transition_free); - g_free (tg); - } -} - -EEwsCalendarTransitions * -e_ews_calendar_transitions_new (void) -{ - return g_new0 (EEwsCalendarTransitions, 1); -} - -void -e_ews_calendar_transitions_free (EEwsCalendarTransitions *transitions) -{ - if (transitions != NULL) { - e_ews_calendar_to_free (transitions->transition); - g_slist_free_full ( - transitions->absolute_date_transitions, - (GDestroyNotify) e_ews_calendar_absolute_date_transition_free); - g_slist_free_full ( - transitions->recurring_day_transitions, - (GDestroyNotify) e_ews_calendar_recurring_day_transition_free); - g_slist_free_full ( - transitions->recurring_date_transitions, - (GDestroyNotify) e_ews_calendar_recurring_date_transition_free); - g_free (transitions); - } -} - -EEwsCalendarTimeZoneDefinition * -e_ews_calendar_time_zone_definition_new (void) -{ - return g_new0 (EEwsCalendarTimeZoneDefinition, 1); -} - -void -e_ews_calendar_time_zone_definition_free (EEwsCalendarTimeZoneDefinition *tzd) -{ - if (tzd != NULL) { - g_free (tzd->name); - g_free (tzd->id); - g_slist_free_full (tzd->periods, (GDestroyNotify) e_ews_calendar_period_free); - g_slist_free_full (tzd->transitions_groups, (GDestroyNotify) e_ews_calendar_transitions_group_free); - e_ews_calendar_transitions_free (tzd->transitions); - g_free (tzd); - } -} - -EEwsExtendedFieldURI * -e_ews_extended_field_uri_new (void) -{ - return g_new0 (EEwsExtendedFieldURI, 1); -} - -void -e_ews_extended_field_uri_free (EEwsExtendedFieldURI *ex_field_uri) -{ - if (ex_field_uri != NULL) { - g_free (ex_field_uri->distinguished_prop_set_id); - g_free (ex_field_uri->prop_set_id); - g_free (ex_field_uri->prop_tag); - g_free (ex_field_uri->prop_name); - g_free (ex_field_uri->prop_id); - g_free (ex_field_uri->prop_type); - g_free (ex_field_uri); - } -} - -EEwsIndexedFieldURI * -e_ews_indexed_field_uri_new (const gchar *uri, - const gchar *index) -{ - EEwsIndexedFieldURI *furi; - - furi = g_new0 (EEwsIndexedFieldURI, 1); - furi->field_uri = g_strdup (uri); - furi->field_index = g_strdup (index); - - return furi; -} - -void -e_ews_indexed_field_uri_free (EEwsIndexedFieldURI *id_field_uri) -{ - if (id_field_uri != NULL) { - g_free (id_field_uri->field_uri); - g_free (id_field_uri->field_index); - g_free (id_field_uri); - } -} - -EEwsAdditionalProps * -e_ews_additional_props_new (void) -{ - return g_new0 (EEwsAdditionalProps, 1); -} - -void -e_ews_additional_props_free (EEwsAdditionalProps *add_props) -{ - if (add_props != NULL) { - g_free (add_props->field_uri); - g_slist_free_full (add_props->extended_furis, (GDestroyNotify) e_ews_extended_field_uri_free); - g_slist_free_full (add_props->indexed_furis, (GDestroyNotify) e_ews_indexed_field_uri_free); - g_free (add_props); - } -} - -static EwsNode * -ews_node_new () -{ - EwsNode *node; - - node = g_new0 (EwsNode, 1); - return node; -} - -static gboolean -autodiscover_parse_protocol (xmlNode *node, - EwsUrls *urls) -{ - for (node = node->children; node; node = node->next) { - if (node->type == XML_ELEMENT_NODE && - !strcmp ((gchar *) node->name, "ASUrl")) { - if (urls->as_url != NULL) - xmlFree (urls->as_url); - - urls->as_url = xmlNodeGetContent (node); - } else if (node->type == XML_ELEMENT_NODE && - !strcmp ((gchar *) node->name, "OABUrl")) { - if (urls->oab_url != NULL) - xmlFree (urls->oab_url); - - urls->oab_url = xmlNodeGetContent (node); - } - - /* Once we find both, we can stop looking for the URLs */ - if (urls->as_url && urls->oab_url) - return TRUE; - } - - return FALSE; -} - -static gint -comp_func (gconstpointer a, - gconstpointer b) -{ - EwsNode *node1 = (EwsNode *) a; - EwsNode *node2 = (EwsNode *) b; - if (node1->pri > node2->pri) - return -1; - else if (node1->pri < node2->pri) - return 1; - else - return 0; -} - -typedef enum _EwsScheduleOp { - EWS_SCHEDULE_OP_QUEUE_MESSAGE, - EWS_SCHEDULE_OP_CANCEL, - EWS_SCHEDULE_OP_ABORT -} EwsScheduleOp; - -typedef struct _EwsScheduleData -{ - EEwsConnection *cnc; - SoupMessage *message; - - EwsScheduleOp op; - - SoupSessionCallback queue_callback; - gpointer queue_user_data; -} EwsScheduleData; - -/* this is run in priv->soup_thread */ -static gboolean -ews_connection_scheduled_cb (gpointer user_data) -{ - EwsScheduleData *sd = user_data; - - g_return_val_if_fail (sd != NULL, FALSE); - - switch (sd->op) { - case EWS_SCHEDULE_OP_QUEUE_MESSAGE: - if (!e_ews_connection_utils_prepare_message (sd->cnc, sd->message, NULL)) { - e_ews_debug_dump_raw_soup_request (sd->message); - - if (sd->queue_callback) { - sd->queue_callback (sd->cnc->priv->soup_session, sd->message, sd->queue_user_data); - } else { - /* This should not happen */ - g_warn_if_reached (); - - soup_session_queue_message ( - sd->cnc->priv->soup_session, sd->message, - sd->queue_callback, sd->queue_user_data); - soup_session_cancel_message (sd->cnc->priv->soup_session, sd->message, sd->message->status_code); - } - } else { - e_ews_debug_dump_raw_soup_request (sd->message); - - soup_session_queue_message ( - sd->cnc->priv->soup_session, sd->message, - sd->queue_callback, sd->queue_user_data); - } - break; - case EWS_SCHEDULE_OP_CANCEL: - soup_session_cancel_message (sd->cnc->priv->soup_session, sd->message, SOUP_STATUS_CANCELLED); - break; - case EWS_SCHEDULE_OP_ABORT: - soup_session_abort (sd->cnc->priv->soup_session); - break; - } - - if (sd->message) - g_object_unref (sd->message); - /* in case this is the last reference */ - e_ews_connection_utils_unref_in_thread (sd->cnc); - g_free (sd); - - return FALSE; -} - -static void -ews_connection_schedule_queue_message (EEwsConnection *cnc, - SoupMessage *message, - SoupSessionCallback callback, - gpointer user_data) -{ - EwsScheduleData *sd; - GSource *source; - - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - g_return_if_fail (SOUP_IS_MESSAGE (message)); - - sd = g_new0 (EwsScheduleData, 1); - sd->cnc = g_object_ref (cnc); - sd->message = g_object_ref (message); - sd->op = EWS_SCHEDULE_OP_QUEUE_MESSAGE; - sd->queue_callback = callback; - sd->queue_user_data = user_data; - - source = g_idle_source_new (); - g_source_set_priority (source, G_PRIORITY_DEFAULT); - g_source_set_callback (source, ews_connection_scheduled_cb, sd, NULL); - g_source_attach (source, cnc->priv->soup_context); - g_source_unref (source); -} - -static void -ews_connection_schedule_cancel_message (EEwsConnection *cnc, - SoupMessage *message) -{ - EwsScheduleData *sd; - GSource *source; - - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - g_return_if_fail (SOUP_IS_MESSAGE (message)); - - sd = g_new0 (EwsScheduleData, 1); - sd->cnc = g_object_ref (cnc); - sd->message = g_object_ref (message); - sd->op = EWS_SCHEDULE_OP_CANCEL; - - source = g_idle_source_new (); - g_source_set_priority (source, G_PRIORITY_DEFAULT); - g_source_set_callback (source, ews_connection_scheduled_cb, sd, NULL); - g_source_attach (source, cnc->priv->soup_context); - g_source_unref (source); -} - -static void -ews_connection_schedule_abort (EEwsConnection *cnc) -{ - EwsScheduleData *sd; - GSource *source; - - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - - sd = g_new0 (EwsScheduleData, 1); - sd->cnc = g_object_ref (cnc); - sd->op = EWS_SCHEDULE_OP_ABORT; - - source = g_idle_source_new (); - g_source_set_priority (source, G_PRIORITY_DEFAULT); - g_source_set_callback (source, ews_connection_scheduled_cb, sd, NULL); - g_source_attach (source, cnc->priv->soup_context); - g_source_unref (source); -} - -static void ews_cancel_request (GCancellable *cancellable, gpointer user_data); - -static void -ews_discover_server_version (EEwsConnection *cnc, - ESoapResponse *response) -{ - ESoapParameter *param; - gchar *version; - - g_return_if_fail (cnc != NULL); - - if (cnc->priv->version != E_EWS_EXCHANGE_UNKNOWN) - return; - - param = e_soap_response_get_first_parameter_by_name ( - response, "ServerVersionInfo", NULL); - if (!param) - return; - - version = e_soap_parameter_get_property (param, "Version"); - - e_ews_connection_set_server_version_from_string (cnc, version); - - g_free (version); -} - -/* this is run in priv->soup_thread */ -static gboolean -ews_next_request (gpointer _cnc) -{ - EEwsConnection *cnc = _cnc; - GSList *l; - EwsNode *node; - - QUEUE_LOCK (cnc); - - l = cnc->priv->jobs; - - if (!l || g_slist_length (cnc->priv->active_job_queue) >= EWS_CONNECTION_MAX_REQUESTS) { - QUEUE_UNLOCK (cnc); - return FALSE; - } - - node = (EwsNode *) l->data; - - /* Remove the node from the priority queue */ - cnc->priv->jobs = g_slist_remove (cnc->priv->jobs, (gconstpointer *) node); - - /* Add to active job queue */ - cnc->priv->active_job_queue = g_slist_append (cnc->priv->active_job_queue, node); - - if (cnc->priv->soup_session) { - SoupMessage *msg = SOUP_MESSAGE (node->msg); - - if (!e_ews_connection_utils_prepare_message (cnc, msg, node->cancellable)) { - e_ews_debug_dump_raw_soup_request (msg); - QUEUE_UNLOCK (cnc); - - ews_response_cb (cnc->priv->soup_session, msg, node); - } else { - e_ews_debug_dump_raw_soup_request (msg); - soup_session_queue_message (cnc->priv->soup_session, msg, ews_response_cb, node); - QUEUE_UNLOCK (cnc); - } - } else { - QUEUE_UNLOCK (cnc); - - ews_cancel_request (NULL, node); - } - - return FALSE; -} - -static void -ews_trigger_next_request (EEwsConnection *cnc) -{ - GSource *source; - - g_return_if_fail (cnc != NULL); - - if (cnc->priv->soup_session) { - source = g_idle_source_new (); - g_source_set_priority (source, G_PRIORITY_DEFAULT); - g_source_set_callback (source, ews_next_request, cnc, NULL); - g_source_attach (source, cnc->priv->soup_context); - g_source_unref (source); - } else { - ews_next_request (cnc); - } -} - -/** - * ews_active_job_done - * @cnc: - * @msg: - * Removes the node from active Queue and free's the node - * - * Returns: - **/ -static void -ews_active_job_done (EEwsConnection *cnc, - EwsNode *ews_node) -{ - g_return_if_fail (cnc != NULL); - g_return_if_fail (ews_node != NULL); - - QUEUE_LOCK (cnc); - - cnc->priv->active_job_queue = g_slist_remove (cnc->priv->active_job_queue, ews_node); - if (ews_node->cancellable && ews_node->cancel_handler_id) - g_signal_handler_disconnect (ews_node->cancellable, ews_node->cancel_handler_id); - - QUEUE_UNLOCK (cnc); - - ews_trigger_next_request (cnc); - - if (ews_node->cancellable) - g_object_unref (ews_node->cancellable); - - if (ews_node->simple) { - /* the 'simple' holds reference on 'cnc' and this function - * is called in a dedicated thread, which 'cnc' joins on dispose, - * thus to avoid race condition, unref the object in its own thread */ - e_ews_connection_utils_unref_in_thread (ews_node->simple); - } - - g_free (ews_node); -} - -static void -ews_cancel_request (GCancellable *cancellable, - gpointer user_data) -{ - EwsNode *node = user_data; - EEwsConnection *cnc = node->cnc; - GSimpleAsyncResult *simple = node->simple; - ESoapMessage *msg = node->msg; - GSList *found; - - QUEUE_LOCK (cnc); - found = g_slist_find (cnc->priv->active_job_queue, node); - cnc->priv->jobs = g_slist_remove (cnc->priv->jobs, node); - QUEUE_UNLOCK (cnc); - - g_simple_async_result_set_error ( - simple, - G_IO_ERROR, - G_IO_ERROR_CANCELLED, - _("Operation Cancelled")); - if (found) { - ews_connection_schedule_cancel_message (cnc, SOUP_MESSAGE (msg)); - } else { - ews_response_cb (cnc->priv->soup_session, SOUP_MESSAGE (msg), node); - } -} - -void -e_ews_connection_queue_request (EEwsConnection *cnc, - ESoapMessage *msg, - EEwsResponseCallback cb, - gint pri, - GCancellable *cancellable, - GSimpleAsyncResult *simple) -{ - EwsNode *node; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (cb != NULL); - g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple)); - - node = ews_node_new (); - node->msg = msg; - node->pri = pri; - node->cb = cb; - node->cnc = cnc; - node->simple = g_object_ref (simple); - - QUEUE_LOCK (cnc); - cnc->priv->jobs = g_slist_insert_sorted (cnc->priv->jobs, (gpointer *) node, (GCompareFunc) comp_func); - QUEUE_UNLOCK (cnc); - - if (cancellable) { - node->cancellable = g_object_ref (cancellable); - if (g_cancellable_is_cancelled (cancellable)) - ews_cancel_request (cancellable, node); - else - node->cancel_handler_id = g_cancellable_connect ( - cancellable, - G_CALLBACK (ews_cancel_request), - (gpointer) node, NULL); - } - - ews_trigger_next_request (cnc); -} - -static gboolean -ews_connection_credentials_failed (EEwsConnection *connection, - SoupMessage *message, - GSimpleAsyncResult *simple) -{ - gint expire_in_days = 0; - gboolean expired = FALSE; - gchar *service_url = NULL; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (connection), FALSE); - g_return_val_if_fail (SOUP_IS_MESSAGE (message), FALSE); - g_return_val_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple), FALSE); - - if (!e_ews_connection_utils_check_x_ms_credential_headers (message, &expire_in_days, &expired, &service_url)) - return FALSE; - - if (expired) { - GError *error = NULL; - - e_ews_connection_utils_expired_password_to_error (service_url, &error); - g_simple_async_result_take_error (simple, error); - } else if (expire_in_days > 0) { - g_signal_emit (connection, signals[PASSWORD_WILL_EXPIRE], 0, expire_in_days, service_url); - } - - g_free (service_url); - - return expired; -} - -static void -ews_connection_check_ssl_error (EEwsConnection *connection, - SoupMessage *message) -{ - g_return_if_fail (E_IS_EWS_CONNECTION (connection)); - g_return_if_fail (SOUP_IS_MESSAGE (message)); - - if (message->status_code == SOUP_STATUS_SSL_FAILED) { - GTlsCertificate *certificate = NULL; - - g_mutex_lock (&connection->priv->property_lock); - - g_clear_pointer (&connection->priv->ssl_certificate_pem, g_free); - connection->priv->ssl_info_set = FALSE; - - g_object_get (G_OBJECT (message), - "tls-certificate", &certificate, - "tls-errors", &connection->priv->ssl_certificate_errors, - NULL); - - if (certificate) { - g_object_get (certificate, "certificate-pem", &connection->priv->ssl_certificate_pem, NULL); - connection->priv->ssl_info_set = TRUE; - - g_object_unref (certificate); - } - - g_mutex_unlock (&connection->priv->property_lock); - } -} - -/* Response callbacks */ - -static void -ews_response_cb (SoupSession *session, - SoupMessage *msg, - gpointer data) -{ - EwsNode *enode = (EwsNode *) data; - ESoapResponse *response; - ESoapParameter *param; - gint log_level; - gint wait_ms = 0; - - if (g_cancellable_is_cancelled (enode->cancellable)) - goto exit; - - ews_connection_check_ssl_error (enode->cnc, msg); - - if (ews_connection_credentials_failed (enode->cnc, msg, enode->simple)) { - goto exit; - } else if (msg->status_code == SOUP_STATUS_SSL_FAILED) { - g_simple_async_result_set_error ( - enode->simple, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED, - "%s", msg->reason_phrase); - goto exit; - } else if (msg->status_code == SOUP_STATUS_UNAUTHORIZED) { - if (msg->response_headers) { - const gchar *diagnostics; - - diagnostics = soup_message_headers_get_list (msg->response_headers, "X-MS-DIAGNOSTICS"); - if (diagnostics && strstr (diagnostics, "invalid_grant")) { - g_simple_async_result_set_error ( - enode->simple, - EWS_CONNECTION_ERROR, - EWS_CONNECTION_ERROR_ACCESSDENIED, - "%s", diagnostics); - goto exit; - } else if (diagnostics && *diagnostics) { - g_simple_async_result_set_error ( - enode->simple, - EWS_CONNECTION_ERROR, - EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED, - "%s", diagnostics); - goto exit; - } - } - - g_simple_async_result_set_error ( - enode->simple, - EWS_CONNECTION_ERROR, - EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED, - _("Authentication failed")); - goto exit; - } else if (msg->status_code == SOUP_STATUS_CANT_RESOLVE || - msg->status_code == SOUP_STATUS_CANT_RESOLVE_PROXY || - msg->status_code == SOUP_STATUS_CANT_CONNECT || - msg->status_code == SOUP_STATUS_CANT_CONNECT_PROXY || - msg->status_code == SOUP_STATUS_IO_ERROR) { - g_simple_async_result_set_error ( - enode->simple, - EWS_CONNECTION_ERROR, - EWS_CONNECTION_ERROR_UNAVAILABLE, - "%s", msg->reason_phrase); - goto exit; - } - - response = e_soap_message_parse_response ((ESoapMessage *) msg); - - if (response == NULL) { - g_simple_async_result_set_error ( - enode->simple, - EWS_CONNECTION_ERROR, - EWS_CONNECTION_ERROR_NORESPONSE, - _("No response: %s"), msg->reason_phrase); - goto exit; - } - - /* TODO: The stdout can be replaced with Evolution's - * Logging framework also */ - - log_level = e_ews_debug_get_log_level (); - if (log_level >= 1 && log_level < 3) { - /* This will dump only the headers, since we stole the body. - * And only if EWS_DEBUG=1, since higher levels will have dumped - * it directly from libsoup anyway. */ - e_ews_debug_dump_raw_soup_response (msg); - /* And this will dump the body... */ - e_soap_response_dump_response (response, stdout); - } - - param = e_soap_response_get_first_parameter_by_name (response, "detail", NULL); - if (param) - param = e_soap_parameter_get_first_child_by_name (param, "ResponseCode"); - if (param) { - gchar *value; - - value = e_soap_parameter_get_string_value (param); - if (value && ews_get_error_code (value) == EWS_CONNECTION_ERROR_SERVERBUSY) { - param = e_soap_response_get_first_parameter_by_name (response, "detail", NULL); - if (param) - param = e_soap_parameter_get_first_child_by_name (param, "MessageXml"); - if (param) { - param = e_soap_parameter_get_first_child_by_name (param, "Value"); - if (param) { - g_free (value); - - value = e_soap_parameter_get_property (param, "Name"); - if (g_strcmp0 (value, "BackOffMilliseconds") == 0) { - wait_ms = e_soap_parameter_get_int_value (param); - } - } - } - } - - g_free (value); - } - - if (wait_ms > 0) { - GCancellable *cancellable = enode->cancellable; - EFlag *flag; - - if (cancellable) - g_object_ref (cancellable); - g_object_ref (msg); - - flag = e_flag_new (); - while (wait_ms > 0 && !g_cancellable_is_cancelled (cancellable) && msg->status_code != SOUP_STATUS_CANCELLED) { - gint64 now = g_get_monotonic_time (); - gint left_minutes, left_seconds; - - left_minutes = wait_ms / 60000; - left_seconds = (wait_ms / 1000) % 60; - - if (left_minutes > 0) { - camel_operation_push_message (cancellable, - g_dngettext (GETTEXT_PACKAGE, - "Exchange server is busy, waiting to retry (%d:%02d minute)", - "Exchange server is busy, waiting to retry (%d:%02d minutes)", left_minutes), - left_minutes, left_seconds); - } else { - camel_operation_push_message (cancellable, - g_dngettext (GETTEXT_PACKAGE, - "Exchange server is busy, waiting to retry (%d second)", - "Exchange server is busy, waiting to retry (%d seconds)", left_seconds), - left_seconds); - } - - e_flag_wait_until (flag, now + (G_TIME_SPAN_MILLISECOND * (wait_ms > 1000 ? 1000 : wait_ms))); - - now = g_get_monotonic_time () - now; - now = now / G_TIME_SPAN_MILLISECOND; - - if (now >= wait_ms) - wait_ms = 0; - wait_ms -= now; - - camel_operation_pop_message (cancellable); - } - - e_flag_free (flag); - - g_object_unref (response); - - if (g_cancellable_is_cancelled (cancellable) || - msg->status_code == SOUP_STATUS_CANCELLED) { - g_clear_object (&cancellable); - g_object_unref (msg); - } else { - EwsNode *new_node; - - new_node = ews_node_new (); - new_node->msg = E_SOAP_MESSAGE (msg); /* takes ownership */ - new_node->pri = enode->pri; - new_node->cb = enode->cb; - new_node->cnc = enode->cnc; - new_node->simple = enode->simple; - - enode->simple = NULL; - - QUEUE_LOCK (enode->cnc); - enode->cnc->priv->jobs = g_slist_prepend (enode->cnc->priv->jobs, new_node); - QUEUE_UNLOCK (enode->cnc); - - if (cancellable) { - new_node->cancellable = g_object_ref (cancellable); - new_node->cancel_handler_id = g_cancellable_connect ( - cancellable, G_CALLBACK (ews_cancel_request), new_node, NULL); - } - - g_clear_object (&cancellable); - } - - goto exit; - } - - if (enode->cb != NULL) - enode->cb (response, enode->simple); - - g_object_unref (response); - -exit: - if (enode->simple) - g_simple_async_result_complete_in_idle (enode->simple); - - ews_active_job_done (enode->cnc, enode); -} - -typedef gpointer (*ItemParser) (ESoapParameter *param); - -static void -sync_xxx_response_cb (ESoapParameter *subparam, - EwsAsyncData *async_data, - ItemParser parser, - const gchar *last_tag, - const gchar *delete_id_tag) -{ - ESoapParameter *node; - gchar *new_sync_state = NULL, *value, *last; - GSList *items_created = NULL, *items_updated = NULL, *items_deleted = NULL; - gboolean includes_last_item; - - node = e_soap_parameter_get_first_child_by_name (subparam, "SyncState"); - new_sync_state = e_soap_parameter_get_string_value (node); - - node = e_soap_parameter_get_first_child_by_name (subparam, last_tag); - last = e_soap_parameter_get_string_value (node); - /* - * Set the includes_last_item to TRUE as default. - * It can avoid an infinite loop in caller, when, for some reason, - * we don't receive the last_tag property from the server. - */ - includes_last_item = g_strcmp0 (last, "false") != 0; - g_free (last); - - node = e_soap_parameter_get_first_child_by_name (subparam, "Changes"); - - if (node) { - ESoapParameter *subparam1; - for (subparam1 = e_soap_parameter_get_first_child_by_name (node, "Create"); - subparam1 != NULL; - subparam1 = e_soap_parameter_get_next_child_by_name (subparam1, "Create")) { - EEwsFolder *folder; - - folder = parser (subparam1); - if (folder) - items_created = g_slist_append (items_created, folder); - } - - for (subparam1 = e_soap_parameter_get_first_child_by_name (node, "Update"); - subparam1 != NULL; - subparam1 = e_soap_parameter_get_next_child_by_name (subparam1, "Update")) { - EEwsFolder *folder; - - folder = parser (subparam1); - if (folder) - items_updated = g_slist_append (items_updated, folder); - } - /* Exchange 2007SP1 introduced which is basically identical - * to ; no idea why they thought it was a good idea. */ - for (subparam1 = e_soap_parameter_get_first_child_by_name (node, "ReadFlagChange"); - subparam1 != NULL; - subparam1 = e_soap_parameter_get_next_child_by_name (subparam1, "ReadFlagChange")) { - EEwsFolder *folder; - - folder = parser (subparam1); - if (folder) - items_updated = g_slist_append (items_updated, folder); - } - - for (subparam1 = e_soap_parameter_get_first_child_by_name (node, "Delete"); - subparam1 != NULL; - subparam1 = e_soap_parameter_get_next_child_by_name (subparam1, "Delete")) { - ESoapParameter *folder_param; - - folder_param = e_soap_parameter_get_first_child_by_name (subparam1, delete_id_tag); - value = e_soap_parameter_get_property (folder_param, "Id"); - items_deleted = g_slist_append (items_deleted, value); - } - } - - async_data->items_created = items_created; - async_data->items_updated = items_updated; - async_data->items_deleted = items_deleted; - async_data->sync_state = new_sync_state; - async_data->includes_last_item = includes_last_item; -} - -static void -sync_hierarchy_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - /* - * During the first connection, we are able to get the current version of the Exchange server. - * For Addressbook/Calendar backends, we are ensuring it happens during the - * e_ews_connection_try_credentials_sync(), that calls e_e_ews_connection_get_folder_sync() and then - * we are able to get the current version of the server from this first response. - * - * For Camel, the first connection is done calling e_ews_connection_sync_folder_hierarchy_sync(). - */ - ews_discover_server_version (async_data->cnc, response); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "SyncFolderHierarchyResponseMessage")) - sync_xxx_response_cb ( - subparam, async_data, - (ItemParser) e_ews_folder_new_from_soap_parameter, - "IncludesLastFolderInRange", "FolderId"); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -static void -sync_folder_items_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "SyncFolderItemsResponseMessage")) - sync_xxx_response_cb ( - subparam, async_data, - (ItemParser) e_ews_item_new_from_soap_parameter, - "IncludesLastItemInRange", "ItemId"); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -static void -ews_handle_folders_param (ESoapParameter *subparam, - EwsAsyncData *async_data) -{ - ESoapParameter *node; - EEwsFolder *folder; - - for (node = e_soap_parameter_get_first_child_by_name (subparam, "Folders"); - node; node = e_soap_parameter_get_next_child_by_name (subparam, "Folders")) { - folder = e_ews_folder_new_from_soap_parameter (node); - if (!folder) continue; - async_data->items = g_slist_append (async_data->items, folder); - } -} - -static void -get_folder_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - /* - * During the first connection, we are able to get the current version of the Exchange server. - * For Addressbook/Calendar backends, we are ensuring it happens during the - * e_ews_connection_try_credentials_sync(), that calls e_e_ews_connection_get_folder_sync() and then - * we are able to get the current version of the server from this first response. - * - * For Camel, the first connection is done calling e_ews_connection_sync_folder_hierarchy_sync(). - */ - ews_discover_server_version (async_data->cnc, response); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - if (g_strcmp0 (name, "GetFolderResponseMessage") == 0) { - async_data->items = g_slist_append (async_data->items, e_ews_folder_new_from_error (error)); - g_clear_error (&error); - } else { - g_simple_async_result_take_error (simple, error); - return; - } - } else if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "GetFolderResponseMessage")) - ews_handle_folders_param (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -static void -ews_handle_root_folder_param_items (ESoapParameter *subparam, - EwsAsyncData *async_data) -{ - ESoapParameter *node, *subparam1; - gchar *last, *total; - gint total_items; - EEwsItem *item; - gboolean includes_last_item; - - node = e_soap_parameter_get_first_child_by_name (subparam, "RootFolder"); - total = e_soap_parameter_get_property (node, "TotalItemsInView"); - total_items = atoi (total); - g_free (total); - last = e_soap_parameter_get_property (node, "IncludesLastItemInRange"); - /* - * Set the includes_last_item to TRUE as default. - * It can avoid an infinite loop in caller, when, for some reason, - * we don't receive the last_tag property from the server. - */ - includes_last_item = g_strcmp0 (last, "false") != 0; - g_free (last); - - node = e_soap_parameter_get_first_child_by_name (node, "Items"); - for (subparam1 = e_soap_parameter_get_first_child (node); - subparam1; subparam1 = e_soap_parameter_get_next_child (subparam1)) { - item = e_ews_item_new_from_soap_parameter (subparam1); - if (!item) continue; - async_data->items = g_slist_append (async_data->items, item); - } - async_data->total_items = total_items; - async_data->includes_last_item = includes_last_item; -} - -static void -find_folder_items_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "FindItemResponseMessage")) - ews_handle_root_folder_param_items (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -/* Used for CreateItems and GetItems */ -static void -ews_handle_items_param (ESoapParameter *subparam, - EwsAsyncData *async_data, - const GError *error) -{ - ESoapParameter *node; - EEwsItem *item; - - for (node = e_soap_parameter_get_first_child_by_name (subparam, "Items"); - node; node = e_soap_parameter_get_next_child_by_name (subparam, "Items")) { - if (node->children) - item = e_ews_item_new_from_soap_parameter (node); - else - item = NULL; - if (!item && error != NULL) - item = e_ews_item_new_from_error (error); - if (!item) continue; - async_data->items = g_slist_append (async_data->items, item); - } -} - -static void -handle_get_items_response_cb (EwsAsyncData *async_data, ESoapParameter *param) -{ - ESoapParameter *subparam; - GError *error = NULL; - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (g_str_has_suffix (name, "ResponseMessage")) { - if (ews_get_response_status (subparam, &error)) - error = NULL; - - ews_handle_items_param (subparam, async_data, error); - } else { - g_warning ( - "%s: Unexpected element <%s>", - G_STRFUNC, name); - } - - /* Do not stop on errors. */ - if (error != NULL) - g_clear_error (&error); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -static void -get_items_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - handle_get_items_response_cb (async_data, param); -} - -static void -ews_handle_resolution_set_param (ESoapParameter *subparam, - EwsAsyncData *async_data) -{ - ESoapParameter *node; - gchar *prop; - gboolean includes_last_item; - GSList *mailboxes = NULL, *contact_items = NULL; - - subparam = e_soap_parameter_get_first_child_by_name (subparam, "ResolutionSet"); - prop = e_soap_parameter_get_property (subparam, "IncludesLastItemInRange"); - /* - * Set the includes_last_item to TRUE as default. - * It can avoid an infinite loop in caller, when, for some reason, - * we don't receive the last_tag property from the server. - */ - includes_last_item = g_strcmp0 (prop, "false") != 0; - g_free (prop); - - for (subparam = e_soap_parameter_get_first_child_by_name (subparam, "Resolution"); - subparam != NULL; - subparam = e_soap_parameter_get_next_child_by_name (subparam, "Resolution")) { - EwsMailbox *mb; - - node = e_soap_parameter_get_first_child_by_name (subparam, "Mailbox"); - mb = e_ews_item_mailbox_from_soap_param (node); - if (mb) { - EEwsItem *contact_item; - - mailboxes = g_slist_prepend (mailboxes, mb); - - /* 'mailboxes' and 'contact_items' match 1:1, but if the contact information - * wasn't found, then NULL is stored in the corresponding position */ - node = e_soap_parameter_get_first_child_by_name (subparam, "Contact"); - if (node) { - contact_item = e_ews_item_new_from_soap_parameter (node); - contact_items = g_slist_prepend (contact_items, contact_item); - } else { - contact_items = g_slist_prepend (contact_items, NULL); - } - } - } - - /* Reuse existing variables */ - async_data->items = g_slist_reverse (mailboxes); - async_data->includes_last_item = includes_last_item; - async_data->items_created = g_slist_reverse (contact_items); -} - -static void -resolve_names_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "ResolveNamesResponseMessage")) - ews_handle_resolution_set_param (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -static void -ews_handle_dl_expansion_param (ESoapParameter *subparam, - EwsAsyncData *async_data) -{ - gboolean includes_last_item; - GSList *mailboxes = NULL; - gchar *prop; - - subparam = e_soap_parameter_get_first_child_by_name (subparam, "DLExpansion"); - prop = e_soap_parameter_get_property (subparam, "IncludesLastItemInRange"); - /* - * Set the includes_last_item to TRUE as default. - * It can avoid an infinite loop in caller, when, for some reason, - * we don't receive the last_tag property from the server. - */ - includes_last_item = g_strcmp0 (prop, "false") != 0; - g_free (prop); - - for (subparam = e_soap_parameter_get_first_child_by_name (subparam, "Mailbox"); - subparam != NULL; - subparam = e_soap_parameter_get_next_child_by_name (subparam, "Mailbox")) { - EwsMailbox *mb; - - mb = e_ews_item_mailbox_from_soap_param (subparam); - if (mb) - mailboxes = g_slist_append (mailboxes, mb); - } - - /* Reuse existing variables */ - async_data->items = mailboxes; - async_data->includes_last_item = includes_last_item; -} - -static void -expand_dl_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "ExpandDLResponseMessage")) - ews_handle_dl_expansion_param (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -/* TODO scan all folders if we support creating multiple folders in the request */ -static void -ews_handle_create_folders_param (ESoapParameter *soapparam, - EwsAsyncData *async_data) -{ - ESoapParameter *param, *node; - EwsFolderId *fid = NULL; - GSList *fids = NULL; - const gchar *folder_element; - - switch (async_data->folder_type) { - case E_EWS_FOLDER_TYPE_MAILBOX: - folder_element = "Folder"; - break; - case E_EWS_FOLDER_TYPE_CALENDAR: - folder_element = "CalendarFolder"; - break; - case E_EWS_FOLDER_TYPE_CONTACTS: - folder_element = "ContactsFolder"; - break; - case E_EWS_FOLDER_TYPE_SEARCH: - folder_element = "SearchFolder"; - break; - case E_EWS_FOLDER_TYPE_TASKS: - folder_element = "TasksFolder"; - break; - default: - g_warn_if_reached (); - folder_element = "Folder"; - break; - } - - node = e_soap_parameter_get_first_child_by_name (soapparam, "Folders"); - node = e_soap_parameter_get_first_child_by_name (node, folder_element); - param = e_soap_parameter_get_first_child_by_name (node, "FolderId"); - - fid = g_new0 (EwsFolderId, 1); - fid->id = e_soap_parameter_get_property (param, "Id"); - fid->change_key = e_soap_parameter_get_property (param, "ChangeKey"); - fids = g_slist_append (fids, fid); - - async_data->items_created = fids; -} - -static void -create_folder_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "CreateFolderResponseMessage")) - ews_handle_create_folders_param (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -static gpointer -e_ews_soup_thread (gpointer user_data) -{ - EEwsConnection *cnc = user_data; - - g_main_context_push_thread_default (cnc->priv->soup_context); - g_main_loop_run (cnc->priv->soup_loop); - g_main_context_pop_thread_default (cnc->priv->soup_context); - - /* abort any pending operations */ - soup_session_abort (cnc->priv->soup_session); - - g_object_unref (cnc->priv->soup_session); - cnc->priv->soup_session = NULL; - - return NULL; -} - -/* - * e_ews_debug_handler: - * - * GLib debug message handler, which is passed all messages from g_debug() calls, - * and decides whether to print them. - */ -static void -e_ews_debug_handler (const gchar *log_domain, - GLogLevelFlags log_level, - const gchar *message, - gpointer user_data) -{ - if (e_ews_debug_get_log_level () >= 3) - g_log_default_handler (log_domain, log_level, message, NULL); -} - -/* - * e_ews_soup_log_print: - * - * Log printer for the libsoup logging functionality, which just marshal all soup log - * output to the standard GLib logging framework (and thus to debug_handler(), above). - */ -static void -e_ews_soup_log_printer (SoupLogger *logger, - SoupLoggerLogLevel level, - char direction, - const gchar *data, - gpointer user_data) -{ - const gchar *filtered_data = NULL; - - if (e_ews_debug_get_log_level () >= 3) { - if (direction == '>' && g_ascii_strncasecmp (data, "Host:", 5) == 0) - filtered_data = "Host: "; - else if (direction == '>' && g_ascii_strncasecmp (data, "Authorization:", 14) == 0) - filtered_data = "Authorization: "; - else if (direction == '<' && g_ascii_strncasecmp (data, "Set-Cookie:", 11) == 0) - filtered_data = "Set-Cookie: "; - else - filtered_data = data; - } - - g_debug ("%c %s", direction, filtered_data ? filtered_data : data); -} - -static void -ews_connection_set_settings (EEwsConnection *connection, - CamelEwsSettings *settings) -{ - g_return_if_fail (CAMEL_IS_EWS_SETTINGS (settings)); - g_return_if_fail (connection->priv->settings == NULL); - - connection->priv->settings = g_object_ref (settings); -} - -static void -ews_connection_set_source (EEwsConnection *connection, - ESource *source) -{ - if (source) - g_return_if_fail (E_IS_SOURCE (source)); - g_return_if_fail (connection->priv->source == NULL); - - connection->priv->source = source ? g_object_ref (source) : NULL; -} - -static void -ews_connection_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_PASSWORD: - e_ews_connection_set_password ( - E_EWS_CONNECTION (object), - g_value_get_string (value)); - return; - - case PROP_PROXY_RESOLVER: - e_ews_connection_set_proxy_resolver ( - E_EWS_CONNECTION (object), - g_value_get_object (value)); - return; - - case PROP_SETTINGS: - ews_connection_set_settings ( - E_EWS_CONNECTION (object), - g_value_get_object (value)); - return; - - case PROP_SOURCE: - ews_connection_set_source ( - E_EWS_CONNECTION (object), - g_value_get_object (value)); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -ews_connection_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - switch (property_id) { - case PROP_PASSWORD: - g_value_take_string ( - value, - e_ews_connection_dup_password ( - E_EWS_CONNECTION (object))); - return; - - case PROP_PROXY_RESOLVER: - g_value_take_object ( - value, - e_ews_connection_ref_proxy_resolver ( - E_EWS_CONNECTION (object))); - return; - - case PROP_SETTINGS: - g_value_take_object ( - value, - e_ews_connection_ref_settings ( - E_EWS_CONNECTION (object))); - return; - - case PROP_SOURCE: - g_value_set_object ( - value, - e_ews_connection_get_source ( - E_EWS_CONNECTION (object))); - return; - } - - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); -} - -static void -ews_connection_constructed (GObject *object) -{ - EEwsConnection *cnc = E_EWS_CONNECTION (object); - gint log_level; - - /* Chain up to parent's method. */ - G_OBJECT_CLASS (e_ews_connection_parent_class)->constructed (object); - - cnc->priv->soup_thread = g_thread_new (NULL, e_ews_soup_thread, cnc); - - cnc->priv->soup_session = soup_session_async_new_with_options ( - SOUP_SESSION_TIMEOUT, 90, - SOUP_SESSION_SSL_STRICT, TRUE, - SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE, - SOUP_SESSION_ASYNC_CONTEXT, cnc->priv->soup_context, - NULL); - - /* Do not use G_BINDING_SYNC_CREATE because the property_lock is - * not initialized and we don't have a GProxyResolver yet anyway. */ - e_binding_bind_property ( - cnc, "proxy-resolver", - cnc->priv->soup_session, "proxy-resolver", - G_BINDING_DEFAULT); - - cnc->priv->version = E_EWS_EXCHANGE_UNKNOWN; - - log_level = e_ews_debug_get_log_level (); - - if (log_level >= 2) { - SoupLogger *logger; - logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, -1); - - if (log_level >= 3) { - soup_logger_set_printer (logger, e_ews_soup_log_printer, NULL, NULL); - g_log_set_handler ( - G_LOG_DOMAIN, - G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING | - G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO, - e_ews_debug_handler, cnc); - } - - soup_session_add_feature ( - cnc->priv->soup_session, - SOUP_SESSION_FEATURE (logger)); - g_object_unref (logger); - } - - soup_session_add_feature_by_type (cnc->priv->soup_session, - SOUP_TYPE_COOKIE_JAR); - - g_signal_connect ( - cnc->priv->soup_session, "authenticate", - G_CALLBACK (ews_connection_authenticate), cnc); - - e_ews_connection_utils_prepare_auth_method (cnc->priv->soup_session, - camel_ews_settings_get_auth_mechanism (cnc->priv->settings)); -} - -static void -ews_connection_dispose (GObject *object) -{ - EEwsConnectionPrivate *priv; - - priv = E_EWS_CONNECTION_GET_PRIVATE (object); - - g_mutex_lock (&connecting); - - /* remove the connection from the hash table */ - if (loaded_connections_permissions != NULL && - g_hash_table_lookup (loaded_connections_permissions, priv->hash_key) == (gpointer) object) { - g_hash_table_remove (loaded_connections_permissions, priv->hash_key); - if (g_hash_table_size (loaded_connections_permissions) == 0) { - g_hash_table_destroy (loaded_connections_permissions); - loaded_connections_permissions = NULL; - } - } - - g_mutex_unlock (&connecting); - - if (priv->soup_session) { - g_signal_handlers_disconnect_by_func ( - priv->soup_session, - ews_connection_authenticate, object); - - g_main_loop_quit (priv->soup_loop); - g_thread_join (priv->soup_thread); - priv->soup_thread = NULL; - - g_main_loop_unref (priv->soup_loop); - priv->soup_loop = NULL; - g_main_context_unref (priv->soup_context); - priv->soup_context = NULL; - } - - g_clear_object (&priv->proxy_resolver); - g_clear_object (&priv->source); - g_clear_object (&priv->settings); - - e_ews_connection_set_password (E_EWS_CONNECTION (object), NULL); - - g_slist_free (priv->jobs); - priv->jobs = NULL; - - g_slist_free (priv->active_job_queue); - priv->active_job_queue = NULL; - - g_slist_free_full (priv->subscribed_folders, g_free); - priv->subscribed_folders = NULL; - - if (priv->subscriptions != NULL) { - g_hash_table_destroy (priv->subscriptions); - priv->subscriptions = NULL; - } - - /* Chain up to parent's dispose() method. */ - G_OBJECT_CLASS (e_ews_connection_parent_class)->dispose (object); -} - -static void -ews_connection_finalize (GObject *object) -{ - EEwsConnectionPrivate *priv; - - priv = E_EWS_CONNECTION_GET_PRIVATE (object); - - g_free (priv->uri); - g_free (priv->password); - g_free (priv->email); - g_free (priv->hash_key); - g_free (priv->impersonate_user); - g_free (priv->ssl_certificate_pem); - - g_clear_object (&priv->bearer_auth); - - g_mutex_clear (&priv->property_lock); - g_rec_mutex_clear (&priv->queue_lock); - g_mutex_clear (&priv->notification_lock); - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (e_ews_connection_parent_class)->finalize (object); -} - -static void -e_ews_connection_class_init (EEwsConnectionClass *class) -{ - GObjectClass *object_class; - - g_type_class_add_private (class, sizeof (EEwsConnectionPrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->set_property = ews_connection_set_property; - object_class->get_property = ews_connection_get_property; - object_class->constructed = ews_connection_constructed; - object_class->dispose = ews_connection_dispose; - object_class->finalize = ews_connection_finalize; - - g_object_class_install_property ( - object_class, - PROP_PASSWORD, - g_param_spec_string ( - "password", - "Password", - "Authentication password", - NULL, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property ( - object_class, - PROP_PROXY_RESOLVER, - g_param_spec_object ( - "proxy-resolver", - "Proxy Resolver", - "The proxy resolver for this backend", - G_TYPE_PROXY_RESOLVER, - G_PARAM_READWRITE | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property ( - object_class, - PROP_SETTINGS, - g_param_spec_object ( - "settings", - "Settings", - "Connection settings", - CAMEL_TYPE_EWS_SETTINGS, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - g_object_class_install_property ( - object_class, - PROP_SOURCE, - g_param_spec_object ( - "source", - "Source", - "Corresponding ESource", - E_TYPE_SOURCE, - G_PARAM_READWRITE | - G_PARAM_CONSTRUCT_ONLY | - G_PARAM_STATIC_STRINGS)); - - signals[SERVER_NOTIFICATION] = g_signal_new ( - "server-notification", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED | G_SIGNAL_ACTION, - 0, NULL, NULL, - g_cclosure_marshal_VOID__POINTER, - G_TYPE_NONE, 1, - G_TYPE_POINTER); - - signals[PASSWORD_WILL_EXPIRE] = g_signal_new ( - "password-will-expire", - G_OBJECT_CLASS_TYPE (object_class), - G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED | G_SIGNAL_ACTION, - G_STRUCT_OFFSET (EEwsConnectionClass, password_will_expire), - NULL, NULL, NULL, - G_TYPE_NONE, 2, - G_TYPE_INT, - G_TYPE_STRING); -} - -static void -e_ews_connection_folders_list_free (gpointer data) -{ - g_slist_free_full ((GSList *) data, g_free); -} - -static void -e_ews_connection_init (EEwsConnection *cnc) -{ - cnc->priv = E_EWS_CONNECTION_GET_PRIVATE (cnc); - - cnc->priv->soup_context = g_main_context_new (); - cnc->priv->soup_loop = g_main_loop_new (cnc->priv->soup_context, FALSE); - cnc->priv->disconnected_flag = FALSE; - - cnc->priv->subscriptions = g_hash_table_new_full ( - g_direct_hash, g_direct_equal, - NULL, e_ews_connection_folders_list_free); - - g_mutex_init (&cnc->priv->property_lock); - g_rec_mutex_init (&cnc->priv->queue_lock); - g_mutex_init (&cnc->priv->notification_lock); -} - -static void -ews_connection_authenticate (SoupSession *sess, - SoupMessage *msg, - SoupAuth *auth, - gboolean retrying, - gpointer data) -{ - EEwsConnection *cnc = data; - - g_return_if_fail (cnc != NULL); - - e_ews_connection_utils_authenticate (cnc, sess, msg, auth, retrying); -} - -void -ews_oal_free (EwsOAL *oal) -{ - if (oal != NULL) { - g_free (oal->id); - g_free (oal->dn); - g_free (oal->name); - g_free (oal); - } -} - -void -ews_oal_details_free (EwsOALDetails *details) -{ - if (details != NULL) { - g_free (details->type); - g_free (details->sha); - g_free (details->filename); - g_free (details); - } -} - -void -ews_user_id_free (EwsUserId *id) -{ - if (id) { - g_free (id->sid); - g_free (id->primary_smtp); - g_free (id->display_name); - g_free (id->distinguished_user); - g_free (id->external_user); - g_free (id); - } -} - -void -ews_delegate_info_free (EwsDelegateInfo *info) -{ - if (!info) - return; - - ews_user_id_free (info->user_id); - g_free (info); -} - -EEwsAttachmentInfo * -e_ews_attachment_info_new (EEwsAttachmentInfoType type) -{ - EEwsAttachmentInfo *info; - info = g_new0 (EEwsAttachmentInfo, 1); - - info->type = type; - return info; -} - -void -e_ews_attachment_info_free (EEwsAttachmentInfo *info) -{ - if (!info) - return; - - switch (info->type) { - case E_EWS_ATTACHMENT_INFO_TYPE_INLINED: - g_free (info->data.inlined.filename); - g_free (info->data.inlined.mime_type); - g_free (info->data.inlined.data); - break; - case E_EWS_ATTACHMENT_INFO_TYPE_URI: - g_free (info->data.uri); - break; - default: - g_warning ("Unknown EEwsAttachmentInfoType %d", info->type); - break; - } - - g_free (info->prefer_filename); - g_free (info->id); - g_free (info); -} - -EEwsAttachmentInfoType -e_ews_attachment_info_get_type (EEwsAttachmentInfo *info) -{ - return info->type; -} - -const gchar * -e_ews_attachment_info_get_prefer_filename (EEwsAttachmentInfo *info) -{ - g_return_val_if_fail (info != NULL, NULL); - - return info->prefer_filename; -} - -void -e_ews_attachment_info_set_prefer_filename (EEwsAttachmentInfo *info, - const gchar *prefer_filename) -{ - g_return_if_fail (info != NULL); - - if (info->prefer_filename == prefer_filename) - return; - - g_free (info->prefer_filename); - info->prefer_filename = g_strdup (prefer_filename); -} - -const gchar * -e_ews_attachment_info_get_inlined_data (EEwsAttachmentInfo *info, - gsize *len) -{ - g_return_val_if_fail (info != NULL, NULL); - g_return_val_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_INLINED, NULL); - - *len = info->data.inlined.length; - return info->data.inlined.data; -} - -void -e_ews_attachment_info_set_inlined_data (EEwsAttachmentInfo *info, - const guchar *data, - gsize len) -{ - g_return_if_fail (info != NULL); - g_return_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_INLINED); - - info->data.inlined.data = g_malloc (len); - memcpy (info->data.inlined.data, data, len); - info->data.inlined.length = len; -} - -const gchar * -e_ews_attachment_info_get_mime_type (EEwsAttachmentInfo *info) -{ - g_return_val_if_fail (info != NULL, NULL); - g_return_val_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_INLINED, NULL); - - return info->data.inlined.mime_type; -} - -void -e_ews_attachment_info_set_mime_type (EEwsAttachmentInfo *info, - const gchar *mime_type) -{ - g_return_if_fail (info != NULL); - g_return_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_INLINED); - - g_free (info->data.inlined.mime_type); - info->data.inlined.mime_type = g_strdup (mime_type); -} - -const gchar * -e_ews_attachment_info_get_filename (EEwsAttachmentInfo *info) -{ - g_return_val_if_fail (info != NULL, NULL); - g_return_val_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_INLINED, NULL); - - return info->data.inlined.filename; -} - -void -e_ews_attachment_info_set_filename (EEwsAttachmentInfo *info, - const gchar *filename) -{ - g_return_if_fail (info != NULL); - g_return_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_INLINED); - - g_free (info->data.inlined.filename); - info->data.inlined.filename = g_strdup (filename); -} - -const gchar * -e_ews_attachment_info_get_uri (EEwsAttachmentInfo *info) -{ - g_return_val_if_fail (info != NULL, NULL); - g_return_val_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_URI, NULL); - - return info->data.uri; -} - -void -e_ews_attachment_info_set_uri (EEwsAttachmentInfo *info, - const gchar *uri) -{ - g_return_if_fail (info != NULL); - g_return_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_URI); - - g_free (info->data.uri); - info->data.uri = g_strdup (uri); -} - -const gchar * -e_ews_attachment_info_get_id (EEwsAttachmentInfo *info) -{ - g_return_val_if_fail (info != NULL, NULL); - - return info->id; -} - -void -e_ews_attachment_info_set_id (EEwsAttachmentInfo *info, - const gchar *id) -{ - g_return_if_fail (info != NULL); - - if (info->id != id) { - g_free (info->id); - info->id = g_strdup (id); - } -} - -/* Connection APIS */ - -/** - * e_ews_connection_find - * @uri: Exchange server uri - * @username: - * - * Find an existing connection for this user/uri, if it exists. - * - * Returns: EEwsConnection - **/ -EEwsConnection * -e_ews_connection_find (const gchar *uri, - const gchar *username) -{ - EEwsConnection *cnc; - gchar *hash_key; - - g_mutex_lock (&connecting); - - /* search the connection in our hash table */ - if (loaded_connections_permissions != NULL) { - hash_key = g_strdup_printf ( - "%s@%s", - username ? username : "", - uri); - cnc = g_hash_table_lookup ( - loaded_connections_permissions, hash_key); - g_free (hash_key); - - if (E_IS_EWS_CONNECTION (cnc) && - !e_ews_connection_get_disconnected_flag (cnc)) { - g_object_ref (cnc); - g_mutex_unlock (&connecting); - return cnc; - } - } - - g_mutex_unlock (&connecting); - - return NULL; -} - -/** - * e_ews_connection_list_existing: - * - * Returns: (transfer full) (element-type EEwsConnection): a new #GSList of all currently - * opened connections to all servers. Free the returned #GSList with - * g_slist_free_full (connections, g_object_unref); - * when no longer needed. - **/ -GSList * /* EEwsConnection * */ -e_ews_connection_list_existing (void) -{ - GSList *connections = NULL; - - g_mutex_lock (&connecting); - - /* search the connection in our hash table */ - if (loaded_connections_permissions != NULL) { - GHashTableIter iter; - gpointer value; - - g_hash_table_iter_init (&iter, loaded_connections_permissions); - while (g_hash_table_iter_next (&iter, NULL, &value)) { - if (value && !e_ews_connection_get_disconnected_flag (value)) - connections = g_slist_prepend (connections, g_object_ref (value)); - } - } - - g_mutex_unlock (&connecting); - - return connections; -} - -/** - * e_ews_connection_new_full - * @source: corresponding #ESource - * @uri: Exchange server uri - * @settings: a #CamelEwsSettings - * @allow_connection_reuse: whether can return already created connection - * - * This does not authenticate to the server. It merely stores the username and password. - * Authentication happens when a request is made to the server. - * - * Returns: EEwsConnection - **/ -EEwsConnection * -e_ews_connection_new_full (ESource *source, - const gchar *uri, - CamelEwsSettings *settings, - gboolean allow_connection_reuse) -{ - CamelNetworkSettings *network_settings; - EEwsConnection *cnc; - gchar *hash_key; - gchar *user; - - if (source) - g_return_val_if_fail (E_IS_SOURCE (source), NULL); - g_return_val_if_fail (uri != NULL, NULL); - g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), NULL); - - network_settings = CAMEL_NETWORK_SETTINGS (settings); - user = camel_network_settings_dup_user (network_settings); - hash_key = g_strdup_printf ("%s@%s", user, uri); - g_free (user); - - g_mutex_lock (&connecting); - - /* search the connection in our hash table */ - if (allow_connection_reuse && loaded_connections_permissions != NULL) { - cnc = g_hash_table_lookup ( - loaded_connections_permissions, hash_key); - - if (E_IS_EWS_CONNECTION (cnc) && - !e_ews_connection_get_disconnected_flag (cnc)) { - g_object_ref (cnc); - - g_free (hash_key); - - g_mutex_unlock (&connecting); - return cnc; - } - } - - /* not found, so create a new connection */ - cnc = g_object_new (E_TYPE_EWS_CONNECTION, - "settings", settings, - "source", source, - NULL); - - cnc->priv->uri = g_strdup (uri); - cnc->priv->hash_key = hash_key; /* takes ownership */ - - g_free (cnc->priv->impersonate_user); - if (camel_ews_settings_get_use_impersonation (settings)) { - cnc->priv->impersonate_user = camel_ews_settings_dup_impersonate_user (settings); - if (cnc->priv->impersonate_user && !*cnc->priv->impersonate_user) { - g_free (cnc->priv->impersonate_user); - cnc->priv->impersonate_user = NULL; - } - } else { - cnc->priv->impersonate_user = NULL; - } - - e_binding_bind_property ( - settings, "timeout", - cnc->priv->soup_session, "timeout", - G_BINDING_SYNC_CREATE); - - if (allow_connection_reuse) { - /* add the connection to the loaded_connections_permissions hash table */ - if (loaded_connections_permissions == NULL) - loaded_connections_permissions = g_hash_table_new_full ( - g_str_hash, g_str_equal, - g_free, NULL); - g_hash_table_insert ( - loaded_connections_permissions, - g_strdup (cnc->priv->hash_key), cnc); - } - - /* free memory */ - g_mutex_unlock (&connecting); - return cnc; - -} - -EEwsConnection * -e_ews_connection_new (ESource *source, - const gchar *uri, - CamelEwsSettings *settings) -{ - return e_ews_connection_new_full (source, uri, settings, TRUE); -} - -EEwsConnection * -e_ews_connection_new_for_backend (EBackend *backend, - ESourceRegistry *registry, - const gchar *uri, - CamelEwsSettings *settings) -{ - ESource *source; - EEwsConnection *cnc; - - g_return_val_if_fail (E_IS_BACKEND (backend), NULL); - g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL); - - source = e_backend_get_source (backend); - if (!source) - return e_ews_connection_new (source, uri, settings); - - g_object_ref (source); - - while (source && !e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION) && - e_source_get_parent (source)) { - ESource *parent; - - parent = e_source_registry_ref_source (registry, e_source_get_parent (source)); - if (!parent) { - g_clear_object (&source); - break; - } - - g_object_unref (source); - source = parent; - } - - if (source) - cnc = e_ews_connection_new (source, uri, settings); - else - cnc = e_ews_connection_new (e_backend_get_source (backend), uri, settings); - - g_clear_object (&source); - - return cnc; -} - -void -e_ews_connection_update_credentials (EEwsConnection *cnc, - const ENamedParameters *credentials) -{ - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - - if (credentials) { - const gchar *password; - - /* Update password only if it's provided, otherwise keep the previously set, if any */ - password = e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD); - if (password && *password) - e_ews_connection_set_password (cnc, password); - - if (e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME)) { - CamelNetworkSettings *network_settings; - - network_settings = CAMEL_NETWORK_SETTINGS (cnc->priv->settings); - camel_network_settings_set_user (network_settings, e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME)); - } - } else { - e_ews_connection_set_password (cnc, NULL); - } -} - -ESourceAuthenticationResult -e_ews_connection_try_credentials_sync (EEwsConnection *cnc, - const ENamedParameters *credentials, - ESource *use_source, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, - GCancellable *cancellable, - GError **error) -{ - ESourceAuthenticationResult result; - ESource *source; - gboolean de_set_source; - EwsFolderId *fid = NULL; - GSList *ids = NULL; - GError *local_error = NULL; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), E_SOURCE_AUTHENTICATION_ERROR); - - e_ews_connection_update_credentials (cnc, credentials); - - fid = g_new0 (EwsFolderId, 1); - fid->id = g_strdup ("inbox"); - fid->is_distinguished_id = TRUE; - ids = g_slist_append (ids, fid); - - source = e_ews_connection_get_source (cnc); - if (use_source && use_source != source) { - cnc->priv->source = g_object_ref (use_source); - de_set_source = TRUE; - } else { - source = NULL; - de_set_source = FALSE; - } - - e_ews_connection_get_folder_sync ( - cnc, EWS_PRIORITY_MEDIUM, "Default", - NULL, ids, NULL, cancellable, &local_error); - - if (de_set_source) { - g_clear_object (&cnc->priv->source); - cnc->priv->source = source; - } - - g_slist_free_full (ids, (GDestroyNotify) e_ews_folder_id_free); - - if (local_error == NULL) { - result = E_SOURCE_AUTHENTICATION_ACCEPTED; - } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) && - e_ews_connection_get_ssl_error_details (cnc, out_certificate_pem, out_certificate_errors)) { - result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED; - } else { - gboolean auth_failed; - - auth_failed = g_error_matches ( - local_error, EWS_CONNECTION_ERROR, - EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED); - - if (auth_failed) { - g_clear_error (&local_error); - - if (camel_ews_settings_get_auth_mechanism (cnc->priv->settings) != EWS_AUTH_TYPE_GSSAPI && - camel_ews_settings_get_auth_mechanism (cnc->priv->settings) != EWS_AUTH_TYPE_OAUTH2 && - (!credentials || !e_named_parameters_exists (credentials, E_SOURCE_CREDENTIAL_PASSWORD))) { - result = E_SOURCE_AUTHENTICATION_REQUIRED; - } else { - result = E_SOURCE_AUTHENTICATION_REJECTED; - } - } else { - g_propagate_error (error, local_error); - result = E_SOURCE_AUTHENTICATION_ERROR; - } - - e_ews_connection_set_password (cnc, NULL); - } - - return result; -} - -ESource * -e_ews_connection_get_source (EEwsConnection *cnc) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - return cnc->priv->source; -} - -gboolean -e_ews_connection_get_ssl_error_details (EEwsConnection *cnc, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (out_certificate_pem != NULL, FALSE); - g_return_val_if_fail (out_certificate_errors != NULL, FALSE); - - g_mutex_lock (&cnc->priv->property_lock); - if (!cnc->priv->ssl_info_set) { - g_mutex_unlock (&cnc->priv->property_lock); - return FALSE; - } - - *out_certificate_pem = g_strdup (cnc->priv->ssl_certificate_pem); - *out_certificate_errors = cnc->priv->ssl_certificate_errors; - - g_mutex_unlock (&cnc->priv->property_lock); - - return TRUE; -} - -const gchar * -e_ews_connection_get_uri (EEwsConnection *cnc) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - return cnc->priv->uri; -} - -ESoupAuthBearer * -e_ews_connection_ref_bearer_auth (EEwsConnection *cnc) -{ - ESoupAuthBearer *bearer_auth; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - g_mutex_lock (&cnc->priv->property_lock); - bearer_auth = cnc->priv->bearer_auth; - if (bearer_auth) - g_object_ref (bearer_auth); - g_mutex_unlock (&cnc->priv->property_lock); - - return bearer_auth; -} - -void -e_ews_connection_set_bearer_auth (EEwsConnection *cnc, - ESoupAuthBearer *bearer_auth) -{ - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - if (bearer_auth) - g_return_if_fail (E_IS_SOUP_AUTH_BEARER (bearer_auth)); - - g_mutex_lock (&cnc->priv->property_lock); - - if (bearer_auth != cnc->priv->bearer_auth) { - g_clear_object (&cnc->priv->bearer_auth); - cnc->priv->bearer_auth = bearer_auth; - - if (cnc->priv->bearer_auth) - g_object_ref (cnc->priv->bearer_auth); - } - - g_mutex_unlock (&cnc->priv->property_lock); -} - -const gchar * -e_ews_connection_get_password (EEwsConnection *cnc) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - return cnc->priv->password; -} - -gchar * -e_ews_connection_dup_password (EEwsConnection *cnc) -{ - const gchar *protected; - gchar *duplicate; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - g_mutex_lock (&cnc->priv->property_lock); - - protected = e_ews_connection_get_password (cnc); - duplicate = g_strdup (protected); - - g_mutex_unlock (&cnc->priv->property_lock); - - return duplicate; -} - -void -e_ews_connection_set_password (EEwsConnection *cnc, - const gchar *password) -{ - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - - g_mutex_lock (&cnc->priv->property_lock); - - /* Zero-fill the old password before freeing it. */ - if (cnc->priv->password != NULL && *cnc->priv->password != '\0') - memset (cnc->priv->password, 0, strlen (cnc->priv->password)); - - g_free (cnc->priv->password); - cnc->priv->password = g_strdup (password); - - g_mutex_unlock (&cnc->priv->property_lock); - - g_object_notify (G_OBJECT (cnc), "password"); -} - -const gchar * -e_ews_connection_get_impersonate_user (EEwsConnection *cnc) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - return cnc->priv->impersonate_user; -} - -GProxyResolver * -e_ews_connection_ref_proxy_resolver (EEwsConnection *cnc) -{ - GProxyResolver *proxy_resolver = NULL; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - g_mutex_lock (&cnc->priv->property_lock); - - if (cnc->priv->proxy_resolver != NULL) - proxy_resolver = g_object_ref (cnc->priv->proxy_resolver); - - g_mutex_unlock (&cnc->priv->property_lock); - - return proxy_resolver; -} - -void -e_ews_connection_set_proxy_resolver (EEwsConnection *cnc, - GProxyResolver *proxy_resolver) -{ - gboolean notify = FALSE; - - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - - g_mutex_lock (&cnc->priv->property_lock); - - /* Emitting a "notify" signal unnecessarily might have - * unwanted side effects like cancelling a SoupMessage. - * Only emit if we now have a different GProxyResolver. */ - - if (proxy_resolver != cnc->priv->proxy_resolver) { - g_clear_object (&cnc->priv->proxy_resolver); - cnc->priv->proxy_resolver = proxy_resolver; - - if (proxy_resolver != NULL) - g_object_ref (proxy_resolver); - - notify = TRUE; - } - - g_mutex_unlock (&cnc->priv->property_lock); - - if (notify) - g_object_notify (G_OBJECT (cnc), "proxy-resolver"); -} - -CamelEwsSettings * -e_ews_connection_ref_settings (EEwsConnection *cnc) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - return g_object_ref (cnc->priv->settings); -} - -SoupSession * -e_ews_connection_ref_soup_session (EEwsConnection *cnc) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - return g_object_ref (cnc->priv->soup_session); -} - -gboolean -e_ews_connection_get_disconnected_flag (EEwsConnection *cnc) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - - return cnc->priv->disconnected_flag; -} - -void -e_ews_connection_set_disconnected_flag (EEwsConnection *cnc, - gboolean disconnected_flag) -{ - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - - cnc->priv->disconnected_flag = disconnected_flag; -} - -static xmlDoc * -e_ews_autodiscover_ws_xml (const gchar *email_address) -{ - xmlDoc *doc; - xmlNode *node; - xmlNs *ns; - - doc = xmlNewDoc ((xmlChar *) "1.0"); - node = xmlNewDocNode (doc, NULL, (xmlChar *)"Autodiscover", NULL); - xmlDocSetRootElement (doc, node); - ns = xmlNewNs ( - node, - (xmlChar *)"http://schemas.microsoft.com/exchange/autodiscover/outlook/requestschema/2006", NULL); - - node = xmlNewChild (node, ns, (xmlChar *)"Request", NULL); - xmlNewChild ( - node, ns, (xmlChar *)"EMailAddress", - (xmlChar *) email_address); - xmlNewChild ( - node, ns, (xmlChar *)"AcceptableResponseSchema", - (xmlChar *)"http://schemas.microsoft.com/exchange/autodiscover/outlook/responseschema/2006a"); - - return doc; -} - -struct _autodiscover_data { - EEwsConnection *cnc; - xmlOutputBuffer *buf; - SoupMessage *msgs[5]; - - GCancellable *cancellable; - gulong cancel_id; - - /* Results */ - gchar *as_url; - gchar *oab_url; -}; - -static void -autodiscover_data_free (struct _autodiscover_data *ad) -{ - xmlOutputBufferClose (ad->buf); - - if (ad->cancellable != NULL) { - g_cancellable_disconnect (ad->cancellable, ad->cancel_id); - g_object_unref (ad->cancellable); - } - - /* Unref the connection after the cancellable is disconnected, - to avoid race condition when the connection is freed inside - the g_cancellable_disconnect() function, which holds the - cancellable lock and blocks all other threads, while at - the same time the connection can wait for the finish of - its worker thread. */ - g_object_unref (ad->cnc); - - g_free (ad->as_url); - g_free (ad->oab_url); - - g_slice_free (struct _autodiscover_data, ad); -} - -static void -autodiscover_cancelled_cb (GCancellable *cancellable, - EEwsConnection *cnc) -{ - ews_connection_schedule_abort (cnc); -} - -/* Called when each soup message completes */ -static void -autodiscover_response_cb (SoupSession *session, - SoupMessage *msg, - gpointer data) - -{ - GSimpleAsyncResult *simple = data; - struct _autodiscover_data *ad; - EwsUrls *urls = NULL; - guint status = msg->status_code; - xmlDoc *doc; - xmlNode *node; - gint idx; - gboolean success = FALSE; - GError *error = NULL; - - ad = g_simple_async_result_get_op_res_gpointer (simple); - - for (idx = 0; idx < 5; idx++) { - if (ad->msgs[idx] == msg) - break; - } - if (idx == 5) { - /* We already got removed (cancelled). Do nothing */ - goto unref; - } - - ad->msgs[idx] = NULL; - - if (status != 200) { - gboolean expired = FALSE; - gchar *service_url = NULL; - - if (e_ews_connection_utils_check_x_ms_credential_headers (msg, NULL, &expired, &service_url) && expired) { - e_ews_connection_utils_expired_password_to_error (service_url, &error); - } else { - g_set_error ( - &error, SOUP_HTTP_ERROR, status, - "%d %s", status, msg->reason_phrase); - - if (status == SOUP_STATUS_SSL_FAILED) - ews_connection_check_ssl_error (ad->cnc, msg); - } - - g_free (service_url); - - goto failed; - } - - e_ews_debug_dump_raw_soup_response (msg); - doc = xmlReadMemory ( - msg->response_body->data, - msg->response_body->length, - "autodiscover.xml", NULL, 0); - if (!doc) { - g_set_error ( - &error, EWS_CONNECTION_ERROR, -1, - _("Failed to parse autodiscover response XML")); - goto failed; - } - node = xmlDocGetRootElement (doc); - if (strcmp ((gchar *) node->name, "Autodiscover")) { - g_set_error ( - &error, EWS_CONNECTION_ERROR, -1, - _("Failed to find element")); - goto failed; - } - for (node = node->children; node; node = node->next) { - if (node->type == XML_ELEMENT_NODE && - !strcmp ((gchar *) node->name, "Response")) - break; - } - if (!node) { - g_set_error ( - &error, EWS_CONNECTION_ERROR, -1, - _("Failed to find element")); - goto failed; - } - for (node = node->children; node; node = node->next) { - if (node->type == XML_ELEMENT_NODE && - !strcmp ((gchar *) node->name, "Account")) - break; - } - if (!node) { - g_set_error ( - &error, EWS_CONNECTION_ERROR, -1, - _("Failed to find element")); - goto failed; - } - - urls = g_new0 (EwsUrls, 1); - for (node = node->children; node; node = node->next) { - if (node->type == XML_ELEMENT_NODE && - !strcmp ((gchar *) node->name, "Protocol")) { - success = autodiscover_parse_protocol (node, urls); - /* Since the server may send back multiple nodes - * don't break unless we found the both URLs. - */ - if (success) - break; - } - } - - if (!success) { - if (urls->as_url != NULL) - xmlFree (urls->as_url); - if (urls->oab_url != NULL) - xmlFree (urls->oab_url); - g_free (urls); - g_set_error ( - &error, EWS_CONNECTION_ERROR, -1, - _("Failed to find and in autodiscover response")); - goto failed; - } - - /* We have a good response; cancel all the others */ - for (idx = 0; idx < 5; idx++) { - if (ad->msgs[idx]) { - SoupMessage *m = ad->msgs[idx]; - ad->msgs[idx] = NULL; - ews_connection_schedule_cancel_message (ad->cnc, m); - } - } - - if (urls->as_url != NULL) { - ad->as_url = g_strdup ((gchar *) urls->as_url); - xmlFree (urls->as_url); - } - - if (urls->oab_url != NULL) { - ad->oab_url = g_strdup ((gchar *) urls->oab_url); - xmlFree (urls->oab_url); - } - - g_free (urls); - - goto exit; - - failed: - for (idx = 0; idx < 5; idx++) { - if (ad->msgs[idx]) { - /* There's another request outstanding. - * Hope that it has better luck. */ - g_clear_error (&error); - goto unref; - } - } - - /* FIXME: We're actually returning the *last* error here, - * and in some cases (stupid firewalls causing timeouts) - * that's going to be the least interesting one. We probably - * want the *first* error */ - g_simple_async_result_take_error (simple, error); - - exit: - g_simple_async_result_complete_in_idle (simple); - - unref: - /* This function is processed within e_ews_soup_thread() and the 'simple' - * holds reference to EEwsConnection. For cases when this is the last - * reference to 'simple' the unref would cause crash, because of g_thread_join() - * in connection's dispose, trying to wait on the end of itself, thus it's - * safer to unref the 'simple' in a dedicated thread. - */ - e_ews_connection_utils_unref_in_thread (simple); -} - -static void post_restarted (SoupMessage *msg, gpointer data) -{ - xmlOutputBuffer *buf = data; - - /* Not all restarts are due to a redirect; some are for auth */ - if (msg->status_code == 401) - return; - - /* In violation of RFC2616, libsoup will change a POST request to - * a GET on receiving a 302 redirect. */ - printf ("Working around libsoup bug with redirect\n"); - g_object_set (msg, SOUP_MESSAGE_METHOD, "POST", NULL); - - soup_message_set_request ( - msg, "text/xml; charset=utf-8", SOUP_MEMORY_COPY, - (gchar *) - #ifdef LIBXML2_NEW_BUFFER - xmlOutputBufferGetContent (buf), xmlOutputBufferGetSize (buf) - #else - buf->buffer->content, buf->buffer->use - #endif - ); -} - -static SoupMessage * -e_ews_get_msg_for_url (EEwsConnection *cnc, - CamelEwsSettings *settings, - const gchar *url, - xmlOutputBuffer *buf, - GError **error) -{ - SoupMessage *msg; - - if (url == NULL) { - g_set_error_literal ( - error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("URL cannot be NULL")); - return NULL; - } - - msg = soup_message_new (buf != NULL ? "POST" : "GET", url); - if (!msg) { - g_set_error ( - error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, - _("URL “%s” is not valid"), url); - return NULL; - } - - if (cnc->priv->source) - e_soup_ssl_trust_connect (msg, cnc->priv->source); - - e_ews_message_attach_chunk_allocator (msg); - - e_ews_message_set_user_agent_header (msg, settings); - - if (buf != NULL) { - soup_message_set_request ( - msg, "text/xml; charset=utf-8", SOUP_MEMORY_COPY, - (gchar *) - #ifdef LIBXML2_NEW_BUFFER - xmlOutputBufferGetContent (buf), xmlOutputBufferGetSize (buf) - #else - buf->buffer->content, buf->buffer->use - #endif - ); - g_signal_connect ( - msg, "restarted", - G_CALLBACK (post_restarted), buf); - } - - e_ews_debug_dump_raw_soup_request (msg); - - return msg; -} - -gboolean -e_ews_autodiscover_ws_url_sync (ESource *source, - CamelEwsSettings *settings, - const gchar *email_address, - const gchar *password, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (CAMEL_IS_EWS_SETTINGS (settings), FALSE); - g_return_val_if_fail (email_address != NULL, FALSE); - g_return_val_if_fail (password != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_autodiscover_ws_url (source, settings, email_address, password, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_autodiscover_ws_url_finish (settings, result, out_certificate_pem, out_certificate_errors, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_autodiscover_ws_url (ESource *source, - CamelEwsSettings *settings, - const gchar *email_address, - const gchar *password, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - struct _autodiscover_data *ad; - xmlOutputBuffer *buf; - gchar *url1, *url2, *url3, *url4, *url5; - gchar *domain; - xmlDoc *doc; - EEwsConnection *cnc; - SoupURI *soup_uri = NULL; - gboolean use_secure = TRUE; - const gchar *host_url; - GError *error = NULL; - - g_return_if_fail (CAMEL_IS_EWS_SETTINGS (settings)); - g_return_if_fail (email_address != NULL); - g_return_if_fail (password != NULL); - - simple = g_simple_async_result_new ( - G_OBJECT (settings), callback, - user_data, e_ews_autodiscover_ws_url); - - domain = strchr (email_address, '@'); - if (domain == NULL || *domain == '\0') { - g_simple_async_result_set_error ( - simple, EWS_CONNECTION_ERROR, -1, - "%s", _("Email address is missing a domain part")); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - return; - } - domain++; - - doc = e_ews_autodiscover_ws_xml (email_address); - buf = xmlAllocOutputBuffer (NULL); - xmlNodeDumpOutput (buf, doc, xmlDocGetRootElement (doc), 0, 1, NULL); - xmlOutputBufferFlush (buf); - - url1 = NULL; - url2 = NULL; - url3 = NULL; - url4 = NULL; - url5 = NULL; - - host_url = camel_ews_settings_get_hosturl (settings); - if (host_url != NULL) - soup_uri = soup_uri_new (host_url); - - if (soup_uri != NULL) { - const gchar *host = soup_uri_get_host (soup_uri); - const gchar *scheme = soup_uri_get_scheme (soup_uri); - - use_secure = g_strcmp0 (scheme, "https") == 0; - - url1 = g_strdup_printf ("http%s://%s/autodiscover/autodiscover.xml", use_secure ? "s" : "", host); - url2 = g_strdup_printf ("http%s://autodiscover.%s/autodiscover/autodiscover.xml", use_secure ? "s" : "", host); - - /* outlook.office365.com has its autodiscovery at outlook.com */ - if (host && g_ascii_strcasecmp (host, "outlook.office365.com") == 0 && - domain && g_ascii_strcasecmp (host, "outlook.com") != 0) { - url5 = g_strdup_printf ("https://outlook.com/autodiscover/autodiscover.xml"); - } - - soup_uri_free (soup_uri); - } - - url3 = g_strdup_printf ("http%s://%s/autodiscover/autodiscover.xml", use_secure ? "s" : "", domain); - url4 = g_strdup_printf ("http%s://autodiscover.%s/autodiscover/autodiscover.xml", use_secure ? "s" : "", domain); - - cnc = e_ews_connection_new (source, url3, settings); - e_ews_connection_set_password (cnc, password); - - /* - * http://msdn.microsoft.com/en-us/library/ee332364.aspx says we are - * supposed to try $domain and then autodiscover.$domain. But some - * people have broken firewalls on the former which drop packets - * instead of rejecting connections, and make the request take ages - * to time out. So run both queries in parallel and let the fastest - * (successful) one win. - */ - ad = g_slice_new0 (struct _autodiscover_data); - ad->cnc = cnc; /* takes ownership */ - ad->buf = buf; /* takes ownership */ - - if (G_IS_CANCELLABLE (cancellable)) { - ad->cancellable = g_object_ref (cancellable); - ad->cancel_id = g_cancellable_connect ( - ad->cancellable, - G_CALLBACK (autodiscover_cancelled_cb), - g_object_ref (cnc), - g_object_unref); - } - - g_simple_async_result_set_op_res_gpointer ( - simple, ad, (GDestroyNotify) autodiscover_data_free); - - /* Passing a NULL URL string returns NULL. */ - ad->msgs[0] = e_ews_get_msg_for_url (cnc, settings, url1, buf, &error); - ad->msgs[1] = e_ews_get_msg_for_url (cnc, settings, url2, buf, NULL); - ad->msgs[2] = e_ews_get_msg_for_url (cnc, settings, url3, buf, NULL); - ad->msgs[3] = e_ews_get_msg_for_url (cnc, settings, url4, buf, NULL); - ad->msgs[4] = e_ews_get_msg_for_url (cnc, settings, url5, buf, NULL); - - /* These have to be submitted only after they're both set in ad->msgs[] - * or there will be races with fast completion */ - if (ad->msgs[0] != NULL) - ews_connection_schedule_queue_message (cnc, ad->msgs[0], autodiscover_response_cb, g_object_ref (simple)); - if (ad->msgs[1] != NULL) - ews_connection_schedule_queue_message (cnc, ad->msgs[1], autodiscover_response_cb, g_object_ref (simple)); - if (ad->msgs[2] != NULL) - ews_connection_schedule_queue_message (cnc, ad->msgs[2], autodiscover_response_cb, g_object_ref (simple)); - if (ad->msgs[3] != NULL) - ews_connection_schedule_queue_message (cnc, ad->msgs[3], autodiscover_response_cb, g_object_ref (simple)); - if (ad->msgs[4] != NULL) - ews_connection_schedule_queue_message (cnc, ad->msgs[4], autodiscover_response_cb, g_object_ref (simple)); - - xmlFreeDoc (doc); - g_free (url1); - g_free (url2); - g_free (url3); - g_free (url4); - - if (error && !ad->msgs[0] && !ad->msgs[1] && !ad->msgs[2] && !ad->msgs[3] && !ad->msgs[4]) { - g_simple_async_result_take_error (simple, error); - g_simple_async_result_complete_in_idle (simple); - } else { - g_clear_error (&error); - - /* each request holds a reference to 'simple', - * thus remove one, to have it actually freed */ - g_object_unref (simple); - } -} - -static gboolean -has_suffix_icmp (const gchar *text, - const gchar *suffix) -{ - gint ii, tlen, slen; - - g_return_val_if_fail (text != NULL, FALSE); - g_return_val_if_fail (suffix != NULL, FALSE); - - tlen = strlen (text); - slen = strlen (suffix); - - if (!*text || !*suffix || tlen < slen) - return FALSE; - - for (ii = 0; ii < slen; ii++) { - if (g_ascii_tolower (text[tlen - ii - 1]) != - g_ascii_tolower (suffix[slen - ii - 1])) - break; - } - - return ii == slen; -} - -gboolean -e_ews_autodiscover_ws_url_finish (CamelEwsSettings *settings, - GAsyncResult *result, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, - GError **error) -{ - GSimpleAsyncResult *simple; - struct _autodiscover_data *ad; - GError *local_error = NULL; - - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (settings), - e_ews_autodiscover_ws_url), FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - ad = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, &local_error)) { - if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) { - if (!e_ews_connection_get_ssl_error_details (ad->cnc, out_certificate_pem, out_certificate_errors)) { - if (out_certificate_pem) - *out_certificate_pem = NULL; - if (out_certificate_errors) - *out_certificate_errors = 0; - } - } - - g_propagate_error (error, local_error); - - return FALSE; - } - - g_warn_if_fail (ad->as_url != NULL); - g_warn_if_fail (ad->oab_url != NULL); - - camel_ews_settings_set_hosturl (settings, ad->as_url); - - if (!has_suffix_icmp (ad->oab_url, "oab.xml")) { - gchar *tmp; - - if (g_str_has_suffix (ad->oab_url, "/")) - tmp = g_strconcat (ad->oab_url, "oab.xml", NULL); - else - tmp = g_strconcat (ad->oab_url, "/", "oab.xml", NULL); - - camel_ews_settings_set_oaburl (settings, tmp); - g_free (tmp); - } else { - camel_ews_settings_set_oaburl (settings, ad->oab_url); - } - - return TRUE; -} - -struct _oal_req_data { - EEwsConnection *cnc; - SoupMessage *soup_message; - gchar *oal_id; - gchar *oal_element; - - GSList *oals; - GSList *elements; - gchar *etag; - - GCancellable *cancellable; - gulong cancel_id; - - /* for dowloading oal file */ - gchar *cache_filename; - GError *error; - EwsProgressFn progress_fn; - gpointer progress_data; - gsize response_size; - gsize received_size; -}; - -static void -oal_req_data_free (struct _oal_req_data *data) -{ - /* The SoupMessage is owned by the SoupSession. */ - g_object_unref (data->cnc); - - g_free (data->oal_id); - g_free (data->oal_element); - g_free (data->etag); - - g_slist_free_full (data->oals, (GDestroyNotify) ews_oal_free); - g_slist_free_full (data->elements, (GDestroyNotify) ews_oal_details_free); - - if (data->cancellable != NULL) { - g_cancellable_disconnect (data->cancellable, data->cancel_id); - g_object_unref (data->cancellable); - } - - g_free (data->cache_filename); - - g_slice_free (struct _oal_req_data, data); -} - -static gchar * -get_property (xmlNodePtr node_ptr, - const gchar *name) -{ - xmlChar *xml_s; - gchar *s; - - xml_s = xmlGetProp (node_ptr, (const xmlChar *) name); - s = g_strdup ((gchar *) xml_s); - xmlFree (xml_s); - - return s; -} - -static guint32 -get_property_as_uint32 (xmlNodePtr node_ptr, - const gchar *name) -{ - gchar *s; - guint32 val = -1; - - s = get_property (node_ptr, name); - if (s) - sscanf (s,"%"G_GUINT32_FORMAT, &val); - g_free (s); - - return val; -} - -static gchar * -get_content (xmlNodePtr node_ptr) -{ - xmlChar *xml_s; - gchar *s; - - xml_s = xmlNodeGetContent (node_ptr); - s = g_strdup ((gchar *) xml_s); - xmlFree (xml_s); - - return s; -} - -static GSList * -parse_oal_full_details (xmlNode *node, - const gchar *element) -{ - GSList *elements = NULL; - - for (node = node->children; node; node = node->next) { - EwsOALDetails *det; - if (node->type != XML_ELEMENT_NODE) - continue; - if (element && strcmp ((gchar *) node->name, element)) - continue; - if (!element && strcmp ((gchar *) node->name, "Full") && - strcmp((gchar *) node->name, "Diff")) - continue; - - det = g_new0 (EwsOALDetails, 1); - det->type = g_strdup((gchar *) node->name); - det->seq = get_property_as_uint32 (node, "seq"); - det->ver = get_property_as_uint32 (node, "ver"); - det->size = get_property_as_uint32 (node, "size"); - det->uncompressed_size = get_property_as_uint32 (node, "uncompressedsize"); - det->sha = get_property (node, "uncompressedsize"); - det->filename = g_strstrip (get_content (node)); - - elements = g_slist_prepend (elements, det); - if (element && !strcmp (element, "Full")) - break; - } - - return elements; -} - -/* this is run in cnc->priv->soup_thread */ -static void -oal_response_cb (SoupSession *soup_session, - SoupMessage *soup_message, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - struct _oal_req_data *data; - const gchar *etag; - xmlDoc *doc; - xmlNode *node; - - simple = G_SIMPLE_ASYNC_RESULT (user_data); - data = g_simple_async_result_get_op_res_gpointer (simple); - - ews_connection_check_ssl_error (data->cnc, soup_message); - - if (ews_connection_credentials_failed (data->cnc, soup_message, simple)) { - goto exit; - } else if (soup_message->status_code != 200) { - if (soup_message->status_code == SOUP_STATUS_UNAUTHORIZED && - soup_message->response_headers) { - const gchar *diagnostics; - - diagnostics = soup_message_headers_get_list (soup_message->response_headers, "X-MS-DIAGNOSTICS"); - if (diagnostics && strstr (diagnostics, "invalid_grant")) { - g_simple_async_result_set_error ( - simple, - EWS_CONNECTION_ERROR, - EWS_CONNECTION_ERROR_ACCESSDENIED, - "%s", diagnostics); - goto exit; - } else if (diagnostics && *diagnostics) { - g_simple_async_result_set_error ( - simple, - EWS_CONNECTION_ERROR, - EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED, - "%s", diagnostics); - goto exit; - } - } - g_simple_async_result_set_error ( - simple, SOUP_HTTP_ERROR, - soup_message->status_code, - "%d %s", - soup_message->status_code, - soup_message->reason_phrase); - goto exit; - } - - etag = soup_message_headers_get_one(soup_message->response_headers, - "ETag"); - if (etag) - data->etag = g_strdup(etag); - - e_ews_debug_dump_raw_soup_response (soup_message); - - doc = xmlReadMemory ( - soup_message->response_body->data, - soup_message->response_body->length, - "oab.xml", NULL, 0); - if (doc == NULL) { - g_simple_async_result_set_error ( - simple, EWS_CONNECTION_ERROR, -1, - "%s", _("Failed to parse oab XML")); - goto exit; - } - - node = xmlDocGetRootElement (doc); - if (strcmp ((gchar *) node->name, "OAB") != 0) { - g_simple_async_result_set_error ( - simple, EWS_CONNECTION_ERROR, -1, - "%s", _("Failed to find element\n")); - goto exit_doc; - } - - for (node = node->children; node; node = node->next) { - if (node->type == XML_ELEMENT_NODE && strcmp ((gchar *) node->name, "OAL") == 0) { - if (data->oal_id == NULL) { - EwsOAL *oal = g_new0 (EwsOAL, 1); - - oal->id = get_property (node, "id"); - oal->dn = get_property (node, "dn"); - oal->name = get_property (node, "name"); - - data->oals = g_slist_prepend (data->oals, oal); - } else { - gchar *id = get_property (node, "id"); - - if (strcmp (id, data->oal_id) == 0) { - /* parse details of full_details file */ - data->elements = parse_oal_full_details (node, data->oal_element); - - g_free (id); - break; - } - - g_free (id); - } - } - } - - data->oals = g_slist_reverse (data->oals); - - exit_doc: - xmlFreeDoc (doc); - exit: - g_simple_async_result_complete_in_idle (simple); - /* This is run in cnc->priv->soup_thread, and the cnc is held by simple, thus - * for cases when the complete_in_idle is finished before the unref call, when - * the cnc will be left with the last reference and thus cannot join the soup_thread - * while still in it, the unref is done in a dedicated thread. */ - e_ews_connection_utils_unref_in_thread (simple); -} - -static void -ews_cancel_msg (GCancellable *cancellable, - struct _oal_req_data *data) -{ - ews_connection_schedule_cancel_message (data->cnc, data->soup_message); -} - -gboolean -e_ews_connection_get_oal_list_sync (EEwsConnection *cnc, - GSList **oals, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_oal_list ( - cnc, cancellable, e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_oal_list_finish ( - cnc, result, oals, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_get_oal_list (EEwsConnection *cnc, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - SoupMessage *soup_message; - struct _oal_req_data *data; - GError *error = NULL; - - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - - soup_message = e_ews_get_msg_for_url (cnc, cnc->priv->settings, cnc->priv->uri, NULL, &error); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_oal_list); - - if (!soup_message) { - g_simple_async_result_take_error (simple, error); - g_simple_async_result_complete_in_idle (simple); - return; - } - - data = g_slice_new0 (struct _oal_req_data); - data->cnc = g_object_ref (cnc); - data->soup_message = soup_message; /* the session owns this */ - - if (G_IS_CANCELLABLE (cancellable)) { - data->cancellable = g_object_ref (cancellable); - data->cancel_id = g_cancellable_connect ( - data->cancellable, - G_CALLBACK (ews_cancel_msg), - data, (GDestroyNotify) NULL); - } - - g_simple_async_result_set_op_res_gpointer ( - simple, data, (GDestroyNotify) oal_req_data_free); - - ews_connection_schedule_queue_message (cnc, soup_message, oal_response_cb, simple); -} - -gboolean -e_ews_connection_get_oal_list_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **oals, - GError **error) -{ - GSimpleAsyncResult *simple; - struct _oal_req_data *data; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_oal_list), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (oals != NULL) { - *oals = data->oals; - data->oals = NULL; - } - - return TRUE; -} - -/** - * e_ews_connection_get_oal_detail - * @cnc: - * @oal_id: - * @oal_element: - * @elements: "Full" "Diff" "Template" are the possible values. - * @cancellable: - * @error: - * - * - * Returns: - **/ -gboolean -e_ews_connection_get_oal_detail_sync (EEwsConnection *cnc, - const gchar *oal_id, - const gchar *oal_element, - const gchar *old_etag, - GSList **elements, - gchar **etag, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_oal_detail ( - cnc, oal_id, oal_element, old_etag, - cancellable, e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_oal_detail_finish ( - cnc, result, elements, etag, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_get_oal_detail (EEwsConnection *cnc, - const gchar *oal_id, - const gchar *oal_element, - const gchar *etag, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - SoupMessage *soup_message; - struct _oal_req_data *data; - gchar *sep; - GError *error = NULL; - - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - - soup_message = e_ews_get_msg_for_url (cnc, cnc->priv->settings, cnc->priv->uri, NULL, &error); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_oal_detail); - - if (!soup_message) { - g_simple_async_result_take_error (simple, error); - g_simple_async_result_complete_in_idle (simple); - return; - } - - if (etag && *etag) - soup_message_headers_append (soup_message->request_headers, - "If-None-Match", etag); - - data = g_slice_new0 (struct _oal_req_data); - data->cnc = g_object_ref (cnc); - data->soup_message = soup_message; /* the session owns this */ - data->oal_id = g_strdup (oal_id); - data->oal_element = g_strdup (oal_element); - - /* oal_id can be of form "GUID:name", but here is compared only GUID */ - sep = strchr (data->oal_id, ':'); - if (sep) - *sep = '\0'; - - if (G_IS_CANCELLABLE (cancellable)) { - data->cancellable = g_object_ref (cancellable); - data->cancel_id = g_cancellable_connect ( - data->cancellable, - G_CALLBACK (ews_cancel_msg), - data, (GDestroyNotify) NULL); - } - - g_simple_async_result_set_op_res_gpointer ( - simple, data, (GDestroyNotify) oal_req_data_free); - - ews_connection_schedule_queue_message (cnc, soup_message, oal_response_cb, simple); -} - -gboolean -e_ews_connection_get_oal_detail_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **elements, - gchar **etag, - GError **error) -{ - GSimpleAsyncResult *simple; - struct _oal_req_data *data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_oal_detail), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (elements != NULL) { - *elements = data->elements; - data->elements = NULL; - } - if (etag != NULL) { - *etag = data->etag; - data->etag = NULL; - } - - return TRUE; - -} - -static void -oal_download_response_cb (SoupSession *soup_session, - SoupMessage *soup_message, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - struct _oal_req_data *data; - - simple = G_SIMPLE_ASYNC_RESULT (user_data); - data = g_simple_async_result_get_op_res_gpointer (simple); - - ews_connection_check_ssl_error (data->cnc, soup_message); - - if (ews_connection_credentials_failed (data->cnc, soup_message, simple)) { - g_unlink (data->cache_filename); - } else if (soup_message->status_code != 200) { - g_simple_async_result_set_error ( - simple, SOUP_HTTP_ERROR, - soup_message->status_code, - "%d %s", - soup_message->status_code, - soup_message->reason_phrase); - g_unlink (data->cache_filename); - - } else if (data->error != NULL) { - g_simple_async_result_take_error (simple, data->error); - data->error = NULL; - g_unlink (data->cache_filename); - } - - e_ews_debug_dump_raw_soup_response (soup_message); - - g_simple_async_result_complete_in_idle (simple); - e_ews_connection_utils_unref_in_thread (simple); -} - -static void -ews_soup_got_headers (SoupMessage *msg, - gpointer user_data) -{ - struct _oal_req_data *data = (struct _oal_req_data *) user_data; - const gchar *size; - - size = soup_message_headers_get_one ( - msg->response_headers, - "Content-Length"); - - if (size) - data->response_size = strtol (size, NULL, 10); -} - -static void -ews_soup_restarted (SoupMessage *msg, - gpointer user_data) -{ - struct _oal_req_data *data = (struct _oal_req_data *) user_data; - - data->response_size = 0; - data->received_size = 0; -} - -static void -ews_soup_got_chunk (SoupMessage *msg, - SoupBuffer *chunk, - gpointer user_data) -{ - struct _oal_req_data *data = (struct _oal_req_data *) user_data; - gint fd; - - if (msg->status_code != 200) - return; - - data->received_size += chunk->length; - - if (data->response_size && data->progress_fn) { - gint pc = data->received_size * 100 / data->response_size; - data->progress_fn (data->progress_data, pc); - } - - fd = g_open (data->cache_filename, O_RDONLY | O_WRONLY | O_APPEND | O_CREAT, 0600); - if (fd != -1) { - if (write (fd, (const gchar *) chunk->data, chunk->length) != chunk->length) { - g_set_error ( - &data->error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_UNKNOWN, - "Failed to write streaming data to file '%s': %s", data->cache_filename, g_strerror (errno)); - } - close (fd); - } else { - g_set_error ( - &data->error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_UNKNOWN, - "Failed to open the cache file '%s': %s", data->cache_filename, g_strerror (errno)); - } -} - -gboolean -e_ews_connection_download_oal_file_sync (EEwsConnection *cnc, - const gchar *cache_filename, - EwsProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_download_oal_file ( - cnc, cache_filename, - progress_fn, progress_data, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_download_oal_file_finish ( - cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_download_oal_file (EEwsConnection *cnc, - const gchar *cache_filename, - EwsProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *simple; - SoupMessage *soup_message; - struct _oal_req_data *data; - GError *error = NULL; - - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - - soup_message = e_ews_get_msg_for_url (cnc, cnc->priv->settings, cnc->priv->uri, NULL, &error); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_download_oal_file); - - if (!soup_message) { - g_simple_async_result_take_error (simple, error); - g_simple_async_result_complete_in_idle (simple); - return; - } - - data = g_slice_new0 (struct _oal_req_data); - data->cnc = g_object_ref (cnc); - data->soup_message = soup_message; /* the session owns this */ - data->cache_filename = g_strdup (cache_filename); - data->progress_fn = progress_fn; - data->progress_data = progress_data; - - if (G_IS_CANCELLABLE (cancellable)) { - data->cancellable = g_object_ref (cancellable); - data->cancel_id = g_cancellable_connect ( - data->cancellable, - G_CALLBACK (ews_cancel_msg), - data, (GDestroyNotify) NULL); - } - - g_simple_async_result_set_op_res_gpointer ( - simple, data, (GDestroyNotify) oal_req_data_free); - - /* - * Don't use streaming-based messages when we are loggin the traffic - * to generate trace files for tests - */ - if (e_ews_debug_get_log_level () <= 2) - soup_message_body_set_accumulate (soup_message->response_body, FALSE); - - g_signal_connect ( - soup_message, "got-headers", - G_CALLBACK (ews_soup_got_headers), data); - g_signal_connect ( - soup_message, "got-chunk", - G_CALLBACK (ews_soup_got_chunk), data); - g_signal_connect ( - soup_message, "restarted", - G_CALLBACK (ews_soup_restarted), data); - - ews_connection_schedule_queue_message (cnc, soup_message, oal_download_response_cb, simple); -} - -gboolean -e_ews_connection_download_oal_file_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), - e_ews_connection_download_oal_file), FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - /* Assume success unless a GError is set. */ - return !g_simple_async_result_propagate_error (simple, error); -} - -const gchar * -e_ews_connection_get_mailbox (EEwsConnection *cnc) -{ - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL); - - if (!cnc->priv->email || !*cnc->priv->email) - return camel_ews_settings_get_email (cnc->priv->settings); - - return cnc->priv->email; -} - -void -e_ews_connection_set_mailbox (EEwsConnection *cnc, - const gchar *email) -{ - g_return_if_fail (E_IS_EWS_CONNECTION (cnc)); - g_return_if_fail (email != NULL); - - g_free (cnc->priv->email); - cnc->priv->email = g_strdup (email); -} - -static void -ews_append_additional_props_to_msg (ESoapMessage *msg, - const EEwsAdditionalProps *add_props) -{ - GSList *l; - - if (!add_props) - return; - - e_soap_message_start_element (msg, "AdditionalProperties", NULL, NULL); - - if (add_props->field_uri) { - gchar **prop = g_strsplit (add_props->field_uri, " ", 0); - gint i = 0; - - while (prop[i]) { - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", prop[i]); - i++; - } - - g_strfreev (prop); - } - - if (add_props->extended_furis) { - for (l = add_props->extended_furis; l != NULL; l = g_slist_next (l)) { - EEwsExtendedFieldURI *ex_furi = l->data; - - e_soap_message_start_element (msg, "ExtendedFieldURI", NULL, NULL); - - if (ex_furi->distinguished_prop_set_id) - e_soap_message_add_attribute (msg, "DistinguishedPropertySetId", ex_furi->distinguished_prop_set_id, NULL, NULL); - - if (ex_furi->prop_tag) - e_soap_message_add_attribute (msg, "PropertyTag", ex_furi->prop_tag, NULL, NULL); - - if (ex_furi->prop_set_id) - e_soap_message_add_attribute (msg, "PropertySetId", ex_furi->prop_set_id, NULL, NULL); - - if (ex_furi->prop_name) - e_soap_message_add_attribute (msg, "PropertyName", ex_furi->prop_name, NULL, NULL); - - if (ex_furi->prop_id) - e_soap_message_add_attribute (msg, "PropertyId", ex_furi->prop_id, NULL, NULL); - - if (ex_furi->prop_type) - e_soap_message_add_attribute (msg, "PropertyType", ex_furi->prop_type, NULL, NULL); - - e_soap_message_end_element (msg); - } - } - - if (add_props->indexed_furis) { - for (l = add_props->indexed_furis; l != NULL; l = g_slist_next (l)) { - EEwsIndexedFieldURI *in_furi = l->data; - - e_soap_message_start_element (msg, "IndexedFieldURI", NULL, NULL); - - e_soap_message_add_attribute (msg, "FieldURI", in_furi->field_uri, NULL, NULL); - e_soap_message_add_attribute (msg, "FieldIndex", in_furi->field_index, NULL, NULL); - - e_soap_message_end_element (msg); - } - } - - e_soap_message_end_element (msg); -} - -static void -ews_write_sort_order_to_msg (ESoapMessage *msg, - EwsSortOrder *sort_order) -{ - if (!sort_order) - return; - - e_soap_message_start_element (msg, "SortOrder", NULL, NULL); - e_soap_message_start_element (msg, "FieldOrder", NULL, NULL); - e_soap_message_add_attribute (msg, "Order", sort_order->order, NULL, NULL); - - if (sort_order->uri_type == NORMAL_FIELD_URI) - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", (gchar *) sort_order->field_uri); - else if (sort_order->uri_type == INDEXED_FIELD_URI) { - EEwsIndexedFieldURI *in_furi = sort_order->field_uri; - - e_soap_message_start_element (msg, "IndexedFieldURI", NULL, NULL); - e_soap_message_add_attribute (msg, "FieldURI", in_furi->field_uri, NULL, NULL); - e_soap_message_add_attribute (msg, "FieldIndex", in_furi->field_index, NULL, NULL); - e_soap_message_end_element (msg); - } else if (sort_order->uri_type == EXTENDED_FIELD_URI) { - EEwsExtendedFieldURI *ex_furi = sort_order->field_uri; - - e_soap_message_start_element (msg, "ExtendedFieldURI", NULL, NULL); - - if (ex_furi->distinguished_prop_set_id) - e_soap_message_add_attribute (msg, "DistinguishedPropertySetId", ex_furi->distinguished_prop_set_id, NULL, NULL); - if (ex_furi->prop_set_id) - e_soap_message_add_attribute (msg, "PropertySetId", ex_furi->prop_set_id, NULL, NULL); - if (ex_furi->prop_name) - e_soap_message_add_attribute (msg, "PropertyName", ex_furi->prop_name, NULL, NULL); - if (ex_furi->prop_id) - e_soap_message_add_attribute (msg, "PropertyId", ex_furi->prop_id, NULL, NULL); - if (ex_furi->prop_type) - e_soap_message_add_attribute (msg, "PropertyType", ex_furi->prop_type, NULL, NULL); - - e_soap_message_end_element (msg); - } - - e_soap_message_end_element (msg); - e_soap_message_end_element (msg); -} - -/** - * e_ews_connection_sync_folder_items: - * @cnc: The EWS Connection - * @pri: The priority associated with the request - * @last_sync_state: To sync with the previous requests - * @folder_id: The folder to which the items belong - * @default_props: Can take one of the values: IdOnly,Default or AllProperties - * @additional_props: Specify any additional properties to be fetched - * @max_entries: Maximum number of items to be returned - * @cancellable: a GCancellable to monitor cancelled operations - * @callback: Responses are parsed and returned to this callback - * @user_data: user data passed to callback - **/ -void -e_ews_connection_sync_folder_items (EEwsConnection *cnc, - gint pri, - const gchar *last_sync_state, - const gchar *fid, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - guint max_entries, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "SyncFolderItems", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - e_soap_message_start_element (msg, "ItemShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, default_props); - - ews_append_additional_props_to_msg (msg, add_props); - - e_soap_message_end_element (msg); - - e_soap_message_start_element (msg, "SyncFolderId", "messages", NULL); - e_ews_message_write_string_parameter_with_attribute (msg, "FolderId", NULL, NULL, "Id", fid); - e_soap_message_end_element (msg); - - if (last_sync_state) - e_ews_message_write_string_parameter (msg, "SyncState", "messages", last_sync_state); - - /* Max changes requested */ - e_ews_message_write_int_parameter (msg, "MaxChangesReturned", "messages", max_entries); - - /* Complete the footer and print the request */ - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_sync_folder_items); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, sync_folder_items_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_sync_folder_items_finish (EEwsConnection *cnc, - GAsyncResult *result, - gchar **new_sync_state, - gboolean *includes_last_item, - GSList **items_created, - GSList **items_updated, - GSList **items_deleted, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_sync_folder_items), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *new_sync_state = async_data->sync_state; - *includes_last_item = async_data->includes_last_item; - *items_created = async_data->items_created; - *items_updated = async_data->items_updated; - *items_deleted = async_data->items_deleted; - - return TRUE; -} - -gboolean -e_ews_connection_sync_folder_items_sync (EEwsConnection *cnc, - gint pri, - const gchar *old_sync_state, - const gchar *fid, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - guint max_entries, - gchar **new_sync_state, - gboolean *includes_last_item, - GSList **items_created, - GSList **items_updated, - GSList **items_deleted, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_sync_folder_items ( - cnc, pri, old_sync_state, fid, default_props, - add_props, max_entries, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_sync_folder_items_finish ( - cnc, result, new_sync_state, includes_last_item, - items_created, items_updated, items_deleted, error); - - e_async_closure_free (closure); - - return success; -} - -static void -ews_append_folder_id_to_msg (ESoapMessage *msg, - const gchar *email, - const EwsFolderId *fid) -{ - g_return_if_fail (msg != NULL); - g_return_if_fail (fid != NULL); - - if (fid->is_distinguished_id) - e_soap_message_start_element (msg, "DistinguishedFolderId", NULL, NULL); - else - e_soap_message_start_element (msg, "FolderId", NULL, NULL); - - e_soap_message_add_attribute (msg, "Id", fid->id, NULL, NULL); - if (fid->change_key) - e_soap_message_add_attribute (msg, "ChangeKey", fid->change_key, NULL, NULL); - - if (fid->is_distinguished_id && email) { - e_soap_message_start_element (msg, "Mailbox", NULL, NULL); - e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, email); - e_soap_message_end_element (msg); - } - - e_soap_message_end_element (msg); -} - -static void -ews_append_folder_ids_to_msg (ESoapMessage *msg, - const gchar *email, - GSList *folder_ids) -{ - GSList *l; - - for (l = folder_ids; l != NULL; l = g_slist_next (l)) { - const EwsFolderId *fid = l->data; - - ews_append_folder_id_to_msg (msg, email, fid); - } -} - -static void -ews_connection_write_only_ids_restriction (ESoapMessage *msg, - GPtrArray *only_ids) -{ - guint ii; - - g_return_if_fail (E_IS_SOAP_MESSAGE (msg)); - g_return_if_fail (only_ids && only_ids->len); - - for (ii = 0; ii < only_ids->len; ii++) { - const gchar *itemid = g_ptr_array_index (only_ids, ii); - - e_soap_message_start_element (msg, "IsEqualTo", NULL, NULL); - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", "item:ItemId"); - e_soap_message_start_element (msg, "FieldURIOrConstant", NULL, NULL); - e_ews_message_write_string_parameter_with_attribute (msg, "Constant", NULL, NULL, "Value", itemid); - e_soap_message_end_element (msg); /* FieldURIOrConstant */ - e_soap_message_end_element (msg); /* IsEqualTo */ - } -} - -/** - * e_ews_connection_find_folder_items: - * @cnc: The EWS Connection - * @pri: The priority associated with the request - * @fid: The folder id to which the items belong - * @default_props: Can take one of the values: IdOnly,Default or AllProperties - * @add_props: Specify any additional properties to be fetched - * @sort_order: Specific sorting order for items - * @query: evo query based on which items will be fetched - * @only_ids: (element-type utf8) (nullable): a gchar * with item IDs, to check with only; can be %NULL - * @type: type of folder - * @convert_query_cb: a callback method to convert query to ews restiction - * @cancellable: a GCancellable to monitor cancelled operations - * @callback: Responses are parsed and returned to this callback - * @user_data: user data passed to callback - **/ -void -e_ews_connection_find_folder_items (EEwsConnection *cnc, - gint pri, - EwsFolderId *fid, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - EwsSortOrder *sort_order, - const gchar *query, - GPtrArray *only_ids, /* element-type utf8 */ - EEwsFolderType type, - EwsConvertQueryCallback convert_query_cb, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "FindItem", - "Traversal", - "Shallow", - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - e_soap_message_start_element (msg, "ItemShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, default_props); - - ews_append_additional_props_to_msg (msg, add_props); - - e_soap_message_end_element (msg); - - /*write restriction message based on query*/ - if (convert_query_cb) { - e_soap_message_start_element (msg, "Restriction", "messages", NULL); - - if (only_ids && only_ids->len) { - e_soap_message_start_element (msg, "And", "messages", NULL); - e_soap_message_start_element (msg, "Or", "messages", NULL); - ews_connection_write_only_ids_restriction (msg, only_ids); - e_soap_message_end_element (msg); /* Or */ - } - - convert_query_cb (msg, query, type); - - if (only_ids && only_ids->len) - e_soap_message_end_element (msg); /* And */ - - e_soap_message_end_element (msg); /* Restriction */ - } else if (only_ids && only_ids->len) { - e_soap_message_start_element (msg, "Restriction", "messages", NULL); - ews_connection_write_only_ids_restriction (msg, only_ids); - e_soap_message_end_element (msg); - } - - if (sort_order) - ews_write_sort_order_to_msg (msg, sort_order); - - e_soap_message_start_element (msg, "ParentFolderIds", "messages", NULL); - - if (fid->is_distinguished_id) - e_ews_message_write_string_parameter_with_attribute (msg, "DistinguishedFolderId", NULL, NULL, "Id", fid->id); - else - e_ews_message_write_string_parameter_with_attribute (msg, "FolderId", NULL, NULL, "Id", fid->id); - - e_soap_message_end_element (msg); - - /* Complete the footer and print the request */ - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_find_folder_items); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, find_folder_items_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_find_folder_items_finish (EEwsConnection *cnc, - GAsyncResult *result, - gboolean *includes_last_item, - GSList **items, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_find_folder_items), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *includes_last_item = async_data->includes_last_item; - *items = async_data->items; - - return TRUE; -} - -gboolean -e_ews_connection_find_folder_items_sync (EEwsConnection *cnc, - gint pri, - EwsFolderId *fid, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - EwsSortOrder *sort_order, - const gchar *query, - GPtrArray *only_ids, /* element-type utf8 */ - EEwsFolderType type, - gboolean *includes_last_item, - GSList **items, - EwsConvertQueryCallback convert_query_cb, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_find_folder_items ( - cnc, pri, fid, default_props, - add_props, sort_order, query, - only_ids, type, convert_query_cb, NULL, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_find_folder_items_finish ( - cnc, result, includes_last_item, items, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_sync_folder_hierarchy (EEwsConnection *cnc, - gint pri, - const gchar *sync_state, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "SyncFolderHierarchy", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - e_soap_message_start_element (msg, "FolderShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, "AllProperties"); - e_soap_message_end_element (msg); - - if (sync_state) - e_ews_message_write_string_parameter (msg, "SyncState", "messages", sync_state); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_sync_folder_hierarchy); - - async_data = g_new0 (EwsAsyncData, 1); - async_data->cnc = cnc; - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, sync_hierarchy_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_sync_folder_hierarchy_finish (EEwsConnection *cnc, - GAsyncResult *result, - gchar **sync_state, - gboolean *includes_last_folder, - GSList **folders_created, - GSList **folders_updated, - GSList **folders_deleted, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_sync_folder_hierarchy), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *sync_state = async_data->sync_state; - *includes_last_folder = async_data->includes_last_item; - *folders_created = async_data->items_created; - *folders_updated = async_data->items_updated; - *folders_deleted = async_data->items_deleted; - - return TRUE; -} - -gboolean -e_ews_connection_sync_folder_hierarchy_sync (EEwsConnection *cnc, - gint pri, - const gchar *old_sync_state, - gchar **new_sync_state, - gboolean *includes_last_folder, - GSList **folders_created, - GSList **folders_updated, - GSList **folders_deleted, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_sync_folder_hierarchy ( - cnc, pri, old_sync_state, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_sync_folder_hierarchy_finish ( - cnc, result, new_sync_state, - includes_last_folder, - folders_created, - folders_updated, - folders_deleted, - error); - - e_async_closure_free (closure); - - return success; -} - -EEwsServerVersion -e_ews_connection_get_server_version (EEwsConnection *cnc) -{ - g_return_val_if_fail (cnc != NULL, E_EWS_EXCHANGE_UNKNOWN); - g_return_val_if_fail (cnc->priv != NULL, E_EWS_EXCHANGE_UNKNOWN); - - return cnc->priv->version; -} - -void -e_ews_connection_set_server_version (EEwsConnection *cnc, - EEwsServerVersion version) -{ - g_return_if_fail (cnc != NULL); - g_return_if_fail (cnc->priv != NULL); - - if (cnc->priv->version != version) - cnc->priv->version = version; -} - -void -e_ews_connection_set_server_version_from_string (EEwsConnection *cnc, - const gchar *version) -{ - if (!version) - cnc->priv->version = E_EWS_EXCHANGE_UNKNOWN; - else if (g_strcmp0 (version, "Exchange2007") == 0) - cnc->priv->version = E_EWS_EXCHANGE_2007; - else if (g_strcmp0 (version, "Exchange2007_SP1") == 0 || - g_str_has_prefix (version, "Exchange2007")) - cnc->priv->version = E_EWS_EXCHANGE_2007_SP1; - else if (g_strcmp0 (version, "Exchange2010") == 0) - cnc->priv->version = E_EWS_EXCHANGE_2010; - else if (g_strcmp0 (version, "Exchange2010_SP1") == 0) - cnc->priv->version = E_EWS_EXCHANGE_2010_SP1; - else if (g_strcmp0 (version, "Exchange2010_SP2") == 0 || - g_str_has_prefix (version, "Exchange2010")) - cnc->priv->version = E_EWS_EXCHANGE_2010_SP2; - else - cnc->priv->version = E_EWS_EXCHANGE_FUTURE; -} - -gboolean -e_ews_connection_satisfies_server_version (EEwsConnection *cnc, - EEwsServerVersion version) -{ - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail (cnc->priv != NULL, FALSE); - - /* - * This test always will fail if, for some reason, we were not able to get the server version. - * It occurrs intentionally because we don't want to call any function that expects an EWS - * Server version higher than 2007 SP1 without be sure we using an EWS Server with version - * 2007 SP1 or later. - */ - return cnc->priv->version >= version; -} - -void -e_ews_connection_get_items (EEwsConnection *cnc, - gint pri, - const GSList *ids, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - gboolean include_mime, - const gchar *mime_directory, - EEwsBodyType body_type, - ESoapProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *l; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetItem", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - if (progress_fn && progress_data) - e_soap_message_set_progress_fn (msg, progress_fn, progress_data); - - e_soap_message_start_element (msg, "ItemShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, default_props); - - if (include_mime) - e_ews_message_write_string_parameter (msg, "IncludeMimeContent", NULL, "true"); - else - e_ews_message_write_string_parameter (msg, "IncludeMimeContent", NULL, "false"); - if (mime_directory) - e_soap_message_store_node_data (msg, "MimeContent", mime_directory, TRUE); - - switch (body_type) { - case E_EWS_BODY_TYPE_BEST: - e_ews_message_write_string_parameter (msg, "BodyType", NULL, "Best"); - break; - case E_EWS_BODY_TYPE_HTML: - e_ews_message_write_string_parameter (msg, "BodyType", NULL, "HTML"); - break; - case E_EWS_BODY_TYPE_TEXT: - e_ews_message_write_string_parameter (msg, "BodyType", NULL, "Text"); - break; - case E_EWS_BODY_TYPE_ANY: - break; - } - - ews_append_additional_props_to_msg (msg, add_props); - - e_soap_message_end_element (msg); - - e_soap_message_start_element (msg, "ItemIds", "messages", NULL); - - for (l = ids; l != NULL; l = g_slist_next (l)) - e_ews_message_write_string_parameter_with_attribute (msg, "ItemId", NULL, NULL, "Id", l->data); - - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_items); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_items_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_items_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **items, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_items), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (!async_data->items) { - g_set_error_literal (error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_ITEMNOTFOUND, _("No items found")); - return FALSE; - } - - *items = async_data->items; - - return TRUE; -} - -gboolean -e_ews_connection_get_items_sync (EEwsConnection *cnc, - gint pri, - const GSList *ids, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - gboolean include_mime, - const gchar *mime_directory, - EEwsBodyType body_type, - GSList **items, - ESoapProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_items ( - cnc, pri,ids, default_props, - add_props, include_mime, - mime_directory, body_type, progress_fn, - progress_data, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_items_finish ( - cnc, result, items, error); - - e_async_closure_free (closure); - - return success; -} - -static const gchar * -ews_delete_type_to_str (EwsDeleteType delete_type) -{ - switch (delete_type) { - case EWS_HARD_DELETE: - return "HardDelete"; - case EWS_SOFT_DELETE: - return "SoftDelete"; - case EWS_MOVE_TO_DELETED_ITEMS: - return "MoveToDeletedItems"; - } - return NULL; -} - -static const gchar * -ews_send_cancels_to_str (EwsSendMeetingCancellationsType send_cancels) -{ - switch (send_cancels) { - case EWS_SEND_TO_NONE: - return "SendToNone"; - case EWS_SEND_ONLY_TO_ALL: - return "SendOnlyToAll"; - case EWS_SEND_TO_ALL_AND_SAVE_COPY: - return "SendToAllAndSaveCopy"; - } - return NULL; -} - -static const gchar * -ews_affected_tasks_to_str (EwsAffectedTaskOccurrencesType affected_tasks) -{ - switch (affected_tasks) { - case EWS_NONE_OCCURRENCES: - return NULL; - case EWS_ALL_OCCURRENCES: - return "AllOccurrences"; - case EWS_SPECIFIED_OCCURRENCE_ONLY: - return "SpecifiedOccurrenceOnly"; - } - return NULL; -} - -static void -delete_item_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_delete_items (EEwsConnection *cnc, - gint pri, - const GSList *ids, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *iter; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "DeleteItem", - "DeleteType", - ews_delete_type_to_str (delete_type), - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - if (send_cancels) - e_soap_message_add_attribute ( - msg, "SendMeetingCancellations", - ews_send_cancels_to_str (send_cancels), NULL, NULL); - - if (affected_tasks) - e_soap_message_add_attribute ( - msg, "AffectedTaskOccurrences", - ews_affected_tasks_to_str (affected_tasks), NULL, NULL); - - e_soap_message_start_element (msg, "ItemIds", "messages", NULL); - - for (iter = ids; iter != NULL; iter = g_slist_next (iter)) - e_ews_message_write_string_parameter_with_attribute (msg, "ItemId", NULL, NULL, "Id", iter->data); - - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_delete_items); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, delete_item_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -void -e_ews_connection_delete_item (EEwsConnection *cnc, - gint pri, - EwsId *item_id, - guint index, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - gchar buffer[32]; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "DeleteItem", - "DeleteType", - ews_delete_type_to_str (delete_type), - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - if (send_cancels) - e_soap_message_add_attribute ( - msg, "SendMeetingCancellations", - ews_send_cancels_to_str (send_cancels), NULL, NULL); - - if (affected_tasks != EWS_NONE_OCCURRENCES) - e_soap_message_add_attribute ( - msg, "AffectedTaskOccurrences", - ews_affected_tasks_to_str (affected_tasks), NULL, NULL); - - e_soap_message_start_element (msg, "ItemIds", "messages", NULL); - - if (index) { - e_soap_message_start_element (msg, "OccurrenceItemId", NULL, NULL); - e_soap_message_add_attribute (msg, "RecurringMasterId", item_id->id, NULL, NULL); - if (item_id->change_key) - e_soap_message_add_attribute (msg, "ChangeKey", item_id->change_key, NULL, NULL); - snprintf (buffer, 32, "%u", index); - e_soap_message_add_attribute (msg, "InstanceIndex", buffer, NULL, NULL); - e_soap_message_end_element (msg); - } else { - e_soap_message_start_element (msg, "ItemId", NULL, NULL); - e_soap_message_add_attribute (msg, "Id", item_id->id, NULL, NULL); - if (item_id->change_key) - e_soap_message_add_attribute (msg, "ChangeKey", item_id->change_key, NULL, NULL); - e_soap_message_end_element (msg); - } - - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_delete_items); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, delete_item_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_delete_items_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_delete_items), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - return TRUE; -} - -gboolean -e_ews_connection_delete_items_sync (EEwsConnection *cnc, - gint pri, - const GSList *ids, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_delete_items ( - cnc, pri, ids, delete_type, - send_cancels, affected_tasks, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_delete_items_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -gboolean -e_ews_connection_delete_item_sync (EEwsConnection *cnc, - gint pri, - EwsId *id, - guint index, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_delete_item ( - cnc, pri, id, index, delete_type, - send_cancels, affected_tasks, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_delete_items_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -gboolean -e_ews_connection_delete_items_in_chunks_sync (EEwsConnection *cnc, - gint pri, - const GSList *ids, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GError **error) -{ - const GSList *iter; - guint total_ids = 0, done_ids = 0; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - - g_object_ref (cnc); - - iter = ids; - - while (success && iter) { - guint n_ids; - const GSList *tmp_iter; - - for (tmp_iter = iter, n_ids = 0; tmp_iter && n_ids < EWS_MOVE_ITEMS_CHUNK_SIZE; tmp_iter = g_slist_next (tmp_iter), n_ids++) { - /* Only check bounds first, to avoid unnecessary allocations */ - } - - if (tmp_iter) { - GSList *shorter = NULL; - - if (total_ids == 0) - total_ids = g_slist_length ((GSList *) ids); - - for (n_ids = 0; iter && n_ids < EWS_MOVE_ITEMS_CHUNK_SIZE; iter = g_slist_next (iter), n_ids++) { - shorter = g_slist_prepend (shorter, iter->data); - } - - shorter = g_slist_reverse (shorter); - - success = e_ews_connection_delete_items_sync (cnc, pri, shorter, delete_type, send_cancels, - affected_tasks, cancellable, error); - - g_slist_free (shorter); - - done_ids += n_ids; - } else { - success = e_ews_connection_delete_items_sync (cnc, pri, iter, delete_type, send_cancels, - affected_tasks, cancellable, error); - - iter = NULL; - done_ids = total_ids; - } - - if (total_ids > 0) - camel_operation_progress (cancellable, 100 * (gdouble) done_ids / (gdouble) total_ids); - } - - g_object_unref (cnc); - - return success; -} - -static xmlXPathObjectPtr -xpath_eval (xmlXPathContextPtr ctx, - const gchar *format, - ...) -{ - xmlXPathObjectPtr result; - va_list args; - gchar *expr; - - if (ctx == NULL) - return NULL; - - va_start (args, format); - expr = g_strdup_vprintf (format, args); - va_end (args); - - result = xmlXPathEvalExpression ((xmlChar *) expr, ctx); - g_free (expr); - - if (result == NULL) - return NULL; - - if (result->type == XPATH_NODESET && xmlXPathNodeSetIsEmpty (result->nodesetval)) { - xmlXPathFreeObject (result); - return NULL; - } - - return result; -} - -static gboolean -element_has_child (ESoapMessage *message, - const gchar *path) -{ - xmlDocPtr doc; - xmlXPathContextPtr xpctx; - xmlXPathObjectPtr result; - xmlNodeSetPtr nodeset; - xmlNodePtr node; - gboolean ret = FALSE; - - doc = e_soap_message_get_xml_doc (message); - xpctx = xmlXPathNewContext (doc); - - xmlXPathRegisterNs ( - xpctx, - (xmlChar *) "s", - (xmlChar *) "http://schemas.xmlsoap.org/soap/envelope/"); - - xmlXPathRegisterNs ( - xpctx, - (xmlChar *) "m", - (xmlChar *) "http://schemas.microsoft.com/exchange/services/2006/messages"); - - xmlXPathRegisterNs ( - xpctx, - (xmlChar *) "t", - (xmlChar *) "http://schemas.microsoft.com/exchange/services/2006/types"); - - result = xpath_eval (xpctx, path); - - if (result == NULL) - goto exit; - - if (!xmlXPathNodeSetGetLength (result->nodesetval)) - goto exit; - - nodeset = result->nodesetval; - node = nodeset->nodeTab[0]; - if (!node->children) - goto exit; - - ret = TRUE; - -exit: - xmlXPathFreeObject (result); - xmlXPathFreeContext (xpctx); - return ret; -} - -void -e_ews_connection_update_items (EEwsConnection *cnc, - gint pri, - const gchar *conflict_res, - const gchar *msg_disposition, - const gchar *send_invites, - const gchar *folder_id, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "UpdateItem", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - if (conflict_res) - e_soap_message_add_attribute ( - msg, "ConflictResolution", - conflict_res, NULL, NULL); - if (msg_disposition) - e_soap_message_add_attribute ( - msg, "MessageDisposition", - msg_disposition, NULL, NULL); - if (send_invites) - e_soap_message_add_attribute ( - msg, "SendMeetingInvitationsOrCancellations", - send_invites, NULL, NULL); - - if (folder_id) { - e_soap_message_start_element (msg, "SavedItemFolderId", "messages", NULL); - e_ews_message_write_string_parameter_with_attribute ( - msg, "FolderId", - NULL, NULL, "Id", folder_id); - e_soap_message_end_element (msg); - } - - e_soap_message_start_element (msg, "ItemChanges", "messages", NULL); - - create_cb (msg, create_user_data); - - e_soap_message_end_element (msg); /* ItemChanges */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_update_items); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - /* - * We need to check for both namespaces, because, the message is being wrote without use the types - * namespace. Maybe it is wrong, but the server doesn't complain about that. But this is the reason - * for the first check. The second one, is related to "how it should be" accord with EWS specifications. - */ - if (!element_has_child (msg, "/s:Envelope/s:Body/m:UpdateItem/m:ItemChanges/ItemChange/Updates") && - !element_has_child (msg, "/s:Envelope/s:Body/m:UpdateItem/m:ItemChanges/t:ItemChange/t:Updates")) - g_simple_async_result_complete_in_idle (simple); - else - e_ews_connection_queue_request ( - cnc, msg, get_items_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_update_items_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **ids, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_update_items), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - /* if there is only one item, then check whether it's an error */ - if (async_data->items && !async_data->items->next) { - EEwsItem *item = async_data->items->data; - - if (item && e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) { - if (error) - *error = g_error_copy (e_ews_item_get_error (item)); - - g_slist_free_full (async_data->items, g_object_unref); - async_data->items = NULL; - - return FALSE; - } - } - - if (ids) - *ids = async_data->items; - else - g_slist_free_full (async_data->items, g_object_unref); - - return TRUE; -} - -gboolean -e_ews_connection_update_items_sync (EEwsConnection *cnc, - gint pri, - const gchar *conflict_res, - const gchar *msg_disposition, - const gchar *send_invites, - const gchar *folder_id, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GSList **ids, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_update_items ( - cnc, pri, conflict_res, - msg_disposition, send_invites, - folder_id, create_cb, - create_user_data, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_update_items_finish ( - cnc, result, ids, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_create_items (EEwsConnection *cnc, - gint pri, - const gchar *msg_disposition, - const gchar *send_invites, - const EwsFolderId *fid, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "CreateItem", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - if (msg_disposition) - e_soap_message_add_attribute ( - msg, "MessageDisposition", - msg_disposition, NULL, NULL); - if (send_invites) - e_soap_message_add_attribute ( - msg, "SendMeetingInvitations", - send_invites, NULL, NULL); - - if (fid) { - e_soap_message_start_element (msg, "SavedItemFolderId", "messages", NULL); - ews_append_folder_id_to_msg (msg, cnc->priv->email, fid); - e_soap_message_end_element (msg); - } - - e_soap_message_start_element (msg, "Items", "messages", NULL); - - create_cb (msg, create_user_data); - - e_soap_message_end_element (msg); /* Items */ - - e_ews_message_write_footer (msg); /* CreateItem */ - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_create_items); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_items_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_create_items_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **ids, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_create_items), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - /* if there is only one item, then check whether it's an error */ - if (async_data->items && !async_data->items->next) { - EEwsItem *item = async_data->items->data; - - if (item && e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) { - if (error) - *error = g_error_copy (e_ews_item_get_error (item)); - - g_slist_free_full (async_data->items, g_object_unref); - async_data->items = NULL; - - return FALSE; - } - } - - *ids = async_data->items; - - return TRUE; -} - -gboolean -e_ews_connection_create_items_sync (EEwsConnection *cnc, - gint pri, - const gchar *msg_disposition, - const gchar *send_invites, - const EwsFolderId *fid, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GSList **ids, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_create_items ( - cnc, pri, msg_disposition, - send_invites, fid, - create_cb, create_user_data, - cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_create_items_finish ( - cnc, result, ids, error); - - e_async_closure_free (closure); - - return success; -} - -static const gchar * -get_search_scope_str (EwsContactsSearchScope scope) -{ - switch (scope) { - case EWS_SEARCH_AD: - return "ActiveDirectory"; - case EWS_SEARCH_AD_CONTACTS: - return "ActiveDirectoryContacts"; - case EWS_SEARCH_CONTACTS: - return "Contacts"; - case EWS_SEARCH_CONTACTS_AD: - return "ContactsActiveDirectory"; - default: - g_warn_if_reached (); - return NULL; - - } -} - -void -e_ews_connection_resolve_names (EEwsConnection *cnc, - gint pri, - const gchar *resolve_name, - EwsContactsSearchScope scope, - GSList *parent_folder_ids, - gboolean fetch_contact_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "ResolveNames", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_add_attribute (msg, "SearchScope", get_search_scope_str (scope), NULL, NULL); - - if (fetch_contact_data) - e_soap_message_add_attribute (msg, "ReturnFullContactData", "true", NULL, NULL); - else - e_soap_message_add_attribute (msg, "ReturnFullContactData", "false", NULL, NULL); - - if (parent_folder_ids) { - e_soap_message_start_element (msg, "ParentFolderIds", "messages", NULL); - ews_append_folder_ids_to_msg (msg, cnc->priv->email, parent_folder_ids); - e_soap_message_end_element (msg); - } - - e_ews_message_write_string_parameter (msg, "UnresolvedEntry", "messages", resolve_name); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_resolve_names); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, resolve_names_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_resolve_names_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **mailboxes, - GSList **contact_items, - gboolean *includes_last_item, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_resolve_names), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *includes_last_item = async_data->includes_last_item; - - if (contact_items) - *contact_items = async_data->items_created; - else - e_util_free_nullable_object_slist (async_data->items_created); - *mailboxes = async_data->items; - - return TRUE; -} - -gboolean -e_ews_connection_resolve_names_sync (EEwsConnection *cnc, - gint pri, - const gchar *resolve_name, - EwsContactsSearchScope scope, - GSList *parent_folder_ids, - gboolean fetch_contact_data, - GSList **mailboxes, - GSList **contact_items, - gboolean *includes_last_item, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_resolve_names ( - cnc, pri, resolve_name, - scope, parent_folder_ids, - fetch_contact_data, - cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_resolve_names_finish ( - cnc, result, - mailboxes, contact_items, - includes_last_item, error); - - e_async_closure_free (closure); - - return success; -} - -static void -ews_connection_resolve_by_name (EEwsConnection *cnc, - gint pri, - const gchar *usename, - gboolean is_user_name, - gchar **smtp_address, - GCancellable *cancellable) -{ - GSList *mailboxes = NULL; - GSList *contacts = NULL; - gboolean includes_last_item = FALSE; - GSList *miter; - gint len; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (usename != NULL); - g_return_if_fail (smtp_address != NULL); - - if (!*usename) - return; - - len = strlen (usename); - mailboxes = NULL; - contacts = NULL; - - /* use the first error, not the guess-part error */ - e_ews_connection_resolve_names_sync ( - cnc, pri, usename, - EWS_SEARCH_AD_CONTACTS, NULL, TRUE, &mailboxes, &contacts, - &includes_last_item, cancellable, NULL); - - for (miter = mailboxes; miter; miter = miter->next) { - const EwsMailbox *mailbox = miter->data; - if (mailbox->email && *mailbox->email && g_strcmp0 (mailbox->routing_type, "EX") != 0 - && ((!is_user_name && g_str_has_prefix (mailbox->email, usename) && mailbox->email[len] == '@') || - (is_user_name && g_str_equal (usename, mailbox->name)))) { - *smtp_address = g_strdup (mailbox->email); - break; - } else if (contacts && !contacts->next && contacts->data && - e_ews_item_get_item_type (contacts->data) == E_EWS_ITEM_TYPE_CONTACT) { - EEwsItem *contact_item = contacts->data; - GHashTable *addresses_hash = e_ews_item_get_email_addresses (contact_item); - GList *emails = addresses_hash ? g_hash_table_get_values (addresses_hash) : NULL, *iter; - const gchar *display_name; - gboolean found = FALSE; - - display_name = e_ews_item_get_display_name (contact_item); - if (!display_name || !*display_name) - display_name = e_ews_item_get_fileas (contact_item); - - for (iter = emails; iter && !found; iter = iter->next) { - const gchar *it_email = iter->data; - - if (it_email && g_str_has_prefix (it_email, "SMTP:") - && ((!is_user_name && g_str_has_prefix (it_email, usename) && it_email[len] == '@') || - (is_user_name && display_name && g_str_equal (usename, display_name)))) { - found = TRUE; - break; - } - } - - g_list_free (emails); - - if (found) { - gint ii; - - for (ii = 0; ii < g_hash_table_size (addresses_hash); ii++) { - gchar *key, *value; - - key = g_strdup_printf ("EmailAddress%d", ii + 1); - value = g_hash_table_lookup (addresses_hash, key); - g_free (key); - - if (value && g_str_has_prefix (value, "SMTP:")) { - /* pick the first available SMTP address */ - *smtp_address = g_strdup (value + 5); - break; - } - } - break; - } - } - } - - g_slist_free_full (mailboxes, (GDestroyNotify) e_ews_mailbox_free); - e_util_free_nullable_object_slist (contacts); -} - -gboolean -e_ews_connection_ex_to_smtp_sync (EEwsConnection *cnc, - gint pri, - const gchar *name, - const gchar *ex_address, - gchar **smtp_address, - GCancellable *cancellable, - GError **error) -{ - GSList *mailboxes = NULL; - GSList *contacts = NULL; - gboolean includes_last_item = FALSE; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail (ex_address != NULL, FALSE); - g_return_val_if_fail (smtp_address != NULL, FALSE); - - *smtp_address = NULL; - - e_ews_connection_resolve_names_sync ( - cnc, pri, ex_address, - EWS_SEARCH_AD_CONTACTS, NULL, TRUE, &mailboxes, &contacts, - &includes_last_item, cancellable, error); - - /* only one mailbox matches */ - if (mailboxes && !mailboxes->next && mailboxes->data) { - const EwsMailbox *mailbox = mailboxes->data; - if (mailbox->email && *mailbox->email && g_strcmp0 (mailbox->routing_type, "EX") != 0) { - *smtp_address = g_strdup (mailbox->email); - } else if (contacts && !contacts->next && contacts->data && - e_ews_item_get_item_type (contacts->data) == E_EWS_ITEM_TYPE_CONTACT) { - EEwsItem *contact_item = contacts->data; - GHashTable *addresses = e_ews_item_get_email_addresses (contact_item); - gint ii; - - for (ii = 0; ii < (addresses ? g_hash_table_size (addresses) : 0); ii++) { - gchar *key, *value; - - key = g_strdup_printf ("EmailAddress%d", ii + 1); - value = g_hash_table_lookup (addresses, key); - g_free (key); - - if (value && g_str_has_prefix (value, "SMTP:")) { - /* pick the first available SMTP address */ - *smtp_address = g_strdup (value + 5); - break; - } - } - } - } - - g_slist_free_full (mailboxes, (GDestroyNotify) e_ews_mailbox_free); - e_util_free_nullable_object_slist (contacts); - - if (!*smtp_address) { - const gchar *usename; - - usename = strrchr (ex_address, '/'); - if (usename && g_ascii_strncasecmp (usename, "/cn=", 4) == 0) { - usename += 4; - - /* try to guess from common name of the EX address */ - ews_connection_resolve_by_name (cnc, pri, usename, FALSE, smtp_address, cancellable); - } - - if (!*smtp_address && name && *name) { - /* try to guess from mailbox name */ - ews_connection_resolve_by_name (cnc, pri, name, TRUE, smtp_address, cancellable); - } - } - - if (*smtp_address) - g_clear_error (error); - - return *smtp_address != NULL; -} - -void -e_ews_connection_expand_dl (EEwsConnection *cnc, - gint pri, - const EwsMailbox *mb, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "ExpandDL", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "Mailbox", "messages", NULL); - - if (mb->item_id) { - e_soap_message_start_element (msg, "ItemId", NULL, NULL); - - e_soap_message_add_attribute (msg, "Id", mb->item_id->id, NULL, NULL); - e_soap_message_add_attribute (msg, "ChangeKey", mb->item_id->change_key, NULL, NULL); - - e_soap_message_end_element (msg); /* Mailbox */ - - } else if (mb->email) - e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, mb->email); - - e_soap_message_end_element (msg); /* Mailbox */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_expand_dl); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, expand_dl_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -/* includes_last_item does not make sense as expand_dl does not support recursive - * fetch, wierd */ -gboolean -e_ews_connection_expand_dl_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **mailboxes, - gboolean *includes_last_item, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_expand_dl), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *includes_last_item = async_data->includes_last_item; - *mailboxes = async_data->items; - - return TRUE; - -} - -gboolean -e_ews_connection_expand_dl_sync (EEwsConnection *cnc, - gint pri, - const EwsMailbox *mb, - GSList **mailboxes, - gboolean *includes_last_item, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_expand_dl ( - cnc, pri, mb, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_expand_dl_finish ( - cnc, result, mailboxes, includes_last_item, error); - - e_async_closure_free (closure); - - return success; -} - -static void -update_folder_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_next_child (param); - } -} - -void -e_ews_connection_update_folder (EEwsConnection *cnc, - gint pri, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "UpdateFolder", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "FolderChanges", "messages", NULL); - - create_cb (msg, create_user_data); - - e_soap_message_end_element (msg); /* FolderChanges */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_update_folder); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, update_folder_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_update_folder_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_update_folder), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - return TRUE; -} - -gboolean -e_ews_connection_update_folder_sync (EEwsConnection *cnc, - gint pri, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_update_folder ( - cnc, pri, create_cb, create_user_data, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_update_folder_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -static void -move_folder_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_move_folder (EEwsConnection *cnc, - gint pri, - const gchar *to_folder, - const gchar *folder, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "MoveFolder", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "ToFolderId", "messages", NULL); - if (to_folder) - e_ews_message_write_string_parameter_with_attribute ( - msg, "FolderId", NULL, - NULL, "Id", to_folder); - else - e_ews_message_write_string_parameter_with_attribute ( - msg, "DistinguishedFolderId", NULL, - NULL, "Id", "msgfolderroot"); - - e_soap_message_end_element (msg); - - e_soap_message_start_element (msg, "FolderIds", "messages", NULL); - e_ews_message_write_string_parameter_with_attribute ( - msg, "FolderId", NULL, - NULL, "Id", folder); - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_move_folder); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, move_folder_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_move_folder_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_move_folder), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - return TRUE; -} - -gboolean -e_ews_connection_move_folder_sync (EEwsConnection *cnc, - gint pri, - const gchar *to_folder, - const gchar *folder, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_move_folder ( - cnc, pri, to_folder, folder, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_move_folder_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_get_folder (EEwsConnection *cnc, - gint pri, - const gchar *folder_shape, - const EEwsAdditionalProps *add_props, - GSList *folder_ids, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetFolder", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - TRUE, - TRUE); - - e_soap_message_start_element (msg, "FolderShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, folder_shape); - - ews_append_additional_props_to_msg (msg, add_props); - e_soap_message_end_element (msg); - - if (folder_ids) { - e_soap_message_start_element (msg, "FolderIds", "messages", NULL); - ews_append_folder_ids_to_msg (msg, cnc->priv->email, folder_ids); - e_soap_message_end_element (msg); - } - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_folder); - - async_data = g_new0 (EwsAsyncData, 1); - async_data->cnc = cnc; - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_folder_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_folder_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **folders, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_folder), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (folders != NULL) - *folders = async_data->items; - else - g_slist_free_full (async_data->items, g_object_unref); - - return TRUE; -} - -gboolean -e_ews_connection_get_folder_sync (EEwsConnection *cnc, - gint pri, - const gchar *folder_shape, - const EEwsAdditionalProps *add_props, - GSList *folder_ids, - GSList **folders, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_folder ( - cnc, pri, folder_shape, add_props, - folder_ids, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_folder_finish ( - cnc, result, folders, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_create_folder (EEwsConnection *cnc, - gint pri, - const gchar *parent_folder_id, - gboolean is_distinguished_id, - const gchar *folder_name, - EEwsFolderType folder_type, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const gchar *folder_element; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "CreateFolder", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "ParentFolderId", "messages", NULL); - - /* If NULL passed for parent_folder_id, use "msgfolderroot" */ - if (is_distinguished_id || !parent_folder_id) { - e_soap_message_start_element (msg, "DistinguishedFolderId", NULL, NULL); - e_soap_message_add_attribute ( - msg, "Id", parent_folder_id ? parent_folder_id : "msgfolderroot", NULL, NULL); - if (is_distinguished_id && cnc->priv->email) { - e_soap_message_start_element (msg, "Mailbox", NULL, NULL); - e_ews_message_write_string_parameter( - msg, "EmailAddress", NULL, cnc->priv->email); - e_soap_message_end_element (msg); - } - e_soap_message_end_element (msg); - } else { - e_ews_message_write_string_parameter_with_attribute (msg, "FolderId", NULL, NULL, "Id", parent_folder_id); - } - - e_soap_message_end_element (msg); - - switch (folder_type) { - case E_EWS_FOLDER_TYPE_MAILBOX: - folder_element = "Folder"; - break; - case E_EWS_FOLDER_TYPE_CALENDAR: - folder_element = "CalendarFolder"; - break; - case E_EWS_FOLDER_TYPE_CONTACTS: - folder_element = "ContactsFolder"; - break; - case E_EWS_FOLDER_TYPE_SEARCH: - folder_element = "SearchFolder"; - break; - case E_EWS_FOLDER_TYPE_TASKS: - folder_element = "TasksFolder"; - break; - default: - g_warn_if_reached (); - folder_element = "Folder"; - break; - } - - e_soap_message_start_element (msg, "Folders", "messages", NULL); - e_soap_message_start_element (msg, folder_element, NULL, NULL); - e_ews_message_write_string_parameter (msg, "DisplayName", NULL, folder_name); - - e_soap_message_end_element (msg); - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_create_folder); - - async_data = g_new0 (EwsAsyncData, 1); - async_data->folder_type = folder_type; - - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, create_folder_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_create_folder_finish (EEwsConnection *cnc, - GAsyncResult *result, - EwsFolderId **fid, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_create_folder), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *fid = (EwsFolderId *) async_data->items_created->data; - g_slist_free (async_data->items_created); - - return TRUE; -} - -gboolean -e_ews_connection_create_folder_sync (EEwsConnection *cnc, - gint pri, - const gchar *parent_folder_id, - gboolean is_distinguished_id, - const gchar *folder_name, - EEwsFolderType folder_type, - EwsFolderId **folder_id, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_create_folder ( - cnc, pri, parent_folder_id, - is_distinguished_id, folder_name, - folder_type, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_create_folder_finish ( - cnc, result, folder_id, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_move_items (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean docopy, - const GSList *ids, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *iter; - - g_return_if_fail (cnc != NULL); - - if (docopy) - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "CopyItem", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - else - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "MoveItem", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "ToFolderId", "messages", NULL); - e_soap_message_start_element (msg, "FolderId", NULL, NULL); - e_soap_message_add_attribute (msg, "Id", folder_id, NULL, NULL); - e_soap_message_end_element (msg); /* FolderId */ - e_soap_message_end_element (msg); /* ToFolderId */ - - e_soap_message_start_element (msg, "ItemIds", "messages", NULL); - for (iter = ids; iter != NULL; iter = g_slist_next (iter)) - e_ews_message_write_string_parameter_with_attribute (msg, "ItemId", NULL, NULL, "Id", iter->data); - e_soap_message_end_element (msg); /* ItemIds */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_move_items); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_items_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_move_items_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **items, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_move_items), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - /* if there is only one item, then check whether it's an error */ - if (async_data->items && !async_data->items->next) { - EEwsItem *item = async_data->items->data; - - if (item && e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) { - if (error) - *error = g_error_copy (e_ews_item_get_error (item)); - - g_slist_free_full (async_data->items, g_object_unref); - async_data->items = NULL; - - return FALSE; - } - } - - *items = async_data->items; - - return TRUE; -} - -gboolean -e_ews_connection_move_items_sync (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean docopy, - const GSList *ids, - GSList **items, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_move_items ( - cnc, pri, folder_id, docopy, ids, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_move_items_finish ( - cnc, result, items, error); - - e_async_closure_free (closure); - - return success; -} - -gboolean -e_ews_connection_move_items_in_chunks_sync (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean docopy, - const GSList *ids, - GSList **items, - GCancellable *cancellable, - GError **error) -{ - const GSList *iter; - guint total_ids = 0, done_ids = 0; - gboolean success = TRUE; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (items != NULL, FALSE); - - g_object_ref (cnc); - - *items = NULL; - iter = ids; - - while (success && iter) { - guint n_ids; - const GSList *tmp_iter; - GSList *processed_items = NULL; - - for (tmp_iter = iter, n_ids = 0; tmp_iter && n_ids < EWS_MOVE_ITEMS_CHUNK_SIZE; tmp_iter = g_slist_next (tmp_iter), n_ids++) { - /* Only check bounds first, to avoid unnecessary allocations */ - } - - if (tmp_iter) { - GSList *shorter = NULL; - - if (total_ids == 0) - total_ids = g_slist_length ((GSList *) ids); - - for (n_ids = 0; iter && n_ids < EWS_MOVE_ITEMS_CHUNK_SIZE; iter = g_slist_next (iter), n_ids++) { - shorter = g_slist_prepend (shorter, iter->data); - } - - shorter = g_slist_reverse (shorter); - - success = e_ews_connection_move_items_sync (cnc, pri, folder_id, docopy, - shorter, &processed_items, cancellable, error); - - g_slist_free (shorter); - - done_ids += n_ids; - } else { - success = e_ews_connection_move_items_sync (cnc, pri, folder_id, docopy, - iter, &processed_items, cancellable, error); - - iter = NULL; - done_ids = total_ids; - } - - if (processed_items) - *items = g_slist_concat (*items, processed_items); - - if (total_ids > 0) - camel_operation_progress (cancellable, 100 * (gdouble) done_ids / (gdouble) total_ids); - } - - g_object_unref (cnc); - - return success; -} - -static void -delete_folder_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -/** - * e_ews_connection_delete_folder: - * @cnc: - * @pri: - * @folder_id: folder to be deleted - * @is_distinguished_id: - * @delete_type: "HardDelete", "SoftDelete", "MoveToDeletedItems" - * @cancellable: - * @callback: - * @user_data: - **/ -void -e_ews_connection_delete_folder (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean is_distinguished_id, - const gchar *delete_type, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "DeleteFolder", - "DeleteType", - delete_type, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "FolderIds", "messages", NULL); - - e_soap_message_start_element ( - msg, - is_distinguished_id ? "DistinguishedFolderId" : "FolderId", - NULL, - NULL); - e_soap_message_add_attribute (msg, "Id", folder_id, NULL, NULL); - - /* This element is required for delegate access */ - if (is_distinguished_id && cnc->priv->email) { - e_soap_message_start_element (msg, "Mailbox", NULL, NULL); - e_ews_message_write_string_parameter( - msg, "EmailAddress", NULL, cnc->priv->email); - e_soap_message_end_element (msg); - } - - e_soap_message_end_element (msg); /* || */ - - e_soap_message_end_element (msg); /* */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_delete_folder); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, delete_folder_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_delete_folder_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_delete_folder), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - return TRUE; -} - -/** - * e_ews_connection_delete_folder_sync: - * @cnc: - * @pri: - * @folder_id: folder to be deleted - * @is_distinguished_id: - * @delete_type: "HardDelete", "SoftDelete", "MoveToDeletedItems" - * @cancellable: - * @error: - **/ -gboolean -e_ews_connection_delete_folder_sync (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean is_distinguished_id, - const gchar *delete_type, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_delete_folder ( - cnc, pri, folder_id, - is_distinguished_id, - delete_type, - cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_delete_folder_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -static void -empty_folder_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_empty_folder (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean is_distinguished_id, - const gchar *delete_type, - gboolean delete_subfolders, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "EmptyFolder", - "DeleteType", - delete_type, - cnc->priv->version, - E_EWS_EXCHANGE_2010, - FALSE, - TRUE); - - e_soap_message_add_attribute (msg, "DeleteSubFolders", delete_subfolders ? "true" : "false", NULL, NULL); - - e_soap_message_start_element (msg, "FolderIds", "messages", NULL); - - e_soap_message_start_element ( - msg, - is_distinguished_id ? "DistinguishedFolderId" : "FolderId", - NULL, - NULL); - e_soap_message_add_attribute (msg, "Id", folder_id, NULL, NULL); - - /* This element is required for delegate access */ - if (is_distinguished_id && cnc->priv->email) { - e_soap_message_start_element (msg, "Mailbox", NULL, NULL); - e_ews_message_write_string_parameter( - msg, "EmailAddress", NULL, cnc->priv->email); - e_soap_message_end_element (msg); - } - - e_soap_message_end_element (msg); /* || */ - - e_soap_message_end_element (msg); /* */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_empty_folder); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, empty_folder_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_empty_folder_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_empty_folder), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - return TRUE; -} - -gboolean -e_ews_connection_empty_folder_sync (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean is_distinguished_id, - const gchar *delete_type, - gboolean delete_subfolder, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_empty_folder ( - cnc, pri, folder_id, - is_distinguished_id, - delete_type, - delete_subfolder, - cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_empty_folder_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -static void -ews_handle_create_attachments_param (ESoapParameter *param, - EwsAsyncData *async_data) -{ - /* http://msdn.microsoft.com/en-us/library/aa565877%28v=EXCHG.80%29.aspx */ - ESoapParameter *subparam, *attspara, *last_relevant = NULL, *attparam; - - attspara = e_soap_parameter_get_first_child_by_name (param, "Attachments"); - - for (subparam = e_soap_parameter_get_first_child (attspara); subparam != NULL; subparam = e_soap_parameter_get_next_child (subparam)) { - if (!g_ascii_strcasecmp (e_soap_parameter_get_name (subparam), "FileAttachment")) { - attparam = e_soap_parameter_get_first_child (subparam); - last_relevant = attparam; - - async_data->items = g_slist_append (async_data->items, e_soap_parameter_get_property (attparam, "Id")); - } - } - - if (last_relevant != NULL) { - async_data->sync_state = e_soap_parameter_get_property (last_relevant, "RootItemChangeKey"); - } -} - -static void -create_attachments_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "CreateAttachmentResponseMessage")) - ews_handle_create_attachments_param (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -static gboolean -e_ews_connection_attach_file (ESoapMessage *msg, - EEwsAttachmentInfo *info, - gboolean contact_photo, - GError **error) -{ - EEwsAttachmentInfoType type = e_ews_attachment_info_get_type (info); - gchar *filename = NULL, *buffer = NULL; - const gchar *content = NULL, *prefer_filename; - gsize length; - - switch (type) { - case E_EWS_ATTACHMENT_INFO_TYPE_URI: { - /* TODO - handle a situation where the file isnt accessible/other problem with it */ - /* TODO - This is a naive implementation that just uploads the whole content into */ - /* memory, ie very inefficient */ - const gchar *uri; - gchar *filepath; - GError *local_error = NULL; - - uri = e_ews_attachment_info_get_uri (info); - - /* convert uri to actual file path */ - filepath = g_filename_from_uri (uri, NULL, &local_error); - if (local_error != NULL) { - g_propagate_error (error, local_error); - return FALSE; - } - - g_file_get_contents (filepath, &buffer, &length, &local_error); - if (local_error != NULL) { - g_free (filepath); - g_propagate_error (error, local_error); - return FALSE; - } - - content = buffer; - - filename = strrchr (filepath, G_DIR_SEPARATOR); - filename = filename ? g_strdup (++filename) : g_strdup (filepath); - - g_free (filepath); - break; - } - case E_EWS_ATTACHMENT_INFO_TYPE_INLINED: - content = e_ews_attachment_info_get_inlined_data (info, &length); - filename = g_strdup (e_ews_attachment_info_get_filename (info)); - break; - default: - g_warning ("Unknown EwsAttachmentInfoType %d", type); - return FALSE; - } - - e_soap_message_start_element (msg, "FileAttachment", NULL, NULL); - - prefer_filename = e_ews_attachment_info_get_prefer_filename (info); - e_ews_message_write_string_parameter (msg, "Name", NULL, prefer_filename ? prefer_filename : filename); - if (contact_photo) - e_ews_message_write_string_parameter (msg, "IsContactPhoto", NULL, "true"); - e_soap_message_start_element (msg, "Content", NULL, NULL); - e_soap_message_write_base64 (msg, content, length); - e_soap_message_end_element (msg); /* "Content" */ - e_soap_message_end_element (msg); /* "FileAttachment" */ - - g_free (filename); - g_free (buffer); - - return TRUE; -} - -void -e_ews_connection_create_attachments (EEwsConnection *cnc, - gint pri, - const EwsId *parent, - const GSList *files, - gboolean is_contact_photo, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *l; - GError *local_error = NULL; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (parent != NULL); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_create_attachments); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "CreateAttachment", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "ParentItemId", "messages", NULL); - e_soap_message_add_attribute (msg, "Id", parent->id, NULL, NULL); - if (parent->change_key) - e_soap_message_add_attribute (msg, "ChangeKey", parent->change_key, NULL, NULL); - e_soap_message_end_element (msg); - - /* start interation over all items to get the attachemnts */ - e_soap_message_start_element (msg, "Attachments", "messages", NULL); - - for (l = files; l != NULL; l = g_slist_next (l)) - if (!e_ews_connection_attach_file (msg, l->data, is_contact_photo, &local_error)) { - if (local_error != NULL) - g_simple_async_result_take_error (simple, local_error); - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - - return; - } - - e_soap_message_end_element (msg); /* "Attachments" */ - - e_ews_message_write_footer (msg); - - e_ews_connection_queue_request ( - cnc, msg, create_attachments_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_create_attachments_finish (EEwsConnection *cnc, - gchar **change_key, - GSList **attachments_ids, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_create_attachments), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (attachments_ids) - *attachments_ids = async_data->items; - else - g_slist_free_full (async_data->items, g_free); - - if (change_key) - *change_key = async_data->sync_state; - else - g_free (async_data->sync_state); - - return TRUE; -} - -gboolean -e_ews_connection_create_attachments_sync (EEwsConnection *cnc, - gint pri, - const EwsId *parent, - const GSList *files, - gboolean is_contact_photo, - gchar **change_key, - GSList **attachments_ids, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean ret; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail (parent != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_create_attachments ( - cnc, pri, parent, files, is_contact_photo, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - ret = e_ews_connection_create_attachments_finish ( - cnc, change_key, attachments_ids, result, error); - - e_async_closure_free (closure); - - return ret; -} - -/* Delete attachemnts */ -static void -ews_handle_root_item_id_param (ESoapParameter *subparam, - EwsAsyncData *async_data) -{ - /* http://msdn.microsoft.com/en-us/library/aa580782%28v=EXCHG.80%29.aspx */ - ESoapParameter *attspara; - - attspara = e_soap_parameter_get_first_child_by_name ( - subparam, "RootItemId"); - - if (attspara == NULL) - return; - - async_data->sync_state = e_soap_parameter_get_property (attspara, "RootItemChangeKey"); -} - -static void -delete_attachments_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "DeleteAttachmentResponseMessage")) - ews_handle_root_item_id_param (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_delete_attachments (EEwsConnection *cnc, - gint pri, - const GSList *attachments_ids, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *l; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "DeleteAttachment", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - /* start interation over all items to get the attachemnts */ - e_soap_message_start_element (msg, "AttachmentIds", "messages", NULL); - - for (l = attachments_ids; l != NULL; l = l->next) { - e_ews_message_write_string_parameter_with_attribute (msg, "AttachmentId", NULL, NULL, "Id", l->data); - } - - e_soap_message_end_element (msg); /* "AttachmentIds" */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_delete_attachments); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, delete_attachments_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_delete_attachments_finish (EEwsConnection *cnc, - GAsyncResult *result, - gchar **new_change_key, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_delete_attachments), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (new_change_key) - *new_change_key = async_data->sync_state; - else - g_free (async_data->sync_state); - - return TRUE; -} - -gboolean -e_ews_connection_delete_attachments_sync (EEwsConnection *cnc, - gint pri, - const GSList *attachments_ids, - gchar **new_change_key, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean ret; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_delete_attachments ( - cnc, pri, attachments_ids, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - ret = e_ews_connection_delete_attachments_finish ( - cnc, result, new_change_key, error); - - e_async_closure_free (closure); - - return ret; -} - -static void -ews_handle_attachments_param (ESoapParameter *param, - EwsAsyncData *async_data) -{ - ESoapParameter *subparam, *attspara; - EEwsAttachmentInfo *info = NULL; - EEwsItem *item; - const gchar *name; - - attspara = e_soap_parameter_get_first_child_by_name (param, "Attachments"); - - for (subparam = e_soap_parameter_get_first_child (attspara); subparam != NULL; subparam = e_soap_parameter_get_next_child (subparam)) { - name = e_soap_parameter_get_name (subparam); - - if (!g_ascii_strcasecmp (name, "ItemAttachment")) { - item = e_ews_item_new_from_soap_parameter (subparam); - info = e_ews_item_dump_mime_content (item, async_data->directory); - g_clear_object (&item); - - } else if (!g_ascii_strcasecmp (name, "FileAttachment")) { - info = e_ews_dump_file_attachment_from_soap_parameter ( - subparam, - async_data->directory, - async_data->sync_state); - } - - if (info) - async_data->items = g_slist_append (async_data->items, info); - - info = NULL; - } -} - -static void -get_attachments_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "GetAttachmentResponseMessage")) - ews_handle_attachments_param (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_get_attachments (EEwsConnection *cnc, - gint pri, - const gchar *uid, - const GSList *ids, - const gchar *cache, - gboolean include_mime, - ESoapProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *l; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetAttachment", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - /* not sure why I need it, need to check */ - if (progress_fn && progress_data) - e_soap_message_set_progress_fn (msg, progress_fn, progress_data); - - if (cache) - e_soap_message_store_node_data (msg, "MimeContent Content", cache, TRUE); - - /* wrtie empty attachments shape, need to discover maybe usefull in some cases*/ - e_soap_message_start_element (msg, "AttachmentShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "IncludeMimeContent", NULL, "true"); - e_soap_message_end_element (msg); - - /* start interation over all items to get the attachemnts */ - e_soap_message_start_element (msg, "AttachmentIds", "messages", NULL); - - for (l = ids; l != NULL; l = g_slist_next (l)) - e_ews_message_write_string_parameter_with_attribute (msg, "AttachmentId", NULL, NULL, "Id", l->data); - - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_attachments); - - async_data = g_new0 (EwsAsyncData, 1); - async_data->directory = cache; - async_data->sync_state = (gchar *) uid; - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_attachments_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_attachments_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **items, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_attachments), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (items) - *items = async_data->items; - else - g_slist_free_full (async_data->items, (GDestroyNotify) e_ews_attachment_info_free); - - return TRUE; -} - -gboolean -e_ews_connection_get_attachments_sync (EEwsConnection *cnc, - gint pri, - const gchar *uid, - const GSList *ids, - const gchar *cache, - gboolean include_mime, - GSList **items, - ESoapProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean ret; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_attachments ( - cnc, pri, uid, ids, cache, include_mime, - progress_fn, progress_data, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - ret = e_ews_connection_get_attachments_finish ( - cnc, result, items, error); - - e_async_closure_free (closure); - - return ret; -} - -static void -ews_handle_free_busy_view (ESoapParameter *param, - EwsAsyncData *async_data) -{ - /*parse the response to create a free_busy data - http://msdn.microsoft.com / en - us / library / aa564001 % 28v = EXCHG.140 % 29.aspx */ - icalcomponent *vfb; - icalproperty *icalprop = NULL; - struct icalperiodtype ipt; - ESoapParameter *viewparam, *eventarray, *event_param, *subparam; - GTimeVal t_val; - const gchar *name; - gchar *value, *new_val = NULL, *summary = NULL, *location = NULL, *id = NULL; - - viewparam = e_soap_parameter_get_first_child_by_name (param, "FreeBusyView"); - if (!viewparam) return; - vfb = icalcomponent_new_vfreebusy (); - eventarray = e_soap_parameter_get_first_child_by_name (viewparam, "CalendarEventArray"); - for (event_param = eventarray ? e_soap_parameter_get_first_child (eventarray) : NULL; - event_param != NULL; - event_param = e_soap_parameter_get_next_child (event_param), icalprop = NULL) { - for (subparam = e_soap_parameter_get_first_child (event_param); subparam != NULL; subparam = e_soap_parameter_get_next_child (subparam)) { - name = e_soap_parameter_get_name (subparam); - - if (!g_ascii_strcasecmp (name, "StartTime")) { - value = e_soap_parameter_get_string_value (subparam); - /*We are sending UTC timezone and expect server to return in same*/ - - /*Remove leading and trailing whitespace*/ - g_strstrip (value); - - if (g_utf8_strlen (value, -1) == 19) { - /*If server returns time without zone add Z to treat it in UTC*/ - new_val = g_strdup_printf ("%sZ", value); - g_free (value); - } else - new_val = value; - - g_time_val_from_iso8601 (new_val, &t_val); - g_free (new_val); - - ipt.start = icaltime_from_timet_with_zone (t_val.tv_sec, 0, NULL); - - } else if (!g_ascii_strcasecmp (name, "EndTime")) { - value = e_soap_parameter_get_string_value (subparam); - /*We are sending UTC timezone and expect server to return in same*/ - - /*Remove leading and trailing whitespace*/ - g_strstrip (value); - - if (g_utf8_strlen (value, -1) == 19) { - /*If server returns time without zone add Z to treat it in UTC*/ - new_val = g_strdup_printf ("%sZ", value); - g_free (value); - } else - new_val = value; - - g_time_val_from_iso8601 (new_val, &t_val); - g_free (new_val); - - ipt.end = icaltime_from_timet_with_zone (t_val.tv_sec, 0, NULL); - - icalprop = icalproperty_new_freebusy (ipt); - } else if (!g_ascii_strcasecmp (name, "BusyType")) { - value = e_soap_parameter_get_string_value (subparam); - if (!strcmp (value, "Busy")) - icalproperty_set_parameter_from_string (icalprop, "FBTYPE", "BUSY"); - else if (!strcmp (value, "Tentative")) - icalproperty_set_parameter_from_string (icalprop, "FBTYPE", "BUSY-TENTATIVE"); - else if (!strcmp (value, "OOF")) - icalproperty_set_parameter_from_string (icalprop, "FBTYPE", "BUSY-UNAVAILABLE"); - else if (!strcmp (value, "Free")) - icalproperty_set_parameter_from_string (icalprop, "FBTYPE", "FREE"); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "CalendarEventDetails")) { - ESoapParameter *dparam; - - dparam = e_soap_parameter_get_first_child_by_name (subparam, "ID"); - if (dparam) - id = e_soap_parameter_get_string_value (dparam); - - dparam = e_soap_parameter_get_first_child_by_name (subparam, "Subject"); - if (dparam) - summary = e_soap_parameter_get_string_value (dparam); - - dparam = e_soap_parameter_get_first_child_by_name (subparam, "Location"); - if (dparam) - location = e_soap_parameter_get_string_value (dparam); - } - } - if (icalprop != NULL) { - if (id) - icalproperty_set_parameter_from_string (icalprop, "X-EWS-ID", id); - if (summary) - icalproperty_set_parameter_from_string (icalprop, "X-SUMMARY", summary); - if (location) - icalproperty_set_parameter_from_string (icalprop, "X-LOCATION", location); - icalcomponent_add_property (vfb, icalprop); - } - - g_clear_pointer (&summary, g_free); - g_clear_pointer (&location, g_free); - g_clear_pointer (&id, g_free); - } - - async_data->items = g_slist_append (async_data->items, vfb); -} - -static void -get_free_busy_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "FreeBusyResponseArray", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - ESoapParameter *subsubparam; - - subsubparam = e_soap_parameter_get_first_child_by_name ( - subparam, "ResponseMessage"); - - if (subsubparam) { - if (!ews_get_response_status (subsubparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - ews_handle_free_busy_view (subparam, async_data); - } - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_get_free_busy (EEwsConnection *cnc, - gint pri, - EEwsRequestCreationCallback free_busy_cb, - gpointer free_busy_user_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetUserAvailabilityRequest", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - free_busy_cb (msg, free_busy_user_data); - - e_ews_message_write_footer (msg); /*GetUserAvailabilityRequest */ - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_free_busy); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_free_busy_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_free_busy_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **free_busy, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_free_busy), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - *free_busy = async_data->items; - - return TRUE; -} - -gboolean -e_ews_connection_get_free_busy_sync (EEwsConnection *cnc, - gint pri, - EEwsRequestCreationCallback free_busy_cb, - gpointer free_busy_user_data, - GSList **free_busy, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_free_busy ( - cnc, pri, free_busy_cb, - free_busy_user_data, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_free_busy_finish ( - cnc, result, free_busy, error); - - e_async_closure_free (closure); - - return success; -} - -static EwsPermissionLevel -get_permission_from_string (const gchar *permission) -{ - g_return_val_if_fail (permission != NULL, EwsPermissionLevel_Unknown); - - if (!g_ascii_strcasecmp (permission, "Editor")) - return EwsPermissionLevel_Editor; - else if (!g_ascii_strcasecmp (permission, "Author")) - return EwsPermissionLevel_Author; - else if (!g_ascii_strcasecmp (permission, "Reviewer")) - return EwsPermissionLevel_Reviewer; - else if (!g_ascii_strcasecmp (permission, "Custom")) - return EwsPermissionLevel_Custom; - else - return EwsPermissionLevel_None; - -} - -static void -ews_handle_delegate_user_param (ESoapParameter *param, - EwsAsyncData *async_data) -{ - ESoapParameter *subparam, *node, *child; - EwsDelegateInfo *data; - gchar *value; - - node = e_soap_parameter_get_first_child_by_name (param, "DelegateUser"); - if (!node) - return; - - subparam = e_soap_parameter_get_first_child_by_name (node, "UserId"); - if (!subparam) - return; - - data = g_new0 (EwsDelegateInfo, 1); - data->user_id = g_new0 (EwsUserId, 1); - - /*Parse User Id*/ - - child = e_soap_parameter_get_first_child_by_name (subparam, "SID"); - data->user_id->sid = e_soap_parameter_get_string_value (child); - - child = e_soap_parameter_get_first_child_by_name (subparam, "PrimarySmtpAddress"); - data->user_id->primary_smtp = e_soap_parameter_get_string_value (child); - - child = e_soap_parameter_get_first_child_by_name (subparam, "DisplayName"); - data->user_id->display_name = e_soap_parameter_get_string_value (child); - - subparam = e_soap_parameter_get_first_child_by_name (node, "DelegatePermissions"); - - /*Parse Delegate Permissions*/ - child = e_soap_parameter_get_first_child_by_name (subparam, "CalendarFolderPermissionLevel"); - if (child) { - value = e_soap_parameter_get_string_value (child); - data->calendar = get_permission_from_string (value); - g_free (value); - } - - child = e_soap_parameter_get_first_child_by_name (subparam, "ContactsFolderPermissionLevel"); - if (child) { - value = e_soap_parameter_get_string_value (child); - data->contacts = get_permission_from_string (value); - g_free (value); - } - - child = e_soap_parameter_get_first_child_by_name (subparam, "InboxFolderPermissionLevel"); - if (child) { - value = e_soap_parameter_get_string_value (child); - data->inbox = get_permission_from_string (value); - g_free (value); - } - - child = e_soap_parameter_get_first_child_by_name (subparam, "TasksFolderPermissionLevel"); - if (child) { - value = e_soap_parameter_get_string_value (child); - data->tasks = get_permission_from_string (value); - g_free (value); - } - - child = e_soap_parameter_get_first_child_by_name (subparam, "NotesFolderPermissionLevel"); - if (child) { - value = e_soap_parameter_get_string_value (child); - data->notes = get_permission_from_string (value); - g_free (value); - } - - child = e_soap_parameter_get_first_child_by_name (subparam, "JournalFolderPermissionLevel"); - if (child) { - value = e_soap_parameter_get_string_value (child); - data->journal = get_permission_from_string (value); - g_free (value); - } - - subparam = e_soap_parameter_get_first_child_by_name (node, "ReceiveCopiesOfMeetingMessages"); - if (subparam) { - value = e_soap_parameter_get_string_value (subparam); - data->meetingcopies = g_strcmp0 (value, "true") == 0; - g_free (value); - } - - subparam = e_soap_parameter_get_first_child_by_name (node, "ViewPrivateItems"); - if (subparam) { - value = e_soap_parameter_get_string_value (subparam); - data->view_priv_items = g_strcmp0 (value, "true") == 0; - g_free (value); - } - - async_data->items = g_slist_append (async_data->items, data); -} - -static void -get_delegate_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - gchar *value; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (ews_get_response_status (e_soap_response_get_parameter (response), &error)) - param = e_soap_response_get_first_parameter_by_name ( - response, "DeliverMeetingRequests", &error); - else - param = NULL; - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - value = e_soap_parameter_get_string_value (param); - if (g_strcmp0 (value, "DelegatesOnly") == 0) - async_data->deliver_to = EwsDelegateDeliver_DelegatesOnly; - else if (g_strcmp0 (value, "DelegatesAndMe") == 0) - async_data->deliver_to = EwsDelegateDeliver_DelegatesAndMe; - else if (g_strcmp0 (value, "DelegatesAndSendInformationToMe") == 0) - async_data->deliver_to = EwsDelegateDeliver_DelegatesAndSendInformationToMe; - else { - g_message ("%s: Unknown deliver-to value '%s'", G_STRFUNC, value ? value : "[null]"); - async_data->deliver_to = EwsDelegateDeliver_DelegatesAndSendInformationToMe; - } - g_free (value); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", NULL); - /* it's OK to not have set any delegate */ - if (!param) - return; - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "DelegateUserResponseMessageType")) - ews_handle_delegate_user_param (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_get_delegate (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - gboolean include_permissions, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetDelegate", - "IncludePermissions", - include_permissions ? "true" : "false", - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "Mailbox", "messages", NULL); - - e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, mail_id ? mail_id : cnc->priv->email); - - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_delegate); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_delegate_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_delegate_finish (EEwsConnection *cnc, - GAsyncResult *result, - EwsDelegateDeliver *deliver_to, - GSList **delegates, /* EwsDelegateInfo * */ - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail (delegates != NULL, FALSE); - g_return_val_if_fail (deliver_to != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_delegate), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *deliver_to = async_data->deliver_to; - *delegates = async_data->items; - async_data->items = NULL; - - return TRUE; -} - -gboolean -e_ews_connection_get_delegate_sync (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - gboolean include_permissions, - EwsDelegateDeliver *deliver_to, - GSList **delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (deliver_to != NULL, FALSE); - g_return_val_if_fail (delegates != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_delegate ( - cnc, pri, mail_id, - include_permissions, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_delegate_finish ( - cnc, result, deliver_to, delegates, error); - - e_async_closure_free (closure); - - return success; -} - -static void -update_delegate_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - if (ews_get_response_status (e_soap_response_get_parameter (response), &error)) { - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", NULL); - /* that's OK to not receive any ResponseMessages here */ - if (!param) - return; - } else - param = NULL; - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_next_child (param); - } -} - -static void -set_delegate_permission (ESoapMessage *msg, - const gchar *elem_name, - EwsPermissionLevel perm_level) -{ - const gchar *level_name = NULL; - - if (perm_level == EwsPermissionLevel_None) - level_name = "None"; - else if (perm_level == EwsPermissionLevel_Reviewer) - level_name = "Reviewer"; - else if (perm_level == EwsPermissionLevel_Author) - level_name = "Author"; - else if (perm_level == EwsPermissionLevel_Editor) - level_name = "Editor"; - - if (!level_name) - return; - - e_ews_message_write_string_parameter (msg, elem_name, NULL, level_name); -} - -void -e_ews_connection_add_delegate (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const GSList *delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *iter; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (delegates != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "AddDelegate", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "Mailbox", "messages", NULL); - e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, mail_id ? mail_id : cnc->priv->email); - e_soap_message_end_element (msg); - - e_soap_message_start_element (msg, "DelegateUsers", "messages", NULL); - for (iter = delegates; iter; iter = iter->next) { - const EwsDelegateInfo *di = iter->data; - - if (!di) - continue; - - e_soap_message_start_element (msg, "DelegateUser", NULL, NULL); - - e_soap_message_start_element (msg, "UserId", NULL, NULL); - e_ews_message_write_string_parameter (msg, "PrimarySmtpAddress", NULL, di->user_id->primary_smtp); - e_soap_message_end_element (msg); /* UserId */ - - e_soap_message_start_element (msg, "DelegatePermissions", NULL, NULL); - set_delegate_permission (msg, "CalendarFolderPermissionLevel", di->calendar); - set_delegate_permission (msg, "TasksFolderPermissionLevel", di->tasks); - set_delegate_permission (msg, "InboxFolderPermissionLevel", di->inbox); - set_delegate_permission (msg, "ContactsFolderPermissionLevel", di->contacts); - set_delegate_permission (msg, "NotesFolderPermissionLevel", di->notes); - set_delegate_permission (msg, "JournalFolderPermissionLevel", di->journal); - e_soap_message_end_element (msg); /* DelegatePermissions */ - - e_ews_message_write_string_parameter ( - msg, "ReceiveCopiesOfMeetingMessages", NULL, - di->meetingcopies ? "true" : "false"); - e_ews_message_write_string_parameter ( - msg, "ViewPrivateItems", NULL, - di->view_priv_items ? "true" : "false"); - - e_soap_message_end_element (msg); /* DelegateUser */ - } - - e_soap_message_end_element (msg); /* DelegateUsers */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_add_delegate); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, update_delegate_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_add_delegate_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_add_delegate), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - return !g_simple_async_result_propagate_error (simple, error); -} - -gboolean -e_ews_connection_add_delegate_sync (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const GSList *delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (delegates != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_add_delegate ( - cnc, pri, mail_id, delegates, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_add_delegate_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_remove_delegate (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const GSList *delegate_ids, /* EwsUserId * */ - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *iter; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (delegate_ids != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "RemoveDelegate", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "Mailbox", "messages", NULL); - e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, mail_id ? mail_id : cnc->priv->email); - e_soap_message_end_element (msg); - - e_soap_message_start_element (msg, "UserIds", "messages", NULL); - for (iter = delegate_ids; iter; iter = iter->next) { - const EwsUserId *user_id = iter->data; - - if (!user_id) - continue; - - e_soap_message_start_element (msg, "UserId", NULL, NULL); - e_ews_message_write_string_parameter (msg, "PrimarySmtpAddress", NULL, user_id->primary_smtp); - e_soap_message_end_element (msg); /* UserId */ - } - - e_soap_message_end_element (msg); /* UserIds */ - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_remove_delegate); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, update_delegate_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_remove_delegate_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_remove_delegate), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - return !g_simple_async_result_propagate_error (simple, error); -} - -gboolean -e_ews_connection_remove_delegate_sync (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const GSList *delegate_ids, /* EwsUserId * */ - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (delegate_ids != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_remove_delegate ( - cnc, pri, mail_id, delegate_ids, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_remove_delegate_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_update_delegate (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - EwsDelegateDeliver deliver_to, - const GSList *delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *iter; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "UpdateDelegate", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "Mailbox", "messages", NULL); - e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, mail_id ? mail_id : cnc->priv->email); - e_soap_message_end_element (msg); - - if (delegates) { - e_soap_message_start_element (msg, "DelegateUsers", "messages", NULL); - for (iter = delegates; iter; iter = iter->next) { - const EwsDelegateInfo *di = iter->data; - - if (!di) - continue; - - e_soap_message_start_element (msg, "DelegateUser", NULL, NULL); - - e_soap_message_start_element (msg, "UserId", NULL, NULL); - e_ews_message_write_string_parameter (msg, "PrimarySmtpAddress", NULL, di->user_id->primary_smtp); - e_soap_message_end_element (msg); /* UserId */ - - e_soap_message_start_element (msg, "DelegatePermissions", NULL, NULL); - set_delegate_permission (msg, "CalendarFolderPermissionLevel", di->calendar); - set_delegate_permission (msg, "TasksFolderPermissionLevel", di->tasks); - set_delegate_permission (msg, "InboxFolderPermissionLevel", di->inbox); - set_delegate_permission (msg, "ContactsFolderPermissionLevel", di->contacts); - set_delegate_permission (msg, "NotesFolderPermissionLevel", di->notes); - set_delegate_permission (msg, "JournalFolderPermissionLevel", di->journal); - e_soap_message_end_element (msg); /* DelegatePermissions */ - - e_ews_message_write_string_parameter ( - msg, "ReceiveCopiesOfMeetingMessages", NULL, - di->meetingcopies ? "true" : "false"); - e_ews_message_write_string_parameter ( - msg, "ViewPrivateItems", NULL, - di->view_priv_items ? "true" : "false"); - - e_soap_message_end_element (msg); /* DelegateUser */ - } - - e_soap_message_end_element (msg); /* DelegateUsers */ - } - - e_ews_message_write_string_parameter ( - msg, "DeliverMeetingRequests", "messages", - deliver_to == EwsDelegateDeliver_DelegatesOnly ? "DelegatesOnly" : - deliver_to == EwsDelegateDeliver_DelegatesAndMe ? "DelegatesAndMe" : - "DelegatesAndSendInformationToMe"); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_update_delegate); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, update_delegate_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_update_delegate_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_update_delegate), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - return !g_simple_async_result_propagate_error (simple, error); -} - -gboolean -e_ews_connection_update_delegate_sync (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - EwsDelegateDeliver deliver_to, - const GSList *delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_update_delegate ( - cnc, pri, mail_id, deliver_to, delegates, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_update_delegate_finish (cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -static void -get_folder_permissions_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "GetFolderResponseMessage")) { - ESoapParameter *node; - - node = e_soap_parameter_get_first_child_by_name (subparam, "Folders"); - if (node) { - subparam = node; - - node = e_soap_parameter_get_first_child (subparam); - if (node && node->name && g_str_has_suffix ((const gchar *) node->name, "Folder")) { - node = e_soap_parameter_get_first_child_by_name (node, "PermissionSet"); - if (node) { - async_data->items = e_ews_permissions_from_soap_param (node); - } - } - } - - break; - } - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_get_folder_permissions (EEwsConnection *cnc, - gint pri, - EwsFolderId *folder_id, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (folder_id != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetFolder", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "FolderShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, "IdOnly"); - e_soap_message_start_element (msg, "AdditionalProperties", NULL, NULL); - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", "folder:PermissionSet"); - e_soap_message_end_element (msg); /* AdditionalProperties */ - e_soap_message_end_element (msg); /* FolderShape */ - - e_soap_message_start_element (msg, "FolderIds", "messages", NULL); - ews_append_folder_id_to_msg (msg, cnc->priv->email, folder_id); - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_folder_permissions); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_folder_permissions_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -/* free permissions with e_ews_permissions_free() */ -gboolean -e_ews_connection_get_folder_permissions_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **permissions, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail (permissions != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_folder_permissions), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *permissions = async_data->items; - - return TRUE; -} - -/* free permissions with e_ews_permissions_free() */ -gboolean -e_ews_connection_get_folder_permissions_sync (EEwsConnection *cnc, - gint pri, - EwsFolderId *folder_id, - GSList **permissions, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (folder_id != NULL, FALSE); - g_return_val_if_fail (permissions != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_folder_permissions ( - cnc, pri, folder_id, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_folder_permissions_finish ( - cnc, result, permissions, error); - - e_async_closure_free (closure); - - return success; -} - -void -e_ews_connection_set_folder_permissions (EEwsConnection *cnc, - gint pri, - EwsFolderId *folder_id, - EEwsFolderType folder_type, - const GSList *permissions, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - const GSList *iter; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (folder_id != NULL); - g_return_if_fail (permissions != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "UpdateFolder", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "FolderChanges", "messages", NULL); - e_ews_message_start_item_change ( - msg, E_EWS_ITEMCHANGE_TYPE_FOLDER, - folder_id->id, folder_id->change_key, 0); - - e_soap_message_start_element (msg, "SetFolderField", NULL, NULL); - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", "folder:PermissionSet"); - - switch (folder_type) { - default: - case E_EWS_FOLDER_TYPE_MAILBOX: - e_soap_message_start_element (msg, "Folder", NULL, NULL); - break; - case E_EWS_FOLDER_TYPE_CALENDAR: - e_soap_message_start_element (msg, "CalendarFolder", NULL, NULL); - break; - case E_EWS_FOLDER_TYPE_CONTACTS: - e_soap_message_start_element (msg, "ContactsFolder", NULL, NULL); - break; - case E_EWS_FOLDER_TYPE_SEARCH: - e_soap_message_start_element (msg, "SearchFolder", NULL, NULL); - break; - case E_EWS_FOLDER_TYPE_TASKS: - e_soap_message_start_element (msg, "TasksFolder", NULL, NULL); - break; - } - - e_soap_message_start_element (msg, "PermissionSet", NULL, NULL); - if (folder_type == E_EWS_FOLDER_TYPE_CALENDAR) - e_soap_message_start_element (msg, "CalendarPermissions", NULL, NULL); - else - e_soap_message_start_element (msg, "Permissions", NULL, NULL); - - for (iter = permissions; iter; iter = iter->next) { - EEwsPermission *perm = iter->data; - const gchar *perm_level_name; - - if (!perm) - continue; - - if (folder_type == E_EWS_FOLDER_TYPE_CALENDAR) - e_soap_message_start_element (msg, "CalendarPermission", NULL, NULL); - else - e_soap_message_start_element (msg, "Permission", NULL, NULL); - - e_soap_message_start_element (msg, "UserId", NULL, NULL); - - switch (perm->user_type) { - case E_EWS_PERMISSION_USER_TYPE_NONE: - g_return_if_reached (); - break; - case E_EWS_PERMISSION_USER_TYPE_ANONYMOUS: - e_ews_message_write_string_parameter (msg, "DistinguishedUser", NULL, "Anonymous"); - break; - case E_EWS_PERMISSION_USER_TYPE_DEFAULT: - e_ews_message_write_string_parameter (msg, "DistinguishedUser", NULL, "Default"); - break; - case E_EWS_PERMISSION_USER_TYPE_REGULAR: - e_ews_message_write_string_parameter (msg, "PrimarySmtpAddress", NULL, perm->primary_smtp); - break; - } - - e_soap_message_end_element (msg); /* UserId */ - - e_ews_permission_rights_to_level_name (perm->rights); - - perm_level_name = e_ews_permission_rights_to_level_name (perm->rights); - - if (g_strcmp0 (perm_level_name, "Custom") == 0) { - e_ews_message_write_string_parameter ( - msg, "CanCreateItems", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_CREATE) != 0 ? "true" : "false"); - e_ews_message_write_string_parameter ( - msg, "CanCreateSubFolders", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_CREATE_SUBFOLDER) != 0 ? "true" : "false"); - e_ews_message_write_string_parameter ( - msg, "IsFolderOwner", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_FOLDER_OWNER) != 0 ? "true" : "false"); - e_ews_message_write_string_parameter ( - msg, "IsFolderVisible", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_FOLDER_VISIBLE) != 0 ? "true" : "false"); - e_ews_message_write_string_parameter ( - msg, "IsFolderContact", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_FOLDER_CONTACT) != 0 ? "true" : "false"); - e_ews_message_write_string_parameter ( - msg, "EditItems", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_EDIT_ANY) != 0 ? "All" : - (perm->rights & E_EWS_PERMISSION_BIT_EDIT_OWNED) != 0 ? "Owned" : "None"); - e_ews_message_write_string_parameter ( - msg, "DeleteItems", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_DELETE_ANY) != 0 ? "All" : - (perm->rights & E_EWS_PERMISSION_BIT_DELETE_OWNED) != 0 ? "Owned" : "None"); - if (folder_type == E_EWS_FOLDER_TYPE_CALENDAR) - e_ews_message_write_string_parameter ( - msg, "ReadItems", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_READ_ANY) != 0 ? "FullDetails" : - (perm->rights & E_EWS_PERMISSION_BIT_FREE_BUSY_DETAILED) != 0 ? "TimeAndSubjectAndLocation" : - (perm->rights & E_EWS_PERMISSION_BIT_FREE_BUSY_SIMPLE) != 0 ? "TimeOnly" : "None"); - else - e_ews_message_write_string_parameter ( - msg, "ReadItems", NULL, - (perm->rights & E_EWS_PERMISSION_BIT_READ_ANY) != 0 ? "FullDetails" : "None"); - } - - e_ews_message_write_string_parameter ( - msg, - folder_type == E_EWS_FOLDER_TYPE_CALENDAR ? "CalendarPermissionLevel" : "PermissionLevel", NULL, - perm_level_name); - - e_soap_message_end_element (msg); /* Permission/CalendarPermission */ - } - - e_soap_message_end_element (msg); /* Permissions */ - e_soap_message_end_element (msg); /* PermissionSet */ - e_soap_message_end_element (msg); /* Folder/CalendarFolder/... */ - e_soap_message_end_element (msg); /* SetFolderField */ - - e_ews_message_end_item_change (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_set_folder_permissions); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, update_folder_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_set_folder_permissions_finish (EEwsConnection *cnc, - GAsyncResult *result, - GError **error) -{ - GSimpleAsyncResult *simple; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_set_folder_permissions), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - - return !g_simple_async_result_propagate_error (simple, error); -} - -gboolean -e_ews_connection_set_folder_permissions_sync (EEwsConnection *cnc, - gint pri, - EwsFolderId *folder_id, - EEwsFolderType folder_type, - const GSList *permissions, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (folder_id != NULL, FALSE); - g_return_val_if_fail (permissions != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_set_folder_permissions ( - cnc, pri, folder_id, folder_type, permissions, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_set_folder_permissions_finish ( - cnc, result, error); - - e_async_closure_free (closure); - - return success; -} - -static void -get_password_expiration_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - gchar *exp_date; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "PasswordExpirationDate", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - exp_date = e_soap_parameter_get_string_value (param); - - async_data->items = g_slist_append (async_data->items, exp_date); -} - -/** - * e_ews_connection_get_password_expiration - * @cnc: - * @pri: - * @mail_id: mail is for which password expiration is requested - * @cb: - * @cancellable: - * @user_data: - **/ -void -e_ews_connection_get_password_expiration (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetPasswordExpirationDate", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2010_SP2, - FALSE, - TRUE); - e_ews_message_write_string_parameter (msg, "MailboxSmtpAddress", NULL, mail_id ? mail_id : cnc->priv->email); - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_password_expiration); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_password_expiration_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_password_expiration_finish (EEwsConnection *cnc, - GAsyncResult *result, - gchar **exp_date, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (exp_date != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_password_expiration), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = 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_data->items != NULL, FALSE); - - *exp_date = async_data->items->data; - g_slist_free (async_data->items); - - return TRUE; -} - -/** - * e_ews_connection_get_password_expiration_sync - * @cnc: - * @pri: - * @mail_id: mail id for which password expiration is requested - * @cancellable: - * @error: - **/ -gboolean -e_ews_connection_get_password_expiration_sync (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - gchar **exp_date, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (exp_date != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_password_expiration ( - cnc, pri, mail_id, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_password_expiration_finish ( - cnc, result, exp_date, error); - - e_async_closure_free (closure); - - return success; -} - -static void -get_folder_info_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "GetFolderResponseMessage")) { - ESoapParameter *node; - - node = e_soap_parameter_get_first_child_by_name (subparam, "Folders"); - if (node) { - EEwsFolder *folder = e_ews_folder_new_from_soap_parameter (node); - - if (folder) - async_data->items = g_slist_prepend (NULL, folder); - } - - break; - } - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_get_folder_info (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const EwsFolderId *folder_id, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (folder_id != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetFolder", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "FolderShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, "Default"); - e_soap_message_start_element (msg, "AdditionalProperties", NULL, NULL); - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", "folder:FolderClass"); - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", "folder:ParentFolderId"); - e_soap_message_end_element (msg); /* AdditionalProperties */ - e_soap_message_end_element (msg); /* FolderShape */ - - e_soap_message_start_element (msg, "FolderIds", "messages", NULL); - ews_append_folder_id_to_msg (msg, mail_id, folder_id); - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_get_folder_info); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, get_folder_info_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_folder_info_finish (EEwsConnection *cnc, - GAsyncResult *result, - EEwsFolder **folder, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail (folder != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_get_folder_info), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (!async_data->items) - return FALSE; - - *folder = async_data->items ? async_data->items->data : NULL; - - g_slist_free (async_data->items); - async_data->items = NULL; - - return TRUE; -} - -gboolean -e_ews_connection_get_folder_info_sync (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const EwsFolderId *folder_id, - EEwsFolder **folder, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE); - g_return_val_if_fail (folder_id != NULL, FALSE); - g_return_val_if_fail (folder != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_folder_info ( - cnc, pri, mail_id, folder_id, cancellable, - e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_folder_info_finish ( - cnc, result, folder, error); - - e_async_closure_free (closure); - - return success; -} - -static void -ews_handle_root_folder_param_folders (ESoapParameter *subparam, - EwsAsyncData *async_data) -{ - ESoapParameter *node, *subparam1; - gchar *last, *total; - gint total_items; - EEwsFolder *folder; - gboolean includes_last_item; - - node = e_soap_parameter_get_first_child_by_name (subparam, "RootFolder"); - total = e_soap_parameter_get_property (node, "TotalItemsInView"); - total_items = atoi (total); - g_free (total); - last = e_soap_parameter_get_property (node, "IncludesLastItemInRange"); - /* - * Set the includes_last_item to TRUE as default. - * It can avoid an infinite loop in caller, when, for some reason, - * we don't receive the last_tag property from the server. - */ - includes_last_item = g_strcmp0 (last, "false") != 0; - g_free (last); - - node = e_soap_parameter_get_first_child_by_name (node, "Folders"); - for (subparam1 = e_soap_parameter_get_first_child (node); - subparam1; subparam1 = e_soap_parameter_get_next_child (subparam1)) { - folder = e_ews_folder_new_from_soap_parameter (subparam1); - if (!folder) continue; - async_data->items = g_slist_append (async_data->items, folder); - } - async_data->total_items = total_items; - async_data->includes_last_item = includes_last_item; -} - -static void -find_folder_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "FindFolderResponseMessage")) - ews_handle_root_folder_param_folders (subparam, async_data); - - subparam = e_soap_parameter_get_next_child (subparam); - } -} - -void -e_ews_connection_find_folder (EEwsConnection *cnc, - gint pri, - const EwsFolderId *fid, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "FindFolder", - "Traversal", - "Shallow", - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - FALSE, - TRUE); - e_soap_message_start_element (msg, "FolderShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, "Default"); - e_soap_message_start_element (msg, "AdditionalProperties", NULL, NULL); - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", "folder:FolderClass"); - e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", "folder:ChildFolderCount"); - e_soap_message_end_element (msg); /* AdditionalProperties */ - e_soap_message_end_element (msg); - - e_soap_message_start_element (msg, "ParentFolderIds", "messages", NULL); - - if (fid->is_distinguished_id) - e_ews_message_write_string_parameter_with_attribute (msg, "DistinguishedFolderId", NULL, NULL, "Id", fid->id); - else - e_ews_message_write_string_parameter_with_attribute (msg, "FolderId", NULL, NULL, "Id", fid->id); - - e_soap_message_end_element (msg); - - /* Complete the footer and print the request */ - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_find_folder); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - e_ews_connection_queue_request ( - cnc, msg, find_folder_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_find_folder_finish (EEwsConnection *cnc, - GAsyncResult *result, - gboolean *includes_last_item, - GSList **folders, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_find_folder), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *includes_last_item = async_data->includes_last_item; - *folders = async_data->items; - - return TRUE; -} - -gboolean -e_ews_connection_find_folder_sync (EEwsConnection *cnc, - gint pri, - const EwsFolderId *fid, - gboolean *includes_last_item, - GSList **folders, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_find_folder (cnc, pri, fid, cancellable, e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_find_folder_finish ( - cnc, result, includes_last_item, folders, error); - - e_async_closure_free (closure); - - return success; -} - -#define EWS_OBJECT_KEY_AUTHS_GATHERED "ews-auths-gathered" - -static void -query_auth_methods_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - ESoapParameter *param; - GError *error = NULL; - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - /* nothing to read, it should not get this far anyway */ -} - -static void -ews_connection_gather_auth_methods_cb (SoupMessage *message, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - const gchar *auths_lst; - gboolean has_bearer = FALSE; - gchar **auths; - gint ii; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - g_return_if_fail (async_data != NULL); - - auths_lst = soup_message_headers_get_list (message->response_headers, "WWW-Authenticate"); - if (!auths_lst) - return; - - auths = g_strsplit (auths_lst, ",", -1); - for (ii = 0; auths && auths[ii]; ii++) { - gchar *auth, *space; - - auth = g_strstrip (g_strdup (auths[ii])); - if (auth && *auth) { - space = strchr (auth, ' '); - if (space) - *space = '\0'; - - has_bearer = has_bearer || g_ascii_strcasecmp (auth, "Bearer") == 0; - async_data->items = g_slist_prepend (async_data->items, auth); - } else { - g_free (auth); - } - } - - g_strfreev (auths); - - if (!has_bearer) { - /* Special-case Office365 OAuth2, because outlook.office365.com doesn't advertise Bearer */ - SoupURI *suri; - - suri = soup_message_get_uri (message); - if (suri && soup_uri_get_host (suri) && - g_ascii_strcasecmp (soup_uri_get_host (suri), "outlook.office365.com") == 0) { - async_data->items = g_slist_prepend (async_data->items, g_strdup ("Bearer")); - } - } - - g_object_set_data (G_OBJECT (simple), EWS_OBJECT_KEY_AUTHS_GATHERED, GINT_TO_POINTER (1)); - - soup_message_set_status_full (message, SOUP_STATUS_CANCELLED, "EWS auths gathered"); -} - -/* Note: This only works if the connection is not connected yet */ -void -e_ews_connection_query_auth_methods (EEwsConnection *cnc, - gint pri, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_if_fail (cnc != NULL); - - /* use some simple operation to get WWW-Authenticate headers from the server */ - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetFolder", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2007_SP1, - TRUE, - TRUE); - - e_soap_message_start_element (msg, "FolderShape", "messages", NULL); - e_ews_message_write_string_parameter (msg, "BaseShape", NULL, "IdOnly"); - e_soap_message_end_element (msg); - - e_soap_message_start_element (msg, "FolderIds", "messages", NULL); - e_ews_message_write_string_parameter_with_attribute (msg, "DistinguishedFolderId", NULL, NULL, "Id", "inbox"); - e_soap_message_end_element (msg); - - e_ews_message_write_footer (msg); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, - e_ews_connection_query_auth_methods); - - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer ( - simple, async_data, (GDestroyNotify) async_data_free); - - soup_message_add_header_handler (SOUP_MESSAGE (msg), "got-headers", "WWW-Authenticate", - G_CALLBACK (ews_connection_gather_auth_methods_cb), simple); - - e_ews_connection_queue_request ( - cnc, msg, query_auth_methods_response_cb, - pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_query_auth_methods_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **auth_methods, - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail (auth_methods != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid ( - result, G_OBJECT (cnc), e_ews_connection_query_auth_methods), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (simple), EWS_OBJECT_KEY_AUTHS_GATHERED)) != 1 && - g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - *auth_methods = g_slist_reverse (async_data->items); - - return TRUE; -} - -/* Note: This only works if the connection is not connected yet */ -gboolean -e_ews_connection_query_auth_methods_sync (EEwsConnection *cnc, - gint pri, - GSList **auth_methods, - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_query_auth_methods (cnc, pri, cancellable, e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_query_auth_methods_finish ( - cnc, result, auth_methods, error); - - e_async_closure_free (closure); - - return success; -} - -static void -ews_connection_build_subscribed_folders_list (gpointer key, - gpointer value, - gpointer user_data) -{ - GSList *folders = value, *l; - EEwsConnection *cnc = user_data; - - for (l = folders; l != NULL; l = l->next) { - if (g_slist_find_custom (cnc->priv->subscribed_folders, l->data, (GCompareFunc) g_strcmp0) == NULL) { - cnc->priv->subscribed_folders = - g_slist_prepend (cnc->priv->subscribed_folders, g_strdup (l->data)); - } - } -} - -/* - * Enables server notification on a folder (or a set of folders). - * The events we are listen for notifications are: Copied, Created, Deleted, Modified and Moved. - * - * As we have only one subscription per connection, for every enable_notifications_sync() call, - * we do: - * - Check if we already are subscribed - * - If we are already subscribed: - * - Stop to send events regards to the already subscribed folders' list - * - Unsubscribe the already subscribed folders' list - * - Add the user folders' lst to the hash table if subscribed folders - * - Create a new list of the folders to subscribe for, based in the hash table of subscribed folders - * - Subscribed to listen notifications for the folders - * - Start to send events regards to the newly subscribed folders' list - * - * Pair function for this one is e_ews_connection_disable_notifications_sync() and we do - * something really similar for every disable_notifications_sync() call. - * - * The notification is received to the caller with the "server-notification" signal. - * Note that the signal is used for each notification, without distinction on the - * enabled object. - */ -void -e_ews_connection_enable_notifications_sync (EEwsConnection *cnc, - GSList *folders, - guint *subscription_key) -{ - GSList *new_folders = NULL, *l; - gint subscriptions_size; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (cnc->priv != NULL); - g_return_if_fail (cnc->priv->version >= E_EWS_EXCHANGE_2010_SP1); - g_return_if_fail (folders != NULL); - - NOTIFICATION_LOCK (cnc); - subscriptions_size = g_hash_table_size (cnc->priv->subscriptions); - - if (subscriptions_size == G_MAXUINT - 1) - goto exit; - - if (subscriptions_size > 0) { - e_ews_notification_stop_listening_sync (cnc->priv->notification); - - g_clear_object (&cnc->priv->notification); - - g_slist_free_full (cnc->priv->subscribed_folders, g_free); - cnc->priv->subscribed_folders = NULL; - } - - while (g_hash_table_contains (cnc->priv->subscriptions, GINT_TO_POINTER (notification_key))) { - notification_key++; - if (notification_key == 0) - notification_key++; - } - - for (l = folders; l != NULL; l = l->next) - new_folders = g_slist_prepend (new_folders, g_strdup (l->data)); - - g_hash_table_insert (cnc->priv->subscriptions, GINT_TO_POINTER (notification_key), new_folders); - new_folders = NULL; - - g_hash_table_foreach (cnc->priv->subscriptions, ews_connection_build_subscribed_folders_list, cnc); - - cnc->priv->notification = e_ews_notification_new (cnc); - - e_ews_notification_start_listening_sync (cnc->priv->notification, cnc->priv->subscribed_folders); - -exit: - *subscription_key = notification_key; - notification_key++; - if (notification_key == 0) - notification_key++; - - NOTIFICATION_UNLOCK (cnc); -} - -void -e_ews_connection_disable_notifications_sync (EEwsConnection *cnc, - guint subscription_key) -{ - g_return_if_fail (cnc != NULL); - g_return_if_fail (cnc->priv != NULL); - - NOTIFICATION_LOCK (cnc); - if (cnc->priv->notification == NULL) - goto exit; - - if (!g_hash_table_remove (cnc->priv->subscriptions, GINT_TO_POINTER (subscription_key))) - goto exit; - - e_ews_notification_stop_listening_sync (cnc->priv->notification); - - g_slist_free_full (cnc->priv->subscribed_folders, g_free); - cnc->priv->subscribed_folders = NULL; - - g_hash_table_foreach (cnc->priv->subscriptions, ews_connection_build_subscribed_folders_list, cnc); - if (cnc->priv->subscribed_folders != NULL) { - e_ews_notification_start_listening_sync (cnc->priv->notification, cnc->priv->subscribed_folders); - } else { - g_clear_object (&cnc->priv->notification); - } - -exit: - NOTIFICATION_UNLOCK (cnc); -} - -static EEwsCalendarTo * -ews_get_to (ESoapParameter *node) -{ - EEwsCalendarTo *to = NULL; - ESoapParameter *param; - gchar *kind = NULL; - gchar *value = NULL; - gboolean success = FALSE; - - param = e_soap_parameter_get_first_child_by_name (node, "To"); - if (param == NULL) - goto exit; - - kind = e_soap_parameter_get_property (param, "Kind"); - if (kind == NULL) - goto exit; - - value = e_soap_parameter_get_string_value (param); - if (value == NULL) - goto exit; - - success = TRUE; - -exit: - if (success) { - to = e_ews_calendar_to_new (); - to->kind = kind; - to->value = value; - } else { - g_free (kind); - g_free (value); - } - - return to; -} - -static EEwsCalendarPeriod * -ews_get_period (ESoapParameter *node) -{ - EEwsCalendarPeriod *period = NULL; - gchar *bias = NULL; - gchar *name = NULL; - gchar *id = NULL; - - bias = e_soap_parameter_get_property (node, "Bias"); - name = e_soap_parameter_get_property (node, "Name"); - id = e_soap_parameter_get_property (node, "Id"); - - if (bias == NULL || name == NULL || id == NULL) { - g_free (bias); - g_free (name); - g_free (id); - - return NULL; - } - - period = e_ews_calendar_period_new (); - period->bias = bias; - period->name = name; - period->id = id; - - return period; -} - -static GSList * -ews_get_periods_list (ESoapParameter *node) -{ - ESoapParameter *param; - GSList *periods = NULL; - - for (param = e_soap_parameter_get_first_child_by_name (node, "Period"); - param != NULL; - param = e_soap_parameter_get_next_child_by_name (param, "Period")) { - EEwsCalendarPeriod *period; - - period = ews_get_period (param); - if (period != NULL) { - periods = g_slist_prepend (periods, period); - } else { - g_slist_free_full (periods, (GDestroyNotify) e_ews_calendar_period_free); - return NULL; - } - } - - periods = g_slist_reverse (periods); - return periods; -} - -static EEwsCalendarAbsoluteDateTransition * -ews_get_absolute_date_transition (ESoapParameter *node) -{ - ESoapParameter *param; - EEwsCalendarAbsoluteDateTransition *absolute_date_transition = NULL; - EEwsCalendarTo *to = NULL; - gchar *date_time = NULL; - gboolean success = FALSE; - - param = e_soap_parameter_get_first_child_by_name (node, "To"); - if (param != NULL) - to = ews_get_to (param); - - if (to == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "DateTime"); - if (param != NULL) - date_time = e_soap_parameter_get_string_value (param); - - if (date_time == NULL) - goto exit; - - success = TRUE; - -exit: - if (success) { - absolute_date_transition = e_ews_calendar_absolute_date_transition_new (); - absolute_date_transition->to = to; - absolute_date_transition->date_time = date_time; - } else { - e_ews_calendar_to_free (to); - g_free (date_time); - } - - return absolute_date_transition; -} - -static EEwsCalendarRecurringDateTransition * -ews_get_recurring_date_transition (ESoapParameter *node) -{ - ESoapParameter *param; - EEwsCalendarRecurringDateTransition *recurring_date_transition = NULL; - EEwsCalendarTo *to = NULL; - gchar *time_offset = NULL; - gchar *month = NULL; - gchar *day = NULL; - gboolean success = FALSE; - - to = ews_get_to (node); - if (to == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "TimeOffset"); - if (param != NULL) - time_offset = e_soap_parameter_get_string_value (param); - - if (time_offset == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "Month"); - if (param != NULL) - month = e_soap_parameter_get_string_value (param); - - if (month == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "Day"); - if (param != NULL) - day = e_soap_parameter_get_string_value (param); - - if (day == NULL) - goto exit; - - success = TRUE; - -exit: - if (success) { - recurring_date_transition = e_ews_calendar_recurring_date_transition_new (); - recurring_date_transition->to = to; - recurring_date_transition->time_offset = time_offset; - recurring_date_transition->month = month; - recurring_date_transition->day = day; - } else { - e_ews_calendar_to_free (to); - g_free (time_offset); - g_free (month); - g_free (day); - } - - return recurring_date_transition; -} - -static EEwsCalendarRecurringDayTransition * -ews_get_recurring_day_transition (ESoapParameter *node) -{ - ESoapParameter *param; - EEwsCalendarRecurringDayTransition *recurring_day_transition = NULL; - EEwsCalendarTo *to = NULL; - gchar *time_offset = NULL; - gchar *month = NULL; - gchar *day_of_week = NULL; - gchar *occurrence = NULL; - gboolean success = FALSE; - - to = ews_get_to (node); - if (to == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "TimeOffset"); - if (param != NULL) - time_offset = e_soap_parameter_get_string_value (param); - - if (time_offset == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "Month"); - if (param != NULL) - month = e_soap_parameter_get_string_value (param); - - if (month == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "DayOfWeek"); - if (param != NULL) - day_of_week = e_soap_parameter_get_string_value (param); - - if (day_of_week == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "Occurrence"); - if (param != NULL) - occurrence = e_soap_parameter_get_string_value (param); - - if (occurrence == NULL) - goto exit; - - success = TRUE; - -exit: - if (success) { - recurring_day_transition = e_ews_calendar_recurring_day_transition_new (); - recurring_day_transition->to = to; - recurring_day_transition->time_offset = time_offset; - recurring_day_transition->month = month; - recurring_day_transition->day_of_week = day_of_week; - recurring_day_transition->occurrence = occurrence; - } else { - e_ews_calendar_to_free (to); - g_free (time_offset); - g_free (month); - g_free (day_of_week); - g_free (occurrence); - } - - return recurring_day_transition; -} - -static GSList * -ews_get_absolute_date_transitions_list (ESoapParameter *node) -{ - ESoapParameter *param; - GSList *absolute_date_transitions = NULL; - - for (param = e_soap_parameter_get_first_child_by_name (node, "AbsoluteDateTransition"); - param != NULL; - param = e_soap_parameter_get_next_child_by_name (param, "AbsoluteDateTransition")) { - EEwsCalendarAbsoluteDateTransition *absolute_date_transition; - - absolute_date_transition = ews_get_absolute_date_transition (param); - if (absolute_date_transition != NULL) { - absolute_date_transitions = - g_slist_prepend (absolute_date_transitions, absolute_date_transition); - } else { - g_slist_free_full ( - absolute_date_transitions, - (GDestroyNotify) e_ews_calendar_absolute_date_transition_free); - return NULL; - } - } - - absolute_date_transitions = g_slist_reverse (absolute_date_transitions); - return absolute_date_transitions; -} - -static GSList * -ews_get_recurring_day_transitions_list (ESoapParameter *node) -{ - ESoapParameter *param; - GSList *recurring_day_transitions = NULL; - - for (param = e_soap_parameter_get_first_child_by_name (node, "RecurringDayTransition"); - param != NULL; - param = e_soap_parameter_get_next_child_by_name (param, "RecurringDayTransition")) { - EEwsCalendarRecurringDayTransition *recurring_day_transition; - - recurring_day_transition = ews_get_recurring_day_transition (param); - if (recurring_day_transition != NULL) { - recurring_day_transitions = - g_slist_prepend (recurring_day_transitions, recurring_day_transition); - } else { - g_slist_free_full ( - recurring_day_transitions, - (GDestroyNotify) e_ews_calendar_recurring_day_transition_free); - return NULL; - } - } - - recurring_day_transitions = g_slist_reverse (recurring_day_transitions); - return recurring_day_transitions; -} - -static GSList * -ews_get_recurring_date_transitions_list (ESoapParameter *node) -{ - ESoapParameter *param; - GSList *recurring_date_transitions = NULL; - - for (param = e_soap_parameter_get_first_child_by_name (node, "RecurringDateTransition"); - param != NULL; - param = e_soap_parameter_get_next_child_by_name (param, "RecurringDateTransition")) { - EEwsCalendarRecurringDateTransition *recurring_date_transition; - - recurring_date_transition = ews_get_recurring_date_transition (param); - if (recurring_date_transition != NULL) { - recurring_date_transitions = - g_slist_prepend (recurring_date_transitions, recurring_date_transition); - } else { - g_slist_free_full ( - recurring_date_transitions, - (GDestroyNotify) e_ews_calendar_recurring_date_transition_free); - return NULL; - } - } - - recurring_date_transitions = g_slist_reverse (recurring_date_transitions); - return recurring_date_transitions; -} - -static EEwsCalendarTransitionsGroup * -ews_get_transitions_group (ESoapParameter *node) -{ - EEwsCalendarTransitionsGroup *tg = NULL; - EEwsCalendarTo *transition = NULL; - ESoapParameter *param = NULL; - gchar *id = NULL; - GSList *absolute_date_transitions = NULL; - GSList *recurring_date_transitions = NULL; - GSList *recurring_day_transitions = NULL; - - id = e_soap_parameter_get_property (node, "Id"); - if (id == NULL) - return NULL; - - param = e_soap_parameter_get_first_child_by_name (node, "Transition"); - if (param != NULL) - transition = ews_get_to (param); - - absolute_date_transitions = ews_get_absolute_date_transitions_list (node); - recurring_date_transitions = ews_get_recurring_date_transitions_list (node); - recurring_day_transitions = ews_get_recurring_day_transitions_list (node); - - tg = e_ews_calendar_transitions_group_new (); - tg->id = id; - tg->transition = transition; - tg->absolute_date_transitions = absolute_date_transitions; - tg->recurring_date_transitions = recurring_date_transitions; - tg->recurring_day_transitions = recurring_day_transitions; - - return tg; -} - -static GSList * -ews_get_transitions_groups_list (ESoapParameter *node) -{ - ESoapParameter *param; - GSList *transitions_groups = NULL; - - for (param = e_soap_parameter_get_first_child_by_name (node, "TransitionsGroup"); - param != NULL; - param = e_soap_parameter_get_next_child_by_name (param, "TransitionsGroup")) { - EEwsCalendarTransitionsGroup *tg; - - tg = ews_get_transitions_group (param); - if (tg != NULL) { - transitions_groups = g_slist_prepend (transitions_groups, tg); - } else { - g_slist_free_full (transitions_groups, (GDestroyNotify) e_ews_calendar_transitions_group_free); - return NULL; - } - } - - transitions_groups = g_slist_reverse (transitions_groups); - return transitions_groups; -} - -static EEwsCalendarTransitions * -ews_get_transitions (ESoapParameter *node) -{ - ESoapParameter *param; - EEwsCalendarTransitions *transitions = NULL; - EEwsCalendarTo *transition = NULL; - GSList *absolute_date_transitions = NULL; - GSList *recurring_date_transitions = NULL; - GSList *recurring_day_transitions = NULL; - - param = e_soap_parameter_get_first_child_by_name (node, "Transition"); - if (param != NULL) - transition = ews_get_to (param); - - if (transition == NULL) - return NULL; - - absolute_date_transitions = ews_get_absolute_date_transitions_list (node); - recurring_day_transitions = ews_get_recurring_day_transitions_list (node); - recurring_date_transitions = ews_get_recurring_date_transitions_list (node); - - transitions = e_ews_calendar_transitions_new (); - transitions->transition = transition; - transitions->absolute_date_transitions = absolute_date_transitions; - transitions->recurring_day_transitions = recurring_day_transitions; - transitions->recurring_date_transitions = recurring_date_transitions; - - return transitions; -} - -static EEwsCalendarTimeZoneDefinition * -ews_get_time_zone_definition (ESoapParameter *node) -{ - ESoapParameter *param; - gchar *name = NULL; - gchar *id = NULL; - GSList *periods = NULL; - GSList *transitions_groups = NULL; - EEwsCalendarTransitions *transitions = NULL; - EEwsCalendarTimeZoneDefinition *tzd = NULL; - gboolean success = FALSE; - - name = e_soap_parameter_get_property (node, "Name"); - if (name == NULL) - goto exit; - - id = e_soap_parameter_get_property (node, "Id"); - if (id == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "Periods"); - if (param != NULL) - periods = ews_get_periods_list (param); - if (periods == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "TransitionsGroups"); - if (param != NULL) - transitions_groups = ews_get_transitions_groups_list (param); - if (transitions_groups == NULL) - goto exit; - - param = e_soap_parameter_get_first_child_by_name (node, "Transitions"); - if (param != NULL) - transitions = ews_get_transitions (param); - if (transitions == NULL) - goto exit; - - success = TRUE; - -exit: - if (success) { - tzd = e_ews_calendar_time_zone_definition_new (); - tzd->name = name; - tzd->id = id; - tzd->periods = periods; - tzd->transitions_groups = transitions_groups; - tzd->transitions = transitions; - } else { - g_free (name); - g_free (id); - g_slist_free_full (periods, (GDestroyNotify) e_ews_calendar_period_free); - g_slist_free_full (transitions_groups, (GDestroyNotify) e_ews_calendar_transitions_group_free); - e_ews_calendar_transitions_free (transitions); - } - - return tzd; -} - -static void -get_server_time_zones_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - ESoapParameter *subparam; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "ResponseMessages", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - subparam = e_soap_parameter_get_first_child (param); - - while (subparam != NULL) { - const gchar *name = (const gchar *) subparam->name; - - if (!ews_get_response_status (subparam, &error)) { - g_simple_async_result_take_error (simple, error); - return; - } - - if (E_EWS_CONNECTION_UTILS_CHECK_ELEMENT (name, "GetServerTimeZonesResponseMessage")) { - ESoapParameter *node, *node2; - - node = e_soap_parameter_get_first_child_by_name (subparam, "TimeZoneDefinitions"); - if (node != NULL) { - node2 = e_soap_parameter_get_first_child_by_name (node, "TimeZoneDefinition"); - if (node2 != NULL) { - EEwsCalendarTimeZoneDefinition *tzd; - - tzd = ews_get_time_zone_definition (node2); - if (tzd != NULL) - async_data->tzds = g_slist_prepend (async_data->tzds, tzd); - } - } - } - - subparam = e_soap_parameter_get_next_child (subparam); - } - - async_data->tzds = g_slist_reverse (async_data->tzds); -} - -void -e_ews_connection_get_server_time_zones (EEwsConnection *cnc, - gint pri, - GSList *msdn_locations, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - GSList *l; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (cnc->priv != NULL); - - simple = g_simple_async_result_new ( - G_OBJECT (cnc), callback, user_data, e_ews_connection_get_server_time_zones); - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer (simple, async_data, (GDestroyNotify) async_data_free); - - /* - * EWS server version earlier than 2010 doesn't have support to "GetServerTimeZones". - * So, if the API is called with an older Exchange's version, let's just fail silently. - */ - if (!e_ews_connection_satisfies_server_version (cnc, E_EWS_EXCHANGE_2010_SP1)) { - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - return; - } - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetServerTimeZones", - "ReturnFullTimeZoneData", - "true", - cnc->priv->version, - E_EWS_EXCHANGE_2010, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "Ids", "messages", NULL); - for (l = msdn_locations; l != NULL; l = l->next) - e_ews_message_write_string_parameter_with_attribute (msg, "Id", NULL, l->data, NULL, NULL); - e_soap_message_end_element (msg); /* Ids */ - - e_ews_message_write_footer (msg); /* Complete the footer and print the request */ - - e_ews_connection_queue_request (cnc, msg, get_server_time_zones_response_cb, pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_server_time_zones_finish (EEwsConnection *cnc, - GAsyncResult *result, - GSList **tzds, /*EEwsCalendarTimeZoneDefinition */ - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid (result, G_OBJECT (cnc), e_ews_connection_get_server_time_zones), - FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (async_data->tzds == NULL) - return FALSE; - - if (tzds != NULL) - *tzds = async_data->tzds; - else - g_slist_free_full ( - async_data->tzds, - (GDestroyNotify) e_ews_calendar_time_zone_definition_free); - - return TRUE; -} - -gboolean -e_ews_connection_get_server_time_zones_sync (EEwsConnection *cnc, - gint pri, - GSList *msdn_locations, - GSList **tzds, /* EEwsCalendarTimeZoneDefinition */ - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_server_time_zones ( - cnc, pri, msdn_locations, cancellable, e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_server_time_zones_finish (cnc, result, tzds, error); - - e_async_closure_free (closure); - - return success; -} - -static void -get_user_photo_response_cb (ESoapResponse *response, - GSimpleAsyncResult *simple) -{ - EwsAsyncData *async_data; - ESoapParameter *param; - GError *error = NULL; - - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - param = e_soap_response_get_first_parameter_by_name ( - response, "PictureData", &error); - - /* Sanity check */ - g_return_if_fail ( - (param != NULL && error == NULL) || - (param == NULL && error != NULL)); - - if (error != NULL) { - g_simple_async_result_take_error (simple, error); - return; - } - - async_data->user_photo = e_soap_parameter_get_string_value (param); - if (async_data->user_photo && !*async_data->user_photo) { - g_free (async_data->user_photo); - async_data->user_photo = NULL; - } -} - -void -e_ews_connection_get_user_photo (EEwsConnection *cnc, - gint pri, - const gchar *email, - EEwsSizeRequested size_requested, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data) -{ - ESoapMessage *msg; - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - gchar *tmp; - - g_return_if_fail (cnc != NULL); - g_return_if_fail (cnc->priv != NULL); - g_return_if_fail (email != NULL); - - simple = g_simple_async_result_new (G_OBJECT (cnc), callback, user_data, e_ews_connection_get_user_photo); - async_data = g_new0 (EwsAsyncData, 1); - g_simple_async_result_set_op_res_gpointer (simple, async_data, (GDestroyNotify) async_data_free); - - /* - * EWS server version earlier than 2013 doesn't have support to "GetUserPhoto". - */ - if (!e_ews_connection_satisfies_server_version (cnc, E_EWS_EXCHANGE_2013)) { - g_simple_async_result_complete_in_idle (simple); - g_object_unref (simple); - return; - } - - msg = e_ews_message_new_with_header ( - cnc->priv->settings, - cnc->priv->uri, - cnc->priv->impersonate_user, - "GetUserPhoto", - NULL, - NULL, - cnc->priv->version, - E_EWS_EXCHANGE_2013, - FALSE, - TRUE); - - e_soap_message_start_element (msg, "Email", "messages", NULL); - e_soap_message_write_string (msg, email); - e_soap_message_end_element (msg); /* Email */ - - e_soap_message_start_element (msg, "SizeRequested", "messages", NULL); - tmp = g_strdup_printf ("HR%dx%d", (gint) size_requested, size_requested); - e_soap_message_write_string (msg, tmp); - g_free (tmp); - e_soap_message_end_element (msg); /* SizeRequested */ - - e_ews_message_write_footer (msg); /* Complete the footer and print the request */ - - e_ews_connection_queue_request (cnc, msg, get_user_photo_response_cb, pri, cancellable, simple); - - g_object_unref (simple); -} - -gboolean -e_ews_connection_get_user_photo_finish (EEwsConnection *cnc, - GAsyncResult *result, - gchar **out_picture_data, /* base64-encoded */ - GError **error) -{ - GSimpleAsyncResult *simple; - EwsAsyncData *async_data; - - g_return_val_if_fail (cnc != NULL, FALSE); - g_return_val_if_fail ( - g_simple_async_result_is_valid (result, G_OBJECT (cnc), e_ews_connection_get_user_photo), - FALSE); - g_return_val_if_fail (out_picture_data != NULL, FALSE); - - simple = G_SIMPLE_ASYNC_RESULT (result); - async_data = g_simple_async_result_get_op_res_gpointer (simple); - - if (g_simple_async_result_propagate_error (simple, error)) - return FALSE; - - if (!async_data->user_photo) - return FALSE; - - *out_picture_data = async_data->user_photo; - async_data->user_photo = NULL; - - return TRUE; -} - -gboolean -e_ews_connection_get_user_photo_sync (EEwsConnection *cnc, - gint pri, - const gchar *email, - EEwsSizeRequested size_requested, - gchar **out_picture_data, /* base64-encoded */ - GCancellable *cancellable, - GError **error) -{ - EAsyncClosure *closure; - GAsyncResult *result; - gboolean success; - - g_return_val_if_fail (cnc != NULL, FALSE); - - closure = e_async_closure_new (); - - e_ews_connection_get_user_photo ( - cnc, pri, email, size_requested, cancellable, e_async_closure_callback, closure); - - result = e_async_closure_wait (closure); - - success = e_ews_connection_get_user_photo_finish (cnc, result, out_picture_data, error); - - e_async_closure_free (closure); - - return success; -} diff --git a/src/server/e-ews-connection.h b/src/server/e-ews-connection.h index b99fa9d..76f8cf6 100644 --- a/src/server/e-ews-connection.h +++ b/src/server/e-ews-connection.h @@ -132,15 +132,6 @@ typedef enum { E_EWS_SIZE_REQUESTED_648X648 = 648 } EEwsSizeRequested; -typedef enum { - E_EWS_USER_CONFIGURATION_PROPERTIES_UNKNOWN = -1, - E_EWS_USER_CONFIGURATION_PROPERTIES_ID, - E_EWS_USER_CONFIGURATION_PROPERTIES_DICTIONARY, - E_EWS_USER_CONFIGURATION_PROPERTIES_XMLDATA, - E_EWS_USER_CONFIGURATION_PROPERTIES_BINARYDATA /*, - E_EWS_USER_CONFIGURATION_PROPERTIES_ALL - skip it, be specific */ -} EEwsUserConfigurationProperties; - typedef struct { gchar *id; gchar *dn; @@ -435,16 +426,9 @@ ESourceAuthenticationResult e_ews_connection_try_credentials_sync (EEwsConnection *cnc, const ENamedParameters *credentials, - ESource *use_source, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, GCancellable *cancellable, GError **error); ESource * e_ews_connection_get_source (EEwsConnection *cnc); -gboolean e_ews_connection_get_ssl_error_details - (EEwsConnection *cnc, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors); const gchar * e_ews_connection_get_uri (EEwsConnection *cnc); ESoupAuthBearer * e_ews_connection_ref_bearer_auth(EEwsConnection *cnc); @@ -485,8 +469,6 @@ gboolean e_ews_autodiscover_ws_url_sync (ESource *source, CamelEwsSettings *settings, const gchar *email_address, const gchar *password, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, GCancellable *cancellable, GError **error); void e_ews_autodiscover_ws_url (ESource *source, @@ -499,8 +481,6 @@ void e_ews_autodiscover_ws_url (ESource *source, gboolean e_ews_autodiscover_ws_url_finish (CamelEwsSettings *settings, GAsyncResult *result, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, GError **error); const gchar * e_ews_connection_get_mailbox (EEwsConnection *cnc); void e_ews_connection_set_mailbox (EEwsConnection *cnc, @@ -1386,29 +1366,6 @@ gboolean e_ews_connection_get_user_photo_sync gchar **out_picture_data, /* base64-encoded */ GCancellable *cancellable, GError **error); -void e_ews_connection_get_user_configuration - (EEwsConnection *cnc, - gint pri, - const EwsFolderId *fid, - const gchar *config_name, - EEwsUserConfigurationProperties props, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_user_configuration_finish - (EEwsConnection *cnc, - GAsyncResult *result, - gchar **out_properties, - GError **error); -gboolean e_ews_connection_get_user_configuration_sync - (EEwsConnection *cnc, - gint pri, - const EwsFolderId *fid, - const gchar *config_name, - EEwsUserConfigurationProperties props, - gchar **out_properties, - GCancellable *cancellable, - GError **error); G_END_DECLS diff --git a/src/server/e-ews-connection.h.cve-2019-3890 b/src/server/e-ews-connection.h.cve-2019-3890 deleted file mode 100644 index 76f8cf6..0000000 --- a/src/server/e-ews-connection.h.cve-2019-3890 +++ /dev/null @@ -1,1372 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Authors : - * JP Rosevear - * Rodrigo Moya - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - * 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. - * - * 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -#ifndef E_EWS_CONNECTION_H -#define E_EWS_CONNECTION_H - -#include -#include -#include -#include -#include - -#include "e-soap-message.h" -#include "ews-errors.h" -#include "e-ews-folder.h" -#include "e-ews-item.h" -#include "camel-ews-settings.h" - -/* Standard GObject macros */ -#define E_TYPE_EWS_CONNECTION \ - (e_ews_connection_get_type ()) -#define E_EWS_CONNECTION(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), E_TYPE_EWS_CONNECTION, EEwsConnection)) -#define E_EWS_CONNECTION_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), E_TYPE_EWS_CONNECTION, EEwsConnectionClass)) -#define E_IS_EWS_CONNECTION(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), E_TYPE_EWS_CONNECTION)) -#define E_IS_EWS_CONNECTION_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), E_TYPE_EWS_CONNECTION)) -#define E_EWS_CONNECTION_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), E_TYPE_EWS_CONNECTION, EEwsConnectionClass)) - -G_BEGIN_DECLS - -typedef struct _EEwsConnection EEwsConnection; -typedef struct _EEwsConnectionClass EEwsConnectionClass; -typedef struct _EEwsConnectionPrivate EEwsConnectionPrivate; - -struct _EEwsConnection { - GObject parent; - EEwsConnectionPrivate *priv; -}; - -struct _EEwsConnectionClass { - GObjectClass parent_class; - - void (* password_will_expire) (EEwsConnection *connection, - gint in_days, - const gchar *service_url); -}; - -enum { - EWS_PRIORITY_LOW, - EWS_PRIORITY_MEDIUM, - EWS_PRIORITY_HIGH -}; - -typedef void (*EEwsRequestCreationCallback) (ESoapMessage *msg, - gpointer user_data); -typedef void (*EwsProgressFn) (gpointer object, - gint percent); -typedef void (*EEwsResponseCallback) (ESoapResponse *response, - GSimpleAsyncResult *simple); - -typedef enum { - EWS_SEARCH_AD, - EWS_SEARCH_AD_CONTACTS, - EWS_SEARCH_CONTACTS, - EWS_SEARCH_CONTACTS_AD -} EwsContactsSearchScope; - -typedef enum { - EWS_HARD_DELETE = 1, - EWS_SOFT_DELETE, - EWS_MOVE_TO_DELETED_ITEMS -} EwsDeleteType; - -typedef enum { - EWS_SEND_TO_NONE = 1, - EWS_SEND_ONLY_TO_ALL, - EWS_SEND_TO_ALL_AND_SAVE_COPY -} EwsSendMeetingCancellationsType; - -typedef enum { - EWS_NONE_OCCURRENCES = 0, - EWS_ALL_OCCURRENCES, - EWS_SPECIFIED_OCCURRENCE_ONLY -} EwsAffectedTaskOccurrencesType; - -typedef enum { - E_EWS_BODY_TYPE_ANY, - E_EWS_BODY_TYPE_BEST, - E_EWS_BODY_TYPE_HTML, - E_EWS_BODY_TYPE_TEXT -} EEwsBodyType; - -typedef enum { - E_EWS_SIZE_REQUESTED_UNKNOWN = 0, - E_EWS_SIZE_REQUESTED_48X48 = 48, - E_EWS_SIZE_REQUESTED_64X64 = 64, - E_EWS_SIZE_REQUESTED_96X96 = 96, - E_EWS_SIZE_REQUESTED_120X120 = 120, - E_EWS_SIZE_REQUESTED_240X240 = 240, - E_EWS_SIZE_REQUESTED_360X360 = 360, - E_EWS_SIZE_REQUESTED_432X432 = 432, - E_EWS_SIZE_REQUESTED_504X504 = 504, - E_EWS_SIZE_REQUESTED_648X648 = 648 -} EEwsSizeRequested; - -typedef struct { - gchar *id; - gchar *dn; - gchar *name; -} EwsOAL; - -typedef struct { - gchar *type; - guint32 seq; - guint32 ver; - guint32 size; - guint32 uncompressed_size; - gchar *sha; - gchar *filename; -} EwsOALDetails; - -typedef struct { - gchar *sid; - gchar *primary_smtp; - gchar *display_name; - gchar *distinguished_user; - gchar *external_user; -} EwsUserId; - -typedef enum { - EwsPermissionLevel_Unknown = 0, - EwsPermissionLevel_None, - EwsPermissionLevel_Reviewer, - EwsPermissionLevel_Author, - EwsPermissionLevel_Editor, - EwsPermissionLevel_Custom -} EwsPermissionLevel; - -typedef struct { - EwsUserId *user_id; - EwsPermissionLevel calendar, tasks, inbox, contacts, notes, journal; - gboolean meetingcopies; - gboolean view_priv_items; -} EwsDelegateInfo; - -typedef enum { - EwsDelegateDeliver_DelegatesOnly, - EwsDelegateDeliver_DelegatesAndMe, - EwsDelegateDeliver_DelegatesAndSendInformationToMe -} EwsDelegateDeliver; - -typedef enum { - NORMAL_FIELD_URI, - INDEXED_FIELD_URI, - EXTENDED_FIELD_URI -} EwsFieldURIType; - -typedef struct { - gchar *distinguished_prop_set_id; - gchar *prop_set_id; - gchar *prop_tag; - gchar *prop_name; - gchar *prop_id; - gchar *prop_type; -} EEwsExtendedFieldURI; - -typedef struct { - gchar *field_uri; - gchar *field_index; -} EEwsIndexedFieldURI; - -typedef struct { - gchar *field_uri; - GSList *extended_furis; - GSList *indexed_furis; -} EEwsAdditionalProps; - -typedef struct { - gchar *order; - gint uri_type; - gpointer field_uri; -} EwsSortOrder; - -typedef struct { - gchar *id; - gsize len; -} EwsPhotoAttachmentInfo; - -typedef enum { - E_EWS_NOTIFICATION_EVENT_COPIED = 0, - E_EWS_NOTIFICATION_EVENT_CREATED, - E_EWS_NOTIFICATION_EVENT_DELETED, - E_EWS_NOTIFICATION_EVENT_MODIFIED, - E_EWS_NOTIFICATION_EVENT_MOVED, - E_EWS_NOTIFICATION_EVENT_STATUS -} EEwsNotificationEventType; - -typedef struct { - EEwsNotificationEventType type; - gboolean is_item; - gchar *folder_id; - gchar *old_folder_id; -} EEwsNotificationEvent; - -/* - * - */ -typedef struct { - gchar *kind; - gchar *value; -} EEwsCalendarTo; - -/* - * - * - * - * - */ -typedef struct { - EEwsCalendarTo *to; - gchar *date_time; -} EEwsCalendarAbsoluteDateTransition; - -/* - * - * - * - * - * - * - * - */ -typedef struct { - EEwsCalendarTo *to; - gchar *time_offset; - gchar *month; - gchar *day_of_week; - gchar *occurrence; -} EEwsCalendarRecurringDayTransition; - -/* - * - * - * - * - * - * - */ -typedef struct { - EEwsCalendarTo *to; - gchar *time_offset; - gchar *month; - gchar *day; -} EEwsCalendarRecurringDateTransition; - -/* - * - */ -typedef struct { - gchar *bias; - gchar *name; - gchar *id; -} EEwsCalendarPeriod; - -/* - * - * - * - * - * - * - * - * - */ -typedef struct { - gchar *id; - EEwsCalendarTo *transition; - GSList *absolute_date_transitions; /* EEwsCalendarAbsoluteDateTransition */ - GSList *recurring_day_transitions; /* EEwsCalendarRecurringDayTransition */ - GSList *recurring_date_transitions; /* EEwsCalendarRecurringDateTransition */ -} EEwsCalendarTransitionsGroup; - -/* - * - * - * - * - * - * - * - * - */ -typedef struct { - EEwsCalendarTo *transition; - GSList *absolute_date_transitions; /* EEwsCalendarAbsoluteDateTransition */ - GSList *recurring_day_transitions; /* EEwsCalendarRecurringDayTransition */ - GSList *recurring_date_transitions; /* EEwsCalendarRecurringDateTransition */ -} EEwsCalendarTransitions; - -/* - * - * - * - * - * - * - * - * - * - */ -typedef struct { - gchar *name; - gchar *id; - GSList *periods; /* EEwsCalendarPeriod */ - GSList *transitions_groups; /* EEwsCalendarTrasitionsGroup */ - EEwsCalendarTransitions *transitions; -} EEwsCalendarTimeZoneDefinition; - -EEwsCalendarTo * - e_ews_calendar_to_new (void); -void e_ews_calendar_to_free (EEwsCalendarTo *to); - -EEwsCalendarAbsoluteDateTransition * - e_ews_calendar_absolute_date_transition_new - (void); -void e_ews_calendar_absolute_date_transition_free - (EEwsCalendarAbsoluteDateTransition *adt); - -EEwsCalendarRecurringDayTransition * - e_ews_calendar_recurring_day_transition_new - (void); -void e_ews_calendar_recurring_day_transition_free - (EEwsCalendarRecurringDayTransition *rdayt); - -EEwsCalendarRecurringDateTransition * - e_ews_calendar_recurring_date_transition_new - (void); -void e_ews_calendar_recurring_date_transition_free - (EEwsCalendarRecurringDateTransition *rdatet); - -EEwsCalendarPeriod * - e_ews_calendar_period_new (void); -void e_ews_calendar_period_free (EEwsCalendarPeriod *period); - -EEwsCalendarTransitionsGroup * - e_ews_calendar_transitions_group_new - (void); -void e_ews_calendar_transitions_group_free - (EEwsCalendarTransitionsGroup *tg); - -EEwsCalendarTransitions * - e_ews_calendar_transitions_new (void); -void e_ews_calendar_transitions_free (EEwsCalendarTransitions *transitions); - -EEwsCalendarTimeZoneDefinition * - e_ews_calendar_time_zone_definition_new - (void); -void e_ews_calendar_time_zone_definition_free - (EEwsCalendarTimeZoneDefinition *tzd); - -EEwsExtendedFieldURI * - e_ews_extended_field_uri_new (void); -void e_ews_extended_field_uri_free (EEwsExtendedFieldURI *ex_field_uri); - -EEwsIndexedFieldURI * - e_ews_indexed_field_uri_new (const gchar *uri, - const gchar *index); -void e_ews_indexed_field_uri_free (EEwsIndexedFieldURI *id_field_uri); - -EEwsAdditionalProps * - e_ews_additional_props_new (void); -void e_ews_additional_props_free (EEwsAdditionalProps *add_props); - -EEwsNotificationEvent * - e_ews_notification_event_new (void); -void e_ews_notification_event_free (EEwsNotificationEvent *event); - -void ews_oal_free (EwsOAL *oal); -void ews_oal_details_free (EwsOALDetails *details); - -GType e_ews_connection_get_type (void); -EEwsConnection *e_ews_connection_new (ESource *source, - const gchar *uri, - CamelEwsSettings *settings); -EEwsConnection *e_ews_connection_new_full (ESource *source, - const gchar *uri, - CamelEwsSettings *settings, - gboolean allow_connection_reuse); -EEwsConnection *e_ews_connection_new_for_backend(EBackend *backend, - ESourceRegistry *registry, - const gchar *uri, - CamelEwsSettings *settings); -void e_ews_connection_update_credentials - (EEwsConnection *cnc, - const ENamedParameters *credentials); -ESourceAuthenticationResult - e_ews_connection_try_credentials_sync - (EEwsConnection *cnc, - const ENamedParameters *credentials, - GCancellable *cancellable, - GError **error); -ESource * e_ews_connection_get_source (EEwsConnection *cnc); -const gchar * e_ews_connection_get_uri (EEwsConnection *cnc); -ESoupAuthBearer * - e_ews_connection_ref_bearer_auth(EEwsConnection *cnc); -void e_ews_connection_set_bearer_auth(EEwsConnection *cnc, - ESoupAuthBearer *bearer_auth); -const gchar * e_ews_connection_get_password (EEwsConnection *cnc); -gchar * e_ews_connection_dup_password (EEwsConnection *cnc); -void e_ews_connection_set_password (EEwsConnection *cnc, - const gchar *password); -const gchar * e_ews_connection_get_impersonate_user - (EEwsConnection *cnc); -GProxyResolver * - e_ews_connection_ref_proxy_resolver - (EEwsConnection *cnc); -void e_ews_connection_set_proxy_resolver - (EEwsConnection *cnc, - GProxyResolver *proxy_resolver); -CamelEwsSettings * - e_ews_connection_ref_settings (EEwsConnection *cnc); -SoupSession * e_ews_connection_ref_soup_session - (EEwsConnection *cnc); -gboolean e_ews_connection_get_disconnected_flag - (EEwsConnection *cnc); -void e_ews_connection_set_disconnected_flag - (EEwsConnection *cnc, - gboolean disconnected_flag); -EEwsConnection *e_ews_connection_find (const gchar *uri, - const gchar *username); -GSList * e_ews_connection_list_existing (void); /* EEwsConnection * */ -void e_ews_connection_queue_request (EEwsConnection *cnc, - ESoapMessage *msg, - EEwsResponseCallback cb, - gint pri, - GCancellable *cancellable, - GSimpleAsyncResult *simple); - -gboolean e_ews_autodiscover_ws_url_sync (ESource *source, - CamelEwsSettings *settings, - const gchar *email_address, - const gchar *password, - GCancellable *cancellable, - GError **error); -void e_ews_autodiscover_ws_url (ESource *source, - CamelEwsSettings *settings, - const gchar *email_address, - const gchar *password, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_autodiscover_ws_url_finish - (CamelEwsSettings *settings, - GAsyncResult *result, - GError **error); -const gchar * e_ews_connection_get_mailbox (EEwsConnection *cnc); -void e_ews_connection_set_mailbox (EEwsConnection *cnc, - const gchar *email); - -void ews_user_id_free (EwsUserId *id); -void ews_delegate_info_free (EwsDelegateInfo *info); - -void e_ews_connection_sync_folder_items - (EEwsConnection *cnc, - gint pri, - const gchar *old_sync_state, - const gchar *fid, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - guint max_entries, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_sync_folder_items_finish - (EEwsConnection *cnc, - GAsyncResult *result, - gchar **new_sync_state, - gboolean *includes_last_item, - GSList **items_created, - GSList **items_updated, - GSList **items_deleted, - GError **error); -gboolean e_ews_connection_sync_folder_items_sync - (EEwsConnection *cnc, - gint pri, - const gchar *old_sync_state, - const gchar *fid, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - guint max_entries, - gchar **new_sync_state, - gboolean *includes_last_item, - GSList **items_created, - GSList **items_updated, - GSList **items_deleted, - GCancellable *cancellable, - GError **error); - -typedef void (*EwsConvertQueryCallback) (ESoapMessage *msg, - const gchar *query, - EEwsFolderType type); - -void e_ews_connection_find_folder_items - (EEwsConnection *cnc, - gint pri, - EwsFolderId *fid, - const gchar *props, - const EEwsAdditionalProps *add_props, - EwsSortOrder *sort_order, - const gchar *query, - GPtrArray *only_ids, /* element-type utf8 */ - EEwsFolderType type, - EwsConvertQueryCallback convert_query_cb, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_find_folder_items_finish - (EEwsConnection *cnc, - GAsyncResult *result, - gboolean *includes_last_item, - GSList **items, - GError **error); -gboolean e_ews_connection_find_folder_items_sync - (EEwsConnection *cnc, - gint pri, - EwsFolderId *fid, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - EwsSortOrder *sort_order, - const gchar *query, - GPtrArray *only_ids, /* element-type utf8 */ - EEwsFolderType type, - gboolean *includes_last_item, - GSList **items, - EwsConvertQueryCallback convert_query_cb, - GCancellable *cancellable, - GError **error); - -EEwsServerVersion - e_ews_connection_get_server_version - (EEwsConnection *cnc); -void e_ews_connection_set_server_version - (EEwsConnection *cnc, - EEwsServerVersion version); -void e_ews_connection_set_server_version_from_string - (EEwsConnection *cnc, - const gchar *version); -gboolean e_ews_connection_satisfies_server_version - (EEwsConnection *cnc, - EEwsServerVersion versio); - -void e_ews_connection_get_items (EEwsConnection *cnc, - gint pri, - const GSList *ids, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - gboolean include_mime, - const gchar *mime_directory, - EEwsBodyType body_type, - ESoapProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_items_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **items, - GError **error); -gboolean e_ews_connection_get_items_sync (EEwsConnection *cnc, - gint pri, - const GSList *ids, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - gboolean include_mime, - const gchar *mime_directory, - EEwsBodyType body_type, - GSList **items, - ESoapProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_delete_items (EEwsConnection *cnc, - gint pri, - const GSList *ids, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_delete_items_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_delete_items_sync - (EEwsConnection *cnc, - gint pri, - const GSList *ids, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GError **error); -gboolean e_ews_connection_delete_items_in_chunks_sync - (EEwsConnection *cnc, - gint pri, - const GSList *ids, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GError **error); -void e_ews_connection_delete_item (EEwsConnection *cnc, - gint pri, - EwsId *id, - guint index, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_delete_item_sync - (EEwsConnection *cnc, - gint pri, - EwsId *id, - guint index, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_update_items (EEwsConnection *cnc, - gint pri, - const gchar *conflict_res, - const gchar *msg_disposition, - const gchar *send_invites, - const gchar *folder_id, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_update_items_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **ids, - GError **error); -gboolean e_ews_connection_update_items_sync - (EEwsConnection *cnc, - gint pri, - const gchar *conflict_res, - const gchar *msg_disposition, - const gchar *send_invites, - const gchar *folder_id, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GSList **ids, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_create_items (EEwsConnection *cnc, - gint pri, - const gchar *msg_disposition, - const gchar *send_invites, - const EwsFolderId *fid, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_create_items_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **ids, - GError **error); -gboolean e_ews_connection_create_items_sync - (EEwsConnection *cnc, - gint pri, - const gchar *msg_disposition, - const gchar *send_invites, - const EwsFolderId *fid, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GSList **ids, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_sync_folder_hierarchy - (EEwsConnection *cnc, - gint pri, - const gchar *sync_state, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_sync_folder_hierarchy_finish - (EEwsConnection *cnc, - GAsyncResult *result, - gchar **sync_state, - gboolean *includes_last_folder, - GSList **folders_created, - GSList **folders_updated, - GSList **folders_deleted, - GError **error); -gboolean e_ews_connection_sync_folder_hierarchy_sync - (EEwsConnection *cnc, - gint pri, - const gchar *old_sync_state, - gchar **new_sync_state, - gboolean *includes_last_folder, - GSList **folders_created, - GSList **folders_updated, - GSList **folders_deleted, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_resolve_names (EEwsConnection *cnc, - gint pri, - const gchar *resolve_name, - EwsContactsSearchScope scope, - GSList *parent_folder_ids, - gboolean fetch_contact_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_resolve_names_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **mailboxes, - GSList **contact_items, - gboolean *includes_last_item, - GError **error); -gboolean e_ews_connection_resolve_names_sync - (EEwsConnection *cnc, - gint pri, - const gchar *resolve_name, - EwsContactsSearchScope scope, - GSList *parent_folder_ids, - gboolean fetch_contact_data, - GSList **mailboxes, - GSList **contact_items, - gboolean *includes_last_item, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_expand_dl (EEwsConnection *cnc, - gint pri, - const EwsMailbox *mb, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_expand_dl_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **mailboxes, - gboolean *includes_last_item, - GError **error); -gboolean e_ews_connection_expand_dl_sync (EEwsConnection *cnc, - gint pri, - const EwsMailbox *mb, - GSList **mailboxes, - gboolean *includes_last_item, - GCancellable *cancellable, - GError **error); - -gboolean e_ews_connection_ex_to_smtp_sync - (EEwsConnection *cnc, - gint pri, - const gchar *name, - const gchar *ex_address, - gchar **smtp_address, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_create_folder (EEwsConnection *cnc, - gint pri, - const gchar *parent_folder_id, - gboolean is_distinguished_id, - const gchar *folder_name, - EEwsFolderType folder_type, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_create_folder_finish - (EEwsConnection *cnc, - GAsyncResult *result, - EwsFolderId **folder_id, - GError **error); -gboolean e_ews_connection_create_folder_sync - (EEwsConnection *cnc, - gint pri, - const gchar *parent_folder_id, - gboolean is_distinguished_id, - const gchar *folder_name, - EEwsFolderType folder_type, - EwsFolderId **folder_id, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_delete_folder (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean is_distinguished_id, - const gchar *delete_type, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_delete_folder_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_delete_folder_sync - (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean is_distinguished_id, - const gchar *delete_type, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_empty_folder (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean is_distinguished_id, - const gchar *delete_type, - gboolean delete_subfolders, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_empty_folder_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_empty_folder_sync - (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean is_distinguished_id, - const gchar *delete_type, - gboolean delete_subfolders, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_update_folder (EEwsConnection *cnc, - gint pri, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_update_folder_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_update_folder_sync - (EEwsConnection *cnc, - gint pri, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_move_folder (EEwsConnection *cnc, - gint pri, - const gchar *to_folder, - const gchar *folder, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_move_folder_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_move_folder_sync - (EEwsConnection *cnc, - gint pri, - const gchar *to_folder, - const gchar *folder, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_get_folder (EEwsConnection *cnc, - gint pri, - const gchar *folder_shape, - const EEwsAdditionalProps *add_props, - GSList *folder_ids, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_folder_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **folders, - GError **error); -gboolean e_ews_connection_get_folder_sync - (EEwsConnection *cnc, - gint pri, - const gchar *folder_shape, - const EEwsAdditionalProps *add_props, - GSList *folder_ids, - GSList **folders, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_move_items (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean docopy, - const GSList *ids, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_move_items_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **items, - GError **error); -gboolean e_ews_connection_move_items_sync - (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean docopy, - const GSList *ids, - GSList **items_ret, - GCancellable *cancellable, - GError **error); -gboolean e_ews_connection_move_items_in_chunks_sync - (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean docopy, - const GSList *ids, - GSList **items, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_create_attachments - (EEwsConnection *cnc, - gint pri, - const EwsId *parent, - const GSList *files, - gboolean is_contact_photo, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_create_attachments_finish - (EEwsConnection *cnc, - gchar **change_key, - GSList **attachments_ids, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_create_attachments_sync - (EEwsConnection *cnc, - gint pri, - const EwsId *parent, - const GSList *files, - gboolean is_contact_photo, - gchar **change_key, - GSList **attachments_ids, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_delete_attachments - (EEwsConnection *cnc, - gint pri, - const GSList *attachments_ids, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_delete_attachments_finish - (EEwsConnection *cnc, - GAsyncResult *result, - gchar **new_change_key, - GError **error); -gboolean e_ews_connection_delete_attachments_sync - (EEwsConnection *cnc, - gint pri, - const GSList *attachments_ids, - gchar **new_change_key, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_get_attachments - (EEwsConnection *cnc, - gint pri, - const gchar *comp_uid, - const GSList *ids, - const gchar *cache, - gboolean include_mime, - ESoapProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_attachments_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **items, - GError **error); -gboolean e_ews_connection_get_attachments_sync - (EEwsConnection *cnc, - gint pri, - const gchar *comp_uid, - const GSList *ids, - const gchar *cache, - gboolean include_mime, - GSList **items, - ESoapProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GError **error); - -gboolean e_ews_connection_get_oal_list_sync - (EEwsConnection *cnc, - GSList **oals, - GCancellable *cancellable, - GError **error); -void e_ews_connection_get_oal_list (EEwsConnection *cnc, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_oal_list_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **oals, - GError **error); -gboolean e_ews_connection_get_oal_detail_sync - (EEwsConnection *cnc, - const gchar *oal_id, - const gchar *oal_element, - const gchar *old_etag, - GSList **elements, - gchar **etag, - GCancellable *cancellable, - GError **error); -void e_ews_connection_get_oal_detail (EEwsConnection *cnc, - const gchar *oal_id, - const gchar *oal_element, - const gchar *etag, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_oal_detail_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **elements, - gchar **etag, - GError **error); - -void e_ews_connection_get_free_busy (EEwsConnection *cnc, - gint pri, - EEwsRequestCreationCallback free_busy_cb, - gpointer free_busy_user_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_free_busy_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **free_busy, - GError **error); -gboolean e_ews_connection_get_free_busy_sync - (EEwsConnection *cnc, - gint pri, - EEwsRequestCreationCallback free_busy_cb, - gpointer create_user_data, - GSList **free_busy, - GCancellable *cancellable, - GError **error); -gboolean e_ews_connection_download_oal_file_sync - (EEwsConnection *cnc, - const gchar *cache_filename, - EwsProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GError **error); -void e_ews_connection_download_oal_file - (EEwsConnection *cnc, - const gchar *cache_filename, - EwsProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GAsyncReadyCallback cb, - gpointer user_data); -gboolean e_ews_connection_download_oal_file_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); - -void e_ews_connection_get_delegate (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - gboolean include_permissions, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_delegate_finish - (EEwsConnection *cnc, - GAsyncResult *result, - EwsDelegateDeliver *deliver_to, - GSList **delegates, /* EwsDelegateInfo * */ - GError **error); -gboolean e_ews_connection_get_delegate_sync - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - gboolean include_permissions, - EwsDelegateDeliver *deliver_to, - GSList **delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GError **error); -void e_ews_connection_add_delegate (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const GSList *delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_add_delegate_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_add_delegate_sync - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const GSList *delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GError **error); -void e_ews_connection_remove_delegate - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const GSList *delegate_ids, /* EwsUserId * */ - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_remove_delegate_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_remove_delegate_sync - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const GSList *delegate_ids, /* EwsUserId * */ - GCancellable *cancellable, - GError **error); -void e_ews_connection_update_delegate - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - EwsDelegateDeliver deliver_to, - const GSList *delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_update_delegate_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_update_delegate_sync - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - EwsDelegateDeliver deliver_to, - const GSList *delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GError **error); -void e_ews_connection_get_folder_permissions - (EEwsConnection *cnc, - gint pri, - EwsFolderId *folder_id, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_folder_permissions_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **permissions, - GError **error); -gboolean e_ews_connection_get_folder_permissions_sync - (EEwsConnection *cnc, - gint pri, - EwsFolderId *folder_id, - GSList **permissions, - GCancellable *cancellable, - GError **error); -void e_ews_connection_set_folder_permissions - (EEwsConnection *cnc, - gint pri, - EwsFolderId *folder_id, - EEwsFolderType folder_type, - const GSList *permissions, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_set_folder_permissions_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_set_folder_permissions_sync - (EEwsConnection *cnc, - gint pri, - EwsFolderId *folder_id, - EEwsFolderType folder_type, - const GSList *permissions, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_get_password_expiration - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - -gboolean e_ews_connection_get_password_expiration_finish - (EEwsConnection *cnc, - GAsyncResult *result, - gchar **exp_date, - GError **error); - -gboolean e_ews_connection_get_password_expiration_sync - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - gchar **exp_date, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_get_folder_info - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const EwsFolderId *folder_id, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_folder_info_finish - (EEwsConnection *cnc, - GAsyncResult *result, - EEwsFolder **folder, - GError **error); -gboolean e_ews_connection_get_folder_info_sync - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const EwsFolderId *folder_id, - EEwsFolder **folder, - GCancellable *cancellable, - GError **error); -void e_ews_connection_find_folder (EEwsConnection *cnc, - gint pri, - const EwsFolderId *fid, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_find_folder_finish - (EEwsConnection *cnc, - GAsyncResult *result, - gboolean *includes_last_item, - GSList **folders, - GError **error); -gboolean e_ews_connection_find_folder_sync - (EEwsConnection *cnc, - gint pri, - const EwsFolderId *fid, - gboolean *includes_last_item, - GSList **folders, - GCancellable *cancellable, - GError **error); -void e_ews_connection_query_auth_methods - (EEwsConnection *cnc, - gint pri, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_query_auth_methods_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **auth_methods, - GError **error); -gboolean e_ews_connection_query_auth_methods_sync - (EEwsConnection *cnc, - gint pri, - GSList **auth_methods, - GCancellable *cancellable, - GError **error); -void e_ews_connection_enable_notifications_sync - (EEwsConnection *cnc, - GSList *folders, - guint *subscription_key); -void e_ews_connection_disable_notifications_sync - (EEwsConnection *cnc, - guint subscription_key); -void e_ews_connection_get_server_time_zones - (EEwsConnection *cnc, - gint pri, - GSList *msdn_locations, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_server_time_zones_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **tzds, /* EEwsCalendarTimeZoneDefinition */ - GError **error); -gboolean e_ews_connection_get_server_time_zones_sync - (EEwsConnection *cnc, - gint pri, - GSList *msdn_locations, - GSList **tzds, /* EEwsCalendarTimeZoneDefinition */ - GCancellable *cancellable, - GError **error); -void e_ews_connection_get_user_photo (EEwsConnection *cnc, - gint pri, - const gchar *email, - EEwsSizeRequested size_requested, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_user_photo_finish - (EEwsConnection *cnc, - GAsyncResult *result, - gchar **out_picture_data, /* base64-encoded */ - GError **error); -gboolean e_ews_connection_get_user_photo_sync - (EEwsConnection *cnc, - gint pri, - const gchar *email, - EEwsSizeRequested size_requested, - gchar **out_picture_data, /* base64-encoded */ - GCancellable *cancellable, - GError **error); - -G_END_DECLS - -#endif diff --git a/src/server/e-ews-connection.h.sync-category-list b/src/server/e-ews-connection.h.sync-category-list deleted file mode 100644 index 3b56557..0000000 --- a/src/server/e-ews-connection.h.sync-category-list +++ /dev/null @@ -1,1383 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Authors : - * JP Rosevear - * Rodrigo Moya - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - * 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. - * - * 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -#ifndef E_EWS_CONNECTION_H -#define E_EWS_CONNECTION_H - -#include -#include -#include -#include -#include - -#include "e-soap-message.h" -#include "ews-errors.h" -#include "e-ews-folder.h" -#include "e-ews-item.h" -#include "camel-ews-settings.h" - -/* Standard GObject macros */ -#define E_TYPE_EWS_CONNECTION \ - (e_ews_connection_get_type ()) -#define E_EWS_CONNECTION(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), E_TYPE_EWS_CONNECTION, EEwsConnection)) -#define E_EWS_CONNECTION_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), E_TYPE_EWS_CONNECTION, EEwsConnectionClass)) -#define E_IS_EWS_CONNECTION(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), E_TYPE_EWS_CONNECTION)) -#define E_IS_EWS_CONNECTION_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), E_TYPE_EWS_CONNECTION)) -#define E_EWS_CONNECTION_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), E_TYPE_EWS_CONNECTION, EEwsConnectionClass)) - -G_BEGIN_DECLS - -typedef struct _EEwsConnection EEwsConnection; -typedef struct _EEwsConnectionClass EEwsConnectionClass; -typedef struct _EEwsConnectionPrivate EEwsConnectionPrivate; - -struct _EEwsConnection { - GObject parent; - EEwsConnectionPrivate *priv; -}; - -struct _EEwsConnectionClass { - GObjectClass parent_class; - - void (* password_will_expire) (EEwsConnection *connection, - gint in_days, - const gchar *service_url); -}; - -enum { - EWS_PRIORITY_LOW, - EWS_PRIORITY_MEDIUM, - EWS_PRIORITY_HIGH -}; - -typedef void (*EEwsRequestCreationCallback) (ESoapMessage *msg, - gpointer user_data); -typedef void (*EwsProgressFn) (gpointer object, - gint percent); -typedef void (*EEwsResponseCallback) (ESoapResponse *response, - GSimpleAsyncResult *simple); - -typedef enum { - EWS_SEARCH_AD, - EWS_SEARCH_AD_CONTACTS, - EWS_SEARCH_CONTACTS, - EWS_SEARCH_CONTACTS_AD -} EwsContactsSearchScope; - -typedef enum { - EWS_HARD_DELETE = 1, - EWS_SOFT_DELETE, - EWS_MOVE_TO_DELETED_ITEMS -} EwsDeleteType; - -typedef enum { - EWS_SEND_TO_NONE = 1, - EWS_SEND_ONLY_TO_ALL, - EWS_SEND_TO_ALL_AND_SAVE_COPY -} EwsSendMeetingCancellationsType; - -typedef enum { - EWS_NONE_OCCURRENCES = 0, - EWS_ALL_OCCURRENCES, - EWS_SPECIFIED_OCCURRENCE_ONLY -} EwsAffectedTaskOccurrencesType; - -typedef enum { - E_EWS_BODY_TYPE_ANY, - E_EWS_BODY_TYPE_BEST, - E_EWS_BODY_TYPE_HTML, - E_EWS_BODY_TYPE_TEXT -} EEwsBodyType; - -typedef enum { - E_EWS_SIZE_REQUESTED_UNKNOWN = 0, - E_EWS_SIZE_REQUESTED_48X48 = 48, - E_EWS_SIZE_REQUESTED_64X64 = 64, - E_EWS_SIZE_REQUESTED_96X96 = 96, - E_EWS_SIZE_REQUESTED_120X120 = 120, - E_EWS_SIZE_REQUESTED_240X240 = 240, - E_EWS_SIZE_REQUESTED_360X360 = 360, - E_EWS_SIZE_REQUESTED_432X432 = 432, - E_EWS_SIZE_REQUESTED_504X504 = 504, - E_EWS_SIZE_REQUESTED_648X648 = 648 -} EEwsSizeRequested; - -typedef struct { - gchar *id; - gchar *dn; - gchar *name; -} EwsOAL; - -typedef struct { - gchar *type; - guint32 seq; - guint32 ver; - guint32 size; - guint32 uncompressed_size; - gchar *sha; - gchar *filename; -} EwsOALDetails; - -typedef struct { - gchar *sid; - gchar *primary_smtp; - gchar *display_name; - gchar *distinguished_user; - gchar *external_user; -} EwsUserId; - -typedef enum { - EwsPermissionLevel_Unknown = 0, - EwsPermissionLevel_None, - EwsPermissionLevel_Reviewer, - EwsPermissionLevel_Author, - EwsPermissionLevel_Editor, - EwsPermissionLevel_Custom -} EwsPermissionLevel; - -typedef struct { - EwsUserId *user_id; - EwsPermissionLevel calendar, tasks, inbox, contacts, notes, journal; - gboolean meetingcopies; - gboolean view_priv_items; -} EwsDelegateInfo; - -typedef enum { - EwsDelegateDeliver_DelegatesOnly, - EwsDelegateDeliver_DelegatesAndMe, - EwsDelegateDeliver_DelegatesAndSendInformationToMe -} EwsDelegateDeliver; - -typedef enum { - NORMAL_FIELD_URI, - INDEXED_FIELD_URI, - EXTENDED_FIELD_URI -} EwsFieldURIType; - -typedef struct { - gchar *distinguished_prop_set_id; - gchar *prop_set_id; - gchar *prop_tag; - gchar *prop_name; - gchar *prop_id; - gchar *prop_type; -} EEwsExtendedFieldURI; - -typedef struct { - gchar *field_uri; - gchar *field_index; -} EEwsIndexedFieldURI; - -typedef struct { - gchar *field_uri; - GSList *extended_furis; - GSList *indexed_furis; -} EEwsAdditionalProps; - -typedef struct { - gchar *order; - gint uri_type; - gpointer field_uri; -} EwsSortOrder; - -typedef struct { - gchar *id; - gsize len; -} EwsPhotoAttachmentInfo; - -typedef enum { - E_EWS_NOTIFICATION_EVENT_COPIED = 0, - E_EWS_NOTIFICATION_EVENT_CREATED, - E_EWS_NOTIFICATION_EVENT_DELETED, - E_EWS_NOTIFICATION_EVENT_MODIFIED, - E_EWS_NOTIFICATION_EVENT_MOVED, - E_EWS_NOTIFICATION_EVENT_STATUS -} EEwsNotificationEventType; - -typedef struct { - EEwsNotificationEventType type; - gboolean is_item; - gchar *folder_id; - gchar *old_folder_id; -} EEwsNotificationEvent; - -/* - * - */ -typedef struct { - gchar *kind; - gchar *value; -} EEwsCalendarTo; - -/* - * - * - * - * - */ -typedef struct { - EEwsCalendarTo *to; - gchar *date_time; -} EEwsCalendarAbsoluteDateTransition; - -/* - * - * - * - * - * - * - * - */ -typedef struct { - EEwsCalendarTo *to; - gchar *time_offset; - gchar *month; - gchar *day_of_week; - gchar *occurrence; -} EEwsCalendarRecurringDayTransition; - -/* - * - * - * - * - * - * - */ -typedef struct { - EEwsCalendarTo *to; - gchar *time_offset; - gchar *month; - gchar *day; -} EEwsCalendarRecurringDateTransition; - -/* - * - */ -typedef struct { - gchar *bias; - gchar *name; - gchar *id; -} EEwsCalendarPeriod; - -/* - * - * - * - * - * - * - * - * - */ -typedef struct { - gchar *id; - EEwsCalendarTo *transition; - GSList *absolute_date_transitions; /* EEwsCalendarAbsoluteDateTransition */ - GSList *recurring_day_transitions; /* EEwsCalendarRecurringDayTransition */ - GSList *recurring_date_transitions; /* EEwsCalendarRecurringDateTransition */ -} EEwsCalendarTransitionsGroup; - -/* - * - * - * - * - * - * - * - * - */ -typedef struct { - EEwsCalendarTo *transition; - GSList *absolute_date_transitions; /* EEwsCalendarAbsoluteDateTransition */ - GSList *recurring_day_transitions; /* EEwsCalendarRecurringDayTransition */ - GSList *recurring_date_transitions; /* EEwsCalendarRecurringDateTransition */ -} EEwsCalendarTransitions; - -/* - * - * - * - * - * - * - * - * - * - */ -typedef struct { - gchar *name; - gchar *id; - GSList *periods; /* EEwsCalendarPeriod */ - GSList *transitions_groups; /* EEwsCalendarTrasitionsGroup */ - EEwsCalendarTransitions *transitions; -} EEwsCalendarTimeZoneDefinition; - -EEwsCalendarTo * - e_ews_calendar_to_new (void); -void e_ews_calendar_to_free (EEwsCalendarTo *to); - -EEwsCalendarAbsoluteDateTransition * - e_ews_calendar_absolute_date_transition_new - (void); -void e_ews_calendar_absolute_date_transition_free - (EEwsCalendarAbsoluteDateTransition *adt); - -EEwsCalendarRecurringDayTransition * - e_ews_calendar_recurring_day_transition_new - (void); -void e_ews_calendar_recurring_day_transition_free - (EEwsCalendarRecurringDayTransition *rdayt); - -EEwsCalendarRecurringDateTransition * - e_ews_calendar_recurring_date_transition_new - (void); -void e_ews_calendar_recurring_date_transition_free - (EEwsCalendarRecurringDateTransition *rdatet); - -EEwsCalendarPeriod * - e_ews_calendar_period_new (void); -void e_ews_calendar_period_free (EEwsCalendarPeriod *period); - -EEwsCalendarTransitionsGroup * - e_ews_calendar_transitions_group_new - (void); -void e_ews_calendar_transitions_group_free - (EEwsCalendarTransitionsGroup *tg); - -EEwsCalendarTransitions * - e_ews_calendar_transitions_new (void); -void e_ews_calendar_transitions_free (EEwsCalendarTransitions *transitions); - -EEwsCalendarTimeZoneDefinition * - e_ews_calendar_time_zone_definition_new - (void); -void e_ews_calendar_time_zone_definition_free - (EEwsCalendarTimeZoneDefinition *tzd); - -EEwsExtendedFieldURI * - e_ews_extended_field_uri_new (void); -void e_ews_extended_field_uri_free (EEwsExtendedFieldURI *ex_field_uri); - -EEwsIndexedFieldURI * - e_ews_indexed_field_uri_new (const gchar *uri, - const gchar *index); -void e_ews_indexed_field_uri_free (EEwsIndexedFieldURI *id_field_uri); - -EEwsAdditionalProps * - e_ews_additional_props_new (void); -void e_ews_additional_props_free (EEwsAdditionalProps *add_props); - -EEwsNotificationEvent * - e_ews_notification_event_new (void); -void e_ews_notification_event_free (EEwsNotificationEvent *event); - -void ews_oal_free (EwsOAL *oal); -void ews_oal_details_free (EwsOALDetails *details); - -GType e_ews_connection_get_type (void); -EEwsConnection *e_ews_connection_new (ESource *source, - const gchar *uri, - CamelEwsSettings *settings); -EEwsConnection *e_ews_connection_new_full (ESource *source, - const gchar *uri, - CamelEwsSettings *settings, - gboolean allow_connection_reuse); -EEwsConnection *e_ews_connection_new_for_backend(EBackend *backend, - ESourceRegistry *registry, - const gchar *uri, - CamelEwsSettings *settings); -void e_ews_connection_update_credentials - (EEwsConnection *cnc, - const ENamedParameters *credentials); -ESourceAuthenticationResult - e_ews_connection_try_credentials_sync - (EEwsConnection *cnc, - const ENamedParameters *credentials, - ESource *use_source, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, - GCancellable *cancellable, - GError **error); -ESource * e_ews_connection_get_source (EEwsConnection *cnc); -gboolean e_ews_connection_get_ssl_error_details - (EEwsConnection *cnc, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors); -const gchar * e_ews_connection_get_uri (EEwsConnection *cnc); -ESoupAuthBearer * - e_ews_connection_ref_bearer_auth(EEwsConnection *cnc); -void e_ews_connection_set_bearer_auth(EEwsConnection *cnc, - ESoupAuthBearer *bearer_auth); -const gchar * e_ews_connection_get_password (EEwsConnection *cnc); -gchar * e_ews_connection_dup_password (EEwsConnection *cnc); -void e_ews_connection_set_password (EEwsConnection *cnc, - const gchar *password); -const gchar * e_ews_connection_get_impersonate_user - (EEwsConnection *cnc); -GProxyResolver * - e_ews_connection_ref_proxy_resolver - (EEwsConnection *cnc); -void e_ews_connection_set_proxy_resolver - (EEwsConnection *cnc, - GProxyResolver *proxy_resolver); -CamelEwsSettings * - e_ews_connection_ref_settings (EEwsConnection *cnc); -SoupSession * e_ews_connection_ref_soup_session - (EEwsConnection *cnc); -gboolean e_ews_connection_get_disconnected_flag - (EEwsConnection *cnc); -void e_ews_connection_set_disconnected_flag - (EEwsConnection *cnc, - gboolean disconnected_flag); -EEwsConnection *e_ews_connection_find (const gchar *uri, - const gchar *username); -GSList * e_ews_connection_list_existing (void); /* EEwsConnection * */ -void e_ews_connection_queue_request (EEwsConnection *cnc, - ESoapMessage *msg, - EEwsResponseCallback cb, - gint pri, - GCancellable *cancellable, - GSimpleAsyncResult *simple); - -gboolean e_ews_autodiscover_ws_url_sync (ESource *source, - CamelEwsSettings *settings, - const gchar *email_address, - const gchar *password, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, - GCancellable *cancellable, - GError **error); -void e_ews_autodiscover_ws_url (ESource *source, - CamelEwsSettings *settings, - const gchar *email_address, - const gchar *password, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_autodiscover_ws_url_finish - (CamelEwsSettings *settings, - GAsyncResult *result, - gchar **out_certificate_pem, - GTlsCertificateFlags *out_certificate_errors, - GError **error); -const gchar * e_ews_connection_get_mailbox (EEwsConnection *cnc); -void e_ews_connection_set_mailbox (EEwsConnection *cnc, - const gchar *email); - -void ews_user_id_free (EwsUserId *id); -void ews_delegate_info_free (EwsDelegateInfo *info); - -void e_ews_connection_sync_folder_items - (EEwsConnection *cnc, - gint pri, - const gchar *old_sync_state, - const gchar *fid, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - guint max_entries, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_sync_folder_items_finish - (EEwsConnection *cnc, - GAsyncResult *result, - gchar **new_sync_state, - gboolean *includes_last_item, - GSList **items_created, - GSList **items_updated, - GSList **items_deleted, - GError **error); -gboolean e_ews_connection_sync_folder_items_sync - (EEwsConnection *cnc, - gint pri, - const gchar *old_sync_state, - const gchar *fid, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - guint max_entries, - gchar **new_sync_state, - gboolean *includes_last_item, - GSList **items_created, - GSList **items_updated, - GSList **items_deleted, - GCancellable *cancellable, - GError **error); - -typedef void (*EwsConvertQueryCallback) (ESoapMessage *msg, - const gchar *query, - EEwsFolderType type); - -void e_ews_connection_find_folder_items - (EEwsConnection *cnc, - gint pri, - EwsFolderId *fid, - const gchar *props, - const EEwsAdditionalProps *add_props, - EwsSortOrder *sort_order, - const gchar *query, - GPtrArray *only_ids, /* element-type utf8 */ - EEwsFolderType type, - EwsConvertQueryCallback convert_query_cb, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_find_folder_items_finish - (EEwsConnection *cnc, - GAsyncResult *result, - gboolean *includes_last_item, - GSList **items, - GError **error); -gboolean e_ews_connection_find_folder_items_sync - (EEwsConnection *cnc, - gint pri, - EwsFolderId *fid, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - EwsSortOrder *sort_order, - const gchar *query, - GPtrArray *only_ids, /* element-type utf8 */ - EEwsFolderType type, - gboolean *includes_last_item, - GSList **items, - EwsConvertQueryCallback convert_query_cb, - GCancellable *cancellable, - GError **error); - -EEwsServerVersion - e_ews_connection_get_server_version - (EEwsConnection *cnc); -void e_ews_connection_set_server_version - (EEwsConnection *cnc, - EEwsServerVersion version); -void e_ews_connection_set_server_version_from_string - (EEwsConnection *cnc, - const gchar *version); -gboolean e_ews_connection_satisfies_server_version - (EEwsConnection *cnc, - EEwsServerVersion versio); - -void e_ews_connection_get_items (EEwsConnection *cnc, - gint pri, - const GSList *ids, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - gboolean include_mime, - const gchar *mime_directory, - EEwsBodyType body_type, - ESoapProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_items_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **items, - GError **error); -gboolean e_ews_connection_get_items_sync (EEwsConnection *cnc, - gint pri, - const GSList *ids, - const gchar *default_props, - const EEwsAdditionalProps *add_props, - gboolean include_mime, - const gchar *mime_directory, - EEwsBodyType body_type, - GSList **items, - ESoapProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_delete_items (EEwsConnection *cnc, - gint pri, - const GSList *ids, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_delete_items_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_delete_items_sync - (EEwsConnection *cnc, - gint pri, - const GSList *ids, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GError **error); -gboolean e_ews_connection_delete_items_in_chunks_sync - (EEwsConnection *cnc, - gint pri, - const GSList *ids, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GError **error); -void e_ews_connection_delete_item (EEwsConnection *cnc, - gint pri, - EwsId *id, - guint index, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_delete_item_sync - (EEwsConnection *cnc, - gint pri, - EwsId *id, - guint index, - EwsDeleteType delete_type, - EwsSendMeetingCancellationsType send_cancels, - EwsAffectedTaskOccurrencesType affected_tasks, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_update_items (EEwsConnection *cnc, - gint pri, - const gchar *conflict_res, - const gchar *msg_disposition, - const gchar *send_invites, - const gchar *folder_id, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_update_items_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **ids, - GError **error); -gboolean e_ews_connection_update_items_sync - (EEwsConnection *cnc, - gint pri, - const gchar *conflict_res, - const gchar *msg_disposition, - const gchar *send_invites, - const gchar *folder_id, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GSList **ids, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_create_items (EEwsConnection *cnc, - gint pri, - const gchar *msg_disposition, - const gchar *send_invites, - const EwsFolderId *fid, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_create_items_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **ids, - GError **error); -gboolean e_ews_connection_create_items_sync - (EEwsConnection *cnc, - gint pri, - const gchar *msg_disposition, - const gchar *send_invites, - const EwsFolderId *fid, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GSList **ids, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_sync_folder_hierarchy - (EEwsConnection *cnc, - gint pri, - const gchar *sync_state, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_sync_folder_hierarchy_finish - (EEwsConnection *cnc, - GAsyncResult *result, - gchar **sync_state, - gboolean *includes_last_folder, - GSList **folders_created, - GSList **folders_updated, - GSList **folders_deleted, - GError **error); -gboolean e_ews_connection_sync_folder_hierarchy_sync - (EEwsConnection *cnc, - gint pri, - const gchar *old_sync_state, - gchar **new_sync_state, - gboolean *includes_last_folder, - GSList **folders_created, - GSList **folders_updated, - GSList **folders_deleted, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_resolve_names (EEwsConnection *cnc, - gint pri, - const gchar *resolve_name, - EwsContactsSearchScope scope, - GSList *parent_folder_ids, - gboolean fetch_contact_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_resolve_names_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **mailboxes, - GSList **contact_items, - gboolean *includes_last_item, - GError **error); -gboolean e_ews_connection_resolve_names_sync - (EEwsConnection *cnc, - gint pri, - const gchar *resolve_name, - EwsContactsSearchScope scope, - GSList *parent_folder_ids, - gboolean fetch_contact_data, - GSList **mailboxes, - GSList **contact_items, - gboolean *includes_last_item, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_expand_dl (EEwsConnection *cnc, - gint pri, - const EwsMailbox *mb, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_expand_dl_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **mailboxes, - gboolean *includes_last_item, - GError **error); -gboolean e_ews_connection_expand_dl_sync (EEwsConnection *cnc, - gint pri, - const EwsMailbox *mb, - GSList **mailboxes, - gboolean *includes_last_item, - GCancellable *cancellable, - GError **error); - -gboolean e_ews_connection_ex_to_smtp_sync - (EEwsConnection *cnc, - gint pri, - const gchar *name, - const gchar *ex_address, - gchar **smtp_address, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_create_folder (EEwsConnection *cnc, - gint pri, - const gchar *parent_folder_id, - gboolean is_distinguished_id, - const gchar *folder_name, - EEwsFolderType folder_type, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_create_folder_finish - (EEwsConnection *cnc, - GAsyncResult *result, - EwsFolderId **folder_id, - GError **error); -gboolean e_ews_connection_create_folder_sync - (EEwsConnection *cnc, - gint pri, - const gchar *parent_folder_id, - gboolean is_distinguished_id, - const gchar *folder_name, - EEwsFolderType folder_type, - EwsFolderId **folder_id, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_delete_folder (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean is_distinguished_id, - const gchar *delete_type, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_delete_folder_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_delete_folder_sync - (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean is_distinguished_id, - const gchar *delete_type, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_empty_folder (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean is_distinguished_id, - const gchar *delete_type, - gboolean delete_subfolders, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_empty_folder_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_empty_folder_sync - (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean is_distinguished_id, - const gchar *delete_type, - gboolean delete_subfolders, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_update_folder (EEwsConnection *cnc, - gint pri, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_update_folder_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_update_folder_sync - (EEwsConnection *cnc, - gint pri, - EEwsRequestCreationCallback create_cb, - gpointer create_user_data, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_move_folder (EEwsConnection *cnc, - gint pri, - const gchar *to_folder, - const gchar *folder, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_move_folder_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_move_folder_sync - (EEwsConnection *cnc, - gint pri, - const gchar *to_folder, - const gchar *folder, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_get_folder (EEwsConnection *cnc, - gint pri, - const gchar *folder_shape, - const EEwsAdditionalProps *add_props, - GSList *folder_ids, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_folder_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **folders, - GError **error); -gboolean e_ews_connection_get_folder_sync - (EEwsConnection *cnc, - gint pri, - const gchar *folder_shape, - const EEwsAdditionalProps *add_props, - GSList *folder_ids, - GSList **folders, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_move_items (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean docopy, - const GSList *ids, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_move_items_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **items, - GError **error); -gboolean e_ews_connection_move_items_sync - (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean docopy, - const GSList *ids, - GSList **items_ret, - GCancellable *cancellable, - GError **error); -gboolean e_ews_connection_move_items_in_chunks_sync - (EEwsConnection *cnc, - gint pri, - const gchar *folder_id, - gboolean docopy, - const GSList *ids, - GSList **items, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_create_attachments - (EEwsConnection *cnc, - gint pri, - const EwsId *parent, - const GSList *files, - gboolean is_contact_photo, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_create_attachments_finish - (EEwsConnection *cnc, - gchar **change_key, - GSList **attachments_ids, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_create_attachments_sync - (EEwsConnection *cnc, - gint pri, - const EwsId *parent, - const GSList *files, - gboolean is_contact_photo, - gchar **change_key, - GSList **attachments_ids, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_delete_attachments - (EEwsConnection *cnc, - gint pri, - const GSList *attachments_ids, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_delete_attachments_finish - (EEwsConnection *cnc, - GAsyncResult *result, - gchar **new_change_key, - GError **error); -gboolean e_ews_connection_delete_attachments_sync - (EEwsConnection *cnc, - gint pri, - const GSList *attachments_ids, - gchar **new_change_key, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_get_attachments - (EEwsConnection *cnc, - gint pri, - const gchar *comp_uid, - const GSList *ids, - const gchar *cache, - gboolean include_mime, - ESoapProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_attachments_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **items, - GError **error); -gboolean e_ews_connection_get_attachments_sync - (EEwsConnection *cnc, - gint pri, - const gchar *comp_uid, - const GSList *ids, - const gchar *cache, - gboolean include_mime, - GSList **items, - ESoapProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GError **error); - -gboolean e_ews_connection_get_oal_list_sync - (EEwsConnection *cnc, - GSList **oals, - GCancellable *cancellable, - GError **error); -void e_ews_connection_get_oal_list (EEwsConnection *cnc, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_oal_list_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **oals, - GError **error); -gboolean e_ews_connection_get_oal_detail_sync - (EEwsConnection *cnc, - const gchar *oal_id, - const gchar *oal_element, - const gchar *old_etag, - GSList **elements, - gchar **etag, - GCancellable *cancellable, - GError **error); -void e_ews_connection_get_oal_detail (EEwsConnection *cnc, - const gchar *oal_id, - const gchar *oal_element, - const gchar *etag, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_oal_detail_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **elements, - gchar **etag, - GError **error); - -void e_ews_connection_get_free_busy (EEwsConnection *cnc, - gint pri, - EEwsRequestCreationCallback free_busy_cb, - gpointer free_busy_user_data, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_free_busy_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **free_busy, - GError **error); -gboolean e_ews_connection_get_free_busy_sync - (EEwsConnection *cnc, - gint pri, - EEwsRequestCreationCallback free_busy_cb, - gpointer create_user_data, - GSList **free_busy, - GCancellable *cancellable, - GError **error); -gboolean e_ews_connection_download_oal_file_sync - (EEwsConnection *cnc, - const gchar *cache_filename, - EwsProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GError **error); -void e_ews_connection_download_oal_file - (EEwsConnection *cnc, - const gchar *cache_filename, - EwsProgressFn progress_fn, - gpointer progress_data, - GCancellable *cancellable, - GAsyncReadyCallback cb, - gpointer user_data); -gboolean e_ews_connection_download_oal_file_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); - -void e_ews_connection_get_delegate (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - gboolean include_permissions, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_delegate_finish - (EEwsConnection *cnc, - GAsyncResult *result, - EwsDelegateDeliver *deliver_to, - GSList **delegates, /* EwsDelegateInfo * */ - GError **error); -gboolean e_ews_connection_get_delegate_sync - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - gboolean include_permissions, - EwsDelegateDeliver *deliver_to, - GSList **delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GError **error); -void e_ews_connection_add_delegate (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const GSList *delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_add_delegate_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_add_delegate_sync - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const GSList *delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GError **error); -void e_ews_connection_remove_delegate - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const GSList *delegate_ids, /* EwsUserId * */ - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_remove_delegate_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_remove_delegate_sync - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const GSList *delegate_ids, /* EwsUserId * */ - GCancellable *cancellable, - GError **error); -void e_ews_connection_update_delegate - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - EwsDelegateDeliver deliver_to, - const GSList *delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_update_delegate_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_update_delegate_sync - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - EwsDelegateDeliver deliver_to, - const GSList *delegates, /* EwsDelegateInfo * */ - GCancellable *cancellable, - GError **error); -void e_ews_connection_get_folder_permissions - (EEwsConnection *cnc, - gint pri, - EwsFolderId *folder_id, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_folder_permissions_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **permissions, - GError **error); -gboolean e_ews_connection_get_folder_permissions_sync - (EEwsConnection *cnc, - gint pri, - EwsFolderId *folder_id, - GSList **permissions, - GCancellable *cancellable, - GError **error); -void e_ews_connection_set_folder_permissions - (EEwsConnection *cnc, - gint pri, - EwsFolderId *folder_id, - EEwsFolderType folder_type, - const GSList *permissions, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_set_folder_permissions_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GError **error); -gboolean e_ews_connection_set_folder_permissions_sync - (EEwsConnection *cnc, - gint pri, - EwsFolderId *folder_id, - EEwsFolderType folder_type, - const GSList *permissions, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_get_password_expiration - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); - -gboolean e_ews_connection_get_password_expiration_finish - (EEwsConnection *cnc, - GAsyncResult *result, - gchar **exp_date, - GError **error); - -gboolean e_ews_connection_get_password_expiration_sync - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - gchar **exp_date, - GCancellable *cancellable, - GError **error); - -void e_ews_connection_get_folder_info - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const EwsFolderId *folder_id, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_folder_info_finish - (EEwsConnection *cnc, - GAsyncResult *result, - EEwsFolder **folder, - GError **error); -gboolean e_ews_connection_get_folder_info_sync - (EEwsConnection *cnc, - gint pri, - const gchar *mail_id, - const EwsFolderId *folder_id, - EEwsFolder **folder, - GCancellable *cancellable, - GError **error); -void e_ews_connection_find_folder (EEwsConnection *cnc, - gint pri, - const EwsFolderId *fid, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_find_folder_finish - (EEwsConnection *cnc, - GAsyncResult *result, - gboolean *includes_last_item, - GSList **folders, - GError **error); -gboolean e_ews_connection_find_folder_sync - (EEwsConnection *cnc, - gint pri, - const EwsFolderId *fid, - gboolean *includes_last_item, - GSList **folders, - GCancellable *cancellable, - GError **error); -void e_ews_connection_query_auth_methods - (EEwsConnection *cnc, - gint pri, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_query_auth_methods_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **auth_methods, - GError **error); -gboolean e_ews_connection_query_auth_methods_sync - (EEwsConnection *cnc, - gint pri, - GSList **auth_methods, - GCancellable *cancellable, - GError **error); -void e_ews_connection_enable_notifications_sync - (EEwsConnection *cnc, - GSList *folders, - guint *subscription_key); -void e_ews_connection_disable_notifications_sync - (EEwsConnection *cnc, - guint subscription_key); -void e_ews_connection_get_server_time_zones - (EEwsConnection *cnc, - gint pri, - GSList *msdn_locations, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_server_time_zones_finish - (EEwsConnection *cnc, - GAsyncResult *result, - GSList **tzds, /* EEwsCalendarTimeZoneDefinition */ - GError **error); -gboolean e_ews_connection_get_server_time_zones_sync - (EEwsConnection *cnc, - gint pri, - GSList *msdn_locations, - GSList **tzds, /* EEwsCalendarTimeZoneDefinition */ - GCancellable *cancellable, - GError **error); -void e_ews_connection_get_user_photo (EEwsConnection *cnc, - gint pri, - const gchar *email, - EEwsSizeRequested size_requested, - GCancellable *cancellable, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean e_ews_connection_get_user_photo_finish - (EEwsConnection *cnc, - GAsyncResult *result, - gchar **out_picture_data, /* base64-encoded */ - GError **error); -gboolean e_ews_connection_get_user_photo_sync - (EEwsConnection *cnc, - gint pri, - const gchar *email, - EEwsSizeRequested size_requested, - gchar **out_picture_data, /* base64-encoded */ - GCancellable *cancellable, - GError **error); - -G_END_DECLS - -#endif diff --git a/src/server/e-ews-item.c b/src/server/e-ews-item.c index 9bcfddf..c28cca5 100644 --- a/src/server/e-ews-item.c +++ b/src/server/e-ews-item.c @@ -686,7 +686,7 @@ ews_get_physical_address (ESoapParameter *param) if (subparam) address->state = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "CountryOrRegion"); + subparam = e_soap_parameter_get_first_child_by_name (param, "Country"); if (subparam) address->country = e_soap_parameter_get_string_value (subparam); @@ -2695,28 +2695,3 @@ e_ews_item_util_strip_ex_address (const gchar *ex_address) return ex_address; } - -EwsId * -e_ews_id_copy (const EwsId *ews_id) -{ - EwsId *copy; - - if (!ews_id) - return NULL; - - copy = g_new0 (EwsId, 1); - copy->id = g_strdup (ews_id->id); - copy->change_key = g_strdup (ews_id->change_key); - - return copy; -} - -void -e_ews_id_free (EwsId *ews_id) -{ - if (ews_id) { - g_free (ews_id->id); - g_free (ews_id->change_key); - g_free (ews_id); - } -} diff --git a/src/server/e-ews-item.c.contact-country-forgotten b/src/server/e-ews-item.c.contact-country-forgotten deleted file mode 100644 index c28cca5..0000000 --- a/src/server/e-ews-item.c.contact-country-forgotten +++ /dev/null @@ -1,2697 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Authors : - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - * 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. - * - * 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "e-ews-item.h" -#include "e-ews-item-change.h" - -G_DEFINE_TYPE (EEwsItem, e_ews_item, G_TYPE_OBJECT) - -struct _EEwsContactFields { - gchar *fileas; - gchar *display_name; - EwsCompleteName *complete_name; - - GHashTable *email_addresses; - GHashTable *physical_addresses; - GHashTable *phone_numbers; - GHashTable *im_addresses; - - gchar *company_name; - gchar *department; - gchar *job_title; - gchar *assistant_name; - gchar *manager; - gchar *office_location; - - gchar *business_homepage; - - time_t birthday; - time_t wedding_anniversary; - - gchar *profession; - gchar *spouse_name; - gchar *culture; - gchar *surname; - gchar *givenname; - gchar *middlename; - gchar *notes; -}; - -struct _EEwsTaskFields { - gchar *percent_complete; - gchar *status; - gchar *body; - gchar *sensitivity; - gchar *owner; - gchar *delegator; - time_t due_date; - time_t start_date; - time_t complete_date; - gboolean has_due_date; - gboolean has_start_date; - gboolean has_complete_date; -}; - -struct _EEwsItemPrivate { - EwsId *attachment_id; - EEwsItemType item_type; - GError *error; - - /* MAPI properties */ - /* The Exchange server is so fundamentally misdesigned that it doesn't expose - * certain information in a coherent way; the \Answered, and \Deleted message - * flags don't even seem to work properly. It looks like the only way to work - * it out is from the PidTagIconIndex field. Quite what the hell an *icon* - * selector is doing in the database, I have absolutely no fucking idea; a - * database is supposed to represent the *data*, not do bizarre things that - * live in the client. But that's typical Exchange brain damage for you... */ - guint32 mapi_icon_index; /* http://msdn.microsoft.com/en-us/library/cc815472.aspx */ - guint32 mapi_last_verb_executed; /* http://msdn.microsoft.com/en-us/library/cc841968.aspx */ - guint32 mapi_message_status; /* http://msdn.microsoft.com/en-us/library/cc839915.aspx */ - guint32 mapi_message_flags; /* http://msdn.microsoft.com/en-us/library/cc839733.aspx */ - - GHashTable *mapi_extended_tags; /* simple tag->string_value */ - GHashTable *mapi_extended_sets; /* setid-> [ tag->string_value ] */ - - /* properties */ - EwsId *item_id; - gchar *subject; - gchar *mime_content; - gchar *body; - - gchar *date_header; - time_t date_received; - time_t date_sent; - time_t date_created; - time_t last_modified_time; - - gsize size; - gchar *msg_id; - gchar *in_replyto; - gchar *references; - gboolean has_attachments; - gboolean is_read; - EwsImportance importance; - - gchar *uid; - gchar *timezone; - gchar *start_timezone; - gchar *end_timezone; - gchar *contact_photo_id; - gchar *iana_start_time_zone; - gchar *iana_end_time_zone; - - GSList *to_recipients; - GSList *cc_recipients; - GSList *bcc_recipients; - - EwsMailbox *from; - EwsMailbox *sender; - - gboolean is_meeting; - gboolean is_response_requested; - GSList *modified_occurrences; - GSList *attachments_ids; - gchar *my_response_type; - GSList *attendees; - - EwsId *calendar_item_accept_id; - - /* the evolution labels are implemented as exchange - * Categories. These appear in the message headers as - * Keywords: and are set and extracted from the EWS server as - * which is a string array valued XML element */ - GSList *categories; - - struct _EEwsContactFields *contact_fields; - struct _EEwsTaskFields *task_fields; -}; - -static void ews_item_free_attendee (EwsAttendee *attendee); -static void ews_free_contact_fields (struct _EEwsContactFields *con_fields); - -typedef gpointer (* EwsGetValFunc) (ESoapParameter *param); - -static void -e_ews_item_dispose (GObject *object) -{ - EEwsItem *item = (EEwsItem *) object; - EEwsItemPrivate *priv; - - g_return_if_fail (E_IS_EWS_ITEM (item)); - - priv = item->priv; - - g_clear_error (&priv->error); - - if (priv->mapi_extended_sets) { - g_hash_table_destroy (priv->mapi_extended_sets); - priv->mapi_extended_sets = NULL; - } - - if (priv->mapi_extended_tags) { - g_hash_table_destroy (priv->mapi_extended_tags); - priv->mapi_extended_tags = NULL; - } - - if (priv->item_id) { - g_free (priv->item_id->id); - g_free (priv->item_id->change_key); - g_free (priv->item_id); - priv->item_id = NULL; - } - - if (priv->attachment_id) { - g_free (priv->attachment_id->id); - g_free (priv->attachment_id->change_key); - g_free (priv->attachment_id); - priv->attachment_id = NULL; - } - - g_clear_pointer (&priv->mime_content, g_free); - g_clear_pointer (&priv->body, g_free); - g_clear_pointer (&priv->subject, g_free); - g_clear_pointer (&priv->msg_id, g_free); - g_clear_pointer (&priv->uid, g_free); - g_clear_pointer (&priv->in_replyto, g_free); - g_clear_pointer (&priv->references, g_free); - g_clear_pointer (&priv->date_header, g_free); - g_clear_pointer (&priv->timezone, g_free); - g_clear_pointer (&priv->start_timezone, g_free); - g_clear_pointer (&priv->end_timezone, g_free); - g_clear_pointer (&priv->contact_photo_id, g_free); - g_clear_pointer (&priv->iana_start_time_zone, g_free); - g_clear_pointer (&priv->iana_end_time_zone, g_free); - - g_slist_free_full (priv->to_recipients, (GDestroyNotify) e_ews_mailbox_free); - priv->to_recipients = NULL; - - g_slist_free_full (priv->cc_recipients, (GDestroyNotify) e_ews_mailbox_free); - priv->cc_recipients = NULL; - - g_slist_free_full (priv->bcc_recipients, (GDestroyNotify) e_ews_mailbox_free); - priv->bcc_recipients = NULL; - - g_slist_free_full (priv->modified_occurrences, g_free); - priv->modified_occurrences = NULL; - - g_slist_free_full (priv->attachments_ids, g_free); - priv->attachments_ids = NULL; - - g_clear_pointer (&priv->my_response_type, g_free); - - g_slist_free_full (priv->attendees, (GDestroyNotify) ews_item_free_attendee); - priv->attendees = NULL; - - if (priv->calendar_item_accept_id) { - g_free (priv->calendar_item_accept_id->id); - g_free (priv->calendar_item_accept_id->change_key); - g_free (priv->calendar_item_accept_id); - priv->calendar_item_accept_id = NULL; - } - - e_ews_mailbox_free (priv->sender); - e_ews_mailbox_free (priv->from); - - if (priv->item_type == E_EWS_ITEM_TYPE_CONTACT) - ews_free_contact_fields (priv->contact_fields); - - if (priv->task_fields) { - g_free (priv->task_fields->percent_complete); - priv->task_fields->percent_complete = NULL; - g_free (priv->task_fields->status); - priv->task_fields->status = NULL; - g_free (priv->task_fields->body); - priv->task_fields->body = NULL; - g_free (priv->task_fields->sensitivity); - priv->task_fields->sensitivity = NULL; - g_free (priv->task_fields->owner); - priv->task_fields->owner = NULL; - g_free (priv->task_fields); - } - - g_slist_free_full (priv->categories, g_free); - priv->categories = NULL; - - G_OBJECT_CLASS (e_ews_item_parent_class)->dispose (object); -} - -static void -e_ews_item_class_init (EEwsItemClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (EEwsItemPrivate)); - - object_class->dispose = e_ews_item_dispose; -} - -static void -e_ews_item_init (EEwsItem *item) -{ - item->priv = G_TYPE_INSTANCE_GET_PRIVATE (item, E_TYPE_EWS_ITEM, EEwsItemPrivate); - - item->priv->item_type = E_EWS_ITEM_TYPE_UNKNOWN; - item->priv->is_meeting = FALSE; - item->priv->is_response_requested = FALSE; - - item->priv->mapi_extended_tags = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); - item->priv->mapi_extended_sets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy); -} - -static void -ews_free_contact_fields (struct _EEwsContactFields *con_fields) -{ - if (con_fields) { - if (con_fields->complete_name) { - EwsCompleteName *cn = con_fields->complete_name; - - g_free (cn->title); - g_free (cn->first_name); - g_free (cn->middle_name); - g_free (cn->last_name); - g_free (cn->suffix); - g_free (cn->initials); - g_free (cn->full_name); - g_free (cn->nick_name); - g_free (cn->yomi_first_name); - g_free (cn->yomi_last_name); - } - - if (con_fields->email_addresses) - g_hash_table_destroy (con_fields->email_addresses); - - if (con_fields->physical_addresses) - g_hash_table_destroy (con_fields->physical_addresses); - - if (con_fields->phone_numbers) - g_hash_table_destroy (con_fields->phone_numbers); - - if (con_fields->im_addresses) - g_hash_table_destroy (con_fields->im_addresses); - - g_free (con_fields->display_name); - g_free (con_fields->fileas); - g_free (con_fields->company_name); - g_free (con_fields->department); - g_free (con_fields->job_title); - g_free (con_fields->assistant_name); - g_free (con_fields->manager); - g_free (con_fields->office_location); - g_free (con_fields->business_homepage); - g_free (con_fields->profession); - g_free (con_fields->spouse_name); - g_free (con_fields->culture); - g_free (con_fields->surname); - g_free (con_fields->givenname); - g_free (con_fields->middlename); - g_free (con_fields->notes); - g_free (con_fields); - } -} - -static void -ews_item_free_attendee (EwsAttendee *attendee) -{ - if (attendee) { - e_ews_mailbox_free (attendee->mailbox); - g_free (attendee->responsetype); - g_free (attendee); - } -} - -static time_t -ews_item_parse_date (const gchar *dtstring) -{ - time_t t = 0; - GTimeVal t_val; - - g_return_val_if_fail (dtstring != NULL, 0); - - if (g_time_val_from_iso8601 (dtstring, &t_val)) { - t = (time_t) t_val.tv_sec; - } else if (strlen (dtstring) == 8) { - /* It might be a date value */ - GDate date; - struct tm tt; - guint16 year; - guint month; - guint8 day; - - g_date_clear (&date, 1); -#define digit_at(x,y) (x[y] - '0') - year = digit_at (dtstring, 0) * 1000 - + digit_at (dtstring, 1) * 100 - + digit_at (dtstring, 2) * 10 - + digit_at (dtstring, 3); - month = digit_at (dtstring, 4) * 10 + digit_at (dtstring, 5); - day = digit_at (dtstring, 6) * 10 + digit_at (dtstring, 7); - - g_date_set_year (&date, year); - g_date_set_month (&date, month); - g_date_set_day (&date, day); - - g_date_to_struct_tm (&date, &tt); - t = mktime (&tt); - - } else - g_warning ("Could not parse the string \n"); - - return t; -} - -static void -parse_extended_property (EEwsItemPrivate *priv, - ESoapParameter *param) -{ - EEwsMessageDataType data_type; - ESoapParameter *subparam; - gchar *str, *setid, *name, *value; - guint32 tag = 0; - - subparam = e_soap_parameter_get_first_child_by_name (param, "ExtendedFieldURI"); - if (!subparam) - return; - - str = e_soap_parameter_get_property (subparam, "PropertyType"); - if (!str) - return; - - /* We only handle some MAPI properties for now... */ - if (g_ascii_strcasecmp (str, "Boolean") == 0) { - data_type = E_EWS_MESSAGE_DATA_TYPE_BOOLEAN; - } else if (g_ascii_strcasecmp (str, "Integer") == 0) { - data_type = E_EWS_MESSAGE_DATA_TYPE_INT; - } else if (g_ascii_strcasecmp (str, "Double") == 0) { - data_type = E_EWS_MESSAGE_DATA_TYPE_DOUBLE; - } else if (g_ascii_strcasecmp (str, "String") == 0) { - data_type = E_EWS_MESSAGE_DATA_TYPE_STRING; - } else if (g_ascii_strcasecmp (str, "SystemTime") == 0) { - data_type = E_EWS_MESSAGE_DATA_TYPE_TIME; - } else { - g_free (str); - return; - } - g_free (str); - - name = e_soap_parameter_get_property (subparam, "PropertyName"); - if (!name) { - str = e_soap_parameter_get_property (subparam, "PropertyTag"); - if (!str) { - str = e_soap_parameter_get_property (subparam, "PropertyId"); - if (!str) - return; - } - - tag = strtol (str, NULL, 0); - g_free (str); - } - - setid = e_soap_parameter_get_property (subparam, "DistinguishedPropertySetId"); - - subparam = e_soap_parameter_get_first_child_by_name (param, "Value"); - if (!subparam) { - g_free (setid); - g_free (name); - return; - } - - value = e_soap_parameter_get_string_value (subparam); - if (!value) { - g_free (setid); - g_free (name); - return; - } - - if (data_type == E_EWS_MESSAGE_DATA_TYPE_INT) { - guint32 num_value; - - num_value = strtol (value, NULL, 0); - - switch (tag) { - case 0x01080: /* PidTagIconIndex */ - priv->mapi_icon_index = num_value; - break; - - case 0x1081: /* PidTagLastVerbExecuted */ - priv->mapi_last_verb_executed = num_value; - break; - - case 0x0e07: /* PidTagMessageFlags */ - priv->mapi_message_flags = num_value; - break; - - case 0x0e17: /* PidTagMessageStatus */ - priv->mapi_message_status = num_value; - break; - } - } - - if (setid) { - if (g_strcmp0 (name, "EvolutionEWSStartTimeZone") == 0) { - priv->iana_start_time_zone = g_strdup (value); - } else if (g_strcmp0 (name, "EvolutionEWSEndTimeZone") == 0) { - priv->iana_end_time_zone = g_strdup (value); - } else { - GHashTable *set_hash = g_hash_table_lookup (priv->mapi_extended_sets, setid); - - if (!set_hash) { - set_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); - g_hash_table_insert (priv->mapi_extended_sets, setid, set_hash); - setid = NULL; - } - - g_hash_table_insert (set_hash, GUINT_TO_POINTER (tag), g_strdup (value)); - } - } else if (tag != 0) { - g_hash_table_insert (priv->mapi_extended_tags, GUINT_TO_POINTER (tag), g_strdup (value)); - } - - g_free (setid); - g_free (value); - g_free (name); -} - -static void -parse_categories (EEwsItemPrivate *priv, - ESoapParameter *param) -{ - gchar *value; - ESoapParameter *subparam; - - /* release all the old data (if any) */ - g_slist_free_full (priv->categories, g_free); - priv->categories = NULL; - - /* categories are an array of */ - for (subparam = e_soap_parameter_get_first_child (param); - subparam != NULL; - subparam = e_soap_parameter_get_next_child (subparam)) { - value = e_soap_parameter_get_string_value (subparam); - - priv->categories = g_slist_append (priv->categories, value); - } - -} - -static EwsImportance -parse_importance (ESoapParameter *param) -{ - gchar *value; - EwsImportance importance = EWS_ITEM_LOW; - - value = e_soap_parameter_get_string_value (param); - - if (!g_ascii_strcasecmp (value, "Normal")) - importance = EWS_ITEM_NORMAL; - else if (!g_ascii_strcasecmp (value, "High") ) - importance = EWS_ITEM_HIGH; - - g_free (value); - return importance; -} - -static void -process_modified_occurrences (EEwsItemPrivate *priv, - ESoapParameter *param) -{ - ESoapParameter *subparam, *subparam1; - gchar *modified_occurrence_id; - - for (subparam = e_soap_parameter_get_first_child (param); subparam != NULL; subparam = e_soap_parameter_get_next_child (subparam)) { - - subparam1 = e_soap_parameter_get_first_child_by_name (subparam, "ItemId"); - modified_occurrence_id = e_soap_parameter_get_property (subparam1, "Id"); - priv->modified_occurrences = g_slist_append (priv->modified_occurrences, modified_occurrence_id); - } - - return; -} - -static void -process_attachments_list (EEwsItemPrivate *priv, - ESoapParameter *param) -{ - ESoapParameter *subparam, *subparam1; - - GSList *ids = NULL; - - for (subparam = e_soap_parameter_get_first_child (param); subparam != NULL; subparam = e_soap_parameter_get_next_child (subparam)) { - gchar *id; - - subparam1 = e_soap_parameter_get_first_child_by_name (subparam, "AttachmentId"); - id = e_soap_parameter_get_property (subparam1, "Id"); - - subparam1 = e_soap_parameter_get_first_child_by_name (subparam, "IsContactPhoto"); - if (subparam1) { - gchar *value = e_soap_parameter_get_string_value (subparam1); - if (g_strcmp0 (value, "true") == 0) { - priv->contact_photo_id = id; - g_free (value); - continue; - } - g_free (value); - } - - ids = g_slist_append (ids, id); - } - - priv->attachments_ids = ids; - return; -} - -static void -process_attendees (EEwsItemPrivate *priv, - ESoapParameter *param, - const gchar *type) -{ - ESoapParameter *subparam, *subparam1; - EwsAttendee *attendee; - - for (subparam = e_soap_parameter_get_first_child (param); subparam != NULL; subparam = e_soap_parameter_get_next_child (subparam)) { - EwsMailbox *mailbox = NULL; - - subparam1 = e_soap_parameter_get_first_child_by_name (subparam, "Mailbox"); - mailbox = e_ews_item_mailbox_from_soap_param (subparam1); - /* Ignore attendee if mailbox is not valid, - * for instance, ppl that does not exists any more */ - if (!mailbox) - continue; - - attendee = g_new0 (EwsAttendee, 1); - - attendee->mailbox = mailbox; - - subparam1 = e_soap_parameter_get_first_child_by_name (subparam, "ResponseType"); - attendee->responsetype = subparam1 ? e_soap_parameter_get_string_value (subparam1) : NULL; - - attendee->attendeetype = (gchar *) type; - - priv->attendees = g_slist_append (priv->attendees, attendee); - } - - return; -} - -static void -parse_complete_name (struct _EEwsContactFields *con_fields, - ESoapParameter *param) -{ - ESoapParameter *subparam; - EwsCompleteName *cn; - - cn = g_new0 (EwsCompleteName, 1); - - subparam = e_soap_parameter_get_first_child_by_name (param, "Title"); - if (subparam) - cn->title = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "FirstName"); - if (subparam) - cn->first_name = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "MiddleName"); - if (subparam) - cn->middle_name = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "LastName"); - if (subparam) - cn->last_name = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "Suffix"); - if (subparam) - cn->suffix = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "Initials"); - if (subparam) - cn->initials = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "FullName"); - if (subparam) - cn->full_name = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "Nickname"); - if (subparam) - cn->nick_name = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "YomiFirstName"); - if (subparam) - cn->yomi_first_name = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "YomiLastName"); - if (subparam) - cn->yomi_last_name = e_soap_parameter_get_string_value (subparam); - - con_fields->complete_name = cn; -} - -static gpointer -ews_get_physical_address (ESoapParameter *param) -{ - ESoapParameter *subparam; - EwsAddress *address; - - address = g_new0 (EwsAddress, 1); - - subparam = e_soap_parameter_get_first_child_by_name (param, "Street"); - if (subparam) - address->street = e_soap_parameter_get_string_value (subparam); - - subparam = e_soap_parameter_get_first_child_by_name (param, "City"); - if (subparam) - address->city = e_soap_parameter_get_string_value (subparam); - - subparam = e_soap_parameter_get_first_child_by_name (param, "State"); - if (subparam) - address->state = e_soap_parameter_get_string_value (subparam); - - subparam = e_soap_parameter_get_first_child_by_name (param, "Country"); - if (subparam) - address->country = e_soap_parameter_get_string_value (subparam); - - subparam = e_soap_parameter_get_first_child_by_name (param, "PostalCode"); - if (subparam) - address->postal_code = e_soap_parameter_get_string_value (subparam); - - return address; -} - -static void -parse_entries (GHashTable *hash_table, - ESoapParameter *param, - EwsGetValFunc get_val_func) -{ - ESoapParameter *subparam; - - for (subparam = e_soap_parameter_get_first_child_by_name (param, "Entry"); - subparam != NULL; - subparam = e_soap_parameter_get_next_child_by_name (subparam, "Entry")) { - gchar *key; - gpointer value; - - key = e_soap_parameter_get_property (subparam, "Key"); - value = get_val_func (subparam); - - if (value) - g_hash_table_insert (hash_table, key, value); - else - g_free (key); - } -} - -static void -ews_free_physical_address (gpointer value) -{ - EwsAddress *address = (EwsAddress *) value; - - if (address) { - g_free (address->street); - g_free (address->city); - g_free (address->state); - g_free (address->country); - g_free (address->postal_code); - g_free (address); - } -} - -static void -parse_contact_field (EEwsItem *item, - const gchar *name, - ESoapParameter *subparam) -{ - EEwsItemPrivate *priv = item->priv; - gchar *value = NULL; - - if (!g_ascii_strcasecmp (name, "Culture")) { - priv->contact_fields->culture = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "DisplayName")) { - priv->contact_fields->display_name = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "FileAs")) { - priv->contact_fields->fileas = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "CompleteName")) { - parse_complete_name (priv->contact_fields, subparam); - } else if (!g_ascii_strcasecmp (name, "CompanyName")) { - priv->contact_fields->company_name = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "EmailAddresses")) { - priv->contact_fields->email_addresses = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - parse_entries (priv->contact_fields->email_addresses, subparam, (EwsGetValFunc) e_soap_parameter_get_string_value); - } else if (!g_ascii_strcasecmp (name, "PhysicalAddresses")) { - priv->contact_fields->physical_addresses = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, ews_free_physical_address); - parse_entries (priv->contact_fields->physical_addresses, subparam, ews_get_physical_address); - } else if (!g_ascii_strcasecmp (name, "PhoneNumbers")) { - priv->contact_fields->phone_numbers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - parse_entries (priv->contact_fields->phone_numbers, subparam, (EwsGetValFunc) e_soap_parameter_get_string_value); - } else if (!g_ascii_strcasecmp (name, "AssistantName")) { - priv->contact_fields->assistant_name = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Birthday")) { - value = e_soap_parameter_get_string_value (subparam); - priv->contact_fields->birthday = ews_item_parse_date (value); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "BusinessHomePage")) { - priv->contact_fields->business_homepage = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Department")) { - priv->contact_fields->department = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "ImAddresses")) { - priv->contact_fields->im_addresses = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - parse_entries (priv->contact_fields->im_addresses, subparam, (EwsGetValFunc) e_soap_parameter_get_string_value); - } else if (!g_ascii_strcasecmp (name, "JobTitle")) { - priv->contact_fields->job_title = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Manager")) { - priv->contact_fields->manager = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "OfficeLocation")) { - priv->contact_fields->office_location = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Profession")) { - priv->contact_fields->profession = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "SpouseName")) { - priv->contact_fields->spouse_name = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Surname")) { - priv->contact_fields->surname = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "GivenName")) { - priv->contact_fields->givenname = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "MiddleName")) { - priv->contact_fields->middlename = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "WeddingAnniversary")) { - value = e_soap_parameter_get_string_value (subparam); - priv->contact_fields->wedding_anniversary = ews_item_parse_date (value); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "Body")) { - /* - * For Exchange versions >= 2010_SP2 Notes property can be get - * directly from contacts:Notes. But for backward compatibility - * with old servers (< 2010_SP2) we prefer use item:Body. - */ - priv->contact_fields->notes = e_soap_parameter_get_string_value (subparam); - } -} - -static gchar * -strip_html_tags (const gchar *html_text) -{ - gssize haystack_len = strlen (html_text); - gchar *plain_text = g_malloc (haystack_len + 1); - gchar *start = g_strstr_len (html_text, haystack_len, "= end) - break; - - if (*i != ';') - i = from; - else - continue; - } - - if (*i == '<') { - while (i < end && *i != '>') - i++; - - if (i >= end) - break; - } else { - *j = *i; - j++; - } - } - - *j = '\0'; - - return plain_text; -} - -static void -parse_task_field (EEwsItem *item, - const gchar *name, - ESoapParameter *subparam) -{ - EEwsItemPrivate *priv = item->priv; - gchar *value = NULL; - - if (!g_ascii_strcasecmp (name, "Status")) { - priv->task_fields->status = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "PercentComplete")) { - priv->task_fields->percent_complete = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "DueDate")) { - value = e_soap_parameter_get_string_value (subparam); - priv->task_fields->due_date = ews_item_parse_date (value); - g_free (value); - priv->task_fields->has_due_date = TRUE; - } else if (!g_ascii_strcasecmp (name, "StartDate")) { - value = e_soap_parameter_get_string_value (subparam); - priv->task_fields->start_date = ews_item_parse_date (value); - g_free (value); - priv->task_fields->has_start_date = TRUE; - } - else if (!g_ascii_strcasecmp (name, "CompleteDate")) { - value = e_soap_parameter_get_string_value (subparam); - priv->task_fields->complete_date = ews_item_parse_date (value); - g_free (value); - priv->task_fields->has_complete_date = TRUE; - } else if (!g_ascii_strcasecmp (name, "Sensitivity")) { - priv->task_fields->sensitivity = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Body")) { - if (!g_ascii_strcasecmp (e_soap_parameter_get_property (subparam, "BodyType"),"HTML")) { - value = e_soap_parameter_get_string_value (subparam); - priv->task_fields->body = strip_html_tags (value); - g_free (value); - } else - priv->task_fields->body = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Owner")) { - priv->task_fields->owner = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Delegator")) { - priv->task_fields->delegator = e_soap_parameter_get_string_value (subparam); - if (!g_ascii_strcasecmp (priv->task_fields->delegator, "")) { - g_free (priv->task_fields->delegator); - priv->task_fields->delegator = NULL; - } - } -} - -static gboolean -e_ews_item_set_from_soap_parameter (EEwsItem *item, - ESoapParameter *param) -{ - EEwsItemPrivate *priv = item->priv; - ESoapParameter *subparam, *node = NULL, *attach_id; - const gchar *name; - - g_return_val_if_fail (param != NULL, FALSE); - - name = e_soap_parameter_get_name (param); - - /*We get two types of response for items from server like below from two apis - * Syncfolderitems and Finditem - * - * - * - * - * - * - * - * ... - * - * - * ... - * - * So check param is the node we want to use, by comparing name or is it child of the param */ - - if (!g_ascii_strcasecmp (name, "Message") || (node = e_soap_parameter_get_first_child_by_name (param, "Message"))) { - priv->item_type = E_EWS_ITEM_TYPE_MESSAGE; - subparam = e_soap_parameter_get_first_child_by_name (node ? node : param, "ItemClass"); - if (subparam) { - gchar *folder_class = e_soap_parameter_get_string_value (subparam); - - if (g_strcmp0 (folder_class, "IPM.StickyNote") == 0) { - priv->item_type = E_EWS_ITEM_TYPE_MEMO; - priv->task_fields = g_new0 (struct _EEwsTaskFields, 1); - priv->task_fields->has_due_date = FALSE; - priv->task_fields->has_start_date = FALSE; - priv->task_fields->has_complete_date = FALSE; - } - - g_free (folder_class); - } - } else if (!g_ascii_strcasecmp (name, "PostItem") || (node = e_soap_parameter_get_first_child_by_name (param, "PostItem"))) - priv->item_type = E_EWS_ITEM_TYPE_POST_ITEM; - else if (!g_ascii_strcasecmp (name, "CalendarItem") || (node = e_soap_parameter_get_first_child_by_name (param, "CalendarItem"))) - priv->item_type = E_EWS_ITEM_TYPE_EVENT; - else if (!g_ascii_strcasecmp (name, "Contact") || (node = e_soap_parameter_get_first_child_by_name (param, "Contact"))) { - priv->item_type = E_EWS_ITEM_TYPE_CONTACT; - priv->contact_fields = g_new0 (struct _EEwsContactFields, 1); - } else if (!g_ascii_strcasecmp (name, "DistributionList") || (node = e_soap_parameter_get_first_child_by_name (param, "DistributionList"))) - priv->item_type = E_EWS_ITEM_TYPE_GROUP; - else if (!g_ascii_strcasecmp (name, "MeetingMessage") || (node = e_soap_parameter_get_first_child_by_name (param, "MeetingMessage"))) - priv->item_type = E_EWS_ITEM_TYPE_MEETING_MESSAGE; - else if (!g_ascii_strcasecmp (name, "MeetingRequest") || (node = e_soap_parameter_get_first_child_by_name (param, "MeetingRequest"))) - priv->item_type = E_EWS_ITEM_TYPE_MEETING_REQUEST; - else if (!g_ascii_strcasecmp (name, "MeetingResponse") || (node = e_soap_parameter_get_first_child_by_name (param, "MeetingResponse"))) - priv->item_type = E_EWS_ITEM_TYPE_MEETING_RESPONSE; - else if (!g_ascii_strcasecmp (name, "MeetingCancellation") || (node = e_soap_parameter_get_first_child_by_name (param, "MeetingCancellation"))) - priv->item_type = E_EWS_ITEM_TYPE_MEETING_CANCELLATION; - else if (!g_ascii_strcasecmp (name, "Task") || (node = e_soap_parameter_get_first_child_by_name (param, "Task"))) { - priv->item_type = E_EWS_ITEM_TYPE_TASK; - priv->task_fields = g_new0 (struct _EEwsTaskFields, 1); - priv->task_fields->has_due_date = FALSE; - priv->task_fields->has_start_date = FALSE; - priv->task_fields->has_complete_date = FALSE; - } else if (!g_ascii_strcasecmp (name, "Item") || (node = e_soap_parameter_get_first_child_by_name (param, "Item"))) - priv->item_type = E_EWS_ITEM_TYPE_GENERIC_ITEM; - else if ((node = e_soap_parameter_get_first_child_by_name (param, "AttachmentId"))) { - priv->attachment_id = g_new0 (EwsId, 1); - priv->attachment_id->id = e_soap_parameter_get_property (node, "Id"); - priv->attachment_id->change_key = e_soap_parameter_get_property (node, "ChangeKey"); - } else if ((node = e_soap_parameter_get_first_child_by_name (param, "ItemId"))) { - /*Spesial case when we are facing during sync folders*/ - priv->item_id = g_new0 (EwsId, 1); - priv->item_id->id = e_soap_parameter_get_property (node, "Id"); - priv->item_id->change_key = e_soap_parameter_get_property (node, "ChangeKey"); - return TRUE; - } else { - g_warning ("Unable to find the Item type \n"); - return FALSE; - } - - attach_id = e_soap_parameter_get_first_child_by_name (param, "AttachmentId"); - if (attach_id) { - priv->attachment_id = g_new0 (EwsId, 1); - priv->attachment_id->id = e_soap_parameter_get_property (attach_id, "Id"); - priv->attachment_id->change_key = e_soap_parameter_get_property (attach_id, "ChangeKey"); - } - - if (!node) - node = param; - - for (subparam = e_soap_parameter_get_first_child (node); - subparam != NULL; - subparam = e_soap_parameter_get_next_child (subparam)) { - ESoapParameter *subparam1; - const gchar *name; - gchar *value = NULL; - - name = e_soap_parameter_get_name (subparam); - - /* The order is maintained according to the order in soap response */ - if (!g_ascii_strcasecmp (name, "MimeContent")) { - gchar *charset; - guchar *data; - gsize data_len = 0; - - value = e_soap_parameter_get_string_value (subparam); - data = g_base64_decode (value, &data_len); - if (!data || !data_len) { - g_free (value); - g_free (data); - return FALSE; - } - - charset = e_soap_parameter_get_property (subparam, "CharacterSet"); - if (g_strcmp0 (charset, "UTF-8") == 0 && - !g_utf8_validate ((const gchar *) data, data_len, NULL)) { - gchar *tmp; - - tmp = e_util_utf8_data_make_valid ((const gchar *) data, data_len); - if (tmp) { - g_free (data); - data = (guchar *) tmp; - } - } - g_free (charset); - - priv->mime_content = (gchar *) data; - - g_free (value); - } else if (!g_ascii_strcasecmp (name, "ItemId")) { - priv->item_id = g_new0 (EwsId, 1); - priv->item_id->id = e_soap_parameter_get_property (subparam, "Id"); - priv->item_id->change_key = e_soap_parameter_get_property (subparam, "ChangeKey"); - } else if (!g_ascii_strcasecmp (name, "Subject")) { - priv->subject = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "InternetMessageHeaders")) { - for (subparam1 = e_soap_parameter_get_first_child_by_name (subparam, "InternetMessageHeader"); - subparam1; - subparam1 = e_soap_parameter_get_next_child (subparam1)) { - gchar *str = e_soap_parameter_get_property (subparam1, "HeaderName"); - - if (g_strcmp0 (str, "Date") == 0) { - priv->date_header = e_soap_parameter_get_string_value (subparam1); - g_free (str); - break; - } - - g_free (str); - } - } else if (!g_ascii_strcasecmp (name, "DateTimeReceived")) { - value = e_soap_parameter_get_string_value (subparam); - priv->date_received = ews_item_parse_date (value); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "Size")) { - priv->size = e_soap_parameter_get_int_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Categories")) { - parse_categories (priv, subparam); - } else if (!g_ascii_strcasecmp (name, "Importance")) { - priv->importance = parse_importance (subparam); - } else if (!g_ascii_strcasecmp (name, "InReplyTo")) { - priv->in_replyto = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "DateTimeSent")) { - value = e_soap_parameter_get_string_value (subparam); - priv->date_sent = ews_item_parse_date (value); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "DateTimeCreated")) { - value = e_soap_parameter_get_string_value (subparam); - priv->date_created = ews_item_parse_date (value); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "LastModifiedTime")) { - value = e_soap_parameter_get_string_value (subparam); - priv->last_modified_time = ews_item_parse_date (value); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "HasAttachments")) { - value = e_soap_parameter_get_string_value (subparam); - priv->has_attachments = (!g_ascii_strcasecmp (value, "true")); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "Attachments")) { - process_attachments_list (priv, subparam); - } else if (priv->item_type == E_EWS_ITEM_TYPE_CONTACT) - parse_contact_field (item, name, subparam); - /* fields below are not relevant for contacts, so skip them */ - else if (!g_ascii_strcasecmp (name, "Sender")) { - subparam1 = e_soap_parameter_get_first_child_by_name (subparam, "Mailbox"); - priv->sender = e_ews_item_mailbox_from_soap_param (subparam1); - } else if (!g_ascii_strcasecmp (name, "ToRecipients")) { - GSList *list = NULL; - for (subparam1 = e_soap_parameter_get_first_child (subparam); - subparam1 != NULL; - subparam1 = e_soap_parameter_get_next_child (subparam1)) { - EwsMailbox *mb = e_ews_item_mailbox_from_soap_param (subparam1); - list = g_slist_append (list, mb); - } - priv->to_recipients = list; - } else if (!g_ascii_strcasecmp (name, "CcRecipients")) { - GSList *list = NULL; - for (subparam1 = e_soap_parameter_get_first_child (subparam); - subparam1 != NULL; - subparam1 = e_soap_parameter_get_next_child (subparam1)) { - EwsMailbox *mb = e_ews_item_mailbox_from_soap_param (subparam1); - list = g_slist_append (list, mb); - } - priv->cc_recipients = list; - } else if (!g_ascii_strcasecmp (name, "BccRecipients")) { - GSList *list = NULL; - for (subparam1 = e_soap_parameter_get_first_child (subparam); - subparam1 != NULL; - subparam1 = e_soap_parameter_get_next_child (subparam1)) { - EwsMailbox *mb = e_ews_item_mailbox_from_soap_param (subparam1); - list = g_slist_append (list, mb); - } - priv->bcc_recipients = list; - } else if (!g_ascii_strcasecmp (name, "From")) { - subparam1 = e_soap_parameter_get_first_child_by_name (subparam, "Mailbox"); - priv->from = e_ews_item_mailbox_from_soap_param (subparam1); - } else if (!g_ascii_strcasecmp (name, "InternetMessageId")) { - priv->msg_id = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "UID")) { - priv->uid = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "IsRead")) { - value = e_soap_parameter_get_string_value (subparam); - priv->is_read = (!g_ascii_strcasecmp (value, "true")); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "TimeZone")) { - priv->timezone = e_soap_parameter_get_string_value (subparam); - } else if (priv->item_type == E_EWS_ITEM_TYPE_TASK || priv->item_type == E_EWS_ITEM_TYPE_MEMO) { - parse_task_field (item, name, subparam); - /* fields below are not relevant for task, so skip them */ - } else if (!g_ascii_strcasecmp (name, "References")) { - priv->references = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "ExtendedProperty")) { - parse_extended_property (priv, subparam); - } else if (!g_ascii_strcasecmp (name, "ModifiedOccurrences")) { - process_modified_occurrences (priv, subparam); - } else if (!g_ascii_strcasecmp (name, "IsMeeting")) { - value = e_soap_parameter_get_string_value (subparam); - priv->is_meeting = (!g_ascii_strcasecmp (value, "true")); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "IsResponseRequested")) { - value = e_soap_parameter_get_string_value (subparam); - priv->is_response_requested = (!g_ascii_strcasecmp (value, "true")); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "MyResponseType")) { - g_free (priv->my_response_type); - priv->my_response_type = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "RequiredAttendees")) { - process_attendees (priv, subparam, "Required"); - } else if (!g_ascii_strcasecmp (name, "OptionalAttendees")) { - process_attendees (priv, subparam, "Optional"); - } else if (!g_ascii_strcasecmp (name, "Resources")) { - process_attendees (priv, subparam, "Resource"); - } else if (!g_ascii_strcasecmp (name, "AssociatedCalendarItemId")) { - priv->calendar_item_accept_id = g_new0 (EwsId, 1); - priv->calendar_item_accept_id->id = e_soap_parameter_get_property (subparam, "Id"); - priv->calendar_item_accept_id->change_key = e_soap_parameter_get_property (subparam, "ChangeKey"); - } else if (!g_ascii_strcasecmp (name, "StartTimeZone")) { - priv->start_timezone = e_soap_parameter_get_property (subparam, "Id"); - } else if (!g_ascii_strcasecmp (name, "EndTimeZone")) { - priv->end_timezone = e_soap_parameter_get_property (subparam, "Id"); - } else if (!g_ascii_strcasecmp (name, "Body")) { - priv->body = e_soap_parameter_get_string_value (subparam); - } - } - - return TRUE; -} - -EEwsItem * -e_ews_item_new_from_soap_parameter (ESoapParameter *param) -{ - EEwsItem *item; - - g_return_val_if_fail (param != NULL, NULL); - - item = g_object_new (E_TYPE_EWS_ITEM, NULL); - if (!e_ews_item_set_from_soap_parameter (item, param)) { - g_object_unref (item); - return NULL; - } - - return item; -} - -EEwsItem * -e_ews_item_new_from_error (const GError *error) -{ - EEwsItem *item; - - g_return_val_if_fail (error != NULL, NULL); - - item = g_object_new (E_TYPE_EWS_ITEM, NULL); - e_ews_item_set_error (item, error); - - return item; -} - -EEwsItemType -e_ews_item_get_item_type (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), E_EWS_ITEM_TYPE_UNKNOWN); - - return item->priv->item_type; -} - -void -e_ews_item_set_item_type (EEwsItem *item, - EEwsItemType new_type) -{ - g_return_if_fail (E_IS_EWS_ITEM (item)); - - /* once the type is set to error type it stays as error type */ - if (item->priv->item_type != E_EWS_ITEM_TYPE_ERROR) - item->priv->item_type = new_type; -} - -const GError * -e_ews_item_get_error (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->error; -} - -void -e_ews_item_set_error (EEwsItem *item, - const GError *error) -{ - GError *new_error; - - g_return_if_fail (E_IS_EWS_ITEM (item)); - - if (error) - new_error = g_error_copy (error); - else - new_error = NULL; - - g_clear_error (&item->priv->error); - item->priv->error = new_error; - - if (item->priv->error) - e_ews_item_set_item_type (item, E_EWS_ITEM_TYPE_ERROR); -} - -const gchar * -e_ews_item_get_subject (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const gchar *) item->priv->subject; -} - -void -e_ews_item_set_subject (EEwsItem *item, - const gchar *new_subject) -{ - g_return_if_fail (E_IS_EWS_ITEM (item)); - - if (item->priv->subject) - g_free (item->priv->subject); - item->priv->subject = g_strdup (new_subject); -} - -const gchar * -e_ews_item_get_mime_content (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const gchar *) item->priv->mime_content; -} - -void -e_ews_item_set_mime_content (EEwsItem *item, - const gchar *new_mime_content) -{ - g_return_if_fail (E_IS_EWS_ITEM (item)); - - if (item->priv->mime_content) - g_free (item->priv->mime_content); - item->priv->mime_content = g_strdup (new_mime_content); -} - -const EwsId * -e_ews_item_get_id (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const EwsId *) item->priv->item_id; -} - -const EwsId * -e_ews_item_get_attachment_id (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const EwsId *) item->priv->attachment_id; -} - -gsize -e_ews_item_get_size (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - - return item->priv->size; -} - -const gchar * -e_ews_item_get_msg_id (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const gchar *) item->priv->msg_id; -} - -const gchar * -e_ews_item_get_uid (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const gchar *) item->priv->uid; -} - -const gchar * -e_ews_item_get_in_replyto (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const gchar *) item->priv->in_replyto; -} - -const gchar * -e_ews_item_get_references (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const gchar *) item->priv->references; -} - -const gchar * -e_ews_item_get_date_header (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->date_header; -} - -time_t -e_ews_item_get_date_received (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - - return item->priv->date_received; -} - -time_t -e_ews_item_get_date_sent (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - - return item->priv->date_sent; -} - -time_t -e_ews_item_get_date_created (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - - return item->priv->date_created; -} - -time_t -e_ews_item_get_last_modified_time (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - - return item->priv->last_modified_time; -} - -gboolean -e_ews_item_has_attachments (EEwsItem *item, - gboolean *has_attachments) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - - *has_attachments = item->priv->has_attachments; - - return TRUE; -} - -gboolean -e_ews_item_is_read (EEwsItem *item, - gboolean *read) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - - *read = item->priv->is_read; - - return TRUE; -} - -gboolean -e_ews_item_get_is_meeting (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - - return item->priv->is_meeting; -} - -gboolean -e_ews_item_get_is_response_requested (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - - return item->priv->is_response_requested; -} - -gboolean -e_ews_item_is_forwarded (EEwsItem *item, - gboolean *forwarded) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - - *forwarded = (item->priv->mapi_icon_index == 0x106); - - return TRUE; -} - -gboolean -e_ews_item_is_answered (EEwsItem *item, - gboolean *answered) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - - *answered = (item->priv->mapi_icon_index == 0x105); - - return TRUE; -} - -guint32 -e_ews_item_get_message_flags (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), 0); - - return item->priv->mapi_message_flags; -} - -const GSList * -e_ews_item_get_to_recipients (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const GSList *) item->priv->to_recipients; -} - -const GSList * -e_ews_item_get_cc_recipients (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const GSList *) item->priv->cc_recipients; -} - -const GSList * -e_ews_item_get_bcc_recipients (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const GSList *) item->priv->bcc_recipients; -} - -const EwsMailbox * -e_ews_item_get_sender (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const EwsMailbox *) item->priv->sender; -} - -const EwsMailbox * -e_ews_item_get_from (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const EwsMailbox *) item->priv->from; -} - -const GSList * -e_ews_item_get_categories (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->categories; -} - -EwsImportance -e_ews_item_get_importance (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), EWS_ITEM_LOW); - - return item->priv->importance; -} - -const gchar * -e_ews_item_get_iana_start_time_zone (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->iana_start_time_zone; -} - -const gchar * -e_ews_item_get_iana_end_time_zone (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->iana_end_time_zone; -} - -EwsMailbox * -e_ews_item_mailbox_from_soap_param (ESoapParameter *param) -{ - EwsMailbox *mb; - ESoapParameter *subparam; - - mb = g_new0 (EwsMailbox, 1); - - subparam = e_soap_parameter_get_first_child_by_name (param, "Name"); - if (subparam) - mb->name = e_soap_parameter_get_string_value (subparam); - - subparam = e_soap_parameter_get_first_child_by_name (param, "EmailAddress"); - if (subparam) - mb->email = e_soap_parameter_get_string_value (subparam); - - subparam = e_soap_parameter_get_first_child_by_name (param, "RoutingType"); - if (subparam) - mb->routing_type = e_soap_parameter_get_string_value (subparam); - - subparam = e_soap_parameter_get_first_child_by_name (param, "MailboxType"); - if (subparam) - mb->mailbox_type = e_soap_parameter_get_string_value (subparam); - - subparam = e_soap_parameter_get_first_child_by_name (param, "ItemId"); - if (subparam) { - EwsId *id = g_new0 (EwsId, 1); - id->id = e_soap_parameter_get_property (subparam, "Id"); - id->change_key = e_soap_parameter_get_property (subparam, "ChangeKey"); - mb->item_id = id; - } - - if (!mb->email && !mb->name) { - e_ews_mailbox_free (mb); - mb = NULL; - } - - return mb; -} - -void -e_ews_mailbox_free (EwsMailbox *mailbox) -{ - if (!mailbox) - return; - - g_free (mailbox->name); - g_free (mailbox->email); - g_free (mailbox->routing_type); - g_free (mailbox->mailbox_type); - - if (mailbox->item_id) { - g_free (mailbox->item_id->id); - g_free (mailbox->item_id->change_key); - g_free (mailbox->item_id); - } - - g_free (mailbox); -} - -const GSList * -e_ews_item_get_modified_occurrences (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->modified_occurrences; -} - -GSList * -e_ews_item_get_attachments_ids (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->attachments_ids; -} - -const gchar * -e_ews_item_get_extended_tag (EEwsItem *item, - guint32 prop_tag) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - if (!item->priv->mapi_extended_tags) - return NULL; - - return g_hash_table_lookup (item->priv->mapi_extended_tags, GUINT_TO_POINTER (prop_tag)); -} - -const gchar * -e_ews_item_get_extended_distinguished_tag (EEwsItem *item, - const gchar *set_id, - guint32 prop_id) -{ - GHashTable *set_tags; - - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - if (!item->priv->mapi_extended_sets) - return NULL; - - set_tags = g_hash_table_lookup (item->priv->mapi_extended_sets, set_id); - if (!set_tags) - return NULL; - - return g_hash_table_lookup (set_tags, GUINT_TO_POINTER (prop_id)); -} - -gboolean -e_ews_item_get_extended_property_as_boolean (EEwsItem *item, - const gchar *set_id, - guint32 prop_id_or_tag, - gboolean *found) -{ - const gchar *value; - - value = e_ews_item_get_extended_property_as_string (item, set_id, prop_id_or_tag, found); - if (!value) - return FALSE; - - if (g_str_equal (value, "true")) - return TRUE; - - if (g_str_equal (value, "false")) - return FALSE; - - if (found) - *found = FALSE; - - return FALSE; -} - -gint -e_ews_item_get_extended_property_as_int (EEwsItem *item, - const gchar *set_id, - guint32 prop_id_or_tag, - gboolean *found) -{ - const gchar *value; - - value = e_ews_item_get_extended_property_as_string (item, set_id, prop_id_or_tag, found); - if (!value) - return 0; - - return strtol (value, NULL, 0); -} - -gdouble -e_ews_item_get_extended_property_as_double (EEwsItem *item, - const gchar *set_id, - guint32 prop_id_or_tag, - gboolean *found) -{ - const gchar *value; - - value = e_ews_item_get_extended_property_as_string (item, set_id, prop_id_or_tag, found); - if (!value) - return 0.0; - - return g_ascii_strtod (value, NULL); -} - -const gchar * -e_ews_item_get_extended_property_as_string (EEwsItem *item, - const gchar *set_id, - guint32 prop_id_or_tag, - gboolean *found) -{ - const gchar *value; - - if (set_id) - value = e_ews_item_get_extended_distinguished_tag (item, set_id, prop_id_or_tag); - else - value = e_ews_item_get_extended_tag (item, prop_id_or_tag); - - if (found) - *found = value != NULL; - - return value; -} - -time_t -e_ews_item_get_extended_property_as_time (EEwsItem *item, - const gchar *set_id, - guint32 prop_id_or_tag, - gboolean *found) -{ - const gchar *value; - GTimeVal tv; - - value = e_ews_item_get_extended_property_as_string (item, set_id, prop_id_or_tag, found); - if (!value) - return (time_t) 0; - - if (g_time_val_from_iso8601 (value, &tv)) - return tv.tv_sec; - - if (found) - *found = FALSE; - - return (time_t) 0; -} - -gchar * -e_ews_embed_attachment_id_in_uri (const gchar *olduri, - const gchar *attach_id) -{ - gchar *tmpdir, *tmpfilename, *filename, *dirname, *name; - - tmpfilename = g_filename_from_uri (olduri, NULL, NULL); - g_return_val_if_fail (tmpfilename != NULL, NULL); - - name = g_path_get_basename (tmpfilename); - tmpdir = g_path_get_dirname (tmpfilename); - - dirname = g_build_filename (tmpdir, attach_id, NULL); - if (g_mkdir (dirname, 0775) == -1) { - g_warning ("Failed create directory to place file in [%s]: %s\n", dirname, g_strerror (errno)); - } - - filename = g_build_filename (dirname, name, NULL); - if (g_rename (tmpfilename, filename) != 0) { - g_warning ("Failed to move attachment cache file [%s -> %s]: %s\n", tmpfilename, filename, g_strerror (errno)); - } - - g_free (tmpfilename); - g_free (tmpdir); - g_free (dirname); - g_free (name); - - tmpfilename = g_filename_to_uri (filename, NULL, NULL); - - g_free (filename); - - return tmpfilename; -} - -EEwsAttachmentInfo * -e_ews_dump_file_attachment_from_soap_parameter (ESoapParameter *param, - const gchar *cache, - const gchar *comp_uid) -{ - ESoapParameter *subparam; - const gchar *param_name, *tmpfilename; - gchar *name = NULL, *value, *filename, *dirname; - guchar *content = NULL; - gsize data_len = 0; - gchar *tmpdir; - EEwsAttachmentInfo *info; - - g_return_val_if_fail (param != NULL, NULL); - - /* Parse element, look for filename and content */ - for (subparam = e_soap_parameter_get_first_child (param); subparam != NULL; subparam = e_soap_parameter_get_next_child (subparam)) { - param_name = e_soap_parameter_get_name (subparam); - - if (g_ascii_strcasecmp (param_name, "Name") == 0) { - name = e_soap_parameter_get_string_value (subparam); - } else if (g_ascii_strcasecmp (param_name, "Content") == 0) { - value = e_soap_parameter_get_string_value (subparam); - content = g_base64_decode (value, &data_len); - g_free (value); - } - } - - /* Make sure we have needed data */ - if (!content || !name) { - g_free (name); - g_free (content); - return NULL; - } - - if (cache && content && g_file_test ((const gchar *) content, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS)) { - gchar *uri; - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_URI); - - tmpfilename = (gchar *) content; - tmpdir = g_path_get_dirname (tmpfilename); - - dirname = g_build_filename (tmpdir, comp_uid, NULL); - if (g_mkdir_with_parents (dirname, 0775) == -1) { - g_warning ("Failed create directory to place file in [%s]: %s\n", dirname, g_strerror (errno)); - } - - filename = g_build_filename (dirname, name, NULL); - if (g_rename (tmpfilename, filename) != 0) { - g_warning ("Failed to move attachment cache file [%s -> %s]: %s\n", - tmpfilename, filename, g_strerror (errno)); - } - - g_free (dirname); - g_free (tmpdir); - g_free (name); - g_free (content); - - /* Return URI to saved file */ - uri = g_filename_to_uri (filename, NULL, NULL); - e_ews_attachment_info_set_uri (info, uri); - g_free (filename); - g_free (uri); - } else { - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_INLINED); - e_ews_attachment_info_set_inlined_data (info, content, data_len); - e_ews_attachment_info_set_prefer_filename (info, name); - } - return info; -} - -EEwsAttachmentInfo * -e_ews_item_dump_mime_content (EEwsItem *item, - const gchar *cache) -{ - EEwsAttachmentInfo *info; - gchar *filename, *surename, *dirname; - gchar *tmpdir, *uri; - const gchar *tmpfilename; - - g_return_val_if_fail (item->priv->mime_content != NULL, NULL); - g_return_val_if_fail (g_file_test ((const gchar *) item->priv->mime_content, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS), NULL); - - tmpfilename = (gchar *) item->priv->mime_content; - tmpdir = g_path_get_dirname (tmpfilename); - - dirname = g_build_filename (tmpdir, "XXXXXX", NULL); - if (!g_mkdtemp (dirname)) { - g_warning ("Failed to create directory for attachment cache '%s': %s", dirname, g_strerror (errno)); - - g_free (tmpdir); - g_free (dirname); - - return NULL; - } - - surename = g_uri_escape_string (item->priv->subject, "", TRUE); - filename = g_build_filename (dirname, surename, NULL); - - if (g_rename ((const gchar *) item->priv->mime_content, filename) != 0) { - g_warning ("Failed to move attachment cache file '%s': %s", filename, g_strerror (errno)); - - g_free (tmpdir); - g_free (dirname); - g_free (filename); - g_free (surename); - - return NULL; - } - - uri = g_filename_to_uri (filename, NULL, NULL); - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_URI); - e_ews_attachment_info_set_uri (info, uri); - - g_free (uri); - g_free (filename); - g_free (dirname); - g_free (tmpdir); - g_free (surename); - - /* Return URI to saved file */ - return info; -} - -const gchar * -e_ews_item_get_my_response_type (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->my_response_type; -} - -const GSList * -e_ews_item_get_attendees (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->attendees; -} - -const EwsId * -e_ews_item_get_calendar_item_accept_id (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const EwsId *) item->priv->calendar_item_accept_id; -} - -const gchar * -e_ews_item_get_fileas (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->fileas; -} - -const EwsCompleteName * -e_ews_item_get_complete_name (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - if (!item->priv->contact_fields->complete_name && ( - item->priv->contact_fields->surname || - item->priv->contact_fields->middlename || - item->priv->contact_fields->givenname)) { - EwsCompleteName *cn; - - cn = g_new0 (EwsCompleteName, 1); - - cn->first_name = g_strdup (item->priv->contact_fields->givenname); - cn->middle_name = g_strdup (item->priv->contact_fields->middlename); - cn->last_name = g_strdup (item->priv->contact_fields->surname); - - item->priv->contact_fields->complete_name = cn; - } - - return (const EwsCompleteName *) item->priv->contact_fields->complete_name; -} - -const gchar * -e_ews_item_get_display_name (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return item->priv->contact_fields->display_name; -} - -GHashTable * -e_ews_item_get_email_addresses (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return item->priv->contact_fields->email_addresses; -} - -/** - * e_ews_item_get_email_address - * @item: - * @field: "EmailAddress1", "EmailAddress2", "EmailAddress3" - * - * - * Returns: - **/ -const gchar * -e_ews_item_get_email_address (EEwsItem *item, - const gchar *field) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - if (item->priv->contact_fields->email_addresses) - return g_hash_table_lookup (item->priv->contact_fields->email_addresses, field); - - return NULL; -} - -/** - * e_ews_item_get_physical_address - * @item: - * @field: "Business", "Home", "Other" - * - * - * Returns: - **/ -const EwsAddress * -e_ews_item_get_physical_address (EEwsItem *item, - const gchar *field) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - if (item->priv->contact_fields->physical_addresses) - return g_hash_table_lookup (item->priv->contact_fields->physical_addresses, field); - - return NULL; -} - -/** - * e_ews_item_get_phone_number - * @item: - * @field: "AssistantPhone", "BusinessFax", "BusinessPhone", "BusinessPhone2", "Callback" - * "CarPhone", "CompanyMainPhone", "HomeFax", "HomePhone", "HomePhone2", "Isdn", "MobilePhone" - * "OtherFax", "OtherTelephone", "Pager", "PrimaryPhone", "RadioPhone", "Telex", "TtyTddPhone" - * - * - * Returns: - **/ -const gchar * -e_ews_item_get_phone_number (EEwsItem *item, - const gchar *field) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - if (item->priv->contact_fields->phone_numbers) - return g_hash_table_lookup (item->priv->contact_fields->phone_numbers, field); - - return NULL; -} - -/** - * e_ews_item_get_im_address - * @item: - * @field: "ImAddress1", "ImAddress2", "ImAddress3" - * - * - * Returns: - **/ -const gchar * -e_ews_item_get_im_address (EEwsItem *item, - const gchar *field) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - if (item->priv->contact_fields->im_addresses) - return g_hash_table_lookup (item->priv->contact_fields->im_addresses, field); - - return NULL; -} - -const gchar * -e_ews_item_get_company_name (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->company_name; -} - -const gchar * -e_ews_item_get_department (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->department; -} - -const gchar * -e_ews_item_get_job_title (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->job_title; -} - -const gchar * -e_ews_item_get_assistant_name (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->assistant_name; -} - -const gchar * -e_ews_item_get_manager (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->manager; -} - -const gchar * -e_ews_item_get_office_location (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->office_location; -} - -const gchar * -e_ews_item_get_business_homepage (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->business_homepage; -} - -const gchar * -e_ews_item_get_profession (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->profession; -} - -const gchar * -e_ews_item_get_spouse_name (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->spouse_name; -} - -const gchar * -e_ews_item_get_surname (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->surname; -} - -const gchar * -e_ews_item_get_givenname (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return item->priv->contact_fields->givenname; -} - -const gchar * -e_ews_item_get_middlename (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return item->priv->contact_fields->middlename; -} - -const gchar * -e_ews_item_get_notes (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return item->priv->contact_fields->notes; -} - -time_t -e_ews_item_get_birthday (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - g_return_val_if_fail (item->priv->contact_fields != NULL, -1); - - return item->priv->contact_fields->birthday; -} - -time_t -e_ews_item_get_wedding_anniversary (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - g_return_val_if_fail (item->priv->contact_fields != NULL, -1); - - return item->priv->contact_fields->wedding_anniversary; -} - -const gchar * -e_ews_item_get_status (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->task_fields != NULL, NULL); - - return item->priv->task_fields->status; -} - -const gchar * e_ews_item_get_percent_complete (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->task_fields != NULL, NULL); - - return item->priv->task_fields->percent_complete; -} - -time_t -e_ews_item_get_due_date (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - g_return_val_if_fail (item->priv->task_fields != NULL, -1); - - return item->priv->task_fields->due_date; -} - -time_t -e_ews_item_get_start_date (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - g_return_val_if_fail (item->priv->task_fields != NULL, -1); - - return item->priv->task_fields->start_date; -} - -time_t -e_ews_item_get_complete_date (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - g_return_val_if_fail (item->priv->task_fields != NULL, -1); - - return item->priv->task_fields->complete_date; -} - -const gchar * -e_ews_item_get_sensitivity (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->task_fields != NULL, NULL); - - return item->priv->task_fields->sensitivity; -} - -const gchar * -e_ews_item_get_body (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - if (item->priv->body) - return item->priv->body; - - return item->priv->task_fields ? item->priv->task_fields->body : NULL; -} - -const gchar * -e_ews_item_get_owner (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->task_fields != NULL, NULL); - - return item->priv->task_fields->owner; -} - -const gchar * -e_ews_item_get_delegator (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->task_fields != NULL, NULL); - - return item->priv->task_fields->delegator; -} - -gboolean -e_ews_item_task_has_start_date (EEwsItem *item, - gboolean *has_date) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - g_return_val_if_fail (item->priv->task_fields != NULL, FALSE); - - *has_date = item->priv->task_fields->has_start_date; - - return TRUE; -} - -gboolean -e_ews_item_task_has_due_date (EEwsItem *item, - gboolean *has_date) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - g_return_val_if_fail (item->priv->task_fields != NULL, FALSE); - - *has_date = item->priv->task_fields->has_due_date; - - return TRUE; -} - -gboolean -e_ews_item_task_has_complete_date (EEwsItem *item, - gboolean *has_date) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - g_return_val_if_fail (item->priv->task_fields != NULL, FALSE); - - *has_date = item->priv->task_fields->has_complete_date; - - return TRUE; -} - -const gchar * -e_ews_item_get_tzid (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - /* can be NULL */ - return item->priv->timezone; -} - -const gchar * -e_ews_item_get_start_tzid (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - /* can be NULL */ - return item->priv->start_timezone; -} - -const gchar * -e_ews_item_get_end_tzid (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - /* can be NULL */ - return item->priv->end_timezone; -} - -const gchar * -e_ews_item_get_contact_photo_id (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->contact_photo_id; -} - -/* free returned pointer with e_ews_permission_free() */ -EEwsPermission * -e_ews_permission_new (EEwsPermissionUserType user_type, - const gchar *display_name, - const gchar *primary_smtp, - const gchar *sid, - guint32 rights) -{ - EEwsPermission *perm; - - perm = g_new0 (EEwsPermission, 1); - perm->user_type = user_type; - perm->display_name = g_strdup (display_name); - perm->primary_smtp = g_strdup (primary_smtp); - perm->sid = g_strdup (sid); - perm->rights = rights; - - return perm; -} - -void -e_ews_permission_free (EEwsPermission *perm) -{ - if (!perm) - return; - - g_free (perm->display_name); - g_free (perm->primary_smtp); - g_free (perm->sid); - g_free (perm); -} - -static void -ews_level_rights_converter (const gchar **plevel_name, - guint32 *prights, - gboolean level_to_rights) -{ - struct _known { - const gchar *level_name; - guint32 rights; - } known[] = { - { "None", 0 }, - { "Owner", E_EWS_PERMISSION_BIT_READ_ANY | - E_EWS_PERMISSION_BIT_CREATE | - E_EWS_PERMISSION_BIT_CREATE_SUBFOLDER | - E_EWS_PERMISSION_BIT_EDIT_OWNED | - E_EWS_PERMISSION_BIT_EDIT_ANY | - E_EWS_PERMISSION_BIT_DELETE_OWNED | - E_EWS_PERMISSION_BIT_DELETE_ANY | - E_EWS_PERMISSION_BIT_FOLDER_OWNER | - E_EWS_PERMISSION_BIT_FOLDER_CONTACT | - E_EWS_PERMISSION_BIT_FOLDER_VISIBLE }, - { "PublishingEditor", - E_EWS_PERMISSION_BIT_READ_ANY | - E_EWS_PERMISSION_BIT_CREATE | - E_EWS_PERMISSION_BIT_CREATE_SUBFOLDER | - E_EWS_PERMISSION_BIT_EDIT_OWNED | - E_EWS_PERMISSION_BIT_EDIT_ANY | - E_EWS_PERMISSION_BIT_DELETE_OWNED | - E_EWS_PERMISSION_BIT_DELETE_ANY | - E_EWS_PERMISSION_BIT_FOLDER_VISIBLE }, - { "Editor", - E_EWS_PERMISSION_BIT_READ_ANY | - E_EWS_PERMISSION_BIT_CREATE | - E_EWS_PERMISSION_BIT_EDIT_OWNED | - E_EWS_PERMISSION_BIT_EDIT_ANY | - E_EWS_PERMISSION_BIT_DELETE_OWNED | - E_EWS_PERMISSION_BIT_DELETE_ANY | - E_EWS_PERMISSION_BIT_FOLDER_VISIBLE }, - { "PublishingAuthor", - E_EWS_PERMISSION_BIT_READ_ANY | - E_EWS_PERMISSION_BIT_CREATE | - E_EWS_PERMISSION_BIT_CREATE_SUBFOLDER | - E_EWS_PERMISSION_BIT_EDIT_OWNED | - E_EWS_PERMISSION_BIT_DELETE_OWNED | - E_EWS_PERMISSION_BIT_FOLDER_VISIBLE }, - { "Author", - E_EWS_PERMISSION_BIT_READ_ANY | - E_EWS_PERMISSION_BIT_CREATE | - E_EWS_PERMISSION_BIT_EDIT_OWNED | - E_EWS_PERMISSION_BIT_DELETE_OWNED | - E_EWS_PERMISSION_BIT_FOLDER_VISIBLE }, - { "NoneditingAuthor", - E_EWS_PERMISSION_BIT_READ_ANY | - E_EWS_PERMISSION_BIT_CREATE | - E_EWS_PERMISSION_BIT_DELETE_OWNED | - E_EWS_PERMISSION_BIT_FOLDER_VISIBLE }, - { "Reviewer", - E_EWS_PERMISSION_BIT_READ_ANY | - E_EWS_PERMISSION_BIT_FOLDER_VISIBLE }, - { "Contributor", - E_EWS_PERMISSION_BIT_CREATE | - E_EWS_PERMISSION_BIT_FOLDER_VISIBLE }, - { "FreeBusyTimeOnly", - E_EWS_PERMISSION_BIT_FREE_BUSY_SIMPLE }, - { "FreeBusyTimeAndSubjectAndLocation", - E_EWS_PERMISSION_BIT_FREE_BUSY_DETAILED } - - }; - gint ii; - guint32 rights; - - g_return_if_fail (plevel_name != NULL); - g_return_if_fail (prights != NULL); - - rights = (*prights) & ~(E_EWS_PERMISSION_BIT_FREE_BUSY_SIMPLE | E_EWS_PERMISSION_BIT_FREE_BUSY_DETAILED); - - for (ii = 0; ii < G_N_ELEMENTS (known); ii++) { - if (level_to_rights) { - if (g_strcmp0 (*plevel_name, known[ii].level_name) == 0) { - *prights = known[ii].rights; - return; - } - } else { - if (*prights == known[ii].rights || (rights && rights == known[ii].rights)) { - *plevel_name = known[ii].level_name; - return; - } - } - } - - /* here lefts only "Custom" */ - if (level_to_rights) - *prights = 0; - else - *plevel_name = "Custom"; -} - -/* converts user rights to level name suitable for PermissionLevel/CalendarPermissionLevel */ -const gchar * -e_ews_permission_rights_to_level_name (guint32 rights) -{ - const gchar *level_name = NULL; - - ews_level_rights_converter (&level_name, &rights, FALSE); - - return level_name; -} - -/* converts PermissionLevel/CalendarPermissionLevel name to user rights */ -guint32 -e_ews_permission_level_name_to_rights (const gchar *level_name) -{ - guint32 rights = 0; - - ews_level_rights_converter (&level_name, &rights, TRUE); - - return rights; -} - -static EEwsPermission * -ews_permissions_parse (ESoapParameter *param) -{ - EEwsPermission *res; - ESoapParameter *node, *subnode; - EEwsPermissionUserType user_type; - gchar *value, *display_name = NULL, *primary_smtp = NULL, *sid = NULL; - guint32 rights = 0; - - g_return_val_if_fail (param != NULL, NULL); - - node = e_soap_parameter_get_first_child_by_name (param, "UserId"); - if (!node) - return NULL; - - subnode = e_soap_parameter_get_first_child_by_name (node, "DistinguishedUser"); - if (subnode) { - value = e_soap_parameter_get_string_value (subnode); - if (g_strcmp0 (value, "Anonymous") == 0) { - user_type = E_EWS_PERMISSION_USER_TYPE_ANONYMOUS; - } else if (g_strcmp0 (value, "Default") == 0) { - user_type = E_EWS_PERMISSION_USER_TYPE_DEFAULT; - } else { - g_free (value); - return NULL; - } - - g_free (value); - } else { - user_type = E_EWS_PERMISSION_USER_TYPE_REGULAR; - } - - subnode = e_soap_parameter_get_first_child_by_name (node, "SID"); - if (subnode) - sid = e_soap_parameter_get_string_value (subnode); - - subnode = e_soap_parameter_get_first_child_by_name (node, "PrimarySmtpAddress"); - if (subnode) - primary_smtp = e_soap_parameter_get_string_value (subnode); - - subnode = e_soap_parameter_get_first_child_by_name (node, "DisplayName"); - if (subnode) - display_name = e_soap_parameter_get_string_value (subnode); - - node = e_soap_parameter_get_first_child_by_name (param, "PermissionLevel"); - if (!node) - node = e_soap_parameter_get_first_child_by_name (param, "CalendarPermissionLevel"); - - if (node) { - value = e_soap_parameter_get_string_value (node); - rights = e_ews_permission_level_name_to_rights (value); - g_free (value); - } - - node = e_soap_parameter_get_first_child_by_name (param, "CanCreateItems"); - if (node) { - value = e_soap_parameter_get_string_value (node); - if (g_strcmp0 (value, "true") == 0) - rights |= E_EWS_PERMISSION_BIT_CREATE; - g_free (value); - } - - node = e_soap_parameter_get_first_child_by_name (param, "CanCreateSubFolders"); - if (node) { - value = e_soap_parameter_get_string_value (node); - if (g_strcmp0 (value, "true") == 0) - rights |= E_EWS_PERMISSION_BIT_CREATE_SUBFOLDER; - g_free (value); - } - - node = e_soap_parameter_get_first_child_by_name (param, "IsFolderOwner"); - if (node) { - value = e_soap_parameter_get_string_value (node); - if (g_strcmp0 (value, "true") == 0) - rights |= E_EWS_PERMISSION_BIT_FOLDER_OWNER; - g_free (value); - } - - node = e_soap_parameter_get_first_child_by_name (param, "IsFolderVisible"); - if (node) { - value = e_soap_parameter_get_string_value (node); - if (g_strcmp0 (value, "true") == 0) - rights |= E_EWS_PERMISSION_BIT_FOLDER_VISIBLE; - g_free (value); - } - - node = e_soap_parameter_get_first_child_by_name (param, "IsFolderContact"); - if (node) { - value = e_soap_parameter_get_string_value (node); - if (g_strcmp0 (value, "true") == 0) - rights |= E_EWS_PERMISSION_BIT_FOLDER_CONTACT; - g_free (value); - } - - node = e_soap_parameter_get_first_child_by_name (param, "EditItems"); - if (node) { - value = e_soap_parameter_get_string_value (node); - if (g_strcmp0 (value, "None") == 0) - rights |= 0; - else if (g_strcmp0 (value, "Owned") == 0) - rights |= E_EWS_PERMISSION_BIT_EDIT_OWNED; - else if (g_strcmp0 (value, "All") == 0) - rights |= E_EWS_PERMISSION_BIT_EDIT_ANY; - g_free (value); - } - - node = e_soap_parameter_get_first_child_by_name (param, "DeleteItems"); - if (node) { - value = e_soap_parameter_get_string_value (node); - if (g_strcmp0 (value, "None") == 0) - rights |= 0; - else if (g_strcmp0 (value, "Owned") == 0) - rights |= E_EWS_PERMISSION_BIT_DELETE_OWNED; - else if (g_strcmp0 (value, "All") == 0) - rights |= E_EWS_PERMISSION_BIT_DELETE_ANY; - g_free (value); - } - - node = e_soap_parameter_get_first_child_by_name (param, "ReadItems"); - if (node) { - value = e_soap_parameter_get_string_value (node); - if (g_strcmp0 (value, "None") == 0) - rights |= 0; - else if (g_strcmp0 (value, "TimeOnly") == 0) - rights |= E_EWS_PERMISSION_BIT_FREE_BUSY_SIMPLE; - else if (g_strcmp0 (value, "TimeAndSubjectAndLocation") == 0) - rights |= E_EWS_PERMISSION_BIT_FREE_BUSY_DETAILED; - else if (g_strcmp0 (value, "FullDetails") == 0) - rights |= E_EWS_PERMISSION_BIT_READ_ANY; - g_free (value); - } - - res = e_ews_permission_new (user_type, display_name, primary_smtp, sid, rights); - - g_free (display_name); - g_free (primary_smtp); - g_free (sid); - - return res; -} - -/* Returns GSList of EEwsPermission * objects, as read from @param. - * Returned GSList should be freed with e_ews_permissions_free() - * when done with it. Returns NULL when no permissions recognized - * from @param. -*/ -GSList * -e_ews_permissions_from_soap_param (ESoapParameter *param) -{ - GSList *perms = NULL; - ESoapParameter *node; - const gchar *name; - - g_return_val_if_fail (param != NULL, NULL); - - name = e_soap_parameter_get_name (param); - if (g_ascii_strcasecmp (name, "Permissions") == 0 || - g_ascii_strcasecmp (name, "CalendarPermissions") == 0) { - node = param; - } else { - node = e_soap_parameter_get_first_child_by_name (param, "Permissions"); - if (!node) - node = e_soap_parameter_get_first_child_by_name (param, "CalendarPermissions"); - if (!node) - return NULL; - } - - for (node = e_soap_parameter_get_first_child (node); - node; - node = e_soap_parameter_get_next_child (node)) { - name = e_soap_parameter_get_name (node); - if (g_ascii_strcasecmp (name, "Permission") == 0 || - g_ascii_strcasecmp (name, "CalendarPermission") == 0) { - EEwsPermission *perm; - - perm = ews_permissions_parse (node); - if (perm) { - perms = g_slist_prepend (perms, perm); - } - } - } - - return perms ? g_slist_reverse (perms) : NULL; -} - -void -e_ews_permissions_free (GSList *permissions) -{ - g_slist_free_full (permissions, (GDestroyNotify) e_ews_permission_free); -} - -/* strips ex_address by its LDAP-like part, returning position in ex_address where - common name begins; returns whole ex_address, if not found */ -const gchar * -e_ews_item_util_strip_ex_address (const gchar *ex_address) -{ - const gchar *ptr; - - if (!ex_address) - return ex_address; - - ptr = strrchr (ex_address, '/'); - if (ptr && g_ascii_strncasecmp (ptr, "/cn=", 4) == 0) { - return ptr + 4; - } - - return ex_address; -} diff --git a/src/server/e-ews-item.c.meeting-with-attachment b/src/server/e-ews-item.c.meeting-with-attachment deleted file mode 100644 index c919974..0000000 --- a/src/server/e-ews-item.c.meeting-with-attachment +++ /dev/null @@ -1,2697 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Authors : - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - * 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. - * - * 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -#include "evolution-ews-config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "e-ews-item.h" -#include "e-ews-item-change.h" - -G_DEFINE_TYPE (EEwsItem, e_ews_item, G_TYPE_OBJECT) - -struct _EEwsContactFields { - gchar *fileas; - gchar *display_name; - EwsCompleteName *complete_name; - - GHashTable *email_addresses; - GHashTable *physical_addresses; - GHashTable *phone_numbers; - GHashTable *im_addresses; - - gchar *company_name; - gchar *department; - gchar *job_title; - gchar *assistant_name; - gchar *manager; - gchar *office_location; - - gchar *business_homepage; - - time_t birthday; - time_t wedding_anniversary; - - gchar *profession; - gchar *spouse_name; - gchar *culture; - gchar *surname; - gchar *givenname; - gchar *middlename; - gchar *notes; -}; - -struct _EEwsTaskFields { - gchar *percent_complete; - gchar *status; - gchar *body; - gchar *sensitivity; - gchar *owner; - gchar *delegator; - time_t due_date; - time_t start_date; - time_t complete_date; - gboolean has_due_date; - gboolean has_start_date; - gboolean has_complete_date; -}; - -struct _EEwsItemPrivate { - EwsId *attachment_id; - EEwsItemType item_type; - GError *error; - - /* MAPI properties */ - /* The Exchange server is so fundamentally misdesigned that it doesn't expose - * certain information in a coherent way; the \Answered, and \Deleted message - * flags don't even seem to work properly. It looks like the only way to work - * it out is from the PidTagIconIndex field. Quite what the hell an *icon* - * selector is doing in the database, I have absolutely no fucking idea; a - * database is supposed to represent the *data*, not do bizarre things that - * live in the client. But that's typical Exchange brain damage for you... */ - guint32 mapi_icon_index; /* http://msdn.microsoft.com/en-us/library/cc815472.aspx */ - guint32 mapi_last_verb_executed; /* http://msdn.microsoft.com/en-us/library/cc841968.aspx */ - guint32 mapi_message_status; /* http://msdn.microsoft.com/en-us/library/cc839915.aspx */ - guint32 mapi_message_flags; /* http://msdn.microsoft.com/en-us/library/cc839733.aspx */ - - GHashTable *mapi_extended_tags; /* simple tag->string_value */ - GHashTable *mapi_extended_sets; /* setid-> [ tag->string_value ] */ - - /* properties */ - EwsId *item_id; - gchar *subject; - gchar *mime_content; - gchar *body; - - gchar *date_header; - time_t date_received; - time_t date_sent; - time_t date_created; - time_t last_modified_time; - - gsize size; - gchar *msg_id; - gchar *in_replyto; - gchar *references; - gboolean has_attachments; - gboolean is_read; - EwsImportance importance; - - gchar *uid; - gchar *timezone; - gchar *start_timezone; - gchar *end_timezone; - gchar *contact_photo_id; - gchar *iana_start_time_zone; - gchar *iana_end_time_zone; - - GSList *to_recipients; - GSList *cc_recipients; - GSList *bcc_recipients; - - EwsMailbox *from; - EwsMailbox *sender; - - gboolean is_meeting; - gboolean is_response_requested; - GSList *modified_occurrences; - GSList *attachments_ids; - gchar *my_response_type; - GSList *attendees; - - EwsId *calendar_item_accept_id; - - /* the evolution labels are implemented as exchange - * Categories. These appear in the message headers as - * Keywords: and are set and extracted from the EWS server as - * which is a string array valued XML element */ - GSList *categories; - - struct _EEwsContactFields *contact_fields; - struct _EEwsTaskFields *task_fields; -}; - -static void ews_item_free_attendee (EwsAttendee *attendee); -static void ews_free_contact_fields (struct _EEwsContactFields *con_fields); - -typedef gpointer (* EwsGetValFunc) (ESoapParameter *param); - -static void -e_ews_item_dispose (GObject *object) -{ - EEwsItem *item = (EEwsItem *) object; - EEwsItemPrivate *priv; - - g_return_if_fail (E_IS_EWS_ITEM (item)); - - priv = item->priv; - - g_clear_error (&priv->error); - - if (priv->mapi_extended_sets) { - g_hash_table_destroy (priv->mapi_extended_sets); - priv->mapi_extended_sets = NULL; - } - - if (priv->mapi_extended_tags) { - g_hash_table_destroy (priv->mapi_extended_tags); - priv->mapi_extended_tags = NULL; - } - - if (priv->item_id) { - g_free (priv->item_id->id); - g_free (priv->item_id->change_key); - g_free (priv->item_id); - priv->item_id = NULL; - } - - if (priv->attachment_id) { - g_free (priv->attachment_id->id); - g_free (priv->attachment_id->change_key); - g_free (priv->attachment_id); - priv->attachment_id = NULL; - } - - g_clear_pointer (&priv->mime_content, g_free); - g_clear_pointer (&priv->body, g_free); - g_clear_pointer (&priv->subject, g_free); - g_clear_pointer (&priv->msg_id, g_free); - g_clear_pointer (&priv->uid, g_free); - g_clear_pointer (&priv->in_replyto, g_free); - g_clear_pointer (&priv->references, g_free); - g_clear_pointer (&priv->date_header, g_free); - g_clear_pointer (&priv->timezone, g_free); - g_clear_pointer (&priv->start_timezone, g_free); - g_clear_pointer (&priv->end_timezone, g_free); - g_clear_pointer (&priv->contact_photo_id, g_free); - g_clear_pointer (&priv->iana_start_time_zone, g_free); - g_clear_pointer (&priv->iana_end_time_zone, g_free); - - g_slist_free_full (priv->to_recipients, (GDestroyNotify) e_ews_mailbox_free); - priv->to_recipients = NULL; - - g_slist_free_full (priv->cc_recipients, (GDestroyNotify) e_ews_mailbox_free); - priv->cc_recipients = NULL; - - g_slist_free_full (priv->bcc_recipients, (GDestroyNotify) e_ews_mailbox_free); - priv->bcc_recipients = NULL; - - g_slist_free_full (priv->modified_occurrences, g_free); - priv->modified_occurrences = NULL; - - g_slist_free_full (priv->attachments_ids, g_free); - priv->attachments_ids = NULL; - - g_clear_pointer (&priv->my_response_type, g_free); - - g_slist_free_full (priv->attendees, (GDestroyNotify) ews_item_free_attendee); - priv->attendees = NULL; - - if (priv->calendar_item_accept_id) { - g_free (priv->calendar_item_accept_id->id); - g_free (priv->calendar_item_accept_id->change_key); - g_free (priv->calendar_item_accept_id); - priv->calendar_item_accept_id = NULL; - } - - e_ews_mailbox_free (priv->sender); - e_ews_mailbox_free (priv->from); - - if (priv->item_type == E_EWS_ITEM_TYPE_CONTACT) - ews_free_contact_fields (priv->contact_fields); - - if (priv->task_fields) { - g_free (priv->task_fields->percent_complete); - priv->task_fields->percent_complete = NULL; - g_free (priv->task_fields->status); - priv->task_fields->status = NULL; - g_free (priv->task_fields->body); - priv->task_fields->body = NULL; - g_free (priv->task_fields->sensitivity); - priv->task_fields->sensitivity = NULL; - g_free (priv->task_fields->owner); - priv->task_fields->owner = NULL; - g_free (priv->task_fields); - } - - g_slist_free_full (priv->categories, g_free); - priv->categories = NULL; - - G_OBJECT_CLASS (e_ews_item_parent_class)->dispose (object); -} - -static void -e_ews_item_class_init (EEwsItemClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - g_type_class_add_private (klass, sizeof (EEwsItemPrivate)); - - object_class->dispose = e_ews_item_dispose; -} - -static void -e_ews_item_init (EEwsItem *item) -{ - item->priv = G_TYPE_INSTANCE_GET_PRIVATE (item, E_TYPE_EWS_ITEM, EEwsItemPrivate); - - item->priv->item_type = E_EWS_ITEM_TYPE_UNKNOWN; - item->priv->is_meeting = FALSE; - item->priv->is_response_requested = FALSE; - - item->priv->mapi_extended_tags = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); - item->priv->mapi_extended_sets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy); -} - -static void -ews_free_contact_fields (struct _EEwsContactFields *con_fields) -{ - if (con_fields) { - if (con_fields->complete_name) { - EwsCompleteName *cn = con_fields->complete_name; - - g_free (cn->title); - g_free (cn->first_name); - g_free (cn->middle_name); - g_free (cn->last_name); - g_free (cn->suffix); - g_free (cn->initials); - g_free (cn->full_name); - g_free (cn->nick_name); - g_free (cn->yomi_first_name); - g_free (cn->yomi_last_name); - } - - if (con_fields->email_addresses) - g_hash_table_destroy (con_fields->email_addresses); - - if (con_fields->physical_addresses) - g_hash_table_destroy (con_fields->physical_addresses); - - if (con_fields->phone_numbers) - g_hash_table_destroy (con_fields->phone_numbers); - - if (con_fields->im_addresses) - g_hash_table_destroy (con_fields->im_addresses); - - g_free (con_fields->display_name); - g_free (con_fields->fileas); - g_free (con_fields->company_name); - g_free (con_fields->department); - g_free (con_fields->job_title); - g_free (con_fields->assistant_name); - g_free (con_fields->manager); - g_free (con_fields->office_location); - g_free (con_fields->business_homepage); - g_free (con_fields->profession); - g_free (con_fields->spouse_name); - g_free (con_fields->culture); - g_free (con_fields->surname); - g_free (con_fields->givenname); - g_free (con_fields->middlename); - g_free (con_fields->notes); - g_free (con_fields); - } -} - -static void -ews_item_free_attendee (EwsAttendee *attendee) -{ - if (attendee) { - e_ews_mailbox_free (attendee->mailbox); - g_free (attendee->responsetype); - g_free (attendee); - } -} - -static time_t -ews_item_parse_date (const gchar *dtstring) -{ - time_t t = 0; - GTimeVal t_val; - - g_return_val_if_fail (dtstring != NULL, 0); - - if (g_time_val_from_iso8601 (dtstring, &t_val)) { - t = (time_t) t_val.tv_sec; - } else if (strlen (dtstring) == 8) { - /* It might be a date value */ - GDate date; - struct tm tt; - guint16 year; - guint month; - guint8 day; - - g_date_clear (&date, 1); -#define digit_at(x,y) (x[y] - '0') - year = digit_at (dtstring, 0) * 1000 - + digit_at (dtstring, 1) * 100 - + digit_at (dtstring, 2) * 10 - + digit_at (dtstring, 3); - month = digit_at (dtstring, 4) * 10 + digit_at (dtstring, 5); - day = digit_at (dtstring, 6) * 10 + digit_at (dtstring, 7); - - g_date_set_year (&date, year); - g_date_set_month (&date, month); - g_date_set_day (&date, day); - - g_date_to_struct_tm (&date, &tt); - t = mktime (&tt); - - } else - g_warning ("Could not parse the string \n"); - - return t; -} - -static void -parse_extended_property (EEwsItemPrivate *priv, - ESoapParameter *param) -{ - EEwsMessageDataType data_type; - ESoapParameter *subparam; - gchar *str, *setid, *name, *value; - guint32 tag = 0; - - subparam = e_soap_parameter_get_first_child_by_name (param, "ExtendedFieldURI"); - if (!subparam) - return; - - str = e_soap_parameter_get_property (subparam, "PropertyType"); - if (!str) - return; - - /* We only handle some MAPI properties for now... */ - if (g_ascii_strcasecmp (str, "Boolean") == 0) { - data_type = E_EWS_MESSAGE_DATA_TYPE_BOOLEAN; - } else if (g_ascii_strcasecmp (str, "Integer") == 0) { - data_type = E_EWS_MESSAGE_DATA_TYPE_INT; - } else if (g_ascii_strcasecmp (str, "Double") == 0) { - data_type = E_EWS_MESSAGE_DATA_TYPE_DOUBLE; - } else if (g_ascii_strcasecmp (str, "String") == 0) { - data_type = E_EWS_MESSAGE_DATA_TYPE_STRING; - } else if (g_ascii_strcasecmp (str, "SystemTime") == 0) { - data_type = E_EWS_MESSAGE_DATA_TYPE_TIME; - } else { - g_free (str); - return; - } - g_free (str); - - name = e_soap_parameter_get_property (subparam, "PropertyName"); - if (!name) { - str = e_soap_parameter_get_property (subparam, "PropertyTag"); - if (!str) { - str = e_soap_parameter_get_property (subparam, "PropertyId"); - if (!str) - return; - } - - tag = strtol (str, NULL, 0); - g_free (str); - } - - setid = e_soap_parameter_get_property (subparam, "DistinguishedPropertySetId"); - - subparam = e_soap_parameter_get_first_child_by_name (param, "Value"); - if (!subparam) { - g_free (setid); - g_free (name); - return; - } - - value = e_soap_parameter_get_string_value (subparam); - if (!value) { - g_free (setid); - g_free (name); - return; - } - - if (data_type == E_EWS_MESSAGE_DATA_TYPE_INT) { - guint32 num_value; - - num_value = strtol (value, NULL, 0); - - switch (tag) { - case 0x01080: /* PidTagIconIndex */ - priv->mapi_icon_index = num_value; - break; - - case 0x1081: /* PidTagLastVerbExecuted */ - priv->mapi_last_verb_executed = num_value; - break; - - case 0x0e07: /* PidTagMessageFlags */ - priv->mapi_message_flags = num_value; - break; - - case 0x0e17: /* PidTagMessageStatus */ - priv->mapi_message_status = num_value; - break; - } - } - - if (setid) { - if (g_strcmp0 (name, "EvolutionEWSStartTimeZone") == 0) { - priv->iana_start_time_zone = g_strdup (value); - } else if (g_strcmp0 (name, "EvolutionEWSEndTimeZone") == 0) { - priv->iana_end_time_zone = g_strdup (value); - } else { - GHashTable *set_hash = g_hash_table_lookup (priv->mapi_extended_sets, setid); - - if (!set_hash) { - set_hash = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_free); - g_hash_table_insert (priv->mapi_extended_sets, setid, set_hash); - setid = NULL; - } - - g_hash_table_insert (set_hash, GUINT_TO_POINTER (tag), g_strdup (value)); - } - } else if (tag != 0) { - g_hash_table_insert (priv->mapi_extended_tags, GUINT_TO_POINTER (tag), g_strdup (value)); - } - - g_free (setid); - g_free (value); - g_free (name); -} - -static void -parse_categories (EEwsItemPrivate *priv, - ESoapParameter *param) -{ - gchar *value; - ESoapParameter *subparam; - - /* release all the old data (if any) */ - g_slist_free_full (priv->categories, g_free); - priv->categories = NULL; - - /* categories are an array of */ - for (subparam = e_soap_parameter_get_first_child (param); - subparam != NULL; - subparam = e_soap_parameter_get_next_child (subparam)) { - value = e_soap_parameter_get_string_value (subparam); - - priv->categories = g_slist_append (priv->categories, value); - } - -} - -static EwsImportance -parse_importance (ESoapParameter *param) -{ - gchar *value; - EwsImportance importance = EWS_ITEM_LOW; - - value = e_soap_parameter_get_string_value (param); - - if (!g_ascii_strcasecmp (value, "Normal")) - importance = EWS_ITEM_NORMAL; - else if (!g_ascii_strcasecmp (value, "High") ) - importance = EWS_ITEM_HIGH; - - g_free (value); - return importance; -} - -static void -process_modified_occurrences (EEwsItemPrivate *priv, - ESoapParameter *param) -{ - ESoapParameter *subparam, *subparam1; - gchar *modified_occurrence_id; - - for (subparam = e_soap_parameter_get_first_child (param); subparam != NULL; subparam = e_soap_parameter_get_next_child (subparam)) { - - subparam1 = e_soap_parameter_get_first_child_by_name (subparam, "ItemId"); - modified_occurrence_id = e_soap_parameter_get_property (subparam1, "Id"); - priv->modified_occurrences = g_slist_append (priv->modified_occurrences, modified_occurrence_id); - } - - return; -} - -static void -process_attachments_list (EEwsItemPrivate *priv, - ESoapParameter *param) -{ - ESoapParameter *subparam, *subparam1; - - GSList *ids = NULL; - - for (subparam = e_soap_parameter_get_first_child (param); subparam != NULL; subparam = e_soap_parameter_get_next_child (subparam)) { - gchar *id; - - subparam1 = e_soap_parameter_get_first_child_by_name (subparam, "AttachmentId"); - id = e_soap_parameter_get_property (subparam1, "Id"); - - subparam1 = e_soap_parameter_get_first_child_by_name (subparam, "IsContactPhoto"); - if (subparam1) { - gchar *value = e_soap_parameter_get_string_value (subparam1); - if (g_strcmp0 (value, "true") == 0) { - priv->contact_photo_id = id; - g_free (value); - continue; - } - g_free (value); - } - - ids = g_slist_append (ids, id); - } - - priv->attachments_ids = ids; - return; -} - -static void -process_attendees (EEwsItemPrivate *priv, - ESoapParameter *param, - const gchar *type) -{ - ESoapParameter *subparam, *subparam1; - EwsAttendee *attendee; - - for (subparam = e_soap_parameter_get_first_child (param); subparam != NULL; subparam = e_soap_parameter_get_next_child (subparam)) { - EwsMailbox *mailbox = NULL; - - subparam1 = e_soap_parameter_get_first_child_by_name (subparam, "Mailbox"); - mailbox = e_ews_item_mailbox_from_soap_param (subparam1); - /* Ignore attendee if mailbox is not valid, - * for instance, ppl that does not exists any more */ - if (!mailbox) - continue; - - attendee = g_new0 (EwsAttendee, 1); - - attendee->mailbox = mailbox; - - subparam1 = e_soap_parameter_get_first_child_by_name (subparam, "ResponseType"); - attendee->responsetype = subparam1 ? e_soap_parameter_get_string_value (subparam1) : NULL; - - attendee->attendeetype = (gchar *) type; - - priv->attendees = g_slist_append (priv->attendees, attendee); - } - - return; -} - -static void -parse_complete_name (struct _EEwsContactFields *con_fields, - ESoapParameter *param) -{ - ESoapParameter *subparam; - EwsCompleteName *cn; - - cn = g_new0 (EwsCompleteName, 1); - - subparam = e_soap_parameter_get_first_child_by_name (param, "Title"); - if (subparam) - cn->title = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "FirstName"); - if (subparam) - cn->first_name = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "MiddleName"); - if (subparam) - cn->middle_name = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "LastName"); - if (subparam) - cn->last_name = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "Suffix"); - if (subparam) - cn->suffix = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "Initials"); - if (subparam) - cn->initials = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "FullName"); - if (subparam) - cn->full_name = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "Nickname"); - if (subparam) - cn->nick_name = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "YomiFirstName"); - if (subparam) - cn->yomi_first_name = e_soap_parameter_get_string_value (subparam); - subparam = e_soap_parameter_get_first_child_by_name (param, "YomiLastName"); - if (subparam) - cn->yomi_last_name = e_soap_parameter_get_string_value (subparam); - - con_fields->complete_name = cn; -} - -static gpointer -ews_get_physical_address (ESoapParameter *param) -{ - ESoapParameter *subparam; - EwsAddress *address; - - address = g_new0 (EwsAddress, 1); - - subparam = e_soap_parameter_get_first_child_by_name (param, "Street"); - if (subparam) - address->street = e_soap_parameter_get_string_value (subparam); - - subparam = e_soap_parameter_get_first_child_by_name (param, "City"); - if (subparam) - address->city = e_soap_parameter_get_string_value (subparam); - - subparam = e_soap_parameter_get_first_child_by_name (param, "State"); - if (subparam) - address->state = e_soap_parameter_get_string_value (subparam); - - subparam = e_soap_parameter_get_first_child_by_name (param, "CountryOrRegion"); - if (subparam) - address->country = e_soap_parameter_get_string_value (subparam); - - subparam = e_soap_parameter_get_first_child_by_name (param, "PostalCode"); - if (subparam) - address->postal_code = e_soap_parameter_get_string_value (subparam); - - return address; -} - -static void -parse_entries (GHashTable *hash_table, - ESoapParameter *param, - EwsGetValFunc get_val_func) -{ - ESoapParameter *subparam; - - for (subparam = e_soap_parameter_get_first_child_by_name (param, "Entry"); - subparam != NULL; - subparam = e_soap_parameter_get_next_child_by_name (subparam, "Entry")) { - gchar *key; - gpointer value; - - key = e_soap_parameter_get_property (subparam, "Key"); - value = get_val_func (subparam); - - if (value) - g_hash_table_insert (hash_table, key, value); - else - g_free (key); - } -} - -static void -ews_free_physical_address (gpointer value) -{ - EwsAddress *address = (EwsAddress *) value; - - if (address) { - g_free (address->street); - g_free (address->city); - g_free (address->state); - g_free (address->country); - g_free (address->postal_code); - g_free (address); - } -} - -static void -parse_contact_field (EEwsItem *item, - const gchar *name, - ESoapParameter *subparam) -{ - EEwsItemPrivate *priv = item->priv; - gchar *value = NULL; - - if (!g_ascii_strcasecmp (name, "Culture")) { - priv->contact_fields->culture = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "DisplayName")) { - priv->contact_fields->display_name = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "FileAs")) { - priv->contact_fields->fileas = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "CompleteName")) { - parse_complete_name (priv->contact_fields, subparam); - } else if (!g_ascii_strcasecmp (name, "CompanyName")) { - priv->contact_fields->company_name = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "EmailAddresses")) { - priv->contact_fields->email_addresses = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - parse_entries (priv->contact_fields->email_addresses, subparam, (EwsGetValFunc) e_soap_parameter_get_string_value); - } else if (!g_ascii_strcasecmp (name, "PhysicalAddresses")) { - priv->contact_fields->physical_addresses = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, ews_free_physical_address); - parse_entries (priv->contact_fields->physical_addresses, subparam, ews_get_physical_address); - } else if (!g_ascii_strcasecmp (name, "PhoneNumbers")) { - priv->contact_fields->phone_numbers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - parse_entries (priv->contact_fields->phone_numbers, subparam, (EwsGetValFunc) e_soap_parameter_get_string_value); - } else if (!g_ascii_strcasecmp (name, "AssistantName")) { - priv->contact_fields->assistant_name = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Birthday")) { - value = e_soap_parameter_get_string_value (subparam); - priv->contact_fields->birthday = ews_item_parse_date (value); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "BusinessHomePage")) { - priv->contact_fields->business_homepage = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Department")) { - priv->contact_fields->department = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "ImAddresses")) { - priv->contact_fields->im_addresses = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); - parse_entries (priv->contact_fields->im_addresses, subparam, (EwsGetValFunc) e_soap_parameter_get_string_value); - } else if (!g_ascii_strcasecmp (name, "JobTitle")) { - priv->contact_fields->job_title = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Manager")) { - priv->contact_fields->manager = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "OfficeLocation")) { - priv->contact_fields->office_location = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Profession")) { - priv->contact_fields->profession = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "SpouseName")) { - priv->contact_fields->spouse_name = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Surname")) { - priv->contact_fields->surname = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "GivenName")) { - priv->contact_fields->givenname = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "MiddleName")) { - priv->contact_fields->middlename = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "WeddingAnniversary")) { - value = e_soap_parameter_get_string_value (subparam); - priv->contact_fields->wedding_anniversary = ews_item_parse_date (value); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "Body")) { - /* - * For Exchange versions >= 2010_SP2 Notes property can be get - * directly from contacts:Notes. But for backward compatibility - * with old servers (< 2010_SP2) we prefer use item:Body. - */ - priv->contact_fields->notes = e_soap_parameter_get_string_value (subparam); - } -} - -static gchar * -strip_html_tags (const gchar *html_text) -{ - gssize haystack_len = strlen (html_text); - gchar *plain_text = g_malloc (haystack_len + 1); - gchar *start = g_strstr_len (html_text, haystack_len, "= end) - break; - - if (*i != ';') - i = from; - else - continue; - } - - if (*i == '<') { - while (i < end && *i != '>') - i++; - - if (i >= end) - break; - } else { - *j = *i; - j++; - } - } - - *j = '\0'; - - return plain_text; -} - -static void -parse_task_field (EEwsItem *item, - const gchar *name, - ESoapParameter *subparam) -{ - EEwsItemPrivate *priv = item->priv; - gchar *value = NULL; - - if (!g_ascii_strcasecmp (name, "Status")) { - priv->task_fields->status = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "PercentComplete")) { - priv->task_fields->percent_complete = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "DueDate")) { - value = e_soap_parameter_get_string_value (subparam); - priv->task_fields->due_date = ews_item_parse_date (value); - g_free (value); - priv->task_fields->has_due_date = TRUE; - } else if (!g_ascii_strcasecmp (name, "StartDate")) { - value = e_soap_parameter_get_string_value (subparam); - priv->task_fields->start_date = ews_item_parse_date (value); - g_free (value); - priv->task_fields->has_start_date = TRUE; - } - else if (!g_ascii_strcasecmp (name, "CompleteDate")) { - value = e_soap_parameter_get_string_value (subparam); - priv->task_fields->complete_date = ews_item_parse_date (value); - g_free (value); - priv->task_fields->has_complete_date = TRUE; - } else if (!g_ascii_strcasecmp (name, "Sensitivity")) { - priv->task_fields->sensitivity = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Body")) { - if (!g_ascii_strcasecmp (e_soap_parameter_get_property (subparam, "BodyType"),"HTML")) { - value = e_soap_parameter_get_string_value (subparam); - priv->task_fields->body = strip_html_tags (value); - g_free (value); - } else - priv->task_fields->body = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Owner")) { - priv->task_fields->owner = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Delegator")) { - priv->task_fields->delegator = e_soap_parameter_get_string_value (subparam); - if (!g_ascii_strcasecmp (priv->task_fields->delegator, "")) { - g_free (priv->task_fields->delegator); - priv->task_fields->delegator = NULL; - } - } -} - -static gboolean -e_ews_item_set_from_soap_parameter (EEwsItem *item, - ESoapParameter *param) -{ - EEwsItemPrivate *priv = item->priv; - ESoapParameter *subparam, *node = NULL, *attach_id; - const gchar *name; - - g_return_val_if_fail (param != NULL, FALSE); - - name = e_soap_parameter_get_name (param); - - /*We get two types of response for items from server like below from two apis - * Syncfolderitems and Finditem - * - * - * - * - * - * - * - * ... - * - * - * ... - * - * So check param is the node we want to use, by comparing name or is it child of the param */ - - if (!g_ascii_strcasecmp (name, "Message") || (node = e_soap_parameter_get_first_child_by_name (param, "Message"))) { - priv->item_type = E_EWS_ITEM_TYPE_MESSAGE; - subparam = e_soap_parameter_get_first_child_by_name (node ? node : param, "ItemClass"); - if (subparam) { - gchar *folder_class = e_soap_parameter_get_string_value (subparam); - - if (g_strcmp0 (folder_class, "IPM.StickyNote") == 0) { - priv->item_type = E_EWS_ITEM_TYPE_MEMO; - priv->task_fields = g_new0 (struct _EEwsTaskFields, 1); - priv->task_fields->has_due_date = FALSE; - priv->task_fields->has_start_date = FALSE; - priv->task_fields->has_complete_date = FALSE; - } - - g_free (folder_class); - } - } else if (!g_ascii_strcasecmp (name, "PostItem") || (node = e_soap_parameter_get_first_child_by_name (param, "PostItem"))) - priv->item_type = E_EWS_ITEM_TYPE_POST_ITEM; - else if (!g_ascii_strcasecmp (name, "CalendarItem") || (node = e_soap_parameter_get_first_child_by_name (param, "CalendarItem"))) - priv->item_type = E_EWS_ITEM_TYPE_EVENT; - else if (!g_ascii_strcasecmp (name, "Contact") || (node = e_soap_parameter_get_first_child_by_name (param, "Contact"))) { - priv->item_type = E_EWS_ITEM_TYPE_CONTACT; - priv->contact_fields = g_new0 (struct _EEwsContactFields, 1); - } else if (!g_ascii_strcasecmp (name, "DistributionList") || (node = e_soap_parameter_get_first_child_by_name (param, "DistributionList"))) - priv->item_type = E_EWS_ITEM_TYPE_GROUP; - else if (!g_ascii_strcasecmp (name, "MeetingMessage") || (node = e_soap_parameter_get_first_child_by_name (param, "MeetingMessage"))) - priv->item_type = E_EWS_ITEM_TYPE_MEETING_MESSAGE; - else if (!g_ascii_strcasecmp (name, "MeetingRequest") || (node = e_soap_parameter_get_first_child_by_name (param, "MeetingRequest"))) - priv->item_type = E_EWS_ITEM_TYPE_MEETING_REQUEST; - else if (!g_ascii_strcasecmp (name, "MeetingResponse") || (node = e_soap_parameter_get_first_child_by_name (param, "MeetingResponse"))) - priv->item_type = E_EWS_ITEM_TYPE_MEETING_RESPONSE; - else if (!g_ascii_strcasecmp (name, "MeetingCancellation") || (node = e_soap_parameter_get_first_child_by_name (param, "MeetingCancellation"))) - priv->item_type = E_EWS_ITEM_TYPE_MEETING_CANCELLATION; - else if (!g_ascii_strcasecmp (name, "Task") || (node = e_soap_parameter_get_first_child_by_name (param, "Task"))) { - priv->item_type = E_EWS_ITEM_TYPE_TASK; - priv->task_fields = g_new0 (struct _EEwsTaskFields, 1); - priv->task_fields->has_due_date = FALSE; - priv->task_fields->has_start_date = FALSE; - priv->task_fields->has_complete_date = FALSE; - } else if (!g_ascii_strcasecmp (name, "Item") || (node = e_soap_parameter_get_first_child_by_name (param, "Item"))) - priv->item_type = E_EWS_ITEM_TYPE_GENERIC_ITEM; - else if ((node = e_soap_parameter_get_first_child_by_name (param, "AttachmentId"))) { - priv->attachment_id = g_new0 (EwsId, 1); - priv->attachment_id->id = e_soap_parameter_get_property (node, "Id"); - priv->attachment_id->change_key = e_soap_parameter_get_property (node, "ChangeKey"); - } else if ((node = e_soap_parameter_get_first_child_by_name (param, "ItemId"))) { - /*Spesial case when we are facing during sync folders*/ - priv->item_id = g_new0 (EwsId, 1); - priv->item_id->id = e_soap_parameter_get_property (node, "Id"); - priv->item_id->change_key = e_soap_parameter_get_property (node, "ChangeKey"); - return TRUE; - } else { - g_warning ("Unable to find the Item type \n"); - return FALSE; - } - - attach_id = e_soap_parameter_get_first_child_by_name (param, "AttachmentId"); - if (attach_id) { - priv->attachment_id = g_new0 (EwsId, 1); - priv->attachment_id->id = e_soap_parameter_get_property (attach_id, "Id"); - priv->attachment_id->change_key = e_soap_parameter_get_property (attach_id, "ChangeKey"); - } - - if (!node) - node = param; - - for (subparam = e_soap_parameter_get_first_child (node); - subparam != NULL; - subparam = e_soap_parameter_get_next_child (subparam)) { - ESoapParameter *subparam1; - const gchar *name; - gchar *value = NULL; - - name = e_soap_parameter_get_name (subparam); - - /* The order is maintained according to the order in soap response */ - if (!g_ascii_strcasecmp (name, "MimeContent")) { - gchar *charset; - guchar *data; - gsize data_len = 0; - - value = e_soap_parameter_get_string_value (subparam); - data = g_base64_decode (value, &data_len); - if (!data || !data_len) { - g_free (value); - g_free (data); - return FALSE; - } - - charset = e_soap_parameter_get_property (subparam, "CharacterSet"); - if (g_strcmp0 (charset, "UTF-8") == 0 && - !g_utf8_validate ((const gchar *) data, data_len, NULL)) { - gchar *tmp; - - tmp = e_util_utf8_data_make_valid ((const gchar *) data, data_len); - if (tmp) { - g_free (data); - data = (guchar *) tmp; - } - } - g_free (charset); - - priv->mime_content = (gchar *) data; - - g_free (value); - } else if (!g_ascii_strcasecmp (name, "ItemId")) { - priv->item_id = g_new0 (EwsId, 1); - priv->item_id->id = e_soap_parameter_get_property (subparam, "Id"); - priv->item_id->change_key = e_soap_parameter_get_property (subparam, "ChangeKey"); - } else if (!g_ascii_strcasecmp (name, "Subject")) { - priv->subject = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "InternetMessageHeaders")) { - for (subparam1 = e_soap_parameter_get_first_child_by_name (subparam, "InternetMessageHeader"); - subparam1; - subparam1 = e_soap_parameter_get_next_child (subparam1)) { - gchar *str = e_soap_parameter_get_property (subparam1, "HeaderName"); - - if (g_strcmp0 (str, "Date") == 0) { - priv->date_header = e_soap_parameter_get_string_value (subparam1); - g_free (str); - break; - } - - g_free (str); - } - } else if (!g_ascii_strcasecmp (name, "DateTimeReceived")) { - value = e_soap_parameter_get_string_value (subparam); - priv->date_received = ews_item_parse_date (value); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "Size")) { - priv->size = e_soap_parameter_get_int_value (subparam); - } else if (!g_ascii_strcasecmp (name, "Categories")) { - parse_categories (priv, subparam); - } else if (!g_ascii_strcasecmp (name, "Importance")) { - priv->importance = parse_importance (subparam); - } else if (!g_ascii_strcasecmp (name, "InReplyTo")) { - priv->in_replyto = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "DateTimeSent")) { - value = e_soap_parameter_get_string_value (subparam); - priv->date_sent = ews_item_parse_date (value); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "DateTimeCreated")) { - value = e_soap_parameter_get_string_value (subparam); - priv->date_created = ews_item_parse_date (value); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "LastModifiedTime")) { - value = e_soap_parameter_get_string_value (subparam); - priv->last_modified_time = ews_item_parse_date (value); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "HasAttachments")) { - value = e_soap_parameter_get_string_value (subparam); - priv->has_attachments = (!g_ascii_strcasecmp (value, "true")); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "Attachments")) { - process_attachments_list (priv, subparam); - } else if (priv->item_type == E_EWS_ITEM_TYPE_CONTACT) - parse_contact_field (item, name, subparam); - /* fields below are not relevant for contacts, so skip them */ - else if (!g_ascii_strcasecmp (name, "Sender")) { - subparam1 = e_soap_parameter_get_first_child_by_name (subparam, "Mailbox"); - priv->sender = e_ews_item_mailbox_from_soap_param (subparam1); - } else if (!g_ascii_strcasecmp (name, "ToRecipients")) { - GSList *list = NULL; - for (subparam1 = e_soap_parameter_get_first_child (subparam); - subparam1 != NULL; - subparam1 = e_soap_parameter_get_next_child (subparam1)) { - EwsMailbox *mb = e_ews_item_mailbox_from_soap_param (subparam1); - list = g_slist_append (list, mb); - } - priv->to_recipients = list; - } else if (!g_ascii_strcasecmp (name, "CcRecipients")) { - GSList *list = NULL; - for (subparam1 = e_soap_parameter_get_first_child (subparam); - subparam1 != NULL; - subparam1 = e_soap_parameter_get_next_child (subparam1)) { - EwsMailbox *mb = e_ews_item_mailbox_from_soap_param (subparam1); - list = g_slist_append (list, mb); - } - priv->cc_recipients = list; - } else if (!g_ascii_strcasecmp (name, "BccRecipients")) { - GSList *list = NULL; - for (subparam1 = e_soap_parameter_get_first_child (subparam); - subparam1 != NULL; - subparam1 = e_soap_parameter_get_next_child (subparam1)) { - EwsMailbox *mb = e_ews_item_mailbox_from_soap_param (subparam1); - list = g_slist_append (list, mb); - } - priv->bcc_recipients = list; - } else if (!g_ascii_strcasecmp (name, "From")) { - subparam1 = e_soap_parameter_get_first_child_by_name (subparam, "Mailbox"); - priv->from = e_ews_item_mailbox_from_soap_param (subparam1); - } else if (!g_ascii_strcasecmp (name, "InternetMessageId")) { - priv->msg_id = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "UID")) { - priv->uid = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "IsRead")) { - value = e_soap_parameter_get_string_value (subparam); - priv->is_read = (!g_ascii_strcasecmp (value, "true")); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "TimeZone")) { - priv->timezone = e_soap_parameter_get_string_value (subparam); - } else if (priv->item_type == E_EWS_ITEM_TYPE_TASK || priv->item_type == E_EWS_ITEM_TYPE_MEMO) { - parse_task_field (item, name, subparam); - /* fields below are not relevant for task, so skip them */ - } else if (!g_ascii_strcasecmp (name, "References")) { - priv->references = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "ExtendedProperty")) { - parse_extended_property (priv, subparam); - } else if (!g_ascii_strcasecmp (name, "ModifiedOccurrences")) { - process_modified_occurrences (priv, subparam); - } else if (!g_ascii_strcasecmp (name, "IsMeeting")) { - value = e_soap_parameter_get_string_value (subparam); - priv->is_meeting = (!g_ascii_strcasecmp (value, "true")); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "IsResponseRequested")) { - value = e_soap_parameter_get_string_value (subparam); - priv->is_response_requested = (!g_ascii_strcasecmp (value, "true")); - g_free (value); - } else if (!g_ascii_strcasecmp (name, "MyResponseType")) { - g_free (priv->my_response_type); - priv->my_response_type = e_soap_parameter_get_string_value (subparam); - } else if (!g_ascii_strcasecmp (name, "RequiredAttendees")) { - process_attendees (priv, subparam, "Required"); - } else if (!g_ascii_strcasecmp (name, "OptionalAttendees")) { - process_attendees (priv, subparam, "Optional"); - } else if (!g_ascii_strcasecmp (name, "Resources")) { - process_attendees (priv, subparam, "Resource"); - } else if (!g_ascii_strcasecmp (name, "AssociatedCalendarItemId")) { - priv->calendar_item_accept_id = g_new0 (EwsId, 1); - priv->calendar_item_accept_id->id = e_soap_parameter_get_property (subparam, "Id"); - priv->calendar_item_accept_id->change_key = e_soap_parameter_get_property (subparam, "ChangeKey"); - } else if (!g_ascii_strcasecmp (name, "StartTimeZone")) { - priv->start_timezone = e_soap_parameter_get_property (subparam, "Id"); - } else if (!g_ascii_strcasecmp (name, "EndTimeZone")) { - priv->end_timezone = e_soap_parameter_get_property (subparam, "Id"); - } else if (!g_ascii_strcasecmp (name, "Body")) { - priv->body = e_soap_parameter_get_string_value (subparam); - } - } - - return TRUE; -} - -EEwsItem * -e_ews_item_new_from_soap_parameter (ESoapParameter *param) -{ - EEwsItem *item; - - g_return_val_if_fail (param != NULL, NULL); - - item = g_object_new (E_TYPE_EWS_ITEM, NULL); - if (!e_ews_item_set_from_soap_parameter (item, param)) { - g_object_unref (item); - return NULL; - } - - return item; -} - -EEwsItem * -e_ews_item_new_from_error (const GError *error) -{ - EEwsItem *item; - - g_return_val_if_fail (error != NULL, NULL); - - item = g_object_new (E_TYPE_EWS_ITEM, NULL); - e_ews_item_set_error (item, error); - - return item; -} - -EEwsItemType -e_ews_item_get_item_type (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), E_EWS_ITEM_TYPE_UNKNOWN); - - return item->priv->item_type; -} - -void -e_ews_item_set_item_type (EEwsItem *item, - EEwsItemType new_type) -{ - g_return_if_fail (E_IS_EWS_ITEM (item)); - - /* once the type is set to error type it stays as error type */ - if (item->priv->item_type != E_EWS_ITEM_TYPE_ERROR) - item->priv->item_type = new_type; -} - -const GError * -e_ews_item_get_error (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->error; -} - -void -e_ews_item_set_error (EEwsItem *item, - const GError *error) -{ - GError *new_error; - - g_return_if_fail (E_IS_EWS_ITEM (item)); - - if (error) - new_error = g_error_copy (error); - else - new_error = NULL; - - g_clear_error (&item->priv->error); - item->priv->error = new_error; - - if (item->priv->error) - e_ews_item_set_item_type (item, E_EWS_ITEM_TYPE_ERROR); -} - -const gchar * -e_ews_item_get_subject (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const gchar *) item->priv->subject; -} - -void -e_ews_item_set_subject (EEwsItem *item, - const gchar *new_subject) -{ - g_return_if_fail (E_IS_EWS_ITEM (item)); - - if (item->priv->subject) - g_free (item->priv->subject); - item->priv->subject = g_strdup (new_subject); -} - -const gchar * -e_ews_item_get_mime_content (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const gchar *) item->priv->mime_content; -} - -void -e_ews_item_set_mime_content (EEwsItem *item, - const gchar *new_mime_content) -{ - g_return_if_fail (E_IS_EWS_ITEM (item)); - - if (item->priv->mime_content) - g_free (item->priv->mime_content); - item->priv->mime_content = g_strdup (new_mime_content); -} - -const EwsId * -e_ews_item_get_id (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const EwsId *) item->priv->item_id; -} - -const EwsId * -e_ews_item_get_attachment_id (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const EwsId *) item->priv->attachment_id; -} - -gsize -e_ews_item_get_size (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - - return item->priv->size; -} - -const gchar * -e_ews_item_get_msg_id (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const gchar *) item->priv->msg_id; -} - -const gchar * -e_ews_item_get_uid (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const gchar *) item->priv->uid; -} - -const gchar * -e_ews_item_get_in_replyto (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const gchar *) item->priv->in_replyto; -} - -const gchar * -e_ews_item_get_references (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const gchar *) item->priv->references; -} - -const gchar * -e_ews_item_get_date_header (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->date_header; -} - -time_t -e_ews_item_get_date_received (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - - return item->priv->date_received; -} - -time_t -e_ews_item_get_date_sent (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - - return item->priv->date_sent; -} - -time_t -e_ews_item_get_date_created (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - - return item->priv->date_created; -} - -time_t -e_ews_item_get_last_modified_time (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - - return item->priv->last_modified_time; -} - -gboolean -e_ews_item_has_attachments (EEwsItem *item, - gboolean *has_attachments) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - - *has_attachments = item->priv->has_attachments; - - return TRUE; -} - -gboolean -e_ews_item_is_read (EEwsItem *item, - gboolean *read) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - - *read = item->priv->is_read; - - return TRUE; -} - -gboolean -e_ews_item_get_is_meeting (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - - return item->priv->is_meeting; -} - -gboolean -e_ews_item_get_is_response_requested (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - - return item->priv->is_response_requested; -} - -gboolean -e_ews_item_is_forwarded (EEwsItem *item, - gboolean *forwarded) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - - *forwarded = (item->priv->mapi_icon_index == 0x106); - - return TRUE; -} - -gboolean -e_ews_item_is_answered (EEwsItem *item, - gboolean *answered) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - - *answered = (item->priv->mapi_icon_index == 0x105); - - return TRUE; -} - -guint32 -e_ews_item_get_message_flags (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), 0); - - return item->priv->mapi_message_flags; -} - -const GSList * -e_ews_item_get_to_recipients (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const GSList *) item->priv->to_recipients; -} - -const GSList * -e_ews_item_get_cc_recipients (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const GSList *) item->priv->cc_recipients; -} - -const GSList * -e_ews_item_get_bcc_recipients (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const GSList *) item->priv->bcc_recipients; -} - -const EwsMailbox * -e_ews_item_get_sender (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const EwsMailbox *) item->priv->sender; -} - -const EwsMailbox * -e_ews_item_get_from (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const EwsMailbox *) item->priv->from; -} - -const GSList * -e_ews_item_get_categories (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->categories; -} - -EwsImportance -e_ews_item_get_importance (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), EWS_ITEM_LOW); - - return item->priv->importance; -} - -const gchar * -e_ews_item_get_iana_start_time_zone (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->iana_start_time_zone; -} - -const gchar * -e_ews_item_get_iana_end_time_zone (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->iana_end_time_zone; -} - -EwsMailbox * -e_ews_item_mailbox_from_soap_param (ESoapParameter *param) -{ - EwsMailbox *mb; - ESoapParameter *subparam; - - mb = g_new0 (EwsMailbox, 1); - - subparam = e_soap_parameter_get_first_child_by_name (param, "Name"); - if (subparam) - mb->name = e_soap_parameter_get_string_value (subparam); - - subparam = e_soap_parameter_get_first_child_by_name (param, "EmailAddress"); - if (subparam) - mb->email = e_soap_parameter_get_string_value (subparam); - - subparam = e_soap_parameter_get_first_child_by_name (param, "RoutingType"); - if (subparam) - mb->routing_type = e_soap_parameter_get_string_value (subparam); - - subparam = e_soap_parameter_get_first_child_by_name (param, "MailboxType"); - if (subparam) - mb->mailbox_type = e_soap_parameter_get_string_value (subparam); - - subparam = e_soap_parameter_get_first_child_by_name (param, "ItemId"); - if (subparam) { - EwsId *id = g_new0 (EwsId, 1); - id->id = e_soap_parameter_get_property (subparam, "Id"); - id->change_key = e_soap_parameter_get_property (subparam, "ChangeKey"); - mb->item_id = id; - } - - if (!mb->email && !mb->name) { - e_ews_mailbox_free (mb); - mb = NULL; - } - - return mb; -} - -void -e_ews_mailbox_free (EwsMailbox *mailbox) -{ - if (!mailbox) - return; - - g_free (mailbox->name); - g_free (mailbox->email); - g_free (mailbox->routing_type); - g_free (mailbox->mailbox_type); - - if (mailbox->item_id) { - g_free (mailbox->item_id->id); - g_free (mailbox->item_id->change_key); - g_free (mailbox->item_id); - } - - g_free (mailbox); -} - -const GSList * -e_ews_item_get_modified_occurrences (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->modified_occurrences; -} - -GSList * -e_ews_item_get_attachments_ids (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->attachments_ids; -} - -const gchar * -e_ews_item_get_extended_tag (EEwsItem *item, - guint32 prop_tag) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - if (!item->priv->mapi_extended_tags) - return NULL; - - return g_hash_table_lookup (item->priv->mapi_extended_tags, GUINT_TO_POINTER (prop_tag)); -} - -const gchar * -e_ews_item_get_extended_distinguished_tag (EEwsItem *item, - const gchar *set_id, - guint32 prop_id) -{ - GHashTable *set_tags; - - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - if (!item->priv->mapi_extended_sets) - return NULL; - - set_tags = g_hash_table_lookup (item->priv->mapi_extended_sets, set_id); - if (!set_tags) - return NULL; - - return g_hash_table_lookup (set_tags, GUINT_TO_POINTER (prop_id)); -} - -gboolean -e_ews_item_get_extended_property_as_boolean (EEwsItem *item, - const gchar *set_id, - guint32 prop_id_or_tag, - gboolean *found) -{ - const gchar *value; - - value = e_ews_item_get_extended_property_as_string (item, set_id, prop_id_or_tag, found); - if (!value) - return FALSE; - - if (g_str_equal (value, "true")) - return TRUE; - - if (g_str_equal (value, "false")) - return FALSE; - - if (found) - *found = FALSE; - - return FALSE; -} - -gint -e_ews_item_get_extended_property_as_int (EEwsItem *item, - const gchar *set_id, - guint32 prop_id_or_tag, - gboolean *found) -{ - const gchar *value; - - value = e_ews_item_get_extended_property_as_string (item, set_id, prop_id_or_tag, found); - if (!value) - return 0; - - return strtol (value, NULL, 0); -} - -gdouble -e_ews_item_get_extended_property_as_double (EEwsItem *item, - const gchar *set_id, - guint32 prop_id_or_tag, - gboolean *found) -{ - const gchar *value; - - value = e_ews_item_get_extended_property_as_string (item, set_id, prop_id_or_tag, found); - if (!value) - return 0.0; - - return g_ascii_strtod (value, NULL); -} - -const gchar * -e_ews_item_get_extended_property_as_string (EEwsItem *item, - const gchar *set_id, - guint32 prop_id_or_tag, - gboolean *found) -{ - const gchar *value; - - if (set_id) - value = e_ews_item_get_extended_distinguished_tag (item, set_id, prop_id_or_tag); - else - value = e_ews_item_get_extended_tag (item, prop_id_or_tag); - - if (found) - *found = value != NULL; - - return value; -} - -time_t -e_ews_item_get_extended_property_as_time (EEwsItem *item, - const gchar *set_id, - guint32 prop_id_or_tag, - gboolean *found) -{ - const gchar *value; - GTimeVal tv; - - value = e_ews_item_get_extended_property_as_string (item, set_id, prop_id_or_tag, found); - if (!value) - return (time_t) 0; - - if (g_time_val_from_iso8601 (value, &tv)) - return tv.tv_sec; - - if (found) - *found = FALSE; - - return (time_t) 0; -} - -gchar * -e_ews_embed_attachment_id_in_uri (const gchar *olduri, - const gchar *attach_id) -{ - gchar *tmpdir, *tmpfilename, *filename, *dirname, *name; - - tmpfilename = g_filename_from_uri (olduri, NULL, NULL); - g_return_val_if_fail (tmpfilename != NULL, NULL); - - name = g_path_get_basename (tmpfilename); - tmpdir = g_path_get_dirname (tmpfilename); - - dirname = g_build_filename (tmpdir, attach_id, NULL); - if (g_mkdir (dirname, 0775) == -1) { - g_warning ("Failed create directory to place file in [%s]: %s\n", dirname, g_strerror (errno)); - } - - filename = g_build_filename (dirname, name, NULL); - if (g_rename (tmpfilename, filename) != 0) { - g_warning ("Failed to move attachment cache file [%s -> %s]: %s\n", tmpfilename, filename, g_strerror (errno)); - } - - g_free (tmpfilename); - g_free (tmpdir); - g_free (dirname); - g_free (name); - - tmpfilename = g_filename_to_uri (filename, NULL, NULL); - - g_free (filename); - - return tmpfilename; -} - -EEwsAttachmentInfo * -e_ews_dump_file_attachment_from_soap_parameter (ESoapParameter *param, - const gchar *cache, - const gchar *comp_uid) -{ - ESoapParameter *subparam; - const gchar *param_name, *tmpfilename; - gchar *name = NULL, *value, *filename, *dirname; - guchar *content = NULL; - gsize data_len = 0; - gchar *tmpdir; - EEwsAttachmentInfo *info; - - g_return_val_if_fail (param != NULL, NULL); - - /* Parse element, look for filename and content */ - for (subparam = e_soap_parameter_get_first_child (param); subparam != NULL; subparam = e_soap_parameter_get_next_child (subparam)) { - param_name = e_soap_parameter_get_name (subparam); - - if (g_ascii_strcasecmp (param_name, "Name") == 0) { - name = e_soap_parameter_get_string_value (subparam); - } else if (g_ascii_strcasecmp (param_name, "Content") == 0) { - value = e_soap_parameter_get_string_value (subparam); - content = g_base64_decode (value, &data_len); - g_free (value); - } - } - - /* Make sure we have needed data */ - if (!content || !name) { - g_free (name); - g_free (content); - return NULL; - } - - if (cache && content && g_file_test ((const gchar *) content, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS)) { - gchar *uri; - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_URI); - - tmpfilename = (gchar *) content; - tmpdir = g_path_get_dirname (tmpfilename); - - dirname = g_build_filename (tmpdir, comp_uid, NULL); - if (g_mkdir_with_parents (dirname, 0775) == -1) { - g_warning ("Failed create directory to place file in [%s]: %s\n", dirname, g_strerror (errno)); - } - - filename = g_build_filename (dirname, name, NULL); - if (g_rename (tmpfilename, filename) != 0) { - g_warning ("Failed to move attachment cache file [%s -> %s]: %s\n", - tmpfilename, filename, g_strerror (errno)); - } - - g_free (dirname); - g_free (tmpdir); - g_free (name); - g_free (content); - - /* Return URI to saved file */ - uri = g_filename_to_uri (filename, NULL, NULL); - e_ews_attachment_info_set_uri (info, uri); - g_free (filename); - g_free (uri); - } else { - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_INLINED); - e_ews_attachment_info_set_inlined_data (info, content, data_len); - e_ews_attachment_info_set_prefer_filename (info, name); - } - return info; -} - -EEwsAttachmentInfo * -e_ews_item_dump_mime_content (EEwsItem *item, - const gchar *cache) -{ - EEwsAttachmentInfo *info; - gchar *filename, *surename, *dirname; - gchar *tmpdir, *uri; - const gchar *tmpfilename; - - g_return_val_if_fail (item->priv->mime_content != NULL, NULL); - g_return_val_if_fail (g_file_test ((const gchar *) item->priv->mime_content, G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS), NULL); - - tmpfilename = (gchar *) item->priv->mime_content; - tmpdir = g_path_get_dirname (tmpfilename); - - dirname = g_build_filename (tmpdir, "XXXXXX", NULL); - if (!g_mkdtemp (dirname)) { - g_warning ("Failed to create directory for attachment cache '%s': %s", dirname, g_strerror (errno)); - - g_free (tmpdir); - g_free (dirname); - - return NULL; - } - - surename = g_uri_escape_string (item->priv->subject, "", TRUE); - filename = g_build_filename (dirname, surename, NULL); - - if (g_rename ((const gchar *) item->priv->mime_content, filename) != 0) { - g_warning ("Failed to move attachment cache file '%s': %s", filename, g_strerror (errno)); - - g_free (tmpdir); - g_free (dirname); - g_free (filename); - g_free (surename); - - return NULL; - } - - uri = g_filename_to_uri (filename, NULL, NULL); - - info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_URI); - e_ews_attachment_info_set_uri (info, uri); - - g_free (uri); - g_free (filename); - g_free (dirname); - g_free (tmpdir); - g_free (surename); - - /* Return URI to saved file */ - return info; -} - -const gchar * -e_ews_item_get_my_response_type (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->my_response_type; -} - -const GSList * -e_ews_item_get_attendees (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->attendees; -} - -const EwsId * -e_ews_item_get_calendar_item_accept_id (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return (const EwsId *) item->priv->calendar_item_accept_id; -} - -const gchar * -e_ews_item_get_fileas (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->fileas; -} - -const EwsCompleteName * -e_ews_item_get_complete_name (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - if (!item->priv->contact_fields->complete_name && ( - item->priv->contact_fields->surname || - item->priv->contact_fields->middlename || - item->priv->contact_fields->givenname)) { - EwsCompleteName *cn; - - cn = g_new0 (EwsCompleteName, 1); - - cn->first_name = g_strdup (item->priv->contact_fields->givenname); - cn->middle_name = g_strdup (item->priv->contact_fields->middlename); - cn->last_name = g_strdup (item->priv->contact_fields->surname); - - item->priv->contact_fields->complete_name = cn; - } - - return (const EwsCompleteName *) item->priv->contact_fields->complete_name; -} - -const gchar * -e_ews_item_get_display_name (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return item->priv->contact_fields->display_name; -} - -GHashTable * -e_ews_item_get_email_addresses (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return item->priv->contact_fields->email_addresses; -} - -/** - * e_ews_item_get_email_address - * @item: - * @field: "EmailAddress1", "EmailAddress2", "EmailAddress3" - * - * - * Returns: - **/ -const gchar * -e_ews_item_get_email_address (EEwsItem *item, - const gchar *field) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - if (item->priv->contact_fields->email_addresses) - return g_hash_table_lookup (item->priv->contact_fields->email_addresses, field); - - return NULL; -} - -/** - * e_ews_item_get_physical_address - * @item: - * @field: "Business", "Home", "Other" - * - * - * Returns: - **/ -const EwsAddress * -e_ews_item_get_physical_address (EEwsItem *item, - const gchar *field) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - if (item->priv->contact_fields->physical_addresses) - return g_hash_table_lookup (item->priv->contact_fields->physical_addresses, field); - - return NULL; -} - -/** - * e_ews_item_get_phone_number - * @item: - * @field: "AssistantPhone", "BusinessFax", "BusinessPhone", "BusinessPhone2", "Callback" - * "CarPhone", "CompanyMainPhone", "HomeFax", "HomePhone", "HomePhone2", "Isdn", "MobilePhone" - * "OtherFax", "OtherTelephone", "Pager", "PrimaryPhone", "RadioPhone", "Telex", "TtyTddPhone" - * - * - * Returns: - **/ -const gchar * -e_ews_item_get_phone_number (EEwsItem *item, - const gchar *field) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - if (item->priv->contact_fields->phone_numbers) - return g_hash_table_lookup (item->priv->contact_fields->phone_numbers, field); - - return NULL; -} - -/** - * e_ews_item_get_im_address - * @item: - * @field: "ImAddress1", "ImAddress2", "ImAddress3" - * - * - * Returns: - **/ -const gchar * -e_ews_item_get_im_address (EEwsItem *item, - const gchar *field) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - if (item->priv->contact_fields->im_addresses) - return g_hash_table_lookup (item->priv->contact_fields->im_addresses, field); - - return NULL; -} - -const gchar * -e_ews_item_get_company_name (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->company_name; -} - -const gchar * -e_ews_item_get_department (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->department; -} - -const gchar * -e_ews_item_get_job_title (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->job_title; -} - -const gchar * -e_ews_item_get_assistant_name (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->assistant_name; -} - -const gchar * -e_ews_item_get_manager (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->manager; -} - -const gchar * -e_ews_item_get_office_location (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->office_location; -} - -const gchar * -e_ews_item_get_business_homepage (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->business_homepage; -} - -const gchar * -e_ews_item_get_profession (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->profession; -} - -const gchar * -e_ews_item_get_spouse_name (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->spouse_name; -} - -const gchar * -e_ews_item_get_surname (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return (const gchar *) item->priv->contact_fields->surname; -} - -const gchar * -e_ews_item_get_givenname (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return item->priv->contact_fields->givenname; -} - -const gchar * -e_ews_item_get_middlename (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return item->priv->contact_fields->middlename; -} - -const gchar * -e_ews_item_get_notes (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->contact_fields != NULL, NULL); - - return item->priv->contact_fields->notes; -} - -time_t -e_ews_item_get_birthday (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - g_return_val_if_fail (item->priv->contact_fields != NULL, -1); - - return item->priv->contact_fields->birthday; -} - -time_t -e_ews_item_get_wedding_anniversary (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - g_return_val_if_fail (item->priv->contact_fields != NULL, -1); - - return item->priv->contact_fields->wedding_anniversary; -} - -const gchar * -e_ews_item_get_status (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->task_fields != NULL, NULL); - - return item->priv->task_fields->status; -} - -const gchar * e_ews_item_get_percent_complete (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->task_fields != NULL, NULL); - - return item->priv->task_fields->percent_complete; -} - -time_t -e_ews_item_get_due_date (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - g_return_val_if_fail (item->priv->task_fields != NULL, -1); - - return item->priv->task_fields->due_date; -} - -time_t -e_ews_item_get_start_date (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - g_return_val_if_fail (item->priv->task_fields != NULL, -1); - - return item->priv->task_fields->start_date; -} - -time_t -e_ews_item_get_complete_date (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), -1); - g_return_val_if_fail (item->priv->task_fields != NULL, -1); - - return item->priv->task_fields->complete_date; -} - -const gchar * -e_ews_item_get_sensitivity (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->task_fields != NULL, NULL); - - return item->priv->task_fields->sensitivity; -} - -const gchar * -e_ews_item_get_body (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - if (item->priv->body) - return item->priv->body; - - return item->priv->task_fields ? item->priv->task_fields->body : NULL; -} - -const gchar * -e_ews_item_get_owner (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->task_fields != NULL, NULL); - - return item->priv->task_fields->owner; -} - -const gchar * -e_ews_item_get_delegator (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - g_return_val_if_fail (item->priv->task_fields != NULL, NULL); - - return item->priv->task_fields->delegator; -} - -gboolean -e_ews_item_task_has_start_date (EEwsItem *item, - gboolean *has_date) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - g_return_val_if_fail (item->priv->task_fields != NULL, FALSE); - - *has_date = item->priv->task_fields->has_start_date; - - return TRUE; -} - -gboolean -e_ews_item_task_has_due_date (EEwsItem *item, - gboolean *has_date) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - g_return_val_if_fail (item->priv->task_fields != NULL, FALSE); - - *has_date = item->priv->task_fields->has_due_date; - - return TRUE; -} - -gboolean -e_ews_item_task_has_complete_date (EEwsItem *item, - gboolean *has_date) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), FALSE); - g_return_val_if_fail (item->priv->task_fields != NULL, FALSE); - - *has_date = item->priv->task_fields->has_complete_date; - - return TRUE; -} - -const gchar * -e_ews_item_get_tzid (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - /* can be NULL */ - return item->priv->timezone; -} - -const gchar * -e_ews_item_get_start_tzid (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - /* can be NULL */ - return item->priv->start_timezone; -} - -const gchar * -e_ews_item_get_end_tzid (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - /* can be NULL */ - return item->priv->end_timezone; -} - -const gchar * -e_ews_item_get_contact_photo_id (EEwsItem *item) -{ - g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL); - - return item->priv->contact_photo_id; -} - -/* free returned pointer with e_ews_permission_free() */ -EEwsPermission * -e_ews_permission_new (EEwsPermissionUserType user_type, - const gchar *display_name, - const gchar *primary_smtp, - const gchar *sid, - guint32 rights) -{ - EEwsPermission *perm; - - perm = g_new0 (EEwsPermission, 1); - perm->user_type = user_type; - perm->display_name = g_strdup (display_name); - perm->primary_smtp = g_strdup (primary_smtp); - perm->sid = g_strdup (sid); - perm->rights = rights; - - return perm; -} - -void -e_ews_permission_free (EEwsPermission *perm) -{ - if (!perm) - return; - - g_free (perm->display_name); - g_free (perm->primary_smtp); - g_free (perm->sid); - g_free (perm); -} - -static void -ews_level_rights_converter (const gchar **plevel_name, - guint32 *prights, - gboolean level_to_rights) -{ - struct _known { - const gchar *level_name; - guint32 rights; - } known[] = { - { "None", 0 }, - { "Owner", E_EWS_PERMISSION_BIT_READ_ANY | - E_EWS_PERMISSION_BIT_CREATE | - E_EWS_PERMISSION_BIT_CREATE_SUBFOLDER | - E_EWS_PERMISSION_BIT_EDIT_OWNED | - E_EWS_PERMISSION_BIT_EDIT_ANY | - E_EWS_PERMISSION_BIT_DELETE_OWNED | - E_EWS_PERMISSION_BIT_DELETE_ANY | - E_EWS_PERMISSION_BIT_FOLDER_OWNER | - E_EWS_PERMISSION_BIT_FOLDER_CONTACT | - E_EWS_PERMISSION_BIT_FOLDER_VISIBLE }, - { "PublishingEditor", - E_EWS_PERMISSION_BIT_READ_ANY | - E_EWS_PERMISSION_BIT_CREATE | - E_EWS_PERMISSION_BIT_CREATE_SUBFOLDER | - E_EWS_PERMISSION_BIT_EDIT_OWNED | - E_EWS_PERMISSION_BIT_EDIT_ANY | - E_EWS_PERMISSION_BIT_DELETE_OWNED | - E_EWS_PERMISSION_BIT_DELETE_ANY | - E_EWS_PERMISSION_BIT_FOLDER_VISIBLE }, - { "Editor", - E_EWS_PERMISSION_BIT_READ_ANY | - E_EWS_PERMISSION_BIT_CREATE | - E_EWS_PERMISSION_BIT_EDIT_OWNED | - E_EWS_PERMISSION_BIT_EDIT_ANY | - E_EWS_PERMISSION_BIT_DELETE_OWNED | - E_EWS_PERMISSION_BIT_DELETE_ANY | - E_EWS_PERMISSION_BIT_FOLDER_VISIBLE }, - { "PublishingAuthor", - E_EWS_PERMISSION_BIT_READ_ANY | - E_EWS_PERMISSION_BIT_CREATE | - E_EWS_PERMISSION_BIT_CREATE_SUBFOLDER | - E_EWS_PERMISSION_BIT_EDIT_OWNED | - E_EWS_PERMISSION_BIT_DELETE_OWNED | - E_EWS_PERMISSION_BIT_FOLDER_VISIBLE }, - { "Author", - E_EWS_PERMISSION_BIT_READ_ANY | - E_EWS_PERMISSION_BIT_CREATE | - E_EWS_PERMISSION_BIT_EDIT_OWNED | - E_EWS_PERMISSION_BIT_DELETE_OWNED | - E_EWS_PERMISSION_BIT_FOLDER_VISIBLE }, - { "NoneditingAuthor", - E_EWS_PERMISSION_BIT_READ_ANY | - E_EWS_PERMISSION_BIT_CREATE | - E_EWS_PERMISSION_BIT_DELETE_OWNED | - E_EWS_PERMISSION_BIT_FOLDER_VISIBLE }, - { "Reviewer", - E_EWS_PERMISSION_BIT_READ_ANY | - E_EWS_PERMISSION_BIT_FOLDER_VISIBLE }, - { "Contributor", - E_EWS_PERMISSION_BIT_CREATE | - E_EWS_PERMISSION_BIT_FOLDER_VISIBLE }, - { "FreeBusyTimeOnly", - E_EWS_PERMISSION_BIT_FREE_BUSY_SIMPLE }, - { "FreeBusyTimeAndSubjectAndLocation", - E_EWS_PERMISSION_BIT_FREE_BUSY_DETAILED } - - }; - gint ii; - guint32 rights; - - g_return_if_fail (plevel_name != NULL); - g_return_if_fail (prights != NULL); - - rights = (*prights) & ~(E_EWS_PERMISSION_BIT_FREE_BUSY_SIMPLE | E_EWS_PERMISSION_BIT_FREE_BUSY_DETAILED); - - for (ii = 0; ii < G_N_ELEMENTS (known); ii++) { - if (level_to_rights) { - if (g_strcmp0 (*plevel_name, known[ii].level_name) == 0) { - *prights = known[ii].rights; - return; - } - } else { - if (*prights == known[ii].rights || (rights && rights == known[ii].rights)) { - *plevel_name = known[ii].level_name; - return; - } - } - } - - /* here lefts only "Custom" */ - if (level_to_rights) - *prights = 0; - else - *plevel_name = "Custom"; -} - -/* converts user rights to level name suitable for PermissionLevel/CalendarPermissionLevel */ -const gchar * -e_ews_permission_rights_to_level_name (guint32 rights) -{ - const gchar *level_name = NULL; - - ews_level_rights_converter (&level_name, &rights, FALSE); - - return level_name; -} - -/* converts PermissionLevel/CalendarPermissionLevel name to user rights */ -guint32 -e_ews_permission_level_name_to_rights (const gchar *level_name) -{ - guint32 rights = 0; - - ews_level_rights_converter (&level_name, &rights, TRUE); - - return rights; -} - -static EEwsPermission * -ews_permissions_parse (ESoapParameter *param) -{ - EEwsPermission *res; - ESoapParameter *node, *subnode; - EEwsPermissionUserType user_type; - gchar *value, *display_name = NULL, *primary_smtp = NULL, *sid = NULL; - guint32 rights = 0; - - g_return_val_if_fail (param != NULL, NULL); - - node = e_soap_parameter_get_first_child_by_name (param, "UserId"); - if (!node) - return NULL; - - subnode = e_soap_parameter_get_first_child_by_name (node, "DistinguishedUser"); - if (subnode) { - value = e_soap_parameter_get_string_value (subnode); - if (g_strcmp0 (value, "Anonymous") == 0) { - user_type = E_EWS_PERMISSION_USER_TYPE_ANONYMOUS; - } else if (g_strcmp0 (value, "Default") == 0) { - user_type = E_EWS_PERMISSION_USER_TYPE_DEFAULT; - } else { - g_free (value); - return NULL; - } - - g_free (value); - } else { - user_type = E_EWS_PERMISSION_USER_TYPE_REGULAR; - } - - subnode = e_soap_parameter_get_first_child_by_name (node, "SID"); - if (subnode) - sid = e_soap_parameter_get_string_value (subnode); - - subnode = e_soap_parameter_get_first_child_by_name (node, "PrimarySmtpAddress"); - if (subnode) - primary_smtp = e_soap_parameter_get_string_value (subnode); - - subnode = e_soap_parameter_get_first_child_by_name (node, "DisplayName"); - if (subnode) - display_name = e_soap_parameter_get_string_value (subnode); - - node = e_soap_parameter_get_first_child_by_name (param, "PermissionLevel"); - if (!node) - node = e_soap_parameter_get_first_child_by_name (param, "CalendarPermissionLevel"); - - if (node) { - value = e_soap_parameter_get_string_value (node); - rights = e_ews_permission_level_name_to_rights (value); - g_free (value); - } - - node = e_soap_parameter_get_first_child_by_name (param, "CanCreateItems"); - if (node) { - value = e_soap_parameter_get_string_value (node); - if (g_strcmp0 (value, "true") == 0) - rights |= E_EWS_PERMISSION_BIT_CREATE; - g_free (value); - } - - node = e_soap_parameter_get_first_child_by_name (param, "CanCreateSubFolders"); - if (node) { - value = e_soap_parameter_get_string_value (node); - if (g_strcmp0 (value, "true") == 0) - rights |= E_EWS_PERMISSION_BIT_CREATE_SUBFOLDER; - g_free (value); - } - - node = e_soap_parameter_get_first_child_by_name (param, "IsFolderOwner"); - if (node) { - value = e_soap_parameter_get_string_value (node); - if (g_strcmp0 (value, "true") == 0) - rights |= E_EWS_PERMISSION_BIT_FOLDER_OWNER; - g_free (value); - } - - node = e_soap_parameter_get_first_child_by_name (param, "IsFolderVisible"); - if (node) { - value = e_soap_parameter_get_string_value (node); - if (g_strcmp0 (value, "true") == 0) - rights |= E_EWS_PERMISSION_BIT_FOLDER_VISIBLE; - g_free (value); - } - - node = e_soap_parameter_get_first_child_by_name (param, "IsFolderContact"); - if (node) { - value = e_soap_parameter_get_string_value (node); - if (g_strcmp0 (value, "true") == 0) - rights |= E_EWS_PERMISSION_BIT_FOLDER_CONTACT; - g_free (value); - } - - node = e_soap_parameter_get_first_child_by_name (param, "EditItems"); - if (node) { - value = e_soap_parameter_get_string_value (node); - if (g_strcmp0 (value, "None") == 0) - rights |= 0; - else if (g_strcmp0 (value, "Owned") == 0) - rights |= E_EWS_PERMISSION_BIT_EDIT_OWNED; - else if (g_strcmp0 (value, "All") == 0) - rights |= E_EWS_PERMISSION_BIT_EDIT_ANY; - g_free (value); - } - - node = e_soap_parameter_get_first_child_by_name (param, "DeleteItems"); - if (node) { - value = e_soap_parameter_get_string_value (node); - if (g_strcmp0 (value, "None") == 0) - rights |= 0; - else if (g_strcmp0 (value, "Owned") == 0) - rights |= E_EWS_PERMISSION_BIT_DELETE_OWNED; - else if (g_strcmp0 (value, "All") == 0) - rights |= E_EWS_PERMISSION_BIT_DELETE_ANY; - g_free (value); - } - - node = e_soap_parameter_get_first_child_by_name (param, "ReadItems"); - if (node) { - value = e_soap_parameter_get_string_value (node); - if (g_strcmp0 (value, "None") == 0) - rights |= 0; - else if (g_strcmp0 (value, "TimeOnly") == 0) - rights |= E_EWS_PERMISSION_BIT_FREE_BUSY_SIMPLE; - else if (g_strcmp0 (value, "TimeAndSubjectAndLocation") == 0) - rights |= E_EWS_PERMISSION_BIT_FREE_BUSY_DETAILED; - else if (g_strcmp0 (value, "FullDetails") == 0) - rights |= E_EWS_PERMISSION_BIT_READ_ANY; - g_free (value); - } - - res = e_ews_permission_new (user_type, display_name, primary_smtp, sid, rights); - - g_free (display_name); - g_free (primary_smtp); - g_free (sid); - - return res; -} - -/* Returns GSList of EEwsPermission * objects, as read from @param. - * Returned GSList should be freed with e_ews_permissions_free() - * when done with it. Returns NULL when no permissions recognized - * from @param. -*/ -GSList * -e_ews_permissions_from_soap_param (ESoapParameter *param) -{ - GSList *perms = NULL; - ESoapParameter *node; - const gchar *name; - - g_return_val_if_fail (param != NULL, NULL); - - name = e_soap_parameter_get_name (param); - if (g_ascii_strcasecmp (name, "Permissions") == 0 || - g_ascii_strcasecmp (name, "CalendarPermissions") == 0) { - node = param; - } else { - node = e_soap_parameter_get_first_child_by_name (param, "Permissions"); - if (!node) - node = e_soap_parameter_get_first_child_by_name (param, "CalendarPermissions"); - if (!node) - return NULL; - } - - for (node = e_soap_parameter_get_first_child (node); - node; - node = e_soap_parameter_get_next_child (node)) { - name = e_soap_parameter_get_name (node); - if (g_ascii_strcasecmp (name, "Permission") == 0 || - g_ascii_strcasecmp (name, "CalendarPermission") == 0) { - EEwsPermission *perm; - - perm = ews_permissions_parse (node); - if (perm) { - perms = g_slist_prepend (perms, perm); - } - } - } - - return perms ? g_slist_reverse (perms) : NULL; -} - -void -e_ews_permissions_free (GSList *permissions) -{ - g_slist_free_full (permissions, (GDestroyNotify) e_ews_permission_free); -} - -/* strips ex_address by its LDAP-like part, returning position in ex_address where - common name begins; returns whole ex_address, if not found */ -const gchar * -e_ews_item_util_strip_ex_address (const gchar *ex_address) -{ - const gchar *ptr; - - if (!ex_address) - return ex_address; - - ptr = strrchr (ex_address, '/'); - if (ptr && g_ascii_strncasecmp (ptr, "/cn=", 4) == 0) { - return ptr + 4; - } - - return ex_address; -} diff --git a/src/server/e-ews-item.h b/src/server/e-ews-item.h index dbb14f4..5562b6e 100644 --- a/src/server/e-ews-item.h +++ b/src/server/e-ews-item.h @@ -384,8 +384,6 @@ void e_ews_permissions_free (GSList *permissions); /* Utility functions */ const gchar * e_ews_item_util_strip_ex_address (const gchar *ex_address); -EwsId * e_ews_id_copy (const EwsId *ews_id); -void e_ews_id_free (EwsId *ews_id); G_END_DECLS diff --git a/src/server/e-ews-item.h.meeting-with-attachment b/src/server/e-ews-item.h.meeting-with-attachment deleted file mode 100644 index 5562b6e..0000000 --- a/src/server/e-ews-item.h.meeting-with-attachment +++ /dev/null @@ -1,390 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Authors : - * - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - * 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. - * - * 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 this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 - * USA - */ - -#ifndef E_EWS_ITEM_H -#define E_EWS_ITEM_H - -#include "e-soap-message.h" -#include "e-soap-response.h" -#include "e-ews-message.h" - -G_BEGIN_DECLS - -#define E_TYPE_EWS_ITEM (e_ews_item_get_type ()) -#define E_EWS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), E_TYPE_EWS_ITEM, EEwsItem)) -#define E_EWS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), E_TYPE_EWS_ITEM, EEwsItemClass)) -#define E_IS_EWS_ITEM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), E_TYPE_EWS_ITEM)) -#define E_IS_EWS_ITEM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), E_TYPE_EWS_ITEM)) - -typedef struct _EEwsItem EEwsItem; -typedef struct _EEwsItemClass EEwsItemClass; -typedef struct _EEwsItemPrivate EEwsItemPrivate; - -typedef enum { - E_EWS_ITEM_TYPE_UNKNOWN, - E_EWS_ITEM_TYPE_MESSAGE, - E_EWS_ITEM_TYPE_POST_ITEM, - E_EWS_ITEM_TYPE_EVENT, - E_EWS_ITEM_TYPE_CONTACT, - E_EWS_ITEM_TYPE_GROUP, - E_EWS_ITEM_TYPE_MEETING_MESSAGE, - E_EWS_ITEM_TYPE_MEETING_REQUEST, - E_EWS_ITEM_TYPE_MEETING_RESPONSE, - E_EWS_ITEM_TYPE_MEETING_CANCELLATION, - E_EWS_ITEM_TYPE_TASK, - E_EWS_ITEM_TYPE_MEMO, - E_EWS_ITEM_TYPE_GENERIC_ITEM, - E_EWS_ITEM_TYPE_ERROR -} EEwsItemType; - -typedef enum { - EWS_ITEM_LOW, - EWS_ITEM_NORMAL, - EWS_ITEM_HIGH -} EwsImportance; - -struct _EEwsItem { - GObject parent; - EEwsItemPrivate *priv; -}; - -struct _EEwsItemClass { - GObjectClass parent_class; -}; - -typedef struct { - gchar *id; - gchar *change_key; -} EwsId; - -typedef struct { - gchar *name; - gchar *email; - gchar *routing_type; - gchar *mailbox_type; - EwsId *item_id; -} EwsMailbox; - -typedef struct { - EwsMailbox *mailbox; - gchar *attendeetype; - gchar *responsetype; -} EwsAttendee; - -typedef struct { - gchar *title; - gchar *first_name; - gchar *middle_name; - gchar *last_name; - gchar *suffix; - gchar *initials; - gchar *full_name; - gchar *nick_name; - gchar *yomi_first_name; - gchar *yomi_last_name; -} EwsCompleteName; - -typedef struct { - gchar *street; - gchar *city; - gchar *state; - gchar *country; - gchar *postal_code; -} EwsAddress; - -typedef struct { - gchar *filename; - gchar *mime_type; - gsize length; - gchar *data; -} EEwsAttachmentInline; - -typedef enum { - E_EWS_ATTACHMENT_INFO_TYPE_INLINED, - E_EWS_ATTACHMENT_INFO_TYPE_URI -} EEwsAttachmentInfoType; - -typedef struct { - EEwsAttachmentInfoType type; - union { - EEwsAttachmentInline inlined; - gchar *uri; - } data; - gchar *prefer_filename; - gchar *id; -} EEwsAttachmentInfo; - -typedef enum { - E_EWS_PERMISSION_BIT_FREE_BUSY_DETAILED = 0x00001000, - E_EWS_PERMISSION_BIT_FREE_BUSY_SIMPLE = 0x00000800, - E_EWS_PERMISSION_BIT_FOLDER_VISIBLE = 0x00000400, - E_EWS_PERMISSION_BIT_FOLDER_CONTACT = 0x00000200, - E_EWS_PERMISSION_BIT_FOLDER_OWNER = 0x00000100, - E_EWS_PERMISSION_BIT_CREATE_SUBFOLDER = 0x00000080, - E_EWS_PERMISSION_BIT_DELETE_ANY = 0x00000040, - E_EWS_PERMISSION_BIT_EDIT_ANY = 0x00000020, - E_EWS_PERMISSION_BIT_DELETE_OWNED = 0x00000010, - E_EWS_PERMISSION_BIT_EDIT_OWNED = 0x00000008, - E_EWS_PERMISSION_BIT_CREATE = 0x00000002, - E_EWS_PERMISSION_BIT_READ_ANY = 0x00000001 -} EEwsPermissionBits; - -typedef enum { - E_EWS_PERMISSION_USER_TYPE_NONE = 0, - E_EWS_PERMISSION_USER_TYPE_ANONYMOUS = 1 << 1, /* anonymous user */ - E_EWS_PERMISSION_USER_TYPE_DEFAULT = 1 << 2, /* default rights for any users */ - E_EWS_PERMISSION_USER_TYPE_REGULAR = 1 << 3 /* regular user, the EEwsPermission::user_smtp member is valid */ -} EEwsPermissionUserType; - -typedef struct { - EEwsPermissionUserType user_type; /* whether is distinguished name, if 'true' */ - gchar *display_name; /* display name for a user */ - gchar *primary_smtp; /* valid only for E_EWS_PERMISSION_USER_TYPE_REGULAR */ - gchar *sid; /* security identifier (SID), if any */ - guint32 rights; /* EEwsPermissionBits for the user */ -} EEwsPermission; - -GType e_ews_item_get_type (void); -EEwsItem * e_ews_item_new_from_soap_parameter - (ESoapParameter *param); -EEwsItem * e_ews_item_new_from_error (const GError *error); - -EEwsItemType e_ews_item_get_item_type (EEwsItem *item); -void e_ews_item_set_item_type (EEwsItem *item, - EEwsItemType new_type); -const GError * e_ews_item_get_error (EEwsItem *item); -void e_ews_item_set_error (EEwsItem *item, - const GError *error); -const gchar * e_ews_item_get_subject (EEwsItem *item); -void e_ews_item_set_subject (EEwsItem *item, - const gchar *new_subject); -const gchar * e_ews_item_get_mime_content (EEwsItem *item); -void e_ews_item_set_mime_content (EEwsItem *item, - const gchar *new_mime_content); -const EwsId * e_ews_item_get_id (EEwsItem *item); -const EwsId * e_ews_item_get_attachment_id (EEwsItem *item); -gsize e_ews_item_get_size (EEwsItem *item); -const gchar * e_ews_item_get_msg_id (EEwsItem *item); -const gchar * e_ews_item_get_uid (EEwsItem *item); -const gchar * e_ews_item_get_in_replyto (EEwsItem *item); -const gchar * e_ews_item_get_references (EEwsItem *item); -const gchar * e_ews_item_get_date_header (EEwsItem *item); -time_t e_ews_item_get_date_received (EEwsItem *item); -time_t e_ews_item_get_date_sent (EEwsItem *item); -time_t e_ews_item_get_date_created (EEwsItem *item); -time_t e_ews_item_get_last_modified_time - (EEwsItem *item); -gboolean e_ews_item_has_attachments (EEwsItem *item, - gboolean *has_attachments); -gboolean e_ews_item_is_read (EEwsItem *item, - gboolean *is_read); -gboolean e_ews_item_is_forwarded (EEwsItem *item, - gboolean *is_forwarded); -gboolean e_ews_item_is_answered (EEwsItem *item, - gboolean *is_answered); -guint32 e_ews_item_get_message_flags (EEwsItem *item); -const GSList * e_ews_item_get_to_recipients (EEwsItem *item); -const GSList * e_ews_item_get_cc_recipients (EEwsItem *item); -const GSList * e_ews_item_get_bcc_recipients (EEwsItem *item); -const EwsMailbox * - e_ews_item_get_sender (EEwsItem *item); -const EwsMailbox * - e_ews_item_get_from (EEwsItem *item); -EwsImportance - e_ews_item_get_importance (EEwsItem *item); -const GSList * - e_ews_item_get_categories (EEwsItem *item); -EwsMailbox * - e_ews_item_mailbox_from_soap_param - (ESoapParameter *param); -void e_ews_mailbox_free (EwsMailbox *mailbox); - -gboolean e_ews_item_get_is_meeting (EEwsItem *item); -gboolean e_ews_item_get_is_response_requested - (EEwsItem *item); -const GSList * e_ews_item_get_modified_occurrences - (EEwsItem *item); -gchar * e_ews_embed_attachment_id_in_uri (const gchar *olduri, const gchar *attach_id); -GSList * e_ews_item_get_attachments_ids - (EEwsItem *item); -const gchar * e_ews_item_get_extended_tag (EEwsItem *item, - guint32 prop_tag); -const gchar * e_ews_item_get_extended_distinguished_tag - (EEwsItem *item, - const gchar *set_id, - guint32 prop_id); -gboolean e_ews_item_get_extended_property_as_boolean - (EEwsItem *item, - const gchar *set_id, - guint32 prop_id_or_tag, - gboolean *found); -gint e_ews_item_get_extended_property_as_int - (EEwsItem *item, - const gchar *set_id, - guint32 prop_id_or_tag, - gboolean *found); -gdouble e_ews_item_get_extended_property_as_double - (EEwsItem *item, - const gchar *set_id, - guint32 prop_id_or_tag, - gboolean *found); -const gchar * e_ews_item_get_extended_property_as_string - (EEwsItem *item, - const gchar *set_id, - guint32 prop_id_or_tag, - gboolean *found); -time_t e_ews_item_get_extended_property_as_time - (EEwsItem *item, - const gchar *set_id, - guint32 prop_id_or_tag, - gboolean *found); - -EEwsAttachmentInfo * -e_ews_dump_file_attachment_from_soap_parameter (ESoapParameter *param, const gchar *cache, const gchar *comp_uid); - -gchar * -e_ews_item_ical_dump (EEwsItem *item); - -EEwsAttachmentInfo * -e_ews_item_dump_mime_content (EEwsItem *item, const gchar *cache); - -const gchar * e_ews_item_get_my_response_type (EEwsItem *item); -const GSList * e_ews_item_get_attendees (EEwsItem *item); - -const EwsId * e_ews_item_get_calendar_item_accept_id - (EEwsItem *item); - -EEwsAttachmentInfo * - e_ews_attachment_info_new (EEwsAttachmentInfoType type); -void e_ews_attachment_info_free (EEwsAttachmentInfo *info); -EEwsAttachmentInfoType - e_ews_attachment_info_get_type (EEwsAttachmentInfo *info); -const gchar * e_ews_attachment_info_get_prefer_filename - (EEwsAttachmentInfo *info); -void e_ews_attachment_info_set_prefer_filename - (EEwsAttachmentInfo *info, - const gchar *prefer_filename); -const gchar * e_ews_attachment_info_get_inlined_data - (EEwsAttachmentInfo *info, - gsize *len); -void e_ews_attachment_info_set_inlined_data - (EEwsAttachmentInfo *info, - const guchar *data, - gsize len); -const gchar * e_ews_attachment_info_get_mime_type - (EEwsAttachmentInfo *info); -void e_ews_attachment_info_set_mime_type - (EEwsAttachmentInfo *info, - const gchar *mime_type); -const gchar * e_ews_attachment_info_get_filename - (EEwsAttachmentInfo *info); -void e_ews_attachment_info_set_filename - (EEwsAttachmentInfo *info, - const gchar *filename); -const gchar * e_ews_attachment_info_get_uri (EEwsAttachmentInfo *info); -void e_ews_attachment_info_set_uri (EEwsAttachmentInfo *info, - const gchar *uri); -const gchar * e_ews_attachment_info_get_id (EEwsAttachmentInfo *info); -void e_ews_attachment_info_set_id (EEwsAttachmentInfo *info, - const gchar *id); - - -/* Contact fields */ -const gchar * e_ews_item_get_fileas (EEwsItem *item); -const EwsCompleteName * - e_ews_item_get_complete_name (EEwsItem *item); -const gchar * e_ews_item_get_display_name (EEwsItem *item); -GHashTable * e_ews_item_get_email_addresses (EEwsItem *item); -const gchar * e_ews_item_get_email_address (EEwsItem *item, const gchar *type); -const EwsAddress * - e_ews_item_get_physical_address (EEwsItem *item, const gchar *type); -const gchar * e_ews_item_get_phone_number (EEwsItem *item, const gchar *type); -const gchar * e_ews_item_get_im_address (EEwsItem *item, const gchar *type); - -const gchar * e_ews_item_get_company_name (EEwsItem *item); -const gchar * e_ews_item_get_department (EEwsItem *item); -const gchar * e_ews_item_get_job_title (EEwsItem *item); -const gchar * e_ews_item_get_assistant_name (EEwsItem *item); -const gchar * e_ews_item_get_manager (EEwsItem *item); -const gchar * e_ews_item_get_office_location (EEwsItem *item); -const gchar * e_ews_item_get_business_homepage - (EEwsItem *item); -time_t e_ews_item_get_birthday (EEwsItem *item); -time_t e_ews_item_get_wedding_anniversary - (EEwsItem *item); -const gchar * e_ews_item_get_profession (EEwsItem *item); -const gchar * e_ews_item_get_spouse_name (EEwsItem *item); -const gchar * e_ews_item_get_culture (EEwsItem *item); -const gchar * e_ews_item_get_surname (EEwsItem *item); -const gchar * e_ews_item_get_givenname (EEwsItem *item); -const gchar * e_ews_item_get_middlename (EEwsItem *item); -const gchar * e_ews_item_get_notes (EEwsItem *item); - -/*Task fields*/ -const gchar * e_ews_item_get_status (EEwsItem *item); -const gchar * e_ews_item_get_percent_complete (EEwsItem *item); -const gchar * e_ews_item_get_sensitivity (EEwsItem *item); -const gchar * e_ews_item_get_body (EEwsItem *item); -const gchar * e_ews_item_get_owner (EEwsItem *item); -const gchar * e_ews_item_get_delegator (EEwsItem *item); -time_t e_ews_item_get_due_date (EEwsItem *item); -time_t e_ews_item_get_start_date (EEwsItem *item); -time_t e_ews_item_get_complete_date (EEwsItem *item); -gboolean e_ews_item_task_has_start_date (EEwsItem *item, - gboolean *has_date); -gboolean e_ews_item_task_has_due_date (EEwsItem *item, - gboolean *has_date); -gboolean e_ews_item_task_has_complete_date - (EEwsItem * item, - gboolean * has_date); -const gchar * e_ews_item_get_tzid (EEwsItem *item); -const gchar * e_ews_item_get_start_tzid (EEwsItem *item); -const gchar * e_ews_item_get_end_tzid (EEwsItem *item); -const gchar * e_ews_item_get_contact_photo_id (EEwsItem *item); -const gchar * e_ews_item_get_iana_start_time_zone - (EEwsItem *item); -const gchar * e_ews_item_get_iana_end_time_zone - (EEwsItem *item); - -/* Folder Permissions */ -EEwsPermission *e_ews_permission_new (EEwsPermissionUserType user_type, - const gchar *display_name, - const gchar *primary_smtp, - const gchar *sid, - guint32 rights); -void e_ews_permission_free (EEwsPermission *perm); - -const gchar * e_ews_permission_rights_to_level_name - (guint32 rights); -guint32 e_ews_permission_level_name_to_rights - (const gchar *level_name); - -GSList * e_ews_permissions_from_soap_param - (ESoapParameter *param); -void e_ews_permissions_free (GSList *permissions); - -/* Utility functions */ -const gchar * e_ews_item_util_strip_ex_address - (const gchar *ex_address); - -G_END_DECLS - -#endif diff --git a/src/server/e-soap-response.c b/src/server/e-soap-response.c index 6cc57ca..29b9e22 100644 --- a/src/server/e-soap-response.c +++ b/src/server/e-soap-response.c @@ -685,29 +685,3 @@ e_soap_response_dump_response (ESoapResponse *response, return ret; } - -gchar * -e_soap_response_dump_parameter (ESoapResponse *response, - ESoapParameter *param) -{ - xmlBuffer *buffer; - gint len; - gchar *data; - - g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), NULL); - g_return_val_if_fail (param != NULL, NULL); - - buffer = xmlBufferCreate (); - len = xmlNodeDump (buffer, response->priv->xmldoc, param, 0, 0); - - if (len <= 0) { - xmlBufferFree (buffer); - return NULL; - } - - data = g_strndup ((const gchar *) buffer->content, len); - - xmlBufferFree (buffer); - - return data; -} diff --git a/src/server/e-soap-response.c.sync-category-list b/src/server/e-soap-response.c.sync-category-list deleted file mode 100644 index 29b9e22..0000000 --- a/src/server/e-soap-response.c.sync-category-list +++ /dev/null @@ -1,687 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - * - * 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 "evolution-ews-config.h" - -#include - -#include -#include -#include -#include -#include "e-soap-response.h" - -#define E_SOAP_RESPONSE_GET_PRIVATE(obj) \ - (G_TYPE_INSTANCE_GET_PRIVATE \ - ((obj), E_TYPE_SOAP_RESPONSE, ESoapResponsePrivate)) - -struct _ESoapResponsePrivate { - /* the XML document */ - xmlDocPtr xmldoc; - xmlNodePtr xml_root; - xmlNodePtr xml_body; - xmlNodePtr xml_method; - xmlNodePtr soap_fault; - GList *parameters; -}; - -G_DEFINE_TYPE (ESoapResponse, e_soap_response, G_TYPE_OBJECT) - -static xmlNode * -soup_xml_real_node (xmlNode *node) -{ - while (node && (node->type == XML_COMMENT_NODE || - xmlIsBlankNode (node))) - node = node->next; - return node; -} - -static void -soap_response_finalize (GObject *object) -{ - ESoapResponsePrivate *priv; - - priv = E_SOAP_RESPONSE_GET_PRIVATE (object); - - if (priv->xmldoc != NULL) - xmlFreeDoc (priv->xmldoc); - - g_list_free (priv->parameters); - - /* Chain up to parent's finalize() method. */ - G_OBJECT_CLASS (e_soap_response_parent_class)->finalize (object); -} - -static void -e_soap_response_class_init (ESoapResponseClass *class) -{ - GObjectClass *object_class; - - g_type_class_add_private (class, sizeof (ESoapResponsePrivate)); - - object_class = G_OBJECT_CLASS (class); - object_class->finalize = soap_response_finalize; -} - -static void -e_soap_response_init (ESoapResponse *response) -{ - response->priv = E_SOAP_RESPONSE_GET_PRIVATE (response); - - response->priv->xmldoc = xmlNewDoc ((const xmlChar *)"1.0"); -} - -/** - * e_soap_response_new: - * - * Create a new empty #ESoapResponse object, which can be modified - * with the accessor functions provided with this class. - * - * Returns: the new #ESoapResponse (or %NULL if there was an - * error). - */ -ESoapResponse * -e_soap_response_new (void) -{ - ESoapResponse *response; - - response = g_object_new (E_TYPE_SOAP_RESPONSE, NULL); - return response; -} - -/** - * e_soap_response_new_from_string: - * @xmlstr: the XML string to parse. - * @xmlstr_len: XML string's length or -1 for a null-terminated string - * - * Create a new #ESoapResponse object from the XML string contained - * in @xmlstr. - * - * Returns: the new #ESoapResponse (or %NULL if there was an - * error). - */ -ESoapResponse * -e_soap_response_new_from_string (const gchar *xmlstr, - gint xmlstr_length) -{ - ESoapResponse *response; - - g_return_val_if_fail (xmlstr != NULL, NULL); - - response = g_object_new (E_TYPE_SOAP_RESPONSE, NULL); - if (!e_soap_response_from_string (response, xmlstr, xmlstr_length)) { - g_object_unref (response); - return NULL; - } - - return response; -} - -/** - * e_soap_response_new_from_xmldoc: - * @xmldoc: the XML document to parse. - * - * Create a new #ESoapResponse object from the XML document contained - * in @xmldoc. - * - * Returns: the new #ESoapResponse (or %NULL if there was an - * error). - */ -ESoapResponse * -e_soap_response_new_from_xmldoc (xmlDoc *xmldoc) -{ - ESoapResponse *response; - - g_return_val_if_fail (xmldoc != NULL, NULL); - - response = g_object_new (E_TYPE_SOAP_RESPONSE, NULL); - if (!e_soap_response_from_xmldoc (response, xmldoc)) { - g_object_unref (response); - return NULL; - } - - return response; -} - -static void -parse_parameters (ESoapResponse *response, - xmlNodePtr xml_method) -{ - xmlNodePtr tmp; - - for (tmp = soup_xml_real_node (xml_method->children); - tmp != NULL; - tmp = soup_xml_real_node (tmp->next)) { - if (!strcmp ((const gchar *) tmp->name, "Fault")) { - response->priv->soap_fault = tmp; - continue; - } else { - /* regular parameters */ - response->priv->parameters = g_list_append ( - response->priv->parameters, tmp); - } - } -} - -/** - * e_soap_response_from_string: - * @response: the #ESoapResponse object. - * @xmlstr: XML string to parse. - * @xmlstr_len: XML string's length or -1 for a null-terminated string - * - * Parses the string contained in @xmlstr and sets all properties from - * it in the @response object. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ -gboolean -e_soap_response_from_string (ESoapResponse *response, - const gchar *xmlstr, - gint xmlstr_length) -{ - xmlDocPtr xmldoc; - - g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), FALSE); - g_return_val_if_fail (xmlstr != NULL, FALSE); - - /* parse the string */ - xmldoc = xmlParseMemory (xmlstr, xmlstr_length == -1 ? strlen (xmlstr) : xmlstr_length); - if (!xmldoc) - return FALSE; - - return e_soap_response_from_xmldoc (response, xmldoc); -} - -/** - * e_soap_response_from_xmldoc: - * @response: the #ESoapResponse object. - * @xmldoc: XML document. - * - * Sets all properties from the @xmldoc in the @response object. - * - * Returns: %TRUE if successful, %FALSE otherwise. - */ -gboolean -e_soap_response_from_xmldoc (ESoapResponse *response, - xmlDoc *xmldoc) -{ - xmlNodePtr xml_root, xml_body, xml_method = NULL; - - g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), FALSE); - g_return_val_if_fail (xmldoc != NULL, FALSE); - - xml_root = xmlDocGetRootElement (xmldoc); - if (!xml_root) { - xmlFreeDoc (xmldoc); - return FALSE; - } - - if (strcmp ((const gchar *) xml_root->name, "Envelope") != 0) { - xmlFreeDoc (xmldoc); - return FALSE; - } - - xml_body = soup_xml_real_node (xml_root->children); - if (xml_body != NULL) { - if (strcmp ((const gchar *) xml_body->name, "Header") == 0) { - /* read header parameters */ - parse_parameters (response, xml_body); - xml_body = soup_xml_real_node (xml_body->next); - } - if (strcmp ((const gchar *) xml_body->name, "Body") != 0) { - xmlFreeDoc (xmldoc); - return FALSE; - } - - xml_method = soup_xml_real_node (xml_body->children); - - /* read all parameters */ - if (xml_method) - parse_parameters (response, xml_method); - } - - xmlFreeDoc (response->priv->xmldoc); - response->priv->xmldoc = xmldoc; - - response->priv->xml_root = xml_root; - response->priv->xml_body = xml_body; - response->priv->xml_method = xml_method; - - return TRUE; -} - -/** - * e_soap_response_get_method_name: - * @response: the #ESoapResponse object. - * - * Gets the method name from the SOAP response. - * - * Returns: the method name. - */ -const gchar * -e_soap_response_get_method_name (ESoapResponse *response) -{ - g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), NULL); - g_return_val_if_fail (response->priv->xml_method != NULL, NULL); - - return (const gchar *) response->priv->xml_method->name; -} - -/** - * e_soap_response_set_method_name: - * @response: the #ESoapResponse object. - * @method_name: the method name to set. - * - * Sets the method name on the given #ESoapResponse. - */ -void -e_soap_response_set_method_name (ESoapResponse *response, - const gchar *method_name) -{ - g_return_if_fail (E_IS_SOAP_RESPONSE (response)); - g_return_if_fail (response->priv->xml_method != NULL); - g_return_if_fail (method_name != NULL); - - xmlNodeSetName ( - response->priv->xml_method, - (const xmlChar *) method_name); -} - -/** - * e_soap_parameter_get_name: - * @param: the parameter - * - * Returns the parameter name. - * - * Returns: the parameter name. - */ -const gchar * -e_soap_parameter_get_name (ESoapParameter *param) -{ - g_return_val_if_fail (param != NULL, NULL); - - return (const gchar *) param->name; -} - -/** - * e_soap_parameter_get_int_value: - * @param: the parameter - * - * Returns the parameter's (integer) value. - * - * Returns: the parameter value as an integer - */ -gint -e_soap_parameter_get_int_value (ESoapParameter *param) -{ - gint i; - xmlChar *s; - g_return_val_if_fail (param != NULL, -1); - - s = xmlNodeGetContent (param); - if (s) { - i = atoi ((gchar *) s); - xmlFree (s); - - return i; - } - - return -1; -} - -/** - * e_soap_parameter_get_string_value: - * @param: the parameter - * - * Returns the parameter's value. - * - * Returns: the parameter value as a string, which must be freed - * by the caller. - */ -gchar * -e_soap_parameter_get_string_value (ESoapParameter *param) -{ - xmlChar *xml_s; - gchar *s; - g_return_val_if_fail (param != NULL, NULL); - - xml_s = xmlNodeGetContent (param); - s = g_strdup ((gchar *) xml_s); - xmlFree (xml_s); - - return s; -} - -/** - * e_soap_parameter_get_first_child: - * @param: A #ESoapParameter. - * - * Gets the first child of the given #ESoapParameter. This is used - * for compound data types, which can contain several parameters - * themselves. - * - * Returns: the first child or %NULL if there are no children. - */ -ESoapParameter * -e_soap_parameter_get_first_child (ESoapParameter *param) -{ - g_return_val_if_fail (param != NULL, NULL); - - return soup_xml_real_node (param->children); -} - -/** - * e_soap_parameter_get_first_child_by_name: - * @param: A #ESoapParameter. - * @name: The name of the child parameter to look for. - * - * Gets the first child of the given #ESoapParameter whose name is - * @name. - * - * Returns: the first child with the given name or %NULL if there - * are no children. - */ -ESoapParameter * -e_soap_parameter_get_first_child_by_name (ESoapParameter *param, - const gchar *name) -{ - ESoapParameter *tmp; - - g_return_val_if_fail (param != NULL, NULL); - g_return_val_if_fail (name != NULL, NULL); - - for (tmp = e_soap_parameter_get_first_child (param); - tmp != NULL; - tmp = e_soap_parameter_get_next_child (tmp)) { - if (!strcmp (name, (const gchar *) tmp->name)) - return tmp; - } - - return NULL; -} - -/** - * e_soap_parameter_get_next_child: - * @param: A #ESoapParameter. - * - * Gets the next sibling of the given #ESoapParameter. This is used - * for compound data types, which can contain several parameters - * themselves. - * - * FIXME: the name of this method is wrong - * - * Returns: the next sibling, or %NULL if there are no more - * siblings. - */ -ESoapParameter * -e_soap_parameter_get_next_child (ESoapParameter *param) -{ - g_return_val_if_fail (param != NULL, NULL); - - return soup_xml_real_node (param->next); -} - -/** - * e_soap_parameter_get_next_child_by_name: - * @param: A #ESoapParameter. - * @name: The name of the sibling parameter to look for. - * - * Gets the next sibling of the given #ESoapParameter whose name is - * @name. - * - * FIXME: the name of this method is wrong - * - * Returns: the next sibling with the given name, or %NULL - */ -ESoapParameter * -e_soap_parameter_get_next_child_by_name (ESoapParameter *param, - const gchar *name) -{ - ESoapParameter *tmp; - - g_return_val_if_fail (param != NULL, NULL); - g_return_val_if_fail (name != NULL, NULL); - - for (tmp = e_soap_parameter_get_next_child (param); - tmp != NULL; - tmp = e_soap_parameter_get_next_child (tmp)) { - if (!strcmp (name, (const gchar *) tmp->name)) - return tmp; - } - - return NULL; -} - -/** - * e_soap_parameter_get_property: - * @param: the parameter - * @prop_name: Name of the property to retrieve. - * - * Returns the named property of @param. - * - * Returns: the property, which must be freed by the caller. - */ -gchar * -e_soap_parameter_get_property (ESoapParameter *param, - const gchar *prop_name) -{ - xmlChar *xml_s; - gchar *s; - - g_return_val_if_fail (param != NULL, NULL); - g_return_val_if_fail (prop_name != NULL, NULL); - - xml_s = xmlGetProp (param, (const xmlChar *) prop_name); - s = g_strdup ((gchar *) xml_s); - xmlFree (xml_s); - - return s; -} - -/** - * e_soap_response_get_parameters: - * @response: the #ESoapResponse object. - * - * Returns the list of parameters received in the SOAP response. - * - * Returns: a list of #ESoapParameter - */ -const GList * -e_soap_response_get_parameters (ESoapResponse *response) -{ - g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), NULL); - - return (const GList *) response->priv->parameters; -} - -ESoapParameter * -e_soap_response_get_parameter (ESoapResponse *response) -{ - g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), NULL); - - return response->priv->xml_method; -} - -/** - * e_soap_response_get_first_parameter: - * @response: the #ESoapResponse object. - * - * Retrieves the first parameter contained in the SOAP response. - * - * Returns: a #ESoapParameter representing the first - * parameter, or %NULL if there are no parameters. - */ -ESoapParameter * -e_soap_response_get_first_parameter (ESoapResponse *response) -{ - g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), NULL); - - if (response->priv->parameters == NULL) - return NULL; - - return response->priv->parameters->data; -} - -/** - * e_soap_response_get_first_parameter_by_name: - * @response: the #ESoapResponse object. - * @name: the name of the parameter to look for. - * @error: return location for a #GError, or %NULL - * - * Retrieves the first parameter contained in the SOAP response whose - * name is @name. If no parameter is found, the function sets @error - * and returns %NULL. - * - * The function also checks for a SOAP "faultstring" parameter and, - * if found, uses it to set the #GError message. - * - * Returns: a #ESoapParameter representing the first parameter - * with the given name, or %NULL. - */ -ESoapParameter * -e_soap_response_get_first_parameter_by_name (ESoapResponse *response, - const gchar *name, - GError **error) -{ - GList *l; - - g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), NULL); - g_return_val_if_fail (name != NULL, NULL); - - for (l = response->priv->parameters; l != NULL; l = l->next) { - ESoapParameter *param = (ESoapParameter *) l->data; - - if (strcmp (name, (const gchar *) param->name) == 0) - return param; - } - - /* XXX These are probably not the best error codes, but - * wanted to avoid EWS_CONNECTION_ERROR codes since - * this class is potentially reusable. */ - - for (l = response->priv->parameters; l != NULL; l = l->next) { - ESoapParameter *param = (ESoapParameter *) l->data; - - if (strcmp ("faultstring", (const gchar *) param->name) == 0) { - gchar *string; - - string = e_soap_parameter_get_string_value (param); - - g_set_error ( - error, - SOUP_HTTP_ERROR, SOUP_STATUS_IO_ERROR, - "%s", (string != NULL) ? string : - " in SOAP response"); - - g_free (string); - - return NULL; - } - } - - g_set_error ( - error, - SOUP_HTTP_ERROR, SOUP_STATUS_MALFORMED, - "Missing <%s> in SOAP response", name); - - return NULL; -} - -/** - * e_soap_response_get_next_parameter: - * @response: the #ESoapResponse object. - * @from: the parameter to start from. - * - * Retrieves the parameter following @from in the #ESoapResponse - * object. - * - * Returns: a #ESoapParameter representing the parameter. - */ -ESoapParameter * -e_soap_response_get_next_parameter (ESoapResponse *response, - ESoapParameter *from) -{ - GList *l; - - g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), NULL); - g_return_val_if_fail (from != NULL, NULL); - - l = g_list_find (response->priv->parameters, (gconstpointer) from); - if (!l) - return NULL; - - return l->next ? (ESoapParameter *) l->next->data : NULL; -} - -/** - * e_soap_response_get_next_parameter_by_name: - * @response: the #ESoapResponse object. - * @from: the parameter to start from. - * @name: the name of the parameter to look for. - * - * Retrieves the first parameter following @from in the - * #ESoapResponse object whose name matches @name. - * - * Returns: a #ESoapParameter representing the parameter. - */ -ESoapParameter * -e_soap_response_get_next_parameter_by_name (ESoapResponse *response, - ESoapParameter *from, - const gchar *name) -{ - ESoapParameter *param; - - g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), NULL); - g_return_val_if_fail (from != NULL, NULL); - g_return_val_if_fail (name != NULL, NULL); - - param = e_soap_response_get_next_parameter (response, from); - while (param) { - const gchar *param_name = e_soap_parameter_get_name (param); - - if (param_name) { - if (!strcmp (name, param_name)) - return param; - } - - param = e_soap_response_get_next_parameter (response, param); - } - - return NULL; -} - -/** - * e_soap_response_dump_response: - **/ -gint -e_soap_response_dump_response (ESoapResponse *response, - FILE *buffer) -{ - xmlChar *xmlbuff; - gint buffersize, ret; - - g_return_val_if_fail (E_IS_SOAP_RESPONSE (response), -1); - - xmlDocDumpFormatMemory ( - response->priv->xmldoc, - &xmlbuff, &buffersize, 1); - ret = fputs ((gchar *) xmlbuff, buffer); - xmlFree (xmlbuff); - - return ret; -} diff --git a/src/server/e-soap-response.h b/src/server/e-soap-response.h index 25ce77b..1184379 100644 --- a/src/server/e-soap-response.h +++ b/src/server/e-soap-response.h @@ -101,8 +101,6 @@ ESoapParameter * const gchar *name); gint e_soap_response_dump_response (ESoapResponse *response, FILE *buffer); -gchar * e_soap_response_dump_parameter (ESoapResponse *response, - ESoapParameter *param); G_END_DECLS diff --git a/src/server/e-soap-response.h.sync-category-list b/src/server/e-soap-response.h.sync-category-list deleted file mode 100644 index 1184379..0000000 --- a/src/server/e-soap-response.h.sync-category-list +++ /dev/null @@ -1,108 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* - * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) - */ - -#ifndef E_SOAP_RESPONSE_H -#define E_SOAP_RESPONSE_H - -#include -#include - -/* Standard GObject macros */ -#define E_TYPE_SOAP_RESPONSE \ - (e_soap_response_get_type ()) -#define E_SOAP_RESPONSE(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST \ - ((obj), E_TYPE_SOAP_RESPONSE, ESoapResponse)) -#define E_SOAP_RESPONSE_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_CAST \ - ((cls), E_TYPE_SOAP_RESPONSE, ESoapResponseClass)) -#define E_IS_SOAP_RESPONSE(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE \ - ((obj), E_TYPE_SOAP_RESPONSE)) -#define E_IS_SOAP_RESPONSE_CLASS(cls) \ - (G_TYPE_CHECK_CLASS_TYPE \ - ((cls), E_TYPE_SOAP_RESPONSE)) -#define E_SOAP_RESPONSE_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS \ - ((obj), E_TYPE_SOAP_RESPONSE, ESoapResponseClass)) - -G_BEGIN_DECLS - -typedef struct _ESoapResponse ESoapResponse; -typedef struct _ESoapResponseClass ESoapResponseClass; -typedef struct _ESoapResponsePrivate ESoapResponsePrivate; - -struct _ESoapResponse { - GObject parent; - ESoapResponsePrivate *priv; -}; - -struct _ESoapResponseClass { - GObjectClass parent_class; -}; - -GType e_soap_response_get_type (void) G_GNUC_CONST; -ESoapResponse * e_soap_response_new (void); -ESoapResponse * e_soap_response_new_from_string (const gchar *xmlstr, - gint xmlstr_length); -ESoapResponse * e_soap_response_new_from_xmldoc (xmlDoc *xmldoc); -gboolean e_soap_response_from_string (ESoapResponse *response, - const gchar *xmlstr, - gint xmlstr_length); -gboolean e_soap_response_from_xmldoc (ESoapResponse *response, - xmlDoc *xmldoc); -const gchar * e_soap_response_get_method_name (ESoapResponse *response); -void e_soap_response_set_method_name (ESoapResponse *response, - const gchar *method_name); - -typedef xmlNode ESoapParameter; - -const gchar * e_soap_parameter_get_name (ESoapParameter *param); -gint e_soap_parameter_get_int_value (ESoapParameter *param); -gchar * e_soap_parameter_get_string_value - (ESoapParameter *param); -ESoapParameter * - e_soap_parameter_get_first_child - (ESoapParameter *param); -ESoapParameter * - e_soap_parameter_get_first_child_by_name - (ESoapParameter *param, - const gchar *name); -ESoapParameter * - e_soap_parameter_get_next_child (ESoapParameter *param); -ESoapParameter * - e_soap_parameter_get_next_child_by_name - (ESoapParameter *param, - const gchar *name); -gchar * e_soap_parameter_get_property (ESoapParameter *param, - const gchar *prop_name); - -const GList * e_soap_response_get_parameters (ESoapResponse *response); -ESoapParameter * - e_soap_response_get_parameter (ESoapResponse *response); -ESoapParameter * - e_soap_response_get_first_parameter - (ESoapResponse *response); -ESoapParameter * - e_soap_response_get_first_parameter_by_name - (ESoapResponse *response, - const gchar *name, - GError **error); -ESoapParameter * - e_soap_response_get_next_parameter - (ESoapResponse *response, - ESoapParameter *from); -ESoapParameter * - e_soap_response_get_next_parameter_by_name - (ESoapResponse *response, - ESoapParameter *from, - const gchar *name); -gint e_soap_response_dump_response (ESoapResponse *response, - FILE *buffer); - -G_END_DECLS - -#endif /* E_SOAP_RESPONSE_H */ -