[evolution-ews] Derive from EBookMetaBackend and ECalMetaBackend
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-ews] Derive from EBookMetaBackend and ECalMetaBackend
- Date: Wed, 7 Jun 2017 12:58:14 +0000 (UTC)
commit afeea7ba8a6eacdbe59cbfe9e3576bb1a1500cc8
Author: Milan Crha <mcrha redhat com>
Date: Wed Jun 7 14:56:36 2017 +0200
Derive from EBookMetaBackend and ECalMetaBackend
src/addressbook/e-book-backend-ews.c | 4288 ++++++++---------------
src/addressbook/e-book-backend-ews.h | 13 +-
src/calendar/e-cal-backend-ews-utils.c | 35 +-
src/calendar/e-cal-backend-ews-utils.h | 1 -
src/calendar/e-cal-backend-ews.c | 6053 +++++++++++++-------------------
src/calendar/e-cal-backend-ews.h | 18 +-
src/server/e-ews-connection.c | 42 +-
src/server/e-ews-connection.h | 4 +-
src/server/e-ews-item.h | 5 +
9 files changed, 4066 insertions(+), 6393 deletions(-)
---
diff --git a/src/addressbook/e-book-backend-ews.c b/src/addressbook/e-book-backend-ews.c
index 7f0aab3..c37a79d 100644
--- a/src/addressbook/e-book-backend-ews.c
+++ b/src/addressbook/e-book-backend-ews.c
@@ -3,6 +3,7 @@
/* 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
@@ -50,97 +51,38 @@
#include "ews-oab-decoder.h"
#include "ews-oab-decompress.h"
-
-#define d(x) x
+#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)
-static gboolean
-ebews_fetch_items (EBookBackendEws *ebews, GSList *items, GSList **contacts,
- GCancellable *cancellable, GError **error);
+#define EWS_MAX_FETCH_COUNT 500
-static void cursors_contact_added (EBookBackendEws *bf, EContact *contact);
-static void cursors_contact_removed (EBookBackendEws *bf, EContact *contact);
-static void cursors_recalculate (EBookBackendEws *bf);
+#define ELEMENT_TYPE_SIMPLE 0x01 /* simple string fields */
+#define ELEMENT_TYPE_COMPLEX 0x02 /* complex fields while require different get/set functions */
-typedef struct {
- GCond cond;
- GMutex mutex;
- gboolean exit;
-} SyncDelta;
+/* 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 contacts:Manager
contacts:Department contacts:SpouseName contacts:AssistantName contacts:BusinessHomePage contacts:Birthday"
struct _EBookBackendEwsPrivate {
- gchar *base_directory;
+ GRecMutex cnc_lock;
EEwsConnection *cnc;
- gchar *folder_id;
- gchar *oab_url;
- gchar *folder_name;
-
- EBookSqlite *summary;
- gboolean is_writable;
- gboolean marked_for_offline;
- gboolean cache_ready;
+ gchar *folder_id;
gboolean is_gal;
- GHashTable *ops;
-
- /* used for storing attachments */
- gchar *attachment_dir;
-
- GRecMutex rec_mutex;
- GThread *dthread;
- SyncDelta *dlock;
-
- GCancellable *cancellable;
-
guint subscription_key;
- gboolean listen_notifications;
-
- guint rev_counter;
- gchar *locale;
-
- GList *cursors;
-};
-/* using this for backward compatibility with E_DATA_BOOK_MODE */
-enum {
- MODE_LOCAL,
- MODE_REMOTE,
- MODE_ANY
+ /* used for storing attachments */
+ gchar *attachments_dir;
};
-#define EWS_MAX_FETCH_COUNT 500
-#define REFRESH_INTERVAL 21600
-
-#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 contacts:Manager
contacts:Department contacts:SpouseName contacts:AssistantName contacts:BusinessHomePage contacts:Birthday"
-
-/* NB: This is locked *outside* the EBookSqlite lock. Never lock it under e_book_sqlite_lock() */
-#define PRIV_LOCK(p) (g_rec_mutex_lock (&(p)->rec_mutex))
-#define PRIV_UNLOCK(p) (g_rec_mutex_unlock (&(p)->rec_mutex))
-
-/* Forward Declarations */
-static void e_book_backend_ews_initable_init
- (GInitableIface *iface);
-static gpointer ews_update_items_thread (gpointer data);
-
-
-G_DEFINE_TYPE_WITH_CODE (
- EBookBackendEws,
- e_book_backend_ews,
- E_TYPE_BOOK_BACKEND,
- G_IMPLEMENT_INTERFACE (
- G_TYPE_INITABLE,
- e_book_backend_ews_initable_init))
+G_DEFINE_TYPE (EBookBackendEws, e_book_backend_ews, E_TYPE_BOOK_META_BACKEND)
static CamelEwsSettings *
-book_backend_ews_get_collection_settings (EBookBackendEws *backend)
+ebb_ews_get_collection_settings (EBookBackendEws *bbews)
{
ESource *source;
ESource *collection;
@@ -149,15 +91,14 @@ book_backend_ews_get_collection_settings (EBookBackendEws *backend)
CamelSettings *settings;
const gchar *extension_name;
- source = e_backend_get_source (E_BACKEND (backend));
- registry = e_book_backend_get_registry (E_BOOK_BACKEND (backend));
+ 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);
+ 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);
@@ -169,13 +110,11 @@ book_backend_ews_get_collection_settings (EBookBackendEws *backend)
}
static void
-convert_error_to_edb_error (GError **perror)
+ebb_ews_convert_error_to_edb_error (GError **perror)
{
GError *error = NULL;
- g_return_if_fail (perror != NULL);
-
- if (!*perror || (*perror)->domain == E_DATA_BOOK_ERROR)
+ if (!perror || !*perror || (*perror)->domain == E_DATA_BOOK_ERROR)
return;
if ((*perror)->domain == EWS_CONNECTION_ERROR) {
@@ -194,72 +133,15 @@ convert_error_to_edb_error (GError **perror)
error = EDB_ERROR_EX (CONTACT_NOT_FOUND, (*perror)->message);
break;
}
- }
-
- if (!error)
- error = EDB_ERROR_EX (OTHER_ERROR, (*perror)->message);
-
- g_error_free (*perror);
- *perror = error;
-}
-
-static gboolean ebews_bump_revision (EBookBackendEws *ebews, GError **error)
-{
- gboolean ret;
- gchar *prop_value;
- time_t t = time (NULL);
-
- /* rev_counter is protected by the EBookSqlite lock. We only ever
- * call ebews_bump_revision() under e_book_sqlite_lock() */
- prop_value = g_strdup_printf ("%" G_GINT64_FORMAT "(%d)", (gint64) t, ++ebews->priv->rev_counter);
-
- ret = e_book_sqlite_set_key_value (ebews->priv->summary, "revision",
- prop_value, error);
- if (ret)
- e_book_backend_notify_property_changed (E_BOOK_BACKEND (ebews),
- BOOK_BACKEND_PROPERTY_REVISION,
- prop_value);
- g_free (prop_value);
-
- return ret;
-}
-
-
-static gboolean
-book_backend_ews_ensure_connected (EBookBackendEws *bbews,
- GCancellable *cancellable,
- GError **perror)
-{
- CamelEwsSettings *ews_settings;
- GError *local_error = NULL;
-
- g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (bbews), FALSE);
-
- PRIV_LOCK (bbews->priv);
- if (bbews->priv->cnc) {
- PRIV_UNLOCK (bbews->priv);
- return TRUE;
+ if (!error)
+ error = EDB_ERROR_EX (OTHER_ERROR, (*perror)->message);
}
- PRIV_UNLOCK (bbews->priv);
-
- ews_settings = book_backend_ews_get_collection_settings (bbews);
-
- if (e_ews_connection_utils_get_without_password (ews_settings)) {
- e_backend_schedule_authenticate (E_BACKEND (bbews), NULL);
- } else {
- e_backend_credentials_required_sync (E_BACKEND (bbews),
- E_SOURCE_CREDENTIALS_REASON_REQUIRED, NULL, 0, NULL,
- cancellable, &local_error);
+ if (error) {
+ g_error_free (*perror);
+ *perror = error;
}
-
- if (!local_error)
- return TRUE;
-
- g_propagate_error (perror, local_error);
-
- return FALSE;
}
static const struct phone_field_mapping {
@@ -287,7 +169,7 @@ static const struct phone_field_mapping {
};
static void
-ebews_populate_uid (EBookBackendEws *ebews,
+ebews_populate_uid (EBookBackendEws *bbews,
EContact *contact,
EEwsItem *item,
GCancellable *cancellable,
@@ -303,7 +185,7 @@ ebews_populate_uid (EBookBackendEws *ebews,
}
static void
-ebews_populate_full_name (EBookBackendEws *ebews,
+ebews_populate_full_name (EBookBackendEws *bbews,
EContact *contact,
EEwsItem *item,
GCancellable *cancellable,
@@ -317,7 +199,7 @@ ebews_populate_full_name (EBookBackendEws *ebews,
}
static void
-ebews_populate_nick_name (EBookBackendEws *ebews,
+ebews_populate_nick_name (EBookBackendEws *bbews,
EContact *contact,
EEwsItem *item,
GCancellable *cancellable,
@@ -331,7 +213,7 @@ ebews_populate_nick_name (EBookBackendEws *ebews,
}
static void
-ebews_populate_birth_date (EBookBackendEws *ebews,
+ebews_populate_birth_date (EBookBackendEws *bbews,
EContact *contact,
EEwsItem *item,
GCancellable *cancellable,
@@ -357,7 +239,7 @@ ebews_populate_birth_date (EBookBackendEws *ebews,
}
static void
-ebews_populate_anniversary (EBookBackendEws *ebews,
+ebews_populate_anniversary (EBookBackendEws *bbews,
EContact *contact,
EEwsItem *item,
GCancellable *cancellable,
@@ -383,7 +265,7 @@ ebews_populate_anniversary (EBookBackendEws *ebews,
}
static EContactPhoto *
-get_photo (EBookBackendEws *ebews,
+get_photo (EBookBackendEws *bbews,
EEwsItem *item,
GCancellable *cancellable,
GError **error)
@@ -407,7 +289,7 @@ get_photo (EBookBackendEws *ebews,
contact_item_ids = g_slist_prepend (contact_item_ids, g_strdup (id->id));
if (!e_ews_connection_get_items_sync (
- ebews->priv->cnc,
+ bbews->priv->cnc,
EWS_PRIORITY_MEDIUM,
contact_item_ids,
"IdOnly",
@@ -428,7 +310,7 @@ get_photo (EBookBackendEws *ebews,
attachments_ids = g_slist_prepend (attachments_ids, g_strdup (contact_photo_id));
if (!e_ews_connection_get_attachments_sync (
- ebews->priv->cnc,
+ bbews->priv->cnc,
EWS_PRIORITY_MEDIUM,
NULL,
attachments_ids,
@@ -459,7 +341,7 @@ exit:
}
static void
-ebews_populate_photo (EBookBackendEws *ebews,
+ebews_populate_photo (EBookBackendEws *bbews,
EContact *contact,
EEwsItem *item,
GCancellable *cancellable,
@@ -471,10 +353,10 @@ ebews_populate_photo (EBookBackendEws *ebews,
* 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 (ebews->priv->cnc, E_EWS_EXCHANGE_2010_SP2))
+ if (!e_ews_connection_satisfies_server_version (bbews->priv->cnc, E_EWS_EXCHANGE_2010_SP2))
return;
- photo = get_photo (ebews, item, cancellable, error);
+ photo = get_photo (bbews, item, cancellable, error);
if (!photo) {
return;
}
@@ -497,7 +379,7 @@ set_phone_number (EContact *contact,
}
static void
-ebews_populate_phone_numbers (EBookBackendEws *ebews,
+ebews_populate_phone_numbers (EBookBackendEws *bbews,
EContact *contact,
EEwsItem *item,
GCancellable *cancellable,
@@ -542,7 +424,7 @@ set_address (EContact *contact,
}
static void
-ebews_populate_address (EBookBackendEws *ebews,
+ebews_populate_address (EBookBackendEws *bbews,
EContact *contact,
EEwsItem *item,
GCancellable *cancellable,
@@ -555,7 +437,7 @@ ebews_populate_address (EBookBackendEws *ebews,
}
static void
-ebews_populate_ims (EBookBackendEws *ebews,
+ebews_populate_ims (EBookBackendEws *bbews,
EContact *contact,
EEwsItem *item,
GCancellable *cancellable,
@@ -566,7 +448,7 @@ ebews_populate_ims (EBookBackendEws *ebews,
}
static void
-ebews_populate_notes (EBookBackendEws *ebews,
+ebews_populate_notes (EBookBackendEws *bbews,
EContact *contact,
EEwsItem *item,
GCancellable *cancellable,
@@ -599,7 +481,7 @@ set_email_address (EContact *contact,
}
static void
-ebews_populate_emails_ex (EBookBackendEws *ebews,
+ebews_populate_emails_ex (EBookBackendEws *bbews,
EContact *contact,
EEwsItem *item,
gboolean require_smtp_prefix)
@@ -610,13 +492,13 @@ ebews_populate_emails_ex (EBookBackendEws *ebews,
}
static void
-ebews_populate_emails (EBookBackendEws *ebews,
+ebews_populate_emails (EBookBackendEws *bbews,
EContact *contact,
EEwsItem *item,
GCancellable *cancellable,
GError **errror)
{
- ebews_populate_emails_ex (ebews, contact, item, FALSE);
+ ebews_populate_emails_ex (bbews, contact, item, FALSE);
}
static void
@@ -844,15 +726,19 @@ convert_indexed_contact_property_to_updatexml (ESoapMessage *message,
}
static void
-ebews_set_full_name_changes (EBookBackendEws *ebews,
+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)
@@ -878,35 +764,42 @@ ebews_set_full_name_changes (EBookBackendEws *ebews,
}
static void
-ebews_set_birth_date_changes (EBookBackendEws *ebews,
+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))
- return;
+ 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);
- 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);
+ }
- 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 *ebews,
+ebews_set_anniversary_changes (EBookBackendEws *bbews,
ESoapMessage *message,
EContact *new,
EContact *old,
+ gchar **out_new_change_key,
GCancellable *cancellable,
GError **error)
{
@@ -914,21 +807,27 @@ ebews_set_anniversary_changes (EBookBackendEws *ebews,
}
static void
-set_photo (EBookBackendEws *ebews,
+set_photo (EBookBackendEws *bbews,
+ const EwsId *item_id,
EContact *contact,
EContactPhoto *photo,
+ gchar **new_change_key,
GCancellable *cancellable,
GError **error)
{
EEwsAttachmentInfo *info;
- EwsId *id;
+ EwsId *id = NULL;
GSList *files = NULL;
const guchar *data;
gsize len;
- id = g_new0 (EwsId, 1);
- id->id = e_contact_get (contact, E_CONTACT_UID);
- id->change_key = e_contact_get (contact, E_CONTACT_REV);
+ if (!item_id) {
+ id = g_new0 (EwsId, 1);
+ id->id = e_contact_get (contact, E_CONTACT_UID);
+ id->change_key = e_contact_get (contact, E_CONTACT_REV);
+
+ item_id = id;
+ }
data = e_contact_photo_get_inlined (photo, &len);
@@ -940,81 +839,127 @@ set_photo (EBookBackendEws *ebews,
files = g_slist_append (files, info);
e_ews_connection_create_attachments_sync (
- ebews->priv->cnc,
+ bbews->priv->cnc,
EWS_PRIORITY_MEDIUM,
- id,
+ item_id,
files,
TRUE,
- NULL,
+ new_change_key,
NULL,
cancellable,
error);
- g_free (id->change_key);
- g_free (id->id);
- g_free (id);
+ 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
-photos_equal (EContactPhoto *old,
- EContactPhoto *new)
+ebb_ews_photo_changed (EBookMetaBackend *meta_backend,
+ EContact *old_contact,
+ EContact *new_contact,
+ GCancellable *cancellable)
{
- const guchar *old_content, *new_content;
- gsize old_len, new_len;
+ EContact *old_contact_copy = NULL;
+ EContactPhoto *old_photo;
+ EContactPhoto *new_photo;
+ gboolean changed = FALSE;
- if (!old && !new)
- return TRUE;
+ old_photo = e_contact_get (old_contact, E_CONTACT_PHOTO);
+ new_photo = e_contact_get (new_contact, E_CONTACT_PHOTO);
- if (!old || !new)
- return FALSE;
+ if (!old_photo && new_photo)
+ changed = TRUE;
- old_content = e_contact_photo_get_inlined (old, &old_len);
- new_content = e_contact_photo_get_inlined (new, &new_len);
+ if (old_photo && !new_photo)
+ changed = TRUE;
- if (old_len != new_len)
- return FALSE;
+ /* 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;
- if (memcmp (old_content, new_content, old_len) != 0)
- return FALSE;
+ old_contact_copy = e_contact_duplicate (old_contact);
- return TRUE;
+ 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 *ebews,
+ebews_set_photo_changes (EBookBackendEws *bbews,
ESoapMessage *message,
EContact *new,
EContact *old,
+ gchar **out_new_change_key,
GCancellable *cancellable,
GError **error)
{
- EContactPhoto *old_photo, *new_photo;
+ EContactPhoto *new_photo = NULL;
EEwsAdditionalProps *add_props = NULL;
GSList *contact_item_ids = NULL, *new_items = NULL, *attachments_ids = NULL;
- gchar *id = e_contact_get (old, E_CONTACT_UID);
+ 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 (ebews->priv->cnc, E_EWS_EXCHANGE_2010_SP2))
+ 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;
+ }
- old_photo = e_contact_get (old, E_CONTACT_PHOTO);
- new_photo = e_contact_get (new, E_CONTACT_PHOTO);
+ if (!ebb_ews_photo_changed (E_BOOK_META_BACKEND (bbews), old, new, cancellable))
+ return;
- if (photos_equal (old_photo, new_photo))
- goto exit;
+ 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 (
- ebews->priv->cnc,
+ bbews->priv->cnc,
EWS_PRIORITY_MEDIUM,
contact_item_ids,
"IdOnly",
@@ -1033,38 +978,68 @@ ebews_set_photo_changes (EBookBackendEws *ebews,
if (contact_photo_id) {
attachments_ids = g_slist_prepend (attachments_ids, g_strdup (contact_photo_id));
if (!e_ews_connection_delete_attachments_sync (
- ebews->priv->cnc,
+ bbews->priv->cnc,
EWS_PRIORITY_MEDIUM,
attachments_ids,
- NULL,
+ &new_change_key,
cancellable,
error))
goto exit;
}
- if (new_photo)
- set_photo (ebews, new, new_photo, cancellable, error);
+ if (new_photo) {
+ EwsId *item_id = NULL;
-exit:
+ 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 (old_photo);
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 *ebews,
+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);
@@ -1149,23 +1124,28 @@ compare_address (ESoapMessage *message,
}
static void
-ebews_set_address_changes (EBookBackendEws *ebews,
+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 *ebews,
+ebews_set_im_changes (EBookBackendEws *bbews,
ESoapMessage *message,
EContact *new,
EContact *old,
+ gchar **out_new_change_key,
GCancellable *cancellable,
GError **error)
{
@@ -1173,15 +1153,19 @@ ebews_set_im_changes (EBookBackendEws *ebews,
}
static void
-ebews_set_notes_changes (EBookBackendEws *ebews,
+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);
@@ -1195,15 +1179,19 @@ ebews_set_notes_changes (EBookBackendEws *ebews,
}
static void
-ebews_set_email_changes (EBookBackendEws *ebews,
+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)
@@ -1227,7 +1215,7 @@ ebews_set_email_changes (EBookBackendEws *ebews,
}
static void
-ebews_populate_givenname (EBookBackendEws *ebews,
+ebews_populate_givenname (EBookBackendEws *bbews,
EContact *contact,
EEwsItem *item,
GCancellable *cancellable,
@@ -1248,10 +1236,11 @@ ebews_set_givenname (ESoapMessage *message,
}
static void
-ebews_set_givenname_changes (EBookBackendEws *ebews,
+ebews_set_givenname_changes (EBookBackendEws *bbews,
ESoapMessage *message,
EContact *new,
EContact *old,
+ gchar **out_new_change_key,
GCancellable *cancellable,
GError **error)
{
@@ -1264,9 +1253,9 @@ static const struct field_element_mapping {
const gchar *element_name;
/* set function for simple string type values */
const gchar * (*get_simple_prop_func) (EEwsItem *item);
- void (*populate_contact_func)(EBookBackendEws *ebews, EContact *contact, EEwsItem *item, GCancellable
*cancellable, GError **error);
+ 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 *ebews, ESoapMessage *message, EContact *new, EContact *old,
GCancellable *cancellable, GError **error);
+ 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 */
@@ -1301,18 +1290,9 @@ static const struct field_element_mapping {
{ E_CONTACT_UID, ELEMENT_TYPE_COMPLEX, "ItemId", NULL, ebews_populate_uid, ebews_set_item_id},
};
-typedef struct {
- EBookBackendEws *ebews;
- EDataBook *book;
- EContact *contact;
- guint32 opid;
- GCancellable *cancellable;
- gboolean is_dl;
-} EwsCreateContact;
-
static void
-ews_write_dl_members (ESoapMessage *msg,
- EContact *contact)
+ebb_ews_write_dl_members (ESoapMessage *msg,
+ EContact *contact)
{
GSList *emails, *l;
@@ -1346,8 +1326,8 @@ ews_write_dl_members (ESoapMessage *msg,
}
static void
-convert_dl_to_xml (ESoapMessage *msg,
- gpointer user_data)
+ebb_ews_convert_dl_to_xml_cb (ESoapMessage *msg,
+ gpointer user_data)
{
EContact *contact = user_data;
EVCardAttribute *attribute;
@@ -1360,16 +1340,16 @@ convert_dl_to_xml (ESoapMessage *msg,
values = e_vcard_attribute_get_values (attribute);
e_ews_message_write_string_parameter (msg, "DisplayName", NULL, values->data);
- ews_write_dl_members (msg, contact);
+ ebb_ews_write_dl_members (msg, contact);
e_soap_message_end_element (msg); /* DistributionList */
}
static void
-convert_contact_to_xml (ESoapMessage *msg,
- gpointer user_data)
+ebb_ews_convert_contact_to_xml_cb (ESoapMessage *msg,
+ gpointer user_data)
{
- EContact *contact = (EContact *) user_data;
+ EContact *contact = user_data;
gint i, element_type;
/* Prepare Contact node in the SOAP message */
@@ -1393,950 +1373,531 @@ convert_contact_to_xml (ESoapMessage *msg,
mappings[i].set_value_in_soap_message (msg, contact);
}
- // end of "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
-ews_create_contact_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
+ebb_ews_convert_dl_to_updatexml_cb (ESoapMessage *msg,
+ gpointer user_data)
{
- EEwsConnection *cnc = E_EWS_CONNECTION (object);
- EwsCreateContact *create_contact = user_data;
- EBookBackendEws *ebews = create_contact->ebews;
- GError *error = NULL;
- GSList *items = NULL;
- const EwsId *item_id;
+ ConvertData *cd = user_data;
+ EContact *old_contact = cd->old_contact;
+ EContact *new_contact = cd->new_contact;
- /* get a list of ids from server (single item) */
- e_ews_connection_create_items_finish (cnc, res, &items, &error);
+ 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 : e_contact_get_const (old_contact, E_CONTACT_REV),
+ 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_return_if_fail (ebews->priv->summary != NULL);
+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;
+ gint i, element_type;
- if (error == NULL) {
- EEwsItem *item = items->data;
- EContactPhoto *photo;
- EVCardAttribute *attr;
+ /* Pre-flight, to update the ChangeKey if needed */
+ for (i = 0; i < G_N_ELEMENTS (mappings); i++) {
+ element_type = mappings[i].element_type;
- attr = e_vcard_attribute_new (NULL, "X-EWS-KIND");
- e_vcard_add_attribute_with_value (
- E_VCARD (create_contact->contact),
- attr,
- create_contact->is_dl ? "DT_DISTLIST" : "DT_MAILUSER");
+ if (element_type == ELEMENT_TYPE_COMPLEX) {
+ gchar *new_change_key = NULL;
- /* set item id */
- item_id = e_ews_item_get_id (item);
-
- e_contact_set (create_contact->contact, E_CONTACT_UID, item_id->id);
- e_contact_set (create_contact->contact, E_CONTACT_REV, item_id->change_key);
- if (e_book_sqlite_lock (ebews->priv->summary, EBSQL_LOCK_WRITE, create_contact->cancellable,
&error)) {
- if (e_book_sqlite_add_contact (ebews->priv->summary, create_contact->contact, NULL,
- TRUE, create_contact->cancellable, &error) &&
- ebews_bump_revision (ebews, &error))
- e_book_sqlite_unlock (ebews->priv->summary, EBSQL_UNLOCK_COMMIT, &error);
- else
- e_book_sqlite_unlock (ebews->priv->summary, EBSQL_UNLOCK_ROLLBACK, &error);
- }
- if (error == NULL) {
- GSList *contacts;
+ if (mappings[i].field_id == E_CONTACT_UID)
+ continue;
- contacts = g_slist_append (NULL, create_contact->contact);
- e_data_book_respond_create_contacts (create_contact->book, create_contact->opid,
EDB_ERROR (SUCCESS), contacts);
- g_slist_free (contacts);
- cursors_contact_added (ebews, create_contact->contact);
- }
+ mappings[i].set_changes (cd->bbews, NULL, new_contact, old_contact, &new_change_key,
cd->cancellable, cd->error);
- /*
- * 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 (ebews->priv->cnc, E_EWS_EXCHANGE_2010_SP2)) {
- /*
- * 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 (create_contact->contact, E_CONTACT_PHOTO);
- if (photo) {
- set_photo (ebews, create_contact->contact, photo,
create_contact->cancellable, &error);
- e_contact_photo_free (photo);
+ if (new_change_key) {
+ g_free (cd->change_key);
+ cd->change_key = new_change_key;
}
}
-
- g_object_unref (item);
- g_slist_free (items);
- }
-
- if (error) {
- g_warning ("Error while Creating contact: %s", error->message);
- e_data_book_respond_create_contacts (create_contact->book, create_contact->opid, EDB_ERROR_EX
(OTHER_ERROR, error->message), NULL);
- }
-
- /* free memory allocated for create_contact & unref contained objects */
- g_object_unref (create_contact->ebews);
- g_object_unref (create_contact->contact);
- g_object_unref (create_contact->cancellable);
- g_free (create_contact);
- g_clear_error (&error);
-}
-
-static void
-e_book_backend_ews_create_contacts (EBookBackend *backend,
- EDataBook *book,
- guint32 opid,
- GCancellable *cancellable,
- const GSList *vcards)
-{
- EContact *contact = NULL;
- EBookBackendEws *ebews;
- EwsCreateContact *create_contact;
- EwsFolderId *fid;
- EBookBackendEwsPrivate *priv;
- GError *error = NULL;
- gboolean is_dl = FALSE;
-
- if (vcards->next != NULL) {
- e_data_book_respond_create_contacts (
- book, opid,
- EDB_ERROR_EX (NOT_SUPPORTED,
- _("The backend does not support bulk additions")),
- NULL);
- return;
}
- ebews = E_BOOK_BACKEND_EWS (backend);
- priv = ebews->priv;
+ 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 : e_contact_get_const (old_contact, E_CONTACT_REV),
+ 0);
- if (!e_backend_get_online (E_BACKEND (backend)) || !ebews->priv->cnc) {
- if (!priv->is_writable) {
- e_data_book_respond_create_contacts (book, opid, EDB_ERROR (PERMISSION_DENIED), NULL);
- return;
- }
-
- e_data_book_respond_create_contacts (book, opid, EDB_ERROR (REPOSITORY_OFFLINE), NULL);
- return;
- }
+ /* Iterate for each field in contact */
- if (!book_backend_ews_ensure_connected (ebews, cancellable, &error)) {
- convert_error_to_edb_error (&error);
- e_data_book_respond_create_contacts (book, opid, error, NULL);
- return;
- }
+ 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 (!ebews->priv->is_writable) {
- e_data_book_respond_create_contacts (book, opid, EDB_ERROR (PERMISSION_DENIED), NULL);
- return;
- }
+ if (mappings[i].field_id == E_CONTACT_UID)
+ continue;
- contact = e_contact_new_from_vcard (vcards->data);
+ mappings[i].set_changes (cd->bbews, msg, new_contact, old_contact, &new_change_key,
cd->cancellable, cd->error);
- if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
- if (!e_ews_connection_satisfies_server_version (ebews->priv->cnc, E_EWS_EXCHANGE_2010)) {
- g_object_unref (contact);
- e_data_book_respond_create_contacts (
- book,
- opid,
- EDB_ERROR_EX (
- NOT_SUPPORTED,
- _("Cannot save contact list, it’s only supported on EWS Server 2010
or later")),
- NULL);
- return;
+ if (new_change_key) {
+ g_free (cd->change_key);
+ cd->change_key = new_change_key;
+ }
}
- is_dl = TRUE;
}
- create_contact = g_new0 (EwsCreateContact, 1);
- create_contact->ebews = g_object_ref (ebews);
- create_contact->book = g_object_ref (book);
- create_contact->opid = opid;
- create_contact->contact = g_object_ref (contact);
- create_contact->cancellable = g_object_ref (cancellable);
- create_contact->is_dl = is_dl;
-
- fid = e_ews_folder_id_new (priv->folder_id, NULL, FALSE);
-
- /* pass new contact component data to the exchange server and expect response in the callback */
- e_ews_connection_create_items (
- priv->cnc,
- EWS_PRIORITY_MEDIUM, NULL,
- NULL,
- fid,
- is_dl ? convert_dl_to_xml : convert_contact_to_xml,
- contact,
- cancellable,
- ews_create_contact_cb,
- create_contact);
-
- e_ews_folder_id_free (fid);
+ e_ews_message_end_item_change (msg);
}
-typedef struct {
- EBookBackendEws *ebews;
- EDataBook *book;
- guint32 opid;
- GSList *sl_ids;
- GCancellable *cancellable;
-} EwsRemoveContact;
-
-static void
-ews_book_remove_contact_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
+static EContact *
+ebb_ews_item_to_contact (EBookBackendEws *bbews,
+ EEwsItem *item,
+ GCancellable *cancellable,
+ GError **error)
{
- EwsRemoveContact *remove_contact = user_data;
- EBookBackendEws *ebews = remove_contact->ebews;
- EBookBackendEwsPrivate *priv = ebews->priv;
- GSimpleAsyncResult *simple;
- GSList *l, *contacts = NULL;
- GError *error = NULL;
-
- simple = G_SIMPLE_ASYNC_RESULT (res);
+ EContact *contact;
+ gint ii, element_type;
- g_return_if_fail (priv->summary != NULL);
+ contact = e_contact_new ();
- if (!g_simple_async_result_propagate_error (simple, &error) &&
- e_book_sqlite_lock (priv->summary, EBSQL_LOCK_WRITE, remove_contact->cancellable, &error)) {
- /* Wtf? Do we really have to fetch deleted contacts in full, just to tell the cursors? */
- for (l = remove_contact->sl_ids; l != NULL; l = g_slist_next (l)) {
- EContact *contact = NULL;
- e_book_sqlite_get_contact (priv->summary, l->data, FALSE, &contact, NULL);
- if (contact)
- contacts = g_slist_prepend (contacts, contact);
- }
- if (e_book_sqlite_remove_contacts (priv->summary, remove_contact->sl_ids,
remove_contact->cancellable, &error) &&
- ebews_bump_revision (ebews, &error))
- e_book_sqlite_unlock (priv->summary, EBSQL_UNLOCK_COMMIT, &error);
- else
- e_book_sqlite_unlock (priv->summary, EBSQL_UNLOCK_ROLLBACK, NULL);
- }
+ for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) {
+ element_type = mappings[ii].element_type;
- if (error == NULL) {
- e_data_book_respond_remove_contacts (remove_contact->book, remove_contact->opid, EDB_ERROR
(SUCCESS), remove_contact->sl_ids);
- for (l = contacts; l != NULL; l = g_slist_next (l))
- cursors_contact_removed (ebews, E_CONTACT (l->data));
- } else {
- e_data_book_respond_remove_contacts (remove_contact->book, remove_contact->opid, EDB_ERROR_EX
(OTHER_ERROR, error->message), NULL);
+ if (element_type == ELEMENT_TYPE_SIMPLE && !mappings[ii].populate_contact_func) {
+ const gchar *val = mappings[ii].get_simple_prop_func (item);
- g_warning ("\nError removing contact %s \n", error->message);
+ if (val != NULL)
+ e_contact_set (contact, mappings[ii].field_id, val);
+ } else {
+ mappings[ii].populate_contact_func (bbews, contact, item, cancellable, error);
+ }
}
- g_slist_free_full (contacts, g_object_unref);
- g_slist_free_full (remove_contact->sl_ids, g_free);
- g_object_unref (remove_contact->ebews);
- g_object_unref (remove_contact->book);
- g_object_unref (remove_contact->cancellable);
- g_free (remove_contact);
- g_clear_error (&error);
+ return contact;
}
static void
-e_book_backend_ews_remove_contacts (EBookBackend *backend,
- EDataBook *book,
- guint32 opid,
- GCancellable *cancellable,
- const GSList *id_list)
+ebb_ews_items_to_contacts (EBookBackendEws *bbews,
+ const GSList *new_items,
+ GSList **contacts,
+ GCancellable *cancellable,
+ GError **error)
{
- EBookBackendEws *ebews;
- EwsRemoveContact *remove_contact;
- EBookBackendEwsPrivate *priv;
- GSList *l, *copy = NULL;
- GError *error = NULL;
+ GSList *link;
- ebews = E_BOOK_BACKEND_EWS (backend);
-
- priv = ebews->priv;
+ for (link = (GSList *) new_items; link; link = g_slist_next (link)) {
+ EContact *contact;
+ EEwsItem *item = link->data;
+ EVCardAttribute *attr;
- if (!e_backend_get_online (E_BACKEND (backend)) || !ebews->priv->cnc) {
- if (!priv->is_writable) {
- e_data_book_respond_remove_contacts (book, opid, EDB_ERROR (PERMISSION_DENIED), NULL);
- return;
- }
+ if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR)
+ continue;
- e_data_book_respond_remove_contacts (book, opid, EDB_ERROR (REPOSITORY_OFFLINE), NULL);
- return;
- }
+ contact = ebb_ews_item_to_contact (bbews, item, cancellable, error);
- if (!book_backend_ews_ensure_connected (ebews, cancellable, &error)) {
- convert_error_to_edb_error (&error);
- e_data_book_respond_remove_contacts (book, opid, error, NULL);
- return;
- }
+ attr = e_vcard_attribute_new (NULL, "X-EWS-KIND");
+ e_vcard_add_attribute_with_value (E_VCARD (contact), attr, "DT_MAILUSER");
- if (!ebews->priv->is_writable) {
- e_data_book_respond_remove_contacts (book, opid, EDB_ERROR (PERMISSION_DENIED), NULL);
- return;
+ *contacts = g_slist_prepend (*contacts, contact);
}
-
- for (l = (GSList *) id_list; l != NULL; l = g_slist_next (l))
- copy = g_slist_prepend (copy, g_strdup ((gchar *) l->data));
- copy = g_slist_reverse (copy);
-
- remove_contact = g_new0 (EwsRemoveContact, 1);
- remove_contact->ebews = g_object_ref (ebews);
- remove_contact->book = g_object_ref (book);
- remove_contact->opid = opid;
- remove_contact->sl_ids = copy;
- remove_contact->cancellable = g_object_ref(cancellable);
-
- e_ews_connection_delete_items (
- priv->cnc, EWS_PRIORITY_MEDIUM, (GSList *) id_list,
- EWS_HARD_DELETE, 0 , FALSE,
- cancellable,
- ews_book_remove_contact_cb,
- remove_contact);
}
-typedef struct {
- EBookBackendEws *ebews;
- EDataBook *book;
- EContact *new_contact;
- EContact *old_contact;
- guint32 opid;
- GCancellable *cancellable;
-} EwsModifyContact;
-
-static void
-ews_modify_contact_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
+static gboolean
+ebb_ews_traverse_dl (EBookBackendEws *bbews,
+ EContact **contact,
+ GHashTable *items,
+ GHashTable *values,
+ EwsMailbox *mb,
+ GCancellable *cancellable,
+ GError **error)
{
- EEwsConnection *cnc = E_EWS_CONNECTION (object);
- EwsModifyContact *modify_contact = user_data;
- EBookBackendEws *ebews = modify_contact->ebews;
- EBookBackendEwsPrivate *priv = ebews->priv;
- GError *error = NULL;
- GSList *items = NULL;
- gchar *id;
- const EwsId *item_id;
+ 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;
- g_object_ref (modify_contact->new_contact);
- g_object_ref (modify_contact->old_contact);
+ if (mb->item_id && mb->item_id->id)
+ ident = mb->item_id->id;
+ else if (mb->email)
+ ident = mb->email;
+ else
+ return FALSE;
- e_ews_connection_update_items_finish (cnc, res, &items, &error);
+ if (g_hash_table_lookup (items, ident) != NULL)
+ return TRUE;
- g_return_if_fail (priv->summary != NULL);
+ g_hash_table_insert (items, g_strdup (ident), GINT_TO_POINTER (1));
- if (error == NULL) {
- if (items != NULL) {
- EEwsItem *item = (EEwsItem *) items->data;
+ if (!e_ews_connection_expand_dl_sync (
+ bbews->priv->cnc,
+ EWS_PRIORITY_MEDIUM,
+ mb,
+ &members,
+ &includes_last,
+ cancellable,
+ error))
+ return FALSE;
- /* set item id */
- item_id = e_ews_item_get_id (item);
+ for (l = members; l; l = l->next) {
+ ret = ebb_ews_traverse_dl (bbews, contact, items, values, l->data, cancellable,
error);
+ if (!ret)
+ break;
+ }
- e_contact_set (modify_contact->new_contact, E_CONTACT_UID, item_id->id);
- e_contact_set (modify_contact->new_contact, E_CONTACT_REV, item_id->change_key);
+ g_slist_free_full (members, (GDestroyNotify) e_ews_mailbox_free);
+ return ret;
+ } else {
+ EVCardAttribute *attr;
+ CamelInternetAddress *addr;
+ gchar *value = NULL;
- g_object_unref (item);
- }
+ if (mb->name == NULL && mb->email == NULL)
+ return TRUE;
- id = e_contact_get (modify_contact->old_contact, E_CONTACT_UID);
+ addr = camel_internet_address_new ();
+ attr = e_vcard_attribute_new (NULL, EVC_EMAIL);
- if (e_book_sqlite_lock (ebews->priv->summary, EBSQL_LOCK_WRITE, modify_contact->cancellable,
&error)) {
- if (e_book_sqlite_remove_contact (ebews->priv->summary, id,
modify_contact->cancellable, &error) &&
- e_book_sqlite_add_contact (ebews->priv->summary, modify_contact->new_contact,
NULL,
- TRUE, modify_contact->cancellable, &error) &&
- ebews_bump_revision (ebews, &error))
- e_book_sqlite_unlock (ebews->priv->summary, EBSQL_UNLOCK_COMMIT, &error);
- else
- e_book_sqlite_unlock (ebews->priv->summary, EBSQL_UNLOCK_ROLLBACK, NULL);
- }
+ camel_internet_address_add (addr, mb->name, mb->email ? mb->email : "");
+ value = camel_address_encode (CAMEL_ADDRESS (addr));
- if (error == NULL) {
- GSList *new_contacts;
+ if (value && g_hash_table_lookup (values, value) == NULL) {
+ e_vcard_attribute_add_value (attr, value);
+ e_vcard_append_attribute (E_VCARD (*contact), attr);
- new_contacts = g_slist_append (NULL, modify_contact->new_contact);
- e_data_book_respond_modify_contacts (modify_contact->book, modify_contact->opid,
EDB_ERROR (SUCCESS), new_contacts);
- g_slist_free (new_contacts);
- cursors_contact_removed (ebews, modify_contact->old_contact);
- cursors_contact_added (ebews, modify_contact->new_contact);
+ g_hash_table_insert (values, g_strdup (value), GINT_TO_POINTER (1));
}
- g_slist_free (items);
- }
-
- if (error) {
- g_warning ("Error while Modifying contact: %s", error->message);
+ g_object_unref (addr);
- e_data_book_respond_modify_contacts (modify_contact->book, modify_contact->opid, EDB_ERROR_EX
(OTHER_ERROR, error->message), NULL);
+ return TRUE;
}
-
- /* free memory allocated for create_contact & unref contained objects */
- g_object_unref (modify_contact->ebews);
- g_object_unref (modify_contact->new_contact);
- g_object_unref (modify_contact->old_contact);
- g_object_unref (modify_contact->cancellable);
- g_free (modify_contact);
- g_clear_error (&error);
}
-static void
-convert_dl_to_updatexml (ESoapMessage *msg,
- gpointer user_data)
-{
- EwsModifyContact *modify_contact = user_data;
- EwsId *id;
- EContact *old_contact = modify_contact->old_contact;
- EContact *new_contact = modify_contact->new_contact;
-
- id = g_new0 (EwsId, 1);
- id->id = e_contact_get (old_contact, E_CONTACT_UID);
- id->change_key = e_contact_get (old_contact, E_CONTACT_REV);
-
- e_ews_message_start_item_change (msg, E_EWS_ITEMCHANGE_TYPE_ITEM, id->id, id->change_key, 0);
- e_ews_message_start_set_item_field (msg, "Members", "distributionlist", "DistributionList");
- ews_write_dl_members (msg, new_contact);
- e_ews_message_end_set_item_field (msg);
- e_ews_message_end_item_change (msg);
-}
-
-static void
-convert_contact_to_updatexml (ESoapMessage *msg,
- gpointer user_data)
+static EContact *
+ebb_ews_get_dl_info (EBookBackendEws *bbews,
+ const EwsId *id,
+ const gchar *d_name,
+ GSList *members,
+ GCancellable *cancellable,
+ GError **error)
{
- EwsModifyContact *modify_contact = user_data;
- EwsId *id;
- EContact *old_contact = modify_contact->old_contact;
- EContact *new_contact = modify_contact->new_contact;
- gchar *value = NULL, *old_value = NULL;
- gint i, element_type;
- GError *error = NULL;
+ GHashTable *items, *values;
+ GSList *l;
+ EContact *contact;
- id = g_new0 (EwsId, 1);
- id->id = e_contact_get (old_contact, E_CONTACT_UID);
- id->change_key = e_contact_get (old_contact, E_CONTACT_REV);
+ contact = e_contact_new ();
+ e_contact_set (contact, E_CONTACT_UID, id->id);
+ e_contact_set (contact, E_CONTACT_REV, id->change_key);
- e_ews_message_start_item_change (
- msg, E_EWS_ITEMCHANGE_TYPE_ITEM,
- id->id, id->change_key, 0);
+ 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);
- /*Iterate for each field in contact*/
+ 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 (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) {
- if (mappings[i].field_id == E_CONTACT_UID)
- continue;
- mappings[i].set_changes (
- modify_contact->ebews, msg,
- new_contact, old_contact,
- modify_contact->cancellable,
- &error);
-
- if (error != NULL) {
- e_data_book_respond_modify_contacts (
- modify_contact->book,
- modify_contact->opid,
- EDB_ERROR_EX (OTHER_ERROR, error->message),
- NULL);
- g_clear_error (&error);
- }
+ 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;
}
}
- e_ews_message_end_item_change (msg);
+ exit:
+ g_hash_table_destroy (items);
+ g_hash_table_destroy (values);
+
+ return contact;
}
-static void
-e_book_backend_ews_modify_contacts (EBookBackend *backend,
- EDataBook *book,
- guint32 opid,
- GCancellable *cancellable,
- const GSList *vcards)
+static gboolean
+ebb_ews_get_dl_info_gal (EBookBackendEws *bbews,
+ EContact *contact,
+ EwsMailbox *mb,
+ GCancellable *cancellable,
+ GError **error)
{
- EContact *contact = NULL, *old_contact = NULL;
- EwsModifyContact *modify_contact;
- EBookBackendEws *ebews;
- EwsId *id;
- EBookBackendEwsPrivate *priv;
- GError *error;
- gboolean is_dl = FALSE;
-
- if (vcards->next != NULL) {
- e_data_book_respond_modify_contacts (book, opid,
- EDB_ERROR_EX (
- NOT_SUPPORTED,
- _("The backend does not support bulk modifications")),
- NULL);
- return;
- }
-
- ebews = E_BOOK_BACKEND_EWS (backend);
- priv = ebews->priv;
-
- if (!e_backend_get_online (E_BACKEND (backend)) || !ebews->priv->cnc) {
- if (!priv->is_writable) {
- e_data_book_respond_modify_contacts (book, opid, EDB_ERROR (PERMISSION_DENIED), NULL);
- return;
- }
-
- e_data_book_respond_modify_contacts (book, opid, EDB_ERROR (REPOSITORY_OFFLINE), NULL);
- return;
- }
-
- if (!book_backend_ews_ensure_connected (ebews, cancellable, &error)) {
- convert_error_to_edb_error (&error);
- e_data_book_respond_modify_contacts (book, opid, error, NULL);
- return;
- }
-
- if (!priv->is_writable) {
- e_data_book_respond_modify_contacts (book, opid, EDB_ERROR (PERMISSION_DENIED), NULL);
- return;
- }
-
- g_return_if_fail (priv->summary != NULL);
-
- contact = e_contact_new_from_vcard (vcards->data);
-
- if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
- if (!e_ews_connection_satisfies_server_version (ebews->priv->cnc, E_EWS_EXCHANGE_2010)) {
- g_object_unref (contact);
- e_data_book_respond_create_contacts (
- book,
- opid,
- EDB_ERROR_EX (
- NOT_SUPPORTED,
- _("Cannot save contact list, it’s only supported on EWS Server 2010
or later")),
- NULL);
- return;
- }
- is_dl = TRUE;
- }
+ GHashTable *items, *values;
+ gboolean success;
- /*get item id and change key from contact and fetch old contact and assign.*/
+ 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);
- id = g_new0 (EwsId, 1);
- id->id = e_contact_get (contact, E_CONTACT_UID);
- id->change_key = e_contact_get (contact, E_CONTACT_REV);
+ success = ebb_ews_traverse_dl (bbews, &contact, items, values, mb, cancellable, error);
- if (e_book_sqlite_lock (priv->summary, EBSQL_LOCK_READ, cancellable, &error)) {
- e_book_sqlite_get_contact (priv->summary, id->id, TRUE, &old_contact, &error);
- e_book_sqlite_unlock (priv->summary, EBSQL_UNLOCK_NONE, error ? NULL : &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));
}
- if (!old_contact) {
- g_object_unref (contact);
- g_clear_error (&error); // Shouldn't we be using this? NOT_SUPPORTED seems wrong
- e_data_book_respond_modify_contacts (book, opid, EDB_ERROR (NOT_SUPPORTED), NULL);
- return;
- }
+ g_hash_table_destroy (items);
+ g_hash_table_destroy (values);
- /* TODO implement */
- modify_contact = g_new0 (EwsModifyContact, 1);
- modify_contact->ebews = g_object_ref (ebews);
- modify_contact->book = g_object_ref (book);
- modify_contact->opid = opid;
- modify_contact->old_contact = g_object_ref (old_contact);
- modify_contact->new_contact = g_object_ref (contact);
- modify_contact->cancellable = g_object_ref (cancellable);
-
- e_ews_connection_update_items (
- priv->cnc,
- EWS_PRIORITY_MEDIUM,
- "AlwaysOverwrite",
- "SendAndSaveCopy",
- "SendToAllAndSaveCopy",
- priv->folder_id,
- is_dl ? convert_dl_to_updatexml : convert_contact_to_updatexml,
- modify_contact,
- cancellable,
- ews_modify_contact_cb,
- modify_contact);
+ return success;
}
-static void
-e_book_backend_ews_get_contact (EBookBackend *backend,
- EDataBook *book,
- guint32 opid,
- GCancellable *cancellable,
- const gchar *id)
+static gboolean
+ebb_ews_contacts_append_dl (EBookBackendEws *bbews,
+ const EwsId *id,
+ const gchar *d_name,
+ GSList *members,
+ GSList **contacts,
+ GCancellable *cancellable,
+ GError **error)
{
- EBookBackendEws *ebews;
- GError *error = NULL;
+ EContact *contact;
+ EVCardAttribute *attr;
- ebews = E_BOOK_BACKEND_EWS (backend);
+ contact = ebb_ews_get_dl_info (bbews, id, d_name, members, cancellable, error);
+ if (contact == NULL)
+ return FALSE;
- if (!e_backend_get_online (E_BACKEND (backend)) || !ebews->priv->cnc) {
- e_data_book_respond_get_contact (book, opid, EDB_ERROR (REPOSITORY_OFFLINE), NULL);
- return;
- }
+ attr = e_vcard_attribute_new (NULL, "X-EWS-KIND");
+ e_vcard_add_attribute_with_value (E_VCARD (contact), attr, "DT_DISTLIST");
- if (!book_backend_ews_ensure_connected (ebews, cancellable, &error)) {
- convert_error_to_edb_error (&error);
- e_data_book_respond_get_contact (book, opid, error, NULL);
- return;
- }
+ *contacts = g_slist_prepend (*contacts, contact);
- e_data_book_respond_get_contact (book, opid, EDB_ERROR (CONTACT_NOT_FOUND), "");
+ return TRUE;
}
-static void
-e_book_backend_ews_get_contact_list (EBookBackend *backend,
- EDataBook *book,
- guint32 opid,
- GCancellable *cancellable,
- const gchar *query)
+static gboolean
+ebb_ews_fetch_items_sync (EBookBackendEws *bbews,
+ const GSList *items, /* EEwsItem * */
+ GSList **contacts,
+ GCancellable *cancellable,
+ GError **error)
{
- GSList *vcard_list = NULL;
- GSList *list = NULL, *l;
- GError *error = NULL;
- EBookBackendEws *ebews;
- EBookBackendEwsPrivate *priv;
- gint populated = 0;
-
- ebews = E_BOOK_BACKEND_EWS (backend);
- priv = ebews->priv;
-
- if (priv->summary)
- e_book_sqlite_get_key_value_int (priv->summary, E_BOOK_SQL_IS_POPULATED_KEY, &populated,
NULL);
-
- if (!e_backend_get_online (E_BACKEND (backend)) || !ebews->priv->cnc) {
- if (populated) {
- search_db:
- if (e_book_sqlite_lock (priv->summary, EBSQL_LOCK_READ, cancellable, &error)) {
- e_book_sqlite_search (priv->summary, query, FALSE, &list, cancellable,
&error);
-
- l = list;
- while (l) {
- EbSqlSearchData *s_data = (EbSqlSearchData *) l->data;
+ GSList *contact_item_ids = NULL, *dl_ids = NULL, *link;
+ GSList *new_items = NULL;
+ gboolean ret = FALSE;
- vcard_list = g_slist_append (vcard_list, g_strdup (s_data->vcard));
- e_book_sqlite_search_data_free (s_data);
- l = l->next;
- }
- e_book_sqlite_unlock (priv->summary, EBSQL_UNLOCK_NONE, NULL);
- }
- convert_error_to_edb_error (&error);
- e_data_book_respond_get_contact_list (book, opid, error, vcard_list);
+ 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);
- g_slist_free (list);
- g_slist_free_full (vcard_list, g_free);
- } else {
- e_data_book_respond_get_contact_list (book, opid, EDB_ERROR (OFFLINE_UNAVAILABLE),
vcard_list);
+ 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));
}
-
- return;
- }
-
- if (!book_backend_ews_ensure_connected (ebews, cancellable, &error)) {
- convert_error_to_edb_error (&error);
- e_data_book_respond_get_contact_list (book, opid, error, NULL);
- return;
}
- if (populated) {
- goto search_db;
-
- } else if (!priv->marked_for_offline) {
- GSList *items = NULL;
- EwsFolderId *fid = NULL;
- gboolean includes_last_item;
- gboolean any_applicable;
-
- fid = g_new0 (EwsFolderId, 1);
- fid->id = g_strdup (priv->folder_id);
- fid->is_distinguished_id = FALSE;
-
- any_applicable = e_ews_query_check_applicable (query, E_EWS_FOLDER_TYPE_CONTACTS);
-
- e_ews_connection_find_folder_items_sync (
- priv->cnc, EWS_PRIORITY_MEDIUM,
- fid, "IdOnly", NULL, NULL, query, NULL,
- E_EWS_FOLDER_TYPE_CONTACTS,
- &includes_last_item,
- &items, any_applicable ? e_ews_query_to_restriction : NULL,
- cancellable, &error);
-
- /*we have got Id for items lets fetch them using getitem operation*/
- ebews_fetch_items (ebews, items, &list, cancellable, &error);
-
- while (list) {
- gchar *vcard_string;
-
- l = list;
- list = list->next;
-
- vcard_string = e_vcard_to_string (E_VCARD (l->data), EVC_FORMAT_VCARD_30);
-
- g_object_unref (l->data);
- g_slist_free_1 (l);
+ /* 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);
- vcard_list = g_slist_append (vcard_list, vcard_string);
- }
+ 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);
- convert_error_to_edb_error (&error);
- e_data_book_respond_get_contact_list (book, opid, error, vcard_list);
+ e_ews_additional_props_free (add_props);
- e_ews_folder_id_free (fid);
- g_slist_free_full (vcard_list, g_free);
- } else {
- e_data_book_respond_get_contact_list (book, opid, EDB_ERROR_EX (OTHER_ERROR, _("Wait till
syncing is done")), vcard_list);
+ if (!ret)
+ goto cleanup;
}
-}
-typedef struct {
- /* For future use */
- gpointer restriction;
-
- gboolean is_autocompletion;
- gchar *auto_comp_str;
-} EBookBackendEwsSExpData;
+ if (new_items) {
+ ebb_ews_items_to_contacts (bbews, new_items, contacts, cancellable, error);
-static ESExpResult *
-func_not (ESExp *f,
- gint argc,
- ESExpResult **argv,
- gpointer data)
-{
- ESExpResult *r;
+ g_slist_free_full (new_items, g_object_unref);
+ new_items = NULL;
+ }
- if (argc != 1 || argv[0]->type != ESEXP_RES_UNDEFINED) {
- e_sexp_fatal_error (f, "parse error");
- return 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;
}
- r = e_sexp_result_new (f, ESEXP_RES_BOOL);
- r->value.boolean = FALSE;
+ 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;
- return r;
-}
+ if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR)
+ continue;
-static ESExpResult *
-func_and_or (ESExp *f,
- gint argc,
- ESExpResult **argv,
- gpointer and)
-{
- ESExpResult *r;
+ id = e_ews_item_get_id (item);
+ mb = g_new0 (EwsMailbox, 1);
+ mb->item_id = (EwsId *) id;
- r = e_sexp_result_new (f, ESEXP_RES_BOOL);
- r->value.boolean = FALSE;
+ 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, error))
+ goto cleanup;
- return r;
-}
+ ret = ebb_ews_contacts_append_dl (bbews, id, d_name, members, contacts, cancellable, error);
-/* TODO implement */
-static ESExpResult *
-func_is (struct _ESExp *f,
- gint argc,
- struct _ESExpResult **argv,
- gpointer data)
-{
- ESExpResult *r;
+ g_free (mb);
+ g_slist_free_full (members, (GDestroyNotify) e_ews_mailbox_free);
- if (argc != 2
- && argv[0]->type != ESEXP_RES_STRING
- && argv[1]->type != ESEXP_RES_STRING) {
- e_sexp_fatal_error (f, "parse error");
- return NULL;
+ if (!ret)
+ goto cleanup;
}
- r = e_sexp_result_new (f, ESEXP_RES_BOOL);
- r->value.boolean = FALSE;
+ 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 r;
+ return ret;
}
-/* TODO implement */
-static ESExpResult *
-func_endswith (struct _ESExp *f,
- gint argc,
- struct _ESExpResult **argv,
- gpointer data)
+static void
+ebb_ews_server_notification_cb (EBookBackendEws *bbews,
+ const GSList *events,
+ EEwsConnection *cnc)
{
- 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;
- }
+ GSList *link;
+ gboolean update_folder = FALSE;
- r = e_sexp_result_new (f, ESEXP_RES_BOOL);
- r->value.boolean = FALSE;
+ g_return_if_fail (E_IS_BOOK_BACKEND_EWS (bbews));
- return r;
+ 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);
-/* TODO implement */
-static ESExpResult *
-func_contains (struct _ESExp *f,
- gint argc,
- struct _ESExpResult **argv,
- gpointer data)
-{
- ESExpResult *r;
- EBookBackendEwsSExpData *sdata = data;
- const gchar *propname, *str;
+ if (g_strcmp0 (event->folder_id, bbews->priv->folder_id) == 0)
+ update_folder = TRUE;
- if (argc != 2
- && argv[0]->type != ESEXP_RES_STRING
- && argv[1]->type != ESEXP_RES_STRING) {
- e_sexp_fatal_error (f, "parse error");
- return NULL;
- }
+ 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);
- propname = argv[0]->value.string;
- str = argv[1]->value.string;
+ 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;
- if (!strcmp (propname, "full_name") || !strcmp (propname, "email")) {
- if (!sdata->auto_comp_str) {
- sdata->auto_comp_str = g_strdup (str);
- sdata->is_autocompletion = TRUE;
+ g_rec_mutex_unlock (&bbews->priv->cnc_lock);
+ break;
+ default:
+ return;
}
}
- r = e_sexp_result_new (f, ESEXP_RES_BOOL);
- r->value.boolean = FALSE;
-
- return r;
-
+ if (update_folder)
+ e_book_meta_backend_schedule_refresh (E_BOOK_META_BACKEND (bbews));
}
-/* We are just handling for autocompletion now. We need to support other fields after implementing
- * Restrictions and find_items request */
-static ESExpResult *
-func_beginswith (struct _ESExp *f,
- gint argc,
- struct _ESExpResult **argv,
- gpointer data)
+static void
+ebb_ews_unset_connection (EBookBackendEws *bbews)
{
- ESExpResult *r;
- const gchar *propname, *str;
- EBookBackendEwsSExpData *sdata = data;
+ g_return_if_fail (E_IS_BOOK_BACKEND_EWS (bbews));
- if (argc != 2 ||
- argv[0]->type != ESEXP_RES_STRING ||
- argv[1]->type != ESEXP_RES_STRING) {
- e_sexp_fatal_error (f, "parse error");
- return NULL;
- }
+ g_rec_mutex_lock (&bbews->priv->cnc_lock);
- propname = argv[0]->value.string;
- str = argv[1]->value.string;
+ if (bbews->priv->cnc) {
+ g_signal_handlers_disconnect_by_func (bbews->priv->cnc, ebb_ews_server_notification_cb,
bbews);
- if (!strcmp (propname, "full_name") || !strcmp (propname, "email")) {
- if (!sdata->auto_comp_str) {
- sdata->auto_comp_str = g_strdup (str);
- sdata->is_autocompletion = TRUE;
+ if (bbews->priv->subscription_key != 0) {
+ e_ews_connection_disable_notifications_sync (
+ bbews->priv->cnc,
+ bbews->priv->subscription_key);
+ bbews->priv->subscription_key = 0;
}
}
- r = e_sexp_result_new (f, ESEXP_RES_BOOL);
- r->value.boolean = FALSE;
- return r;
+ g_rec_mutex_unlock (&bbews->priv->cnc_lock);
}
-static struct {
- const gchar *name;
- ESExpFunc *func;
- guint flags;
-} symbols[] = {
- { "and", func_and_or, 0 },
- { "or", func_and_or, 0},
- { "not", func_not, 0 },
- { "contains", func_contains, 0},
- { "is", func_is, 0},
- { "beginswith", func_beginswith, 0},
- { "endswith", func_endswith, 0},
-};
-
-/* FIXME build a complete filter from the query that can be used by find_items */
-static gpointer
-e_book_backend_ews_build_restriction (const gchar *query,
- gboolean *autocompletion,
- gchar **auto_comp_str)
+static gint
+det_sort_func (gconstpointer _a,
+ gconstpointer _b)
{
- ESExpResult *r;
- ESExp *sexp;
- EBookBackendEwsSExpData *sdata;
- gint i;
-
- 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;
- *auto_comp_str = sdata->auto_comp_str;
- }
-
- e_sexp_result_free (sexp, r);
- g_object_unref (sexp);
- g_free (sdata);
+ const EwsOALDetails *a = _a, *b = _b;
- return NULL;
+ return a->seq - b->seq;
}
-/************* GAL sync ***********************/
-
static gchar *
-ews_download_gal_file (EBookBackendEws *cbews,
- EwsOALDetails *full,
- GCancellable *cancellable,
- GError **error)
+ebb_ews_download_gal_file (EBookBackendEws *bbews,
+ EwsOALDetails *full,
+ GCancellable *cancellable,
+ GError **error)
{
- EBookBackendEwsPrivate *priv = cbews->priv;
EEwsConnection *oab_cnc;
gchar *full_url, *oab_url;
- const gchar *cache_dir;
gchar *download_path = NULL;
gchar *password;
CamelEwsSettings *ews_settings;
+ const gchar *cache_dir;
- ews_settings = book_backend_ews_get_collection_settings (cbews);
+ ews_settings = ebb_ews_get_collection_settings (bbews);
/* oab url with oab.xml removed from the suffix */
- oab_url = g_strndup (priv->oab_url, strlen (priv->oab_url) - 7);
+ 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 (cbews));
+ 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 (full_url, ews_settings);
e_binding_bind_property (
- cbews, "proxy-resolver",
+ bbews, "proxy-resolver",
oab_cnc, "proxy-resolver",
G_BINDING_SYNC_CREATE);
- password = e_ews_connection_dup_password (priv->cnc);
+ 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)) {
+ if (!e_ews_connection_download_oal_file_sync (oab_cnc, download_path, NULL, NULL, cancellable,
error)) {
g_free (download_path);
download_path = NULL;
- goto exit;
+ } else {
+ d (printf ("OAL file downloaded %s\n", download_path));
}
- d (g_print ("OAL file downloaded %s\n", download_path);)
-
- exit:
g_object_unref (oab_cnc);
g_free (oab_url);
g_free (full_url);
@@ -2345,69 +1906,76 @@ ews_download_gal_file (EBookBackendEws *cbews,
}
static gchar *
-ews_download_full_gal (EBookBackendEws *cbews,
- EwsOALDetails *full,
- GCancellable *cancellable,
- GError **error)
+ebb_ews_download_full_gal (EBookBackendEws *bbews,
+ EwsOALDetails *full,
+ GCancellable *cancellable,
+ GError **error)
{
- EBookBackendEwsPrivate *priv = cbews->priv;
+ ESource *source;
const gchar *cache_dir;
gchar *lzx_path, *oab_file, *oab_path;
- lzx_path = ews_download_gal_file (cbews, full, cancellable, error);
+ lzx_path = ebb_ews_download_gal_file (bbews, full, cancellable, error);
if (!lzx_path)
return NULL;
- cache_dir = e_book_backend_get_cache_dir (E_BOOK_BACKEND (cbews));
- oab_file = g_strdup_printf ("%s-%d.oab", priv->folder_name, full->seq);
+ 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;
- goto exit;
+ } else {
+ d (printf ("OAL file decompressed %s\n", oab_path));
}
- d (g_print ("OAL file decompressed %s \n", oab_path);)
-
-exit:
if (lzx_path) {
g_unlink (lzx_path);
g_free (lzx_path);
}
+
g_free (oab_file);
+
return oab_path;
}
static gchar *
-ews_download_gal (EBookBackendEws *cbews, EwsOALDetails *full, GSList *deltas, guint32 seq,
- GCancellable *cancellable, GError **error)
+ebb_ews_download_gal (EBookBackendEws *bbews,
+ EBookCache *book_cache,
+ EwsOALDetails *full,
+ GSList *deltas,
+ guint32 seq,
+ GCancellable *cancellable,
+ GError **error)
{
#ifdef WITH_MSPACK
- EBookBackendEwsPrivate *priv = cbews->priv;
- GSList *p;
- gchar *thisoab = NULL;
- const gchar *cache_dir;
+ GSList *link;
+ gchar *thisoab;
- cache_dir = e_book_backend_get_cache_dir (E_BOOK_BACKEND (cbews));
-
- if (!e_book_sqlite_get_key_value (priv->summary, "oab-filename", &thisoab, NULL)
- || !thisoab)
+ thisoab = e_cache_dup_key (E_CACHE (book_cache), "oab-filename", NULL);
+ if (!thisoab)
goto full;
- for (p = deltas; p; p = p->next) {
- EwsOALDetails *det = p->data;
- GError *local_error = NULL;
+ 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 = ews_download_gal_file (cbews, det, cancellable, NULL);
+ lzx_path = ebb_ews_download_gal_file (bbews, det, cancellable, NULL);
if (!lzx_path)
break;
- oab_file = g_strdup_printf ("%s-%d.oab", priv->folder_name, seq);
+ 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);
@@ -2426,13 +1994,12 @@ ews_download_gal (EBookBackendEws *cbews, EwsOALDetails *full, GSList *deltas, g
/* 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 (g_print ("Failed to apply incremental patch: %s\n",
- local_error->message));
+ d (printf ("Failed to apply incremental patch: %s\n", local_error->message));
g_error_free (local_error);
break;
}
- d (g_print ("Created %s from delta\n", thisoab));
+ d (printf ("Created %s from delta\n", thisoab));
if (seq == full->seq)
return thisoab;
@@ -2445,46 +2012,39 @@ ews_download_gal (EBookBackendEws *cbews, EwsOALDetails *full, GSList *deltas, g
full:
#endif /* WITH_MSPACK */
d (printf ("Ewsgal: Downloading full gal \n"));
- return ews_download_full_gal (cbews, full, cancellable, error);
+ return ebb_ews_download_full_gal (bbews, full, cancellable, error);
}
-static gboolean
-ews_remove_old_gal_file (EBookBackendEws *cbews,
- GError **error)
+static void
+ebb_ews_remove_old_gal_file (EBookCache *book_cache)
{
- EBookBackendEwsPrivate *priv = cbews->priv;
- gchar *filename = NULL;
+ gchar *filename;
- if (!priv->summary)
- return FALSE;
+ g_return_if_fail (E_IS_BOOK_CACHE (book_cache));
- if (!e_book_sqlite_get_key_value (priv->summary, "oab-filename", &filename, error))
- return FALSE;
+ filename = e_cache_dup_key (E_CACHE (book_cache), "oab-filename", NULL);
if (filename)
g_unlink (filename);
g_free (filename);
-
- return TRUE;
}
struct _db_data {
GHashTable *uids;
GHashTable *sha1s;
- GSList *contact_collector;
- GSList *sha1_collector;
- guint collected_length;
- EBookBackendEws *cbews;
- GCancellable *cancellable;
gint unchanged;
gint changed;
gint added;
gint percent;
+ GSList *created_objects;
+ GSList *modified_objects;
};
static gboolean
-ews_gal_filter_contact (goffset offset, const gchar *sha1,
- gpointer user_data, GError **error)
+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;
@@ -2504,2050 +2064,1298 @@ ews_gal_filter_contact (goffset offset, const gchar *sha1,
}
static void
-ews_gal_store_contact (EContact *contact,
- goffset offset,
- const gchar *sha1,
- guint percent,
- gpointer user_data,
- GError **error)
+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;
- EBookBackendEwsPrivate *priv = data->cbews->priv;
-
- g_return_if_fail (priv->summary != NULL);
if (contact) {
const gchar *uid = e_contact_get_const (contact, E_CONTACT_UID);
- if (g_hash_table_remove (data->uids, uid))
- data->changed++;
- else
- data->added++;
+ EBookMetaBackendInfo *nfo;
- data->contact_collector = g_slist_prepend (data->contact_collector, g_object_ref (contact));
- data->sha1_collector = g_slist_prepend (data->sha1_collector, g_strdup (sha1));
- data->collected_length += 1;
- }
+ e_contact_set (contact, E_CONTACT_REV, sha1);
- if (data->collected_length == 1000 || percent >= 100) {
- GSList *l;
-
- data->contact_collector = g_slist_reverse (data->contact_collector);
- data->sha1_collector = g_slist_reverse (data->sha1_collector);
- if (e_book_sqlite_lock (priv->summary, EBSQL_LOCK_WRITE, data->cancellable, error)) {
- if (e_book_sqlite_add_contacts (priv->summary, data->contact_collector,
data->sha1_collector,
- TRUE, data->cancellable, error) &&
- ebews_bump_revision (data->cbews, error)) {
- if (e_book_sqlite_unlock (priv->summary, EBSQL_UNLOCK_COMMIT, error)) {
- for (l = data->contact_collector; l != NULL; l = g_slist_next (l)) {
- e_book_backend_notify_update (E_BOOK_BACKEND (data->cbews),
E_CONTACT (l->data));
- cursors_contact_added (data->cbews, E_CONTACT (l->data));
- }
- }
- } else
- e_book_sqlite_unlock (priv->summary, EBSQL_UNLOCK_ROLLBACK, NULL);
- }
+ nfo = e_book_meta_backend_info_new (uid, sha1, NULL, NULL);
+ nfo->object = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
- g_slist_free_full (data->contact_collector, g_object_unref);
- g_slist_free_full (data->sha1_collector, g_free);
- data->contact_collector = NULL;
- data->sha1_collector = NULL;
- data->collected_length = 0;
+ 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) {
- gchar *status_message = NULL;
- GList *list, *link;
-
data->percent = percent;
- d (g_print ("GAL processing contacts, %d%% complete (%d added, %d changed, %d unchanged\n",
- percent, data->added, data->changed, data->unchanged);)
-
- status_message = g_strdup_printf (_("Processing contacts in %s %d%% completed... "),
- priv->folder_name, percent);
- list = e_book_backend_list_views (E_BOOK_BACKEND (data->cbews));
- for (link = list; link != NULL; link = g_list_next (link))
- e_data_book_view_notify_progress (E_DATA_BOOK_VIEW (link->data), -1, status_message);
- g_list_free_full (list, g_object_unref);
- g_free (status_message);
+ d (printf ("GAL processing contacts, %d%% complete (%d added, %d changed, %d unchanged\n",
+ percent, data->added, data->changed, data->unchanged));
}
}
-static gint det_sort_func (gconstpointer _a, gconstpointer _b)
+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)
{
- const EwsOALDetails *a = _a, *b = _b;
+ struct _db_data *data = user_data;
+ gchar *dup_uid, *dup_rev;
- return a->seq - b->seq;
-}
+ 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);
-static void append_to_list (gpointer key, gpointer val, gpointer user_data)
-{
- GSList **list = user_data;
+ dup_uid = g_strdup (uid);
+ dup_rev = g_strdup (revision);
+
+ g_hash_table_insert (data->uids, dup_uid, dup_rev);
+ if (dup_rev)
+ g_hash_table_insert (data->sha1s, dup_rev, dup_uid);
- *list = g_slist_prepend (*list, key);
+ return TRUE;
}
static gboolean
-ews_replace_gal_in_db (EBookBackendEws *cbews,
- const gchar *filename,
- GCancellable *cancellable,
- GError **error)
+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)
{
- EBookBackendEwsPrivate *priv = cbews->priv;
EwsOabDecoder *eod;
- gboolean ret = TRUE;
- gint populated = 0;
- GSList *stale_uids = NULL;
+ gboolean success = TRUE;
struct _db_data data;
+#if d(1) + 0
gint64 t1, t2;
+#endif
+ GError *local_error = NULL;
- g_return_val_if_fail (priv->summary != NULL, FALSE);
-
+ 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);
- t1 = g_get_monotonic_time ();
-
- e_book_sqlite_get_key_value_int (priv->summary, E_BOOK_SQL_IS_POPULATED_KEY, &populated, NULL);
- if (populated) {
- GSList *slist = NULL, *l;
- if (e_book_sqlite_lock (priv->summary, EBSQL_LOCK_READ, cancellable, NULL)) {
- e_book_sqlite_search (priv->summary, NULL, TRUE, &slist, cancellable, NULL);
- e_book_sqlite_unlock (priv->summary, EBSQL_UNLOCK_NONE, NULL);
- }
+ d (t1 = g_get_monotonic_time ());
- while (slist) {
- EbSqlSearchData *search_data = slist->data;
+ e_book_cache_search_with_callback (book_cache, NULL, ebb_ews_gather_existing_uids_cb, &data,
cancellable, NULL);
- l = slist;
- slist = slist->next;
- g_slist_free_1 (l);
+ eod = ews_oab_decoder_new (filename, bbews->priv->attachments_dir, &local_error);
+ if (!local_error) {
+ GHashTableIter iter;
+ gpointer key;
- g_hash_table_insert (data.uids, search_data->uid, search_data->extra);
- if (search_data->extra)
- g_hash_table_insert (data.sha1s, search_data->extra, search_data->uid);
+ success = ews_oab_decoder_decode (eod, ebb_ews_gal_filter_contact, ebb_ews_gal_store_contact,
&data, cancellable, &local_error);
- /* We steal these */
- search_data->extra = search_data->uid = NULL;
- e_book_sqlite_search_data_free (search_data);
- }
- }
+ if (success) {
+ *out_created_objects = data.created_objects;
+ *out_modified_objects = data.modified_objects;
+ *out_removed_objects = NULL;
- eod = ews_oab_decoder_new (filename, priv->attachment_dir, error);
- if (*error)
- return FALSE;
+ g_hash_table_iter_init (&iter, data.uids);
+ while (g_hash_table_iter_next (&iter, &key, NULL)) {
+ const gchar *uid = key;
- data.contact_collector = NULL;
- data.sha1_collector = NULL;
- data.collected_length = 0;
- data.cbews = cbews;
- data.cancellable = cancellable;
-
- ret = ews_oab_decoder_decode (eod, ews_gal_filter_contact, ews_gal_store_contact, &data, cancellable,
error);
- /* Flush final entries if there are any */
- if (data.contact_collector)
- ews_gal_store_contact (NULL, 0, NULL, 100, &data, error);
-
- /* Remove any items which were not present in the new OAB */
- g_hash_table_foreach (data.uids, append_to_list, &stale_uids);
- d (g_print ("GAL removing %d contacts\n", g_slist_length (stale_uids)));
-
- /* Remove attachments. This will be easier once we add cursor support. */
- // XXX: Tell cursors
- if (!e_book_sqlite_lock (priv->summary, EBSQL_LOCK_WRITE, cancellable, error))
- ret = FALSE;
- else {
- GSList *contacts = NULL, *l;
- /* Wtf? Do we really have to fetch deleted contacts in full, just to tell the cursors? */
- for (l = stale_uids; l != NULL; l = g_slist_next (l)) {
- EContact *contact = NULL;
- e_book_sqlite_get_contact (priv->summary, l->data, FALSE, &contact, NULL);
- if (contact)
- contacts = g_slist_prepend (contacts, contact);
- }
- if ((stale_uids && !e_book_sqlite_remove_contacts (priv->summary, stale_uids, cancellable,
error)) ||
- !ebews_bump_revision (cbews, error) ||
- !e_book_sqlite_set_key_value_int (priv->summary, E_BOOK_SQL_IS_POPULATED_KEY, TRUE,
error)) {
- ret = FALSE;
- e_book_sqlite_unlock (priv->summary, EBSQL_UNLOCK_ROLLBACK, NULL);
+ *out_removed_objects = g_slist_prepend (*out_removed_objects,
+ e_book_meta_backend_info_new (uid, NULL, NULL, NULL));
+ }
} else {
- ret = e_book_sqlite_unlock (priv->summary, EBSQL_UNLOCK_COMMIT, error);
- if (ret)
- for (l = contacts; l != NULL; l = g_slist_next (l))
- cursors_contact_removed (cbews, E_CONTACT (l->data));
+ 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);
}
- g_slist_free_full (contacts, g_object_unref);
+ } else {
+ success = FALSE;
}
- t2 = g_get_monotonic_time ();
- d (g_print("GAL update completed %ssuccessfully in %" G_GINT64_FORMAT " µs. Added: %d, Changed: %d,
Unchanged %d, Removed: %d\n",
- ret ? "" : "un", (gint64) (t2 - t1),
- data.added, data.changed, data.unchanged, g_slist_length(stale_uids)));
+ 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_slist_free (stale_uids);
g_hash_table_destroy (data.sha1s);
g_hash_table_destroy (data.uids);
- /* always notify views as complete, to not left anything behind,
- if the decode was cancelled before full completion */
- e_book_backend_notify_complete (E_BOOK_BACKEND (cbews));
-
- return ret;
-}
-
-static gboolean
-ebews_start_gal_sync (gpointer data)
-{
- EBookBackendEws *cbews;
- EBookBackendEwsPrivate *priv;
- EwsOALDetails *full = NULL;
- GError *error = NULL;
- EEwsConnection *oab_cnc;
- GSList *full_l = NULL;
- GSList *deltas = NULL;
- gboolean ret = TRUE;
- gint is_populated = 0;
- gchar *uncompressed_filename = NULL;
- gchar *password;
- gchar *old_etag = NULL, *etag = NULL;
- gchar *seq;
- guint32 old_seq = 0;
- guint32 delta_size = 0;
- CamelEwsSettings *ews_settings;
- GCancellable *cancellable;
-
- cbews = (EBookBackendEws *) data;
- ews_settings = book_backend_ews_get_collection_settings (cbews);
- priv = cbews->priv;
-
- g_return_val_if_fail (priv->summary != NULL, FALSE);
-
- cancellable = g_object_ref (priv->cancellable);
-
- oab_cnc = e_ews_connection_new (priv->oab_url, ews_settings);
-
- e_binding_bind_property (
- cbews, "proxy-resolver",
- oab_cnc, "proxy-resolver",
- G_BINDING_SYNC_CREATE);
-
- password = e_ews_connection_dup_password (priv->cnc);
- e_ews_connection_set_password (oab_cnc, password);
- g_free (password);
-
- d (printf ("Ewsgal: Fetching oal full details file \n");)
-
- e_book_sqlite_get_key_value_int (priv->summary, E_BOOK_SQL_IS_POPULATED_KEY, &is_populated, NULL);
- if (is_populated) {
- gchar *tmp = NULL;
- e_book_sqlite_get_key_value (
- priv->summary, "etag", &old_etag, NULL);
- e_book_sqlite_get_key_value (
- priv->summary, "seq", &tmp, NULL);
- if (tmp)
- old_seq = strtoul(tmp, NULL, 10);
- else
- is_populated = FALSE;
- g_free (tmp);
- }
-
- if (!e_ews_connection_get_oal_detail_sync (
- oab_cnc, priv->folder_id, NULL, old_etag, &full_l, &etag,
- cancellable, &error)) {
- if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_NOT_MODIFIED)) {
- g_clear_error (&error);
- } else {
- ret = FALSE;
- }
- goto exit;
- }
-
- g_warn_if_fail (priv->summary != NULL);
- if (!priv->summary)
- goto exit;
-
- if (full_l == NULL)
- goto exit;
- while (full_l) {
- EwsOALDetails *det = full_l->data;
-
- /* Throw away anything older than we already have */
- if (det->seq <= old_seq) {
- ews_oal_details_free (det);
- } else if (!strcmp (det->type, "Full")) {
- if (full)
- ews_oal_details_free (full);
- full = det;
- } else if (is_populated && !strcmp (det->type, "Diff")) {
- delta_size += det->size;
- deltas = g_slist_insert_sorted (deltas, det, det_sort_func);
- } else {
- ews_oal_details_free (det);
- }
- full_l = g_slist_remove (full_l, det);
- }
-
- if (!full)
- goto exit;
-
- /* If the deltas would be bigger, just download the new full file */
- if (delta_size > full->size) {
- g_slist_free_full (deltas, (GDestroyNotify) ews_oal_details_free);
- deltas = NULL;
- }
-
- uncompressed_filename = ews_download_gal (cbews, full, deltas, old_seq, cancellable, &error);
- if (!uncompressed_filename) {
- ret = FALSE;
- goto exit;
- }
-
- d (printf ("Ewsgal: Removing old gal \n");)
- /* remove old_gal_file */
- ews_remove_old_gal_file (cbews, &error);
-
- d (printf ("Ewsgal: Replacing old gal with new gal contents in db \n");)
- ret = ews_replace_gal_in_db (cbews, uncompressed_filename, cancellable, &error);
- if (!ret)
- goto exit;
-
- ret = e_book_sqlite_set_key_value (priv->summary, "etag", etag ? etag : "", NULL);
- if (!ret)
- goto exit;
-
- if (e_book_sqlite_set_key_value (priv->summary, "oab-filename",
- uncompressed_filename, NULL)) {
- /* Don't let it get deleted */
- g_free (uncompressed_filename);
- uncompressed_filename = NULL;
- }
-
- seq = g_strdup_printf ("%"G_GUINT32_FORMAT, full->seq);
- ret = e_book_sqlite_set_key_value (priv->summary, "seq", seq, &error);
- g_free (seq);
-
- if (!ret) {
- gchar *db_filename = g_build_filename (
- e_book_backend_get_cache_dir (E_BOOK_BACKEND (cbews)),
- "contacts.dn", NULL);
- g_object_unref (priv->summary);
- priv->summary = NULL;
- g_unlink (db_filename);
- g_free (db_filename);
- goto exit;
- }
+ if (local_error)
+ g_propagate_error (error, local_error);
- d (printf ("Ews gal: sync successful complete \n");)
+ return success;
+}
-exit:
- g_clear_object (&cancellable);
+typedef struct {
+ /* For future use */
+ gpointer restriction;
- if (error) {
- g_warning ("Unable to update gal : %s \n", error->message);
- g_clear_error (&error);
- }
+ gboolean is_autocompletion;
+ gchar *auto_comp_str;
+} EBookBackendEwsSExpData;
- g_free (old_etag);
- g_free (etag);
+static ESExpResult *
+ebb_ews_func_not (ESExp *f,
+ gint argc,
+ ESExpResult **argv,
+ gpointer data)
+{
+ ESExpResult *r;
- /* preserve the oab file once we are able to decode the differential updates */
- if (uncompressed_filename) {
- g_unlink (uncompressed_filename);
- g_free (uncompressed_filename);
+ if (argc != 1 || argv[0]->type != ESEXP_RES_UNDEFINED) {
+ e_sexp_fatal_error (f, "parse error");
+ return NULL;
}
- if (full)
- ews_oal_details_free (full);
- if (deltas)
- g_slist_free_full (deltas, (GDestroyNotify) ews_oal_details_free);
- if (full_l)
- g_slist_free_full (full_l, (GDestroyNotify) ews_oal_details_free);
+ r = e_sexp_result_new (f, ESEXP_RES_BOOL);
+ r->value.boolean = FALSE;
- g_object_unref (oab_cnc);
- return ret;
+ return r;
}
-/********** GAL sync **************************/
-
-
-static EContact *
-ebews_get_contact_info (EBookBackendEws *ebews,
- EEwsItem *item,
- GCancellable *cancellable,
- GError **error)
+static ESExpResult *
+ebb_ews_func_and_or (ESExp *f,
+ gint argc,
+ ESExpResult **argv,
+ gpointer and)
{
- EContact *contact;
- gint i, element_type;
+ ESExpResult *r;
- contact = e_contact_new ();
+ r = e_sexp_result_new (f, ESEXP_RES_BOOL);
+ r->value.boolean = FALSE;
- for (i = 0; i < G_N_ELEMENTS (mappings); i++) {
- element_type = mappings[i].element_type;
+ return r;
+}
- if (element_type == ELEMENT_TYPE_SIMPLE && !mappings[i].populate_contact_func) {
- const gchar *val = mappings[i].get_simple_prop_func (item);
+/* TODO implement */
+static ESExpResult *
+ebb_ews_func_is (struct _ESExp *f,
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
+{
+ ESExpResult *r;
- if (val != NULL)
- e_contact_set (contact, mappings[i].field_id, val);
- } else {
- mappings[i].populate_contact_func (ebews, contact, item, cancellable, error);
- }
+ if (argc != 2
+ && argv[0]->type != ESEXP_RES_STRING
+ && argv[1]->type != ESEXP_RES_STRING) {
+ e_sexp_fatal_error (f, "parse error");
+ return NULL;
}
- return contact;
+ r = e_sexp_result_new (f, ESEXP_RES_BOOL);
+ r->value.boolean = FALSE;
+
+ return r;
}
-static void
-ebews_get_contacts_list (EBookBackendEws *ebews, GSList *new_items,
- GSList **contacts, GCancellable *cancellable,
- GError **error)
+/* TODO implement */
+static ESExpResult *
+ebb_ews_func_endswith (struct _ESExp *f,
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
{
- GSList *l;
-
- for (l = new_items; l != NULL; l = g_slist_next (l)) {
- EContact *contact;
- EEwsItem *item = l->data;
- EVCardAttribute *attr;
-
- if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) {
- g_object_unref (item);
- continue;
- }
+ ESExpResult *r;
- contact = ebews_get_contact_info (ebews, item, cancellable, error);
+ if (argc != 2
+ && argv[0]->type != ESEXP_RES_STRING
+ && argv[1]->type != ESEXP_RES_STRING) {
+ e_sexp_fatal_error (f, "parse error");
+ return NULL;
+ }
- attr = e_vcard_attribute_new (NULL, "X-EWS-KIND");
- e_vcard_add_attribute_with_value (E_VCARD (contact), attr, "DT_MAILUSER");
+ r = e_sexp_result_new (f, ESEXP_RES_BOOL);
+ r->value.boolean = FALSE;
- *contacts = g_slist_prepend (*contacts, contact);
+ return r;
- g_object_unref (item);
- }
- g_slist_free (new_items);
}
-static gboolean
-ebews_traverse_dl (EBookBackendEws *ebews,
- EContact **contact,
- GHashTable *items,
- GHashTable *values,
- EwsMailbox *mb,
- GError **error)
+/* TODO implement */
+static ESExpResult *
+ebb_ews_func_contains (struct _ESExp *f,
+ gint argc,
+ struct _ESExpResult **argv,
+ gpointer data)
{
- 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;
-
- 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;
+ ESExpResult *r;
+ EBookBackendEwsSExpData *sdata = data;
+ const gchar *propname, *str;
- g_hash_table_insert (items, g_strdup (ident), GINT_TO_POINTER (1));
+ if (argc != 2
+ && argv[0]->type != ESEXP_RES_STRING
+ && argv[1]->type != ESEXP_RES_STRING) {
+ e_sexp_fatal_error (f, "parse error");
+ return NULL;
+ }
- if (!e_ews_connection_expand_dl_sync (
- ebews->priv->cnc,
- EWS_PRIORITY_MEDIUM,
- mb,
- &members,
- &includes_last,
- ebews->priv->cancellable,
- error))
- return FALSE;
+ propname = argv[0]->value.string;
+ str = argv[1]->value.string;
- for (l = members; l; l = l->next) {
- ret = ebews_traverse_dl (ebews, contact, items, values, l->data, error);
- if (!ret)
- break;
+ if (!strcmp (propname, "full_name") || !strcmp (propname, "email")) {
+ if (!sdata->auto_comp_str) {
+ sdata->auto_comp_str = g_strdup (str);
+ sdata->is_autocompletion = TRUE;
}
+ }
- g_slist_free_full (members, (GDestroyNotify) e_ews_mailbox_free);
- return ret;
- } else {
- EVCardAttribute *attr;
- CamelInternetAddress *addr;
- gchar *value = NULL;
-
- if (mb->name == NULL && mb->email == NULL)
- return TRUE;
-
- addr = camel_internet_address_new ();
- attr = e_vcard_attribute_new (NULL, EVC_EMAIL);
-
- camel_internet_address_add (addr, mb->name, mb->email ? mb->email : "");
- value = camel_address_encode (CAMEL_ADDRESS (addr));
-
- if (value && g_hash_table_lookup (values, value) == NULL) {
- e_vcard_attribute_add_value (attr, value);
- e_vcard_append_attribute (E_VCARD (*contact), attr);
-
- g_hash_table_insert (values, g_strdup (value), GINT_TO_POINTER (1));
- }
+ r = e_sexp_result_new (f, ESEXP_RES_BOOL);
+ r->value.boolean = FALSE;
- g_object_unref (addr);
+ return r;
- return TRUE;
- }
}
-static EContact *
-ebews_get_dl_info (EBookBackendEws *ebews,
- const EwsId *id,
- const gchar *d_name,
- GSList *members,
- GError **error)
+/* 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)
{
- GHashTable *items, *values;
- GSList *l;
- EContact *contact;
-
- contact = e_contact_new ();
- e_contact_set (contact, E_CONTACT_UID, id->id);
- e_contact_set (contact, E_CONTACT_REV, id->change_key);
+ ESExpResult *r;
+ const gchar *propname, *str;
+ EBookBackendEwsSExpData *sdata = data;
- 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);
+ if (argc != 2 ||
+ argv[0]->type != ESEXP_RES_STRING ||
+ argv[1]->type != ESEXP_RES_STRING) {
+ e_sexp_fatal_error (f, "parse error");
+ return NULL;
+ }
- 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);
+ propname = argv[0]->value.string;
+ str = argv[1]->value.string;
- for (l = members; l != NULL; l = l->next) {
- if (!ebews_traverse_dl (ebews, &contact, items, values, l->data, error)) {
- g_object_unref (contact);
- contact = NULL;
- goto exit;
+ if (!strcmp (propname, "full_name") || !strcmp (propname, "email")) {
+ if (!sdata->auto_comp_str) {
+ sdata->auto_comp_str = g_strdup (str);
+ sdata->is_autocompletion = TRUE;
}
}
-exit:
- g_hash_table_destroy (items);
- g_hash_table_destroy (values);
- return contact;
+ 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
-ebews_get_dl_info_gal (EBookBackendEws *ebews,
- EContact *contact,
- EwsMailbox *mb,
- GError **error)
+ebb_ews_build_restriction (const gchar *query,
+ gchar **auto_comp_str)
{
- GHashTable *items, *values;
- gboolean success;
+ ESExpResult *r;
+ ESExp *sexp;
+ EBookBackendEwsSExpData *sdata;
+ gboolean autocompletion = FALSE;
+ gint i;
- 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);
+ *auto_comp_str = NULL;
- success = ebews_traverse_dl (ebews, &contact, items, values, mb, error);
+ sexp = e_sexp_new ();
+ sdata = g_new0 (EBookBackendEwsSExpData, 1);
+ sdata->is_autocompletion = FALSE;
- 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));
+ for (i = 0; i < G_N_ELEMENTS (symbols); i++) {
+ e_sexp_add_function (
+ sexp, 0, (gchar *) symbols[i].name,
+ symbols[i].func,
+ sdata);
}
- g_hash_table_destroy (items);
- g_hash_table_destroy (values);
-
- return success;
-}
-
-static gboolean
-ebews_contacts_append_dl (EBookBackendEws *ebews, const EwsId *id,
- const gchar *d_name,GSList *members,
- GSList **contacts, GError **error)
-{
- EContact *contact;
- EVCardAttribute *attr;
-
- contact = ebews_get_dl_info (ebews, id, d_name, members, error);
- if (contact == NULL)
- return FALSE;
+ e_sexp_input_text (sexp, query, strlen (query));
+ e_sexp_parse (sexp);
- attr = e_vcard_attribute_new (NULL, "X-EWS-KIND");
- e_vcard_add_attribute_with_value (E_VCARD (contact), attr, "DT_DISTLIST");
+ 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);
+ }
- *contacts = g_slist_prepend (*contacts, contact);
+ e_sexp_result_free (sexp, r);
+ g_object_unref (sexp);
+ g_free (sdata);
- return TRUE;
+ return autocompletion && *auto_comp_str;
}
static gboolean
-ebews_fetch_items (EBookBackendEws *ebews, GSList *items, GSList **contacts,
- GCancellable *cancellable, GError **error)
+ebb_ews_update_cache_for_expression (EBookBackendEws *bbews,
+ const gchar *expr,
+ GCancellable *cancellable,
+ GError **error)
{
- EBookBackendEwsPrivate *priv;
- EEwsConnection *cnc;
- GSList *l;
- GSList *contact_item_ids = NULL, *dl_ids = NULL;
- GSList *new_items = NULL;
- gboolean ret = FALSE;
+ EBookMetaBackend *meta_backend;
+ CamelEwsSettings *ews_settings;
+ gboolean success = TRUE;
- if (!book_backend_ews_ensure_connected (ebews, cancellable, error) || !ebews->priv->cnc) {
- g_slist_free_full (items, g_object_unref);
- return ret;
- }
+ g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (bbews), FALSE);
- priv = ebews->priv;
- cnc = priv->cnc;
+ /* Resolve names in GAL only for GAL */
+ if (!bbews->priv->is_gal)
+ return TRUE;
- for (l = items; l != NULL; l = g_slist_next (l)) {
- EEwsItem *item = (EEwsItem *) l->data;
- const EwsId *id = e_ews_item_get_id (item);
- EEwsItemType type = e_ews_item_get_item_type (item);
+ ews_settings = ebb_ews_get_collection_settings (bbews);
- 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));
- }
+ if (camel_ews_settings_get_oab_offline (ews_settings))
+ return TRUE;
- g_object_unref (item);
- }
- g_slist_free (items);
+ meta_backend = E_BOOK_META_BACKEND (bbews);
- /* 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);
+ g_rec_mutex_lock (&bbews->priv->cnc_lock);
- ret = e_ews_connection_get_items_sync (
- cnc, EWS_PRIORITY_MEDIUM,
- contact_item_ids, "Default", add_props,
- FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &new_items, NULL, NULL,
- cancellable, error);
+ /* 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;
- e_ews_additional_props_free (add_props);
+ 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 (!ret)
- goto cleanup;
- }
+ if (success) {
+ GSList *mlink, *clink;
- if (new_items)
- ebews_get_contacts_list (ebews, new_items, contacts, cancellable, error);
- new_items = NULL;
+ 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;
+ const gchar *str;
+ gchar *fake_rev;
- /* Get the display names of the distribution lists */
- if (dl_ids)
- if (!e_ews_connection_get_items_sync (
- cnc, EWS_PRIORITY_MEDIUM,
- dl_ids, "Default", NULL,
- FALSE, NULL, E_EWS_BODY_TYPE_TEXT, &new_items, NULL, NULL,
- cancellable, error))
- goto cleanup;
+ if (g_strcmp0 (mb->mailbox_type, "PublicDL") == 0) {
+ contact = e_contact_new ();
- for (l = new_items; l != NULL; l = g_slist_next (l)) {
- EEwsItem *item = (EEwsItem *) l->data;
- const gchar *d_name;
- const EwsId *id;
- EwsMailbox *mb;
- GSList *members = NULL;
- gboolean includes_last;
+ if (!ebb_ews_get_dl_info_gal (bbews, contact, mb, cancellable, NULL))
{
+ g_clear_object (&contact);
+ }
+ }
- if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR)
- continue;
+ 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);
- id = e_ews_item_get_id (item);
- mb = g_new0 (EwsMailbox, 1);
- mb->item_id = (EwsId *) id;
+ if (!contact)
+ contact = e_contact_new ();
- d_name = e_ews_item_get_subject (item);
- if (!e_ews_connection_expand_dl_sync (
- cnc, EWS_PRIORITY_MEDIUM, mb, &members,
- &includes_last, cancellable, error))
- goto cleanup;
+ /* 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);
- ret = ebews_contacts_append_dl (ebews, id, d_name, members, contacts, error);
+ /* There is no ChangeKey provided either, thus make up some revision,
+ to have the contact always updated in the local cache. */
+ fake_rev = e_util_generate_uid ();
- g_free (mb);
- g_slist_free_full (members, (GDestroyNotify) e_ews_mailbox_free);
+ e_contact_set (contact, E_CONTACT_REV, fake_rev);
- if (!ret)
- goto cleanup;
- }
+ g_free (fake_rev);
-cleanup:
- g_slist_free_full (new_items, g_object_unref);
- g_slist_free_full (dl_ids, g_free);
- g_slist_free_full (contact_item_ids, g_free);
+ str = e_contact_get_const (contact, E_CONTACT_FULL_NAME);
+ if (!str || !*str)
+ e_contact_set (contact, E_CONTACT_FULL_NAME, mb->name);
- return ret;
-}
+ str = e_contact_get_const (contact, E_CONTACT_EMAIL_1);
+ if (!str || !*str || (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);
-static gboolean
-ebews_start_sync (gpointer data)
-{
- EBookBackendEws *ebews;
- EBookBackendEwsPrivate *priv;
- GList *list, *link;
- gchar *status_message = NULL;
- GCancellable *cancellable;
- GError *error = NULL;
+ ebews_populate_emails_ex (bbews, contact, contact_item, TRUE);
+ }
- ebews = (EBookBackendEws *) data;
- priv = ebews->priv;
+ 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 (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]);
+ }
+ }
- g_return_val_if_fail (priv->summary != NULL, FALSE);
+ 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);
- /* Not connected? Try again later */
- if (!priv->cnc)
- return TRUE;
+ found_infos = g_slist_prepend (found_infos, nfo);
- cancellable = g_object_ref (priv->cancellable);
+ g_object_unref (contact);
+ }
+ }
- status_message = g_strdup (_("Syncing contacts..."));
- list = e_book_backend_list_views (E_BOOK_BACKEND (ebews));
- for (link = list; link != NULL; link = g_list_next (link))
- e_data_book_view_notify_progress (E_DATA_BOOK_VIEW (link->data), -1, status_message);
- g_list_free_full (list, g_object_unref);
- g_free (status_message);
+ g_slist_free_full (mailboxes, (GDestroyNotify) e_ews_mailbox_free);
+ e_util_free_nullable_object_slist (contacts);
- ews_update_items_thread (g_object_ref (ebews));
+ if (success) {
+ GSList *created_objects = NULL, *modified_objects = NULL;
- /* hide progress message when done */
- list = e_book_backend_list_views (E_BOOK_BACKEND (ebews));
- for (link = list; link != NULL; link = g_list_next (link))
- e_data_book_view_notify_progress (E_DATA_BOOK_VIEW (link->data), -1, NULL);
- g_list_free_full (list, g_object_unref);
+ 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_clear_object (&cancellable);
+ g_slist_free_full (created_objects, e_book_meta_backend_info_free);
+ g_slist_free_full (modified_objects, e_book_meta_backend_info_free);
+ }
- if (error) {
- g_warning ("Error Syncing Contacts: Folder %s Error: %s", priv->folder_id, error->message);
- g_clear_error (&error);
- return FALSE;
+ g_slist_free_full (found_infos, e_book_meta_backend_info_free);
+ g_free (restriction_expr);
}
- return TRUE;
-}
-
-static gpointer
-delta_thread (gpointer data)
-{
- EBookBackendEws *ebews = data;
- EBookBackendEwsPrivate *priv = ebews->priv;
- gint64 end_time;
-
- g_mutex_lock (&priv->dlock->mutex);
- g_object_ref (ebews);
- g_mutex_unlock (&priv->dlock->mutex);
-
- while (TRUE) {
- gboolean succeeded = TRUE;
-
- if (!priv->is_gal)
- succeeded = ebews_start_sync (ebews);
- else if (priv->summary && priv->marked_for_offline)
- succeeded = ebews_start_gal_sync (ebews);
+ g_rec_mutex_unlock (&bbews->priv->cnc_lock);
- g_mutex_lock (&priv->dlock->mutex);
+ ebb_ews_convert_error_to_edb_error (error);
- /* in case this is the last reference, then this cannot join
- the itself thread in dispose */
- e_ews_connection_utils_unref_in_thread (ebews);
-
- if (!succeeded || priv->dlock->exit)
- break;
-
- end_time = g_get_monotonic_time () + REFRESH_INTERVAL * G_TIME_SPAN_SECOND;
- g_cond_wait_until (&priv->dlock->cond, &priv->dlock->mutex, end_time);
-
- if (priv->dlock->exit)
- break;
-
- g_object_ref (ebews);
-
- g_mutex_unlock (&priv->dlock->mutex);
- }
-
- g_mutex_unlock (&priv->dlock->mutex);
- priv->dthread = NULL;
- return NULL;
+ return success;
}
-static gboolean
-fetch_deltas (EBookBackendEws *ebews,
- gboolean force_update)
+static GSList * /* the possibly modified 'in_items' */
+ebb_ews_verify_changes (EBookCache *book_cache,
+ GSList *in_items, /* EEwsItem * */
+ GCancellable *cancellable)
{
- EBookBackendEwsPrivate *priv = ebews->priv;
- GError *error = NULL;
+ GSList *items = NULL, *link;
- /* If the thread is already running just return back */
- if (priv->dthread) {
- if (force_update && priv->dlock) {
- g_mutex_lock (&priv->dlock->mutex);
- g_cond_signal (&priv->dlock->cond);
- g_mutex_unlock (&priv->dlock->mutex);
- }
-
- return FALSE;
- }
-
- if (!priv->dlock) {
- priv->dlock = g_new0 (SyncDelta, 1);
- g_mutex_init (&priv->dlock->mutex);
- g_cond_init (&priv->dlock->cond);
- }
+ g_return_val_if_fail (E_IS_BOOK_CACHE (book_cache), in_items);
- priv->dlock->exit = FALSE;
- priv->dthread = g_thread_try_new (NULL, (GThreadFunc) delta_thread, ebews, &error);
- if (!priv->dthread) {
- g_warning (G_STRLOC ": %s", error->message);
- g_error_free (error);
- }
-
- return TRUE;
-}
+ 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);
-static void
-ebews_start_refreshing (EBookBackendEws *ebews,
- gboolean force_update)
-{
- EBookBackendEwsPrivate *priv;
+ if (!g_cancellable_is_cancelled (cancellable) && (
+ type == E_EWS_ITEM_TYPE_CONTACT ||
+ type == E_EWS_ITEM_TYPE_GROUP)) {
+ EContact *existing = NULL;
- priv = ebews->priv;
+ if (e_book_cache_get_contact (book_cache, id->id, TRUE, &existing, cancellable, NULL)
&&
+ existing && g_strcmp0 (e_contact_get_const (existing, E_CONTACT_REV),
id->change_key) == 0) {
+ g_object_unref (item);
+ } else {
+ items = g_slist_prepend (items, item);
+ }
- PRIV_LOCK (priv);
+ g_clear_object (&existing);
+ } else {
+ items = g_slist_prepend (items, item);
+ }
+ }
- if (e_backend_get_online (E_BACKEND (ebews)) &&
- priv->cnc != NULL && priv->marked_for_offline)
- fetch_deltas (ebews, force_update);
+ g_slist_free (in_items);
- PRIV_UNLOCK (priv);
+ return items;
}
-static gboolean
-fetch_from_offline (EBookBackendEws *ews,
- EDataBookView *book_view,
- const gchar *query,
- GCancellable *cancellable,
- GError **error)
+static GSList * /* EBookMetaBackendInfo */
+ebb_ews_contacts_to_infos (const GSList *contacts) /* EContact * */
{
- GSList *contacts = NULL, *l;
- EBookBackendEwsPrivate *priv;
-
- priv = ews->priv;
-
- /* GAL with folder_id means offline GAL */
- if (priv->is_gal && !priv->folder_id && !g_strcmp0 (query, "(contains \"x-evolution-any-field\"
\"\")"))
- return TRUE;
+ GSList *nfos = NULL, *link;
- if (!e_book_sqlite_lock (priv->summary, EBSQL_LOCK_READ, cancellable, error))
- return FALSE;
+ for (link = (GSList *) contacts; link; link = g_slist_next (link)) {
+ EContact *contact = link->data;
+ EBookMetaBackendInfo *nfo;
- e_book_sqlite_search (priv->summary, query, FALSE, &contacts, cancellable, error);
- e_book_sqlite_unlock (priv->summary, EBSQL_UNLOCK_NONE, NULL);
- for (l = contacts; l != NULL; l = g_slist_next (l)) {
- EbSqlSearchData *s_data = (EbSqlSearchData *) l->data;
+ if (!E_IS_CONTACT (contact))
+ continue;
- e_data_book_view_notify_update_prefiltered_vcard (book_view, s_data->uid, s_data->vcard);
+ 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);
- e_book_sqlite_search_data_free (s_data);
+ nfos = g_slist_prepend (nfos, nfo);
}
- if (contacts)
- g_slist_free (contacts);
-
- return TRUE;
+ return nfos;
}
-static void
-e_book_backend_ews_start_view (EBookBackend *backend,
- EDataBookView *book_view)
+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 *ebews;
- EBookBackendEwsPrivate *priv;
- EBookBackendSExp *sexp;
- const gchar *query;
- gboolean is_autocompletion = FALSE;
- gchar *auto_comp_str = NULL;
- GCancellable *cancellable;
- GSList *mailboxes = NULL, *l, *contacts = NULL, *c;
- EwsFolderId *fid;
- ESource *source;
- ESourceEwsFolder *extension;
- const gchar *extension_name;
- GError *error = NULL;
- gboolean includes_last_item;
- gint is_populated = 0;
-
- ebews = E_BOOK_BACKEND_EWS (backend);
- priv = ebews->priv;
+ EBookBackendEws *bbews;
+ CamelEwsSettings *ews_settings;
+ gchar *hosturl;
+ gboolean success = FALSE;
- sexp = e_data_book_view_get_sexp (book_view);
- query = e_book_backend_sexp_text (sexp);
+ g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE);
+ g_return_val_if_fail (out_auth_result != NULL, FALSE);
- source = e_backend_get_source (E_BACKEND (backend));
+ bbews = E_BOOK_BACKEND_EWS (meta_backend);
- g_object_ref (book_view);
- e_data_book_view_notify_progress (book_view, -1, _("Searching..."));
+ g_rec_mutex_lock (&bbews->priv->cnc_lock);
- PRIV_LOCK (priv);
- cancellable = g_cancellable_new ();
- g_hash_table_insert (priv->ops, book_view, cancellable);
- PRIV_UNLOCK (priv);
+ if (bbews->priv->cnc) {
+ g_rec_mutex_unlock (&bbews->priv->cnc_lock);
- if (!e_backend_get_online (E_BACKEND (backend)) || !priv->cnc) {
- if (priv->summary)
- e_book_sqlite_get_key_value_int (priv->summary, E_BOOK_SQL_IS_POPULATED_KEY,
&is_populated, NULL);
- if (is_populated) {
- fetch_from_offline (ebews, book_view, query, cancellable, &error);
- goto out;
- }
+ *out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED;
- goto out;
+ return TRUE;
}
- g_return_if_fail (priv->cnc != NULL);
-
- ebews_start_refreshing (ebews, FALSE);
-
- if (priv->summary)
- e_book_sqlite_get_key_value_int (priv->summary, E_BOOK_SQL_IS_POPULATED_KEY, &is_populated,
NULL);
- if (is_populated) {
- fetch_from_offline (ebews, book_view, query, cancellable, &error);
- goto out;
- }
+ ews_settings = ebb_ews_get_collection_settings (bbews);
+ hosturl = camel_ews_settings_dup_hosturl (ews_settings);
- e_book_backend_ews_build_restriction (query, &is_autocompletion, &auto_comp_str);
- if (!is_autocompletion || !auto_comp_str) {
- g_free (auto_comp_str);
- goto out;
- }
+ bbews->priv->cnc = e_ews_connection_new (hosturl, ews_settings);
- extension_name = E_SOURCE_EXTENSION_EWS_FOLDER;
- extension = e_source_get_extension (source, extension_name);
-
- /* FIXME Need to convert the Ids from EwsLegacyId format to EwsId format using
- * convert_id operation before using it as the schema has changed between Exchange
- * 2007 and 2007_SP1 */
- fid = g_new0 (EwsFolderId, 1);
- fid->id = g_strdup (priv->folder_id);
- fid->change_key = e_source_ews_folder_dup_change_key (extension);
-
- /* We do not scan until we reach the last_item as it might be good enough to show first 100
- * items during auto-completion. Change it if needed. TODO, Personal Address-book should start using
- * find_items rather than resolve_names to support all queries */
- e_ews_connection_resolve_names_sync (
- priv->cnc, EWS_PRIORITY_MEDIUM, auto_comp_str,
- EWS_SEARCH_AD, NULL, TRUE, &mailboxes, &contacts,
- &includes_last_item, cancellable, &error);
- g_free (auto_comp_str);
- e_ews_folder_id_free (fid);
- if (error != NULL) {
- e_data_book_view_notify_complete (book_view, error);
- g_object_unref (book_view);
- g_clear_error (&error);
- return;
- }
+ e_binding_bind_property (
+ bbews, "proxy-resolver",
+ bbews->priv->cnc, "proxy-resolver",
+ G_BINDING_SYNC_CREATE);
- for (l = mailboxes, c = contacts; l != NULL; l = g_slist_next (l), c = g_slist_next (c)) {
- EwsMailbox *mb = l->data;
- EEwsItem *contact_item = c ? c->data : NULL;
- EContact *contact = NULL;
- const gchar *str;
+ *out_auth_result = e_ews_connection_try_credentials_sync (bbews->priv->cnc, credentials, cancellable,
error);
- if (g_strcmp0 (mb->mailbox_type, "PublicDL") == 0) {
- contact = e_contact_new ();
+ if (*out_auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
+ ESource *source = e_backend_get_source (E_BACKEND (bbews));
+ ESourceEwsFolder *ews_folder;
+ gchar *gal_uid;
- if (!ebews_get_dl_info_gal (ebews, contact, mb, NULL)) {
- g_clear_object (&contact);
- }
- }
+ ews_folder = e_source_get_extension (source, E_SOURCE_EXTENSION_EWS_FOLDER);
- if (!contact && contact_item && e_ews_item_get_item_type (contact_item) ==
E_EWS_ITEM_TYPE_CONTACT)
- contact = ebews_get_contact_info (ebews, contact_item, cancellable, NULL);
+ g_free (bbews->priv->folder_id);
+ bbews->priv->folder_id = e_source_ews_folder_dup_id (ews_folder);
- if (!contact)
- contact = e_contact_new ();
+ gal_uid = camel_ews_settings_dup_gal_uid (ews_settings);
+ bbews->priv->is_gal = g_strcmp0 (e_source_get_uid (source), gal_uid) == 0;
- /* 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);
+ g_free (gal_uid);
- str = e_contact_get_const (contact, E_CONTACT_FULL_NAME);
- if (!str || !*str)
- e_contact_set (contact, E_CONTACT_FULL_NAME, mb->name);
+ g_signal_connect_swapped (bbews->priv->cnc, "server-notification",
+ G_CALLBACK (ebb_ews_server_notification_cb), bbews);
- str = e_contact_get_const (contact, E_CONTACT_EMAIL_1);
- if (!str || !*str || (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);
+ 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;
- ebews_populate_emails_ex (ebews, contact, contact_item, TRUE);
- }
+ folders = g_slist_prepend (folders, bbews->priv->folder_id);
- 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 (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++;
- }
+ e_ews_connection_enable_notifications_sync (bbews->priv->cnc,
+ folders, &bbews->priv->subscription_key);
- g_free (emails[ii]);
- }
+ g_slist_free (folders);
}
- e_data_book_view_notify_update (book_view, contact);
-
- g_object_unref (contact);
+ 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_slist_free_full (mailboxes, (GDestroyNotify) e_ews_mailbox_free);
- e_util_free_nullable_object_slist (contacts);
- out:
- e_data_book_view_notify_complete (book_view, error);
- g_clear_error (&error);
- PRIV_LOCK (priv);
- g_hash_table_remove (priv->ops, book_view);
- PRIV_UNLOCK (priv);
- g_object_unref (cancellable);
- g_object_unref (book_view);
-}
+ g_rec_mutex_unlock (&bbews->priv->cnc_lock);
-static void
-e_book_backend_ews_stop_view (EBookBackend *backend,
- EDataBookView *book_view)
-{
- EBookBackendEws *bews = E_BOOK_BACKEND_EWS (backend);
- EBookBackendEwsPrivate *priv = bews->priv;
- GCancellable *cancellable;
+ g_free (hosturl);
- PRIV_LOCK (priv);
- cancellable = g_hash_table_lookup (priv->ops, book_view);
- if (cancellable)
- g_cancellable_cancel (cancellable);
- PRIV_UNLOCK (priv);
+ return success;
}
static gboolean
-book_backend_ews_initable_init (GInitable *initable,
- GCancellable *cancellable,
- GError **error)
+ebb_ews_disconnect_sync (EBookMetaBackend *meta_backend,
+ GCancellable *cancellable,
+ GError **error)
{
- EBookBackend *backend = E_BOOK_BACKEND (initable);
- ESource *source = e_backend_get_source (E_BACKEND (backend));
- EBookBackendEws *cbews;
- EBookBackendEwsPrivate *priv;
- CamelEwsSettings *settings;
- ESourceExtension *extension;
- const gchar *cache_dir;
- const gchar *display_name;
- const gchar *extension_name;
- const gchar *gal_uid;
- const gchar *uid;
- gchar *db_filename;
-
- cbews = E_BOOK_BACKEND_EWS (backend);
- priv = cbews->priv;
-
- if (priv->base_directory)
- cache_dir = priv->base_directory;
- else
- cache_dir = e_book_backend_get_cache_dir (backend);
- db_filename = g_build_filename (cache_dir, "contacts.db", NULL);
- settings = book_backend_ews_get_collection_settings (cbews);
-
- uid = e_source_get_uid (source);
- gal_uid = camel_ews_settings_get_gal_uid (settings);
- priv->is_gal = (g_strcmp0 (uid, gal_uid) == 0);
-
- display_name = e_source_get_display_name (source);
-
- extension_name = E_SOURCE_EXTENSION_EWS_FOLDER;
- extension = e_source_get_extension (source, extension_name);
-
- priv->folder_id = e_source_ews_folder_dup_id (
- E_SOURCE_EWS_FOLDER (extension));
-
- priv->summary = e_book_sqlite_new (db_filename, source, cancellable, error);
- g_free (db_filename);
- if (priv->summary == NULL) {
- convert_error_to_edb_error (error);
- return FALSE;
- }
-
- if (!e_book_sqlite_get_locale (priv->summary, &priv->locale, error)) {
- convert_error_to_edb_error (error);
- g_object_unref (priv->summary);
- priv->summary = NULL;
- return FALSE;
- }
-
- priv->marked_for_offline = FALSE;
- priv->is_writable = FALSE;
+ EBookBackendEws *bbews;
- extension_name = E_SOURCE_EXTENSION_OFFLINE;
- extension = e_source_get_extension (source, extension_name);
+ g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE);
- priv->marked_for_offline = e_source_offline_get_stay_synchronized (E_SOURCE_OFFLINE (extension));
+ bbews = E_BOOK_BACKEND_EWS (meta_backend);
- if (priv->is_gal) {
- priv->folder_name = g_strdup (display_name);
- priv->oab_url = camel_ews_settings_dup_oaburl (settings);
-
- /* setup stagging dir, remove any old files from there */
- priv->attachment_dir = g_build_filename (
- cache_dir, "attachments", NULL);
- g_mkdir_with_parents (priv->attachment_dir, 0777);
-
- priv->marked_for_offline = camel_ews_settings_get_oab_offline (settings);
- }
+ ebb_ews_unset_connection (bbews);
return TRUE;
}
-static void
-e_book_backend_ews_notify_online_cb (EBookBackend *backend,
- GParamSpec *spec)
+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 *ebews;
-
- ebews = E_BOOK_BACKEND_EWS (backend);
-
- if (e_book_backend_is_opened (backend)) {
- if (ebews->priv->cancellable) {
- g_cancellable_cancel (ebews->priv->cancellable);
- g_object_unref (ebews->priv->cancellable);
- ebews->priv->cancellable = NULL;
- }
-
- if (!e_backend_get_online (E_BACKEND (backend))) {
- e_book_backend_set_writable (backend, FALSE);
- if (ebews->priv->cnc) {
- g_object_unref (ebews->priv->cnc);
- ebews->priv->cnc = NULL;
- }
- } else {
- ebews->priv->cancellable = g_cancellable_new ();
- ebews->priv->is_writable = !ebews->priv->is_gal;
-
- e_book_backend_set_writable (backend, ebews->priv->is_writable);
-
- e_backend_schedule_credentials_required (E_BACKEND (backend),
- E_SOURCE_CREDENTIALS_REASON_REQUIRED, NULL, 0, NULL,
- ebews->priv->cancellable, G_STRFUNC);
- }
- }
-}
+ EBookBackendEws *bbews;
+ EBookCache *book_cache;
+ gboolean success = TRUE;
+ GError *local_error = NULL;
-static gchar *
-e_book_backend_ews_get_backend_property (EBookBackend *backend,
- const gchar *prop_name)
-{
- g_return_val_if_fail (prop_name != NULL, 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);
- if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
- EBookBackendEws *ebews;
+ *out_created_objects = NULL;
+ *out_modified_objects = NULL;
+ *out_removed_objects = NULL;
- ebews = E_BOOK_BACKEND_EWS (backend);
- g_return_val_if_fail (ebews != NULL, NULL);
+ bbews = E_BOOK_BACKEND_EWS (meta_backend);
- /* GAL with folder_id is an offline GAL */
- if (ebews->priv->is_gal && !ebews->priv->folder_id) {
- return g_strdup ("net,bulk-removes,contact-lists");
- } else {
- /* do-initialy-query is enabled for system address book also, so that we get the
- * book_view, which is needed for displaying cache update progress.
- * and null query is handled for system address book.
- */
- return g_strdup ("net,bulk-removes,do-initial-query,contact-lists");
- }
- } 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;
+ book_cache = e_book_meta_backend_ref_cache (meta_backend);
+ g_return_val_if_fail (E_IS_BOOK_CACHE (book_cache), FALSE);
- buffer = g_string_sized_new (1024);
+ g_rec_mutex_lock (&bbews->priv->cnc_lock);
- for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) {
- if (mappings[ii].element_type != ELEMENT_TYPE_SIMPLE)
- continue;
+ if (bbews->priv->is_gal) {
+ CamelEwsSettings *ews_settings;
+ gchar *oab_url;
- if (buffer->len > 0)
- g_string_append_c (buffer, ',');
- g_string_append (buffer, e_contact_field_name (mappings[ii].field_id));
- }
+ ews_settings = ebb_ews_get_collection_settings (bbews);
+ oab_url = camel_ews_settings_dup_oaburl (ews_settings);
- 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));
- }
+ 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;
- 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);
+ sequence = e_cache_get_key_int (E_CACHE (book_cache), "gal-sequence", NULL);
+ if (sequence == -1)
+ sequence = 0;
- g_string_free (buffer, TRUE);
+ oab_cnc = e_ews_connection_new (oab_url, ews_settings);
- return fields;
- } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REVISION)) {
- EBookBackendEws *ebews = E_BOOK_BACKEND_EWS (backend);
- gchar *prop_value = NULL;
+ e_binding_bind_property (
+ bbews, "proxy-resolver",
+ oab_cnc, "proxy-resolver",
+ G_BINDING_SYNC_CREATE);
- e_book_sqlite_get_key_value (ebews->priv->summary, "revision", &prop_value, NULL);
- return prop_value;
- }
+ password = e_ews_connection_dup_password (bbews->priv->cnc);
+ e_ews_connection_set_password (oab_cnc, password);
+ e_util_safe_free_string (password);
- /* Chain up to parent's get_backend_property() method. */
- return E_BOOK_BACKEND_CLASS (e_book_backend_ews_parent_class)->
- get_backend_property (backend, prop_name);
-}
+ 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;
+ }
+ }
-static gpointer
-handle_notifications_thread (gpointer data)
-{
- EBookBackendEws *ebews = data;
+ if (success && full_l) {
+ guint32 delta_size = 0;
+
+ for (link = full_l; link; link = g_slist_next (link)) {
+ EwsOALDetails *det = full_l->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);
+ }
+ }
- PRIV_LOCK (ebews->priv);
- if (ebews->priv->cnc == NULL)
- goto exit;
+ g_slist_free (full_l);
+ full_l = NULL;
- if (ebews->priv->listen_notifications) {
- GSList *folders = 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 (ebews->priv->subscription_key != 0)
- goto exit;
+ if (full) {
+ gchar *uncompressed_filename;
- folders = g_slist_prepend (folders, ebews->priv->folder_id);
+ 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);
- e_ews_connection_enable_notifications_sync (
- ebews->priv->cnc,
- folders,
- &ebews->priv->subscription_key);
+ 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);
- g_slist_free (folders);
- } else {
- if (ebews->priv->subscription_key == 0)
- goto exit;
+ 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_ews_connection_disable_notifications_sync (
- ebews->priv->cnc,
- ebews->priv->subscription_key);
+ e_cache_set_key_int (E_CACHE (book_cache), "gal-sequence",
full->seq, NULL);
- ebews->priv->subscription_key = 0;
- }
+ d (printf ("Ewsgal: sync successfully completed\n"));
+ }
-exit:
- PRIV_UNLOCK (ebews->priv);
- g_object_unref (ebews);
- return NULL;
-}
+ ews_oal_details_free (full);
+ }
-static void
-ebews_listen_notifications_cb (EBookBackendEws *ebews,
- GParamSpec *spec,
- CamelEwsSettings *ews_settings)
-{
- GThread *thread;
+ if (uncompressed_filename) {
+ /* preserve the oab file once we are able to decode the differential
updates */
+ g_unlink (uncompressed_filename);
+ g_free (uncompressed_filename);
+ }
+ }
- PRIV_LOCK (ebews->priv);
- if (ebews->priv->cnc == NULL) {
- PRIV_UNLOCK (ebews->priv);
- return;
- }
+ 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 (!e_ews_connection_satisfies_server_version (ebews->priv->cnc, E_EWS_EXCHANGE_2010_SP1)) {
- PRIV_UNLOCK (ebews->priv);
- return;
- }
+ if (success)
+ *out_new_sync_tag = etag;
+ else
+ g_free (etag);
- ebews->priv->listen_notifications = camel_ews_settings_get_listen_notifications (ews_settings);
- PRIV_UNLOCK (ebews->priv);
+ if (local_error) {
+ g_prefix_error (&local_error, "%s", _("Failed to update GAL:"));
+ g_propagate_error (error, local_error);
+ }
+ }
- thread = g_thread_new (NULL, handle_notifications_thread, g_object_ref (ebews));
- g_thread_unref (thread);
-}
+ g_free (oab_url);
+ } else {
+ GSList *items_created = NULL, *items_modified = NULL, *items_deleted = NULL, *link;
+ gboolean includes_last_item = TRUE;
-static gpointer
-ews_update_items_thread (gpointer data)
-{
- EBookBackendEws *ebews = data;
- EBookBackendEwsPrivate *priv;
- gchar *sync_state = NULL;
- GError *error = NULL;
- gboolean includes_last_item;
- GSList *items_created = NULL;
- GSList *items_updated = NULL;
- GSList *items_deleted = NULL;
- GSList *items_deleted_resync = NULL;
- GSList *contacts_created = NULL;
- GSList *contacts_updated = NULL;
- GSList *l;
+ 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);
- priv = ebews->priv;
-
- e_book_sqlite_get_key_value (priv->summary, E_BOOK_SQL_SYNC_DATA_KEY, &sync_state, NULL);
- do {
- gchar *old_sync_state = sync_state;
-
- sync_state = NULL;
- includes_last_item = TRUE;
-
- e_ews_connection_sync_folder_items_sync (
- priv->cnc,
- EWS_PRIORITY_MEDIUM,
- old_sync_state,
- priv->folder_id,
- "IdOnly",
- NULL,
- EWS_MAX_FETCH_COUNT,
- &sync_state,
- &includes_last_item,
- &items_created,
- &items_updated,
- &items_deleted,
- priv->cancellable,
- &error);
-
- g_free (old_sync_state);
-
- if (error != NULL) {
- if (g_error_matches (error, EWS_CONNECTION_ERROR,
EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) {
- g_clear_error (&error);
-
- if (!e_book_sqlite_search_uids (priv->summary, NULL, &items_deleted_resync,
- priv->cancellable, &error))
- break;
-
- /* This should be the case anyway, but make sure */
- sync_state = NULL;
-
- /* Ensure we go round the loop again */
- includes_last_item = FALSE;
- continue;
- }
- /* Other error */
- break;
- }
+ if (!success &&
+ g_error_matches (local_error, EWS_CONNECTION_ERROR,
EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) {
+ g_clear_error (&local_error);
- if (items_created) {
- ebews_fetch_items (
- ebews,
- items_created, /* freed inside the function */
- &contacts_created,
- priv->cancellable,
- &error);
- items_created = NULL;
- if (error != NULL)
- break;
- }
+ e_book_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL);
- if (items_updated) {
- ebews_fetch_items (
- ebews,
- items_updated, /* freed inside the function */
- &contacts_updated,
- priv->cancellable,
- &error);
- items_updated = NULL;
- if (error != NULL)
- break;
+ 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);
}
- /* Network traffic is done, and database access starts here */
- if (!e_book_sqlite_lock (priv->summary, EBSQL_LOCK_WRITE, priv->cancellable, &error))
- break;
+ if (success) {
+ GSList *contacts_created = NULL, *contacts_modified = NULL;
- if ((items_deleted_resync && !e_book_sqlite_remove_contacts (priv->summary,
items_deleted_resync, priv->cancellable, &error)) ||
- (items_deleted && !e_book_sqlite_remove_contacts (priv->summary, items_deleted,
priv->cancellable, &error)) ||
- (contacts_created && !e_book_sqlite_add_contacts (priv->summary, contacts_created, NULL,
TRUE, priv->cancellable, &error)) ||
- (contacts_updated && !e_book_sqlite_add_contacts (priv->summary, contacts_updated, NULL,
TRUE, priv->cancellable, &error)) ||
- !e_book_sqlite_set_key_value (priv->summary, E_BOOK_SQL_SYNC_DATA_KEY, sync_state,
&error) ||
- (includes_last_item && !e_book_sqlite_set_key_value_int (priv->summary,
E_BOOK_SQL_IS_POPULATED_KEY, TRUE, &error)) ||
- !ebews_bump_revision (ebews, &error)) {
- e_book_sqlite_unlock (priv->summary, EBSQL_UNLOCK_ROLLBACK, NULL);
- break;
- }
- if (!e_book_sqlite_unlock (priv->summary, EBSQL_UNLOCK_COMMIT, &error))
- break;
+ /* 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);
- while (items_deleted_resync || items_deleted) {
- if (items_deleted_resync) {
- l = items_deleted_resync;
- items_deleted_resync = l->next;
- } else {
- l = items_deleted;
- items_deleted = l->next;
+ 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);
}
- e_book_backend_notify_remove (E_BOOK_BACKEND (ebews), l->data);
- g_free (l->data);
- g_slist_free_1 (l);
- }
- while (contacts_created || contacts_updated) {
- if (contacts_created) {
- l = contacts_created;
- contacts_created = l->next;
- } else {
- l = contacts_updated;
- contacts_updated = l->next;
+ 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);
}
- e_book_backend_notify_update (E_BOOK_BACKEND (ebews), l->data);
- g_object_unref (l->data);
- g_slist_free_1 (l);
- }
- cursors_recalculate (ebews);
- } while (!includes_last_item);
+ for (link = items_deleted; link; link = g_slist_next (link)) {
+ const gchar *uid = link->data;
- g_slist_free_full (items_created, g_object_unref);
- g_slist_free_full (items_updated, g_object_unref);
- g_slist_free_full (items_deleted, g_free);
- g_slist_free_full (items_deleted_resync, g_free);
- g_slist_free_full (contacts_created, g_object_unref);
- g_slist_free_full (contacts_updated, g_object_unref);
-
- if (error != NULL) {
- g_warning ("%s: %s", G_STRFUNC, error->message);
- g_clear_error (&error);
- }
-
- g_free (sync_state);
- g_object_unref (ebews);
+ *out_removed_objects = g_slist_prepend (*out_removed_objects,
+ e_book_meta_backend_info_new (uid, NULL, NULL, NULL));
+ }
- return NULL;
-}
+ g_slist_free_full (contacts_created, g_object_unref);
+ g_slist_free_full (contacts_modified, g_object_unref);
-static void
-ebews_server_notification_cb (EBookBackendEws *ebews,
- GSList *events,
- EEwsConnection *cnc)
-{
- GSList *l;
- gboolean update_folder = FALSE;
+ *out_repeat = !includes_last_item;
+ } else if (local_error) {
+ g_propagate_error (error, local_error);
+ }
- g_return_if_fail (ebews != NULL);
- g_return_if_fail (ebews->priv != NULL);
+ 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);
+ }
- for (l = events; l != NULL; l = l->next) {
- EEwsNotificationEvent *event = l->data;
+ g_rec_mutex_unlock (&bbews->priv->cnc_lock);
- switch (event->type) {
- case E_EWS_NOTIFICATION_EVENT_CREATED:
- case E_EWS_NOTIFICATION_EVENT_DELETED:
- case E_EWS_NOTIFICATION_EVENT_MODIFIED:
- PRIV_LOCK (ebews->priv);
- if (g_strcmp0 (event->folder_id, ebews->priv->folder_id) == 0)
- update_folder = TRUE;
- PRIV_UNLOCK (ebews->priv);
- break;
- case E_EWS_NOTIFICATION_EVENT_MOVED:
- case E_EWS_NOTIFICATION_EVENT_COPIED:
- PRIV_LOCK (ebews->priv);
- if (g_strcmp0 (event->folder_id, ebews->priv->folder_id) == 0 ||
- g_strcmp0 (event->old_folder_id, ebews->priv->folder_id) == 0)
- update_folder = TRUE;
- PRIV_UNLOCK (ebews->priv);
- break;
- default:
- return;
- }
- }
+ ebb_ews_convert_error_to_edb_error (error);
- if (update_folder) {
- GThread *thread;
+ g_clear_object (&book_cache);
- thread = g_thread_new (NULL, ews_update_items_thread, g_object_ref (ebews));
- g_thread_unref (thread);
- }
+ return success;
}
static gboolean
-e_book_backend_ews_open_sync (EBookBackend *backend,
- GCancellable *cancellable,
- GError **error)
+ebb_ews_load_contact_sync (EBookMetaBackend *meta_backend,
+ const gchar *uid,
+ const gchar *extra,
+ EContact **out_contact,
+ gchar **out_extra,
+ GCancellable *cancellable,
+ GError **error)
{
- CamelEwsSettings *ews_settings;
- EBookBackendEws *ebews;
- EBookBackendEwsPrivate * priv;
- ESource *source;
- gboolean need_to_authenticate;
- gchar *revision = NULL;
-
- ebews = E_BOOK_BACKEND_EWS (backend);
- priv = ebews->priv;
-
- if (priv->base_directory || e_book_backend_is_opened (backend))
- return TRUE;
-
- ews_settings = book_backend_ews_get_collection_settings (ebews);
- source = e_backend_get_source (E_BACKEND (ebews));
+ EBookBackendEws *bbews;
+ GSList *ids, *items = NULL;
+ gboolean success;
- e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
+ 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);
- PRIV_LOCK (priv);
- need_to_authenticate = priv->cnc == NULL && e_backend_is_destination_reachable (E_BACKEND (backend),
cancellable, NULL);
+ bbews = E_BOOK_BACKEND_EWS (meta_backend);
- PRIV_UNLOCK (priv);
+ g_rec_mutex_lock (&bbews->priv->cnc_lock);
- e_book_sqlite_get_key_value (priv->summary, "revision", &revision, NULL);
- if (revision) {
- e_book_backend_notify_property_changed (backend,
- BOOK_BACKEND_PROPERTY_REVISION,
- revision);
- g_free (revision);
- }
+ ids = g_slist_prepend (NULL, (gpointer) uid);
- if (!ebews->priv->is_gal) {
- PRIV_LOCK (priv);
- priv->listen_notifications = camel_ews_settings_get_listen_notifications (ews_settings);
+ 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);
- if (priv->listen_notifications)
- ebews_listen_notifications_cb (ebews, NULL, ews_settings);
+ g_slist_free (ids);
- PRIV_UNLOCK (priv);
+ if (!items)
+ success = FALSE;
- g_signal_connect_swapped (
- ews_settings,
- "notify::listen-notifications",
- G_CALLBACK (ebews_listen_notifications_cb),
- ebews);
- }
+ if (success) {
+ GSList *contacts = NULL;
- if (ebews->priv->cnc)
- e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
- else
- e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+ success = ebb_ews_fetch_items_sync (bbews, items, &contacts, cancellable, error);
+ if (success && contacts)
+ *out_contact = g_object_ref (contacts->data);
- if (need_to_authenticate &&
- !book_backend_ews_ensure_connected (ebews, cancellable, error)) {
- convert_error_to_edb_error (error);
- return FALSE;
+ g_slist_free_full (contacts, g_object_unref);
}
- return TRUE;
-}
+ g_rec_mutex_unlock (&bbews->priv->cnc_lock);
-/**
- * e_book_backend_ews_new:
- */
-EBookBackend *
-e_book_backend_ews_new (void)
-{
- EBookBackendEws *backend;
+ g_slist_free_full (items, g_object_unref);
- backend = g_object_new (E_TYPE_BOOK_BACKEND_EWS, NULL);
+ ebb_ews_convert_error_to_edb_error (error);
- return E_BOOK_BACKEND (backend);
+ return success;
}
static gboolean
-e_book_backend_ews_get_destination_address (EBackend *backend,
- gchar **host,
- guint16 *port)
+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)
{
- 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;
+ EBookBackendEws *bbews;
+ EwsFolderId *fid;
+ GSList *items = NULL;
+ gboolean is_dl = FALSE;
+ gboolean success;
- ews_settings = book_backend_ews_get_collection_settings (E_BOOK_BACKEND_EWS (backend));
- g_return_val_if_fail (ews_settings != NULL, FALSE);
+ 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);
- host_url = camel_ews_settings_dup_hosturl (ews_settings);
- g_return_val_if_fail (host_url != NULL, FALSE);
+ bbews = E_BOOK_BACKEND_EWS (meta_backend);
- 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);
+ g_rec_mutex_lock (&bbews->priv->cnc_lock);
- result = *host && **host;
- if (!result) {
- g_free (*host);
- *host = NULL;
+ 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;
}
- soup_uri_free (soup_uri);
+ is_dl = TRUE;
}
- g_free (host_url);
+ fid = e_ews_folder_id_new (bbews->priv->folder_id, NULL, FALSE);
+ if (overwrite_existing) {
+ EBookCache *book_cache;
+ EContact *old_contact = NULL;
- return result;
-}
+ book_cache = e_book_meta_backend_ref_cache (meta_backend);
-static void
-e_book_backend_ews_constructed (GObject *object)
-{
- G_OBJECT_CLASS (e_book_backend_ews_parent_class)->constructed (object);
+ success = e_book_cache_get_contact (book_cache, e_contact_get_const (contact, E_CONTACT_UID),
FALSE, &old_contact, cancellable, error);
+ if (success) {
+ ConvertData cd;
+ const gchar *conflict_res = "AlwaysOverwrite";
- /* Reset the connectable, it steals data from Authentication extension,
- where is written incorrect address */
- e_backend_set_connectable (E_BACKEND (object), NULL);
-}
-
-static void
-e_book_backend_ews_dispose (GObject *object)
-{
- EBookBackendEws *bews;
- EBookBackendEwsPrivate *priv;
- CamelEwsSettings *ews_settings;
-
- bews = E_BOOK_BACKEND_EWS (object);
- priv = bews->priv;
-
- ews_settings = book_backend_ews_get_collection_settings (bews);
- g_signal_handlers_disconnect_by_func (ews_settings, ebews_listen_notifications_cb, bews);
-
- if (priv->cancellable)
- g_cancellable_cancel (priv->cancellable);
-
- if (priv->dlock) {
- g_mutex_lock (&priv->dlock->mutex);
- priv->dlock->exit = TRUE;
- g_cond_signal (&priv->dlock->cond);
- g_mutex_unlock (&priv->dlock->mutex);
-
- if (priv->dthread)
- g_thread_join (priv->dthread);
-
- g_mutex_clear (&priv->dlock->mutex);
- g_cond_clear (&priv->dlock->cond);
- g_free (priv->dlock);
- priv->dthread = NULL;
- priv->dlock = NULL;
- }
-
- if (priv->cancellable) {
- g_object_unref (priv->cancellable);
- priv->cancellable = NULL;
- }
+ cd.bbews = bbews;
+ cd.cancellable = cancellable;
+ cd.error = error;
+ cd.old_contact = old_contact;
+ cd.new_contact = contact;
+ cd.change_key = NULL;
- if (priv->cnc) {
- g_signal_handlers_disconnect_by_func (priv->cnc, ebews_server_notification_cb, bews);
+ if (conflict_resolution == E_CONFLICT_RESOLUTION_FAIL)
+ conflict_res = "NeverOverwrite";
- if (priv->listen_notifications) {
- if (priv->subscription_key != 0) {
- e_ews_connection_disable_notifications_sync (
- priv->cnc,
- priv->subscription_key);
- priv->subscription_key = 0;
- }
+ 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);
- priv->listen_notifications = FALSE;
+ g_free (cd.change_key);
}
- g_clear_object (&priv->cnc);
+ 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 (priv->ops)
- g_hash_table_destroy (priv->ops);
-
- g_free (priv->folder_id);
- priv->folder_id = NULL;
+ if (success && items) {
+ EEwsItem *item = items->data;
+ const EwsId *item_id;
- g_free (priv->oab_url);
- priv->oab_url = NULL;
+ item_id = e_ews_item_get_id (item);
+ *out_new_uid = g_strdup (item_id->id);
- g_free (priv->folder_name);
- priv->folder_name = 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 (!overwrite_existing &&
+ e_ews_connection_satisfies_server_version (bbews->priv->cnc, E_EWS_EXCHANGE_2010_SP2)) {
+ EContactPhoto *photo;
- g_free (priv->attachment_dir);
- priv->attachment_dir = NULL;
+ /*
+ * 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;
- if (priv->summary) {
- g_object_unref (priv->summary);
- priv->summary = NULL;
- }
+ set_photo (bbews, item_id, contact, photo, NULL, cancellable, &local_error);
+ e_contact_photo_free (photo);
- if (priv->cursors) {
- g_list_free_full (priv->cursors, g_object_unref);
- priv->cursors = NULL;
+ if (local_error) {
+ g_propagate_error (error, local_error);
+ g_prefix_error (error, "%s", _("Failed to set contact photo:"));
+ success = FALSE;
+ }
+ }
+ }
}
- g_free (priv->locale);
- priv->locale = NULL;
-
- g_free (priv->base_directory);
- priv->base_directory = NULL;
-
- G_OBJECT_CLASS (e_book_backend_ews_parent_class)->dispose (object);
-}
-
-static void
-e_book_backend_ews_finalize (GObject *object)
-{
- EBookBackendEws *bews;
+ g_slist_free_full (items, g_object_unref);
+ e_ews_folder_id_free (fid);
- bews = E_BOOK_BACKEND_EWS (object);
+ g_rec_mutex_unlock (&bbews->priv->cnc_lock);
- g_rec_mutex_clear (&bews->priv->rec_mutex);
+ ebb_ews_convert_error_to_edb_error (error);
- G_OBJECT_CLASS (e_book_backend_ews_parent_class)->finalize (object);
+ return success;
}
-static ESourceAuthenticationResult
-e_book_backend_ews_authenticate_sync (EBackend *backend,
- const ENamedParameters *credentials,
- gchar **out_certificate_pem,
- GTlsCertificateFlags *out_certificate_errors,
- GCancellable *cancellable,
- GError **error)
+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 *ews_backend;
- EEwsConnection *connection;
- ESourceAuthenticationResult result;
- CamelEwsSettings *ews_settings;
- gchar *hosturl;
-
- ews_backend = E_BOOK_BACKEND_EWS (backend);
- ews_settings = book_backend_ews_get_collection_settings (ews_backend);
- hosturl = camel_ews_settings_dup_hosturl (ews_settings);
-
- connection = e_ews_connection_new (hosturl, ews_settings);
-
- e_binding_bind_property (
- ews_backend, "proxy-resolver",
- connection, "proxy-resolver",
- G_BINDING_SYNC_CREATE);
-
- result = e_ews_connection_try_credentials_sync (connection, credentials, cancellable, error);
-
- if (result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
-
- PRIV_LOCK (ews_backend->priv);
-
- if (ews_backend->priv->cnc != NULL)
- g_object_unref (ews_backend->priv->cnc);
- ews_backend->priv->cnc = g_object_ref (connection);
- ews_backend->priv->is_writable = !ews_backend->priv->is_gal;
-
- g_signal_connect_swapped (
- ews_backend->priv->cnc,
- "server-notification",
- G_CALLBACK (ebews_server_notification_cb),
- backend);
-
- PRIV_UNLOCK (ews_backend->priv);
+ EBookBackendEws *bbews;
+ GSList *ids;
+ gboolean success;
- e_backend_set_online (backend, TRUE);
- ebews_start_refreshing (ews_backend, TRUE);
+ g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE);
- if (!ews_backend->priv->is_gal)
- ebews_listen_notifications_cb (ews_backend, NULL, ews_settings);
- } else {
- ews_backend->priv->is_writable = FALSE;
- e_backend_set_online (backend, FALSE);
-
- 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;
- }
- }
+ bbews = E_BOOK_BACKEND_EWS (meta_backend);
- e_book_backend_set_writable (E_BOOK_BACKEND (backend), ews_backend->priv->is_writable);
+ g_rec_mutex_lock (&bbews->priv->cnc_lock);
- g_object_unref (connection);
+ ids = g_slist_prepend (NULL, (gpointer) uid);
- g_free (hosturl);
+ success = e_ews_connection_delete_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM, ids,
EWS_HARD_DELETE, 0, FALSE, cancellable, error);
- return result;
-}
+ g_slist_free (ids);
+ g_rec_mutex_unlock (&bbews->priv->cnc_lock);
-static void
-cursors_contact_added (EBookBackendEws *ebews,
- EContact *contact)
-{
- GList *l;
+ ebb_ews_convert_error_to_edb_error (error);
- for (l = ebews->priv->cursors; l; l = l->next) {
- EDataBookCursor *cursor = l->data;
-
- e_data_book_cursor_contact_added (cursor, contact);
- }
-}
-
-static void
-cursors_contact_removed (EBookBackendEws *ebews,
- EContact *contact)
-{
- GList *l;
-
- for (l = ebews->priv->cursors; l; l = l->next) {
- EDataBookCursor *cursor = l->data;
-
- e_data_book_cursor_contact_removed (cursor, contact);
- }
+ return success;
}
-static void
-cursors_recalculate (EBookBackendEws *ebews)
+static gboolean
+ebb_ews_search_sync (EBookMetaBackend *meta_backend,
+ const gchar *expr,
+ gboolean meta_contact,
+ GSList **out_contacts,
+ GCancellable *cancellable,
+ GError **error)
{
- GList *l;
+ g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE);
- for (l = ebews->priv->cursors; l; l = l->next) {
- EDataBookCursor *cursor = l->data;
+ /* Ignore errors, just try its best */
+ ebb_ews_update_cache_for_expression (E_BOOK_BACKEND_EWS (meta_backend), expr, cancellable, NULL);
- e_data_book_cursor_recalculate (cursor, NULL, 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 EDataBookCursor *
-e_book_backend_ews_create_cursor (EBookBackend *backend,
- EContactField *sort_fields,
- EBookCursorSortType *sort_types,
- guint n_fields,
- GError **error)
+static gboolean
+ebb_ews_search_uids_sync (EBookMetaBackend *meta_backend,
+ const gchar *expr,
+ GSList **out_uids,
+ GCancellable *cancellable,
+ GError **error)
{
- EBookBackendEws *ebews = E_BOOK_BACKEND_EWS (backend);
- EDataBookCursor *cursor;
-
- PRIV_LOCK (ebews->priv);
-
- cursor = e_data_book_cursor_sqlite_new (
- backend,
- ebews->priv->summary,
- "revision",
- sort_fields,
- sort_types,
- n_fields,
- error);
-
- if (cursor != NULL) {
- ebews->priv->cursors =
- g_list_prepend (ebews->priv->cursors, cursor);
- }
+ g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE);
- PRIV_UNLOCK (ebews->priv);
+ /* Ignore errors, just try its best */
+ ebb_ews_update_cache_for_expression (E_BOOK_BACKEND_EWS (meta_backend), expr, cancellable, NULL);
- return cursor;
+ /* 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 gboolean
-e_book_backend_ews_delete_cursor (EBookBackend *backend,
- EDataBookCursor *cursor,
- GError **error)
+static gchar *
+ebb_ews_get_backend_property (EBookBackend *book_backend,
+ const gchar *prop_name)
{
- EBookBackendEws *ebews = E_BOOK_BACKEND_EWS (backend);
- GList *link;
+ EBookBackendEws *bbews;
- PRIV_LOCK (ebews->priv);
+ g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (book_backend), NULL);
+ g_return_val_if_fail (prop_name != NULL, NULL);
- link = g_list_find (ebews->priv->cursors, cursor);
+ bbews = E_BOOK_BACKEND_EWS (book_backend);
- if (link != NULL) {
- ebews->priv->cursors = g_list_delete_link (ebews->priv->cursors, link);
- g_object_unref (cursor);
- } else {
- g_set_error_literal (
- error,
- E_CLIENT_ERROR,
- E_CLIENT_ERROR_INVALID_ARG,
- _("Requested to delete an unrelated cursor"));
- }
+ if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
+ CamelEwsSettings *ews_settings;
- PRIV_UNLOCK (ebews->priv);
+ ews_settings = ebb_ews_get_collection_settings (bbews);
- return link != NULL;
-}
+ 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;
-static gboolean
-e_book_backend_ews_set_locale (EBookBackend *backend,
- const gchar *locale,
- GCancellable *cancellable,
- GError **error)
-{
- EBookBackendEws *ebews = E_BOOK_BACKEND_EWS (backend);
- gboolean success = FALSE;
- GList *l;
+ buffer = g_string_sized_new (1024);
- PRIV_LOCK (ebews->priv);
+ for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) {
+ if (mappings[ii].element_type != ELEMENT_TYPE_SIMPLE)
+ continue;
- if (!e_book_sqlite_lock (ebews->priv->summary, EBSQL_LOCK_WRITE, cancellable, error)) {
- PRIV_UNLOCK (ebews->priv);
- return FALSE;
- }
+ if (buffer->len > 0)
+ g_string_append_c (buffer, ',');
+ g_string_append (buffer, e_contact_field_name (mappings[ii].field_id));
+ }
- if (e_book_sqlite_set_locale (ebews->priv->summary, locale, cancellable, error) &&
- ebews_bump_revision (ebews, error))
- success = e_book_sqlite_unlock (ebews->priv->summary, EBSQL_UNLOCK_COMMIT, error);
- else
- e_book_sqlite_unlock (ebews->priv->summary, EBSQL_UNLOCK_ROLLBACK, NULL);
+ 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));
+ }
- if (success) {
- g_free (ebews->priv->locale);
- ebews->priv->locale = g_strdup (locale);
- }
+ 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);
- /* This must be done outside the EBookSqlite lock,
- * as it may try to acquire the lock as well. */
- for (l = ebews->priv->cursors; success && l; l = l->next) {
- EDataBookCursor *cursor = l->data;
+ g_string_free (buffer, TRUE);
- success = e_data_book_cursor_load_locale (
- cursor, NULL, cancellable, error);
+ return fields;
}
- PRIV_UNLOCK (ebews->priv);
-
- return success;
+ /* 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 gchar *
-e_book_backend_ews_dup_locale (EBookBackend *backend)
+static void
+e_book_backend_ews_constructed (GObject *object)
{
- EBookBackendEws *ebews = E_BOOK_BACKEND_EWS (backend);
- EBookBackendEwsPrivate *priv = ebews->priv;
- gchar *locale;
+ EBookBackendEws *bbews = E_BOOK_BACKEND_EWS (object);
+ EBookCache *book_cache;
+ gchar *cache_dirname;
- PRIV_LOCK (ebews->priv);
- locale = g_strdup (priv->locale);
- PRIV_UNLOCK (ebews->priv);
-
- return locale;
-}
-
-static EDataBookDirect *
-e_book_backend_ews_get_direct_book (EBookBackend *backend)
-{
- EDataBookDirect *direct;
- gchar *backend_path;
- const gchar *dirname;
- const gchar *modules_env = NULL;
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_book_backend_ews_parent_class)->constructed (object);
- modules_env = g_getenv (EDS_ADDRESS_BOOK_MODULES);
+ book_cache = e_book_meta_backend_ref_cache (E_BOOK_META_BACKEND (bbews));
- dirname = e_book_backend_get_cache_dir (backend);
+ cache_dirname = g_path_get_dirname (e_cache_get_filename (E_CACHE (book_cache)));
- /* Support in-tree testing / relocated modules */
- if (modules_env)
- backend_path = g_build_filename (modules_env, "libebookbackendews.so", NULL);
- else
- backend_path = g_build_filename (BACKENDDIR, "libebookbackendews.so", NULL);
- direct = e_data_book_direct_new (backend_path, "EBookBackendEwsFactory", dirname);
+ g_clear_object (&book_cache);
- g_free (backend_path);
+ bbews->priv->attachments_dir = g_build_filename (cache_dirname, "attachments", NULL);
+ g_mkdir_with_parents (bbews->priv->attachments_dir, 0777);
- return direct;
+ g_free (cache_dirname);
}
static void
-e_book_backend_ews_configure_direct (EBookBackend *backend,
- const gchar *config)
+e_book_backend_ews_dispose (GObject *object)
{
- EBookBackendEwsPrivate *priv;
+ EBookBackendEws *bbews = E_BOOK_BACKEND_EWS (object);
+
+ g_rec_mutex_lock (&bbews->priv->cnc_lock);
+
+ g_clear_object (&bbews->priv->cnc);
- priv = E_BOOK_BACKEND_EWS (backend)->priv;
- priv->base_directory = g_strdup (config);
+ 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_class_init (EBookBackendEwsClass *klass)
+e_book_backend_ews_finalize (GObject *object)
{
+ EBookBackendEws *bbews = E_BOOK_BACKEND_EWS (object);
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
- EBackendClass *backend_class;
- EBookBackendClass *parent_class;
+ g_free (bbews->priv->folder_id);
+ g_free (bbews->priv->attachments_dir);
- g_type_class_add_private (klass, sizeof (EBookBackendEwsPrivate));
+ g_rec_mutex_clear (&bbews->priv->cnc_lock);
- backend_class = E_BACKEND_CLASS (klass);
- parent_class = E_BOOK_BACKEND_CLASS (klass);
-
- /* Set the virtual methods. */
- parent_class->open_sync = e_book_backend_ews_open_sync;
- parent_class->get_backend_property = e_book_backend_ews_get_backend_property;
-
- parent_class->create_contacts = e_book_backend_ews_create_contacts;
- parent_class->remove_contacts = e_book_backend_ews_remove_contacts;
- parent_class->modify_contacts = e_book_backend_ews_modify_contacts;
- parent_class->get_contact = e_book_backend_ews_get_contact;
- parent_class->get_contact_list = e_book_backend_ews_get_contact_list;
- parent_class->start_view = e_book_backend_ews_start_view;
- parent_class->stop_view = e_book_backend_ews_stop_view;
- parent_class->create_cursor = e_book_backend_ews_create_cursor;
- parent_class->delete_cursor = e_book_backend_ews_delete_cursor;
- parent_class->set_locale = e_book_backend_ews_set_locale;
- parent_class->dup_locale = e_book_backend_ews_dup_locale;
- parent_class->get_direct_book = e_book_backend_ews_get_direct_book;
- parent_class->configure_direct = e_book_backend_ews_configure_direct;
-
- backend_class->get_destination_address = e_book_backend_ews_get_destination_address;
- backend_class->authenticate_sync = e_book_backend_ews_authenticate_sync;
-
- object_class->constructed = e_book_backend_ews_constructed;
- object_class->dispose = e_book_backend_ews_dispose;
- object_class->finalize = e_book_backend_ews_finalize;
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_book_backend_ews_parent_class)->finalize (object);
}
static void
-e_book_backend_ews_initable_init (GInitableIface *iface)
+e_book_backend_ews_init (EBookBackendEws *bbews)
{
- iface->init = book_backend_ews_initable_init;
+ 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_init (EBookBackendEws *backend)
+e_book_backend_ews_class_init (EBookBackendEwsClass *klass)
{
- backend->priv = G_TYPE_INSTANCE_GET_PRIVATE (backend, E_TYPE_BOOK_BACKEND_EWS,
EBookBackendEwsPrivate);
- backend->priv->ops = g_hash_table_new (NULL, NULL);
+ GObjectClass *object_class;
+ EBookBackendClass *book_backend_class;
+ EBookMetaBackendClass *book_meta_backend_class;
- g_rec_mutex_init (&backend->priv->rec_mutex);
- backend->priv->cancellable = g_cancellable_new ();
+ g_type_class_add_private (klass, sizeof (EBookBackendEwsPrivate));
- g_signal_connect (
- backend, "notify::online",
- G_CALLBACK (e_book_backend_ews_notify_online_cb), NULL);
+ 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;
+
+ 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.h b/src/addressbook/e-book-backend-ews.h
index ba1133f..ea58685 100644
--- a/src/addressbook/e-book-backend-ews.h
+++ b/src/addressbook/e-book-backend-ews.h
@@ -20,8 +20,8 @@
*
*/
-#ifndef __E_BOOK_BACKEND_EWS_H__
-#define __E_BOOK_BACKEND_EWS_H__
+#ifndef E_BOOK_BACKEND_EWS_H
+#define E_BOOK_BACKEND_EWS_H
#include <libedata-book/libedata-book.h>
@@ -31,19 +31,18 @@
#define E_IS_BOOK_BACKEND_EWS(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), E_TYPE_BOOK_BACKEND_EWS))
#define E_IS_BOOK_BACKEND_EWS_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), E_TYPE_BOOK_BACKEND_EWS))
#define E_BOOK_BACKEND_EWS_GET_CLASS(k) (G_TYPE_INSTANCE_GET_CLASS ((obj), E_TYPE_BOOK_BACKEND_EWS,
EBookBackenEwsClass))
+
typedef struct _EBookBackendEwsPrivate EBookBackendEwsPrivate;
typedef struct {
- EBookBackend parent_object;
+ EBookMetaBackend parent_object;
EBookBackendEwsPrivate *priv;
} EBookBackendEws;
typedef struct {
- EBookBackendClass parent_class;
+ EBookMetaBackendClass parent_class;
} EBookBackendEwsClass;
-EBookBackend *e_book_backend_ews_new (void);
GType e_book_backend_ews_get_type (void);
-#endif /* __E_BOOK_BACKEND_EWS_H__ */
-
+#endif /* E_BOOK_BACKEND_EWS_H */
diff --git a/src/calendar/e-cal-backend-ews-utils.c b/src/calendar/e-cal-backend-ews-utils.c
index 7c37be2..d76e2cb 100644
--- a/src/calendar/e-cal-backend-ews-utils.c
+++ b/src/calendar/e-cal-backend-ews-utils.c
@@ -865,36 +865,6 @@ ewscal_set_reccurence_exceptions (ESoapMessage *msg,
e_soap_message_end_element (msg); /* "DeletedOccurrences" */
}
-void
-ewscal_get_attach_differences (const GSList *original,
- const GSList *modified,
- GSList **removed,
- GSList **added)
-{
- gboolean flag;
- GSList *i, *i_next, *j, *j_next, *original_copy, *modified_copy;
- original_copy = g_slist_copy ((GSList *) original);
- modified_copy = g_slist_copy ((GSList *) modified);
-
- for (j = modified_copy; j; j = j_next) {
- j_next = j->next;
-
- for (i = original_copy, flag = FALSE; !flag && i; i = i_next) {
- i_next = i->next;
-
- if (g_strcmp0 (j->data, i->data) == 0) {
- /* Remove from the lists attachments that are on both */
- original_copy = g_slist_delete_link (original_copy, i);
- modified_copy = g_slist_delete_link (modified_copy, j);
- flag = TRUE;
- }
- }
- }
-
- *removed = original_copy;
- *added = modified_copy;
-}
-
/*
* get meeting organizer e-mail address
*/
@@ -1094,7 +1064,7 @@ convert_vevent_calcomp_to_xml (ESoapMessage *msg,
const gchar *ical_location_start, *ical_location_end, *value;
const gchar *msdn_location_start, *msdn_location_end;
- e_cal_component_set_icalcomponent (comp, icalcomp);
+ 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 */
@@ -1224,6 +1194,8 @@ convert_vevent_calcomp_to_xml (ESoapMessage *msg,
}
e_soap_message_end_element (msg); /* "CalendarItem" */
+
+ g_object_unref (comp);
}
static void
@@ -1898,6 +1870,7 @@ e_cal_backend_ews_rid_to_index (icaltimezone *timezone,
g_propagate_error (
error, EDC_ERROR_EX (OtherError,
"Invalid occurrence ID"));
+ index = 0;
}
return index;
diff --git a/src/calendar/e-cal-backend-ews-utils.h b/src/calendar/e-cal-backend-ews-utils.h
index 0427a2f..521e0e8 100644
--- a/src/calendar/e-cal-backend-ews-utils.h
+++ b/src/calendar/e-cal-backend-ews-utils.h
@@ -61,7 +61,6 @@ void ewscal_set_timezone (ESoapMessage *msg, const gchar *name, EEwsCalendarTime
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);
-void ewscal_get_attach_differences (const GSList *original, const GSList *modified, GSList **removed, GSList
**added);
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);
diff --git a/src/calendar/e-cal-backend-ews.c b/src/calendar/e-cal-backend-ews.c
index b32837a..57495a6 100644
--- a/src/calendar/e-cal-backend-ews.c
+++ b/src/calendar/e-cal-backend-ews.c
@@ -1,7 +1,7 @@
/* -*- 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
@@ -63,37 +63,17 @@
/* Private part of the CalBackendEws structure */
struct _ECalBackendEwsPrivate {
- /* Fields required for online server requests */
+ GRecMutex cnc_lock;
EEwsConnection *cnc;
gchar *folder_id;
- gchar *user_email;
- gchar *storage_path;
-
- ECalBackendStore *store;
- gboolean read_only;
-
- /* A mutex to control access to the private structure for the following */
- GRecMutex rec_mutex;
- icaltimezone *default_zone;
- guint refresh_timeout;
- guint refreshing;
- EFlag *refreshing_done;
- GHashTable *item_id_hash;
-
- GMutex cancellable_lock;
- GCancellable *cancellable;
guint subscription_key;
- gboolean listen_notifications;
gboolean is_freebusy_calendar;
-};
-#define PRIV_LOCK(p) (g_rec_mutex_lock (&(p)->rec_mutex))
-#define PRIV_UNLOCK(p) (g_rec_mutex_unlock (&(p)->rec_mutex))
+ gchar *attachments_dir;
+};
-#define SYNC_KEY "sync-state"
#define EWS_MAX_FETCH_COUNT 100
-#define REFRESH_INTERVAL 600
#define GET_ITEMS_SYNC_PROPERTIES \
"item:Attachments" \
@@ -116,7 +96,6 @@ struct _ECalBackendEwsPrivate {
" calendar:StartTimeZone" \
" calendar:EndTimeZone"
-
#define e_data_cal_error_if_fail(expr, _code) \
G_STMT_START { \
if (G_LIKELY (expr)) { \
@@ -132,15 +111,10 @@ struct _ECalBackendEwsPrivate {
} \
} G_STMT_END
-/* Forward Declarations */
-static void ews_cal_component_get_item_id (ECalComponent *comp, gchar **itemid, gchar **changekey);
-static gboolean ews_start_sync (gpointer data);
-static gpointer ews_start_sync_thread (gpointer data);
-
-G_DEFINE_TYPE (ECalBackendEws, e_cal_backend_ews, E_TYPE_CAL_BACKEND)
+G_DEFINE_TYPE (ECalBackendEws, e_cal_backend_ews, E_TYPE_CAL_META_BACKEND)
static CamelEwsSettings *
-cal_backend_ews_get_collection_settings (ECalBackendEws *backend)
+ecb_ews_get_collection_settings (ECalBackendEws *cbews)
{
ESource *source;
ESource *collection;
@@ -149,8 +123,8 @@ cal_backend_ews_get_collection_settings (ECalBackendEws *backend)
CamelSettings *settings;
const gchar *extension_name;
- source = e_backend_get_source (E_BACKEND (backend));
- registry = e_cal_backend_get_registry (E_CAL_BACKEND (backend));
+ 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);
@@ -169,13 +143,11 @@ cal_backend_ews_get_collection_settings (ECalBackendEws *backend)
}
static void
-convert_error_to_edc_error (GError **perror)
+ecb_ews_convert_error_to_edc_error (GError **perror)
{
GError *error = NULL;
- g_return_if_fail (perror != NULL);
-
- if (!*perror || (*perror)->domain == E_DATA_CAL_ERROR)
+ if (!perror || !*perror || (*perror)->domain == E_DATA_CAL_ERROR)
return;
if ((*perror)->domain == EWS_CONNECTION_ERROR) {
@@ -194,1412 +166,1763 @@ convert_error_to_edc_error (GError **perror)
error = EDC_ERROR_EX (ObjectNotFound, (*perror)->message);
break;
}
- }
- if (!error)
- error = EDC_ERROR_EX (OtherError, (*perror)->message);
+ if (!error)
+ error = EDC_ERROR_EX (OtherError, (*perror)->message);
+ }
- g_error_free (*perror);
- *perror = error;
+ if (error) {
+ g_error_free (*perror);
+ *perror = error;
+ }
}
-static GCancellable *
-cal_backend_ews_ref_cancellable (ECalBackendEws *cbews)
+static void
+ecb_ews_server_notification_cb (ECalBackendEws *cbews,
+ GSList *events,
+ EEwsConnection *cnc)
{
- GCancellable *cancellable = NULL;
+ GSList *link;
+ gboolean update_folder = FALSE;
- g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), NULL);
+ g_return_if_fail (cbews != NULL);
+ g_return_if_fail (cbews->priv != NULL);
- g_mutex_lock (&cbews->priv->cancellable_lock);
- if (cbews->priv->cancellable)
- cancellable = g_object_ref (cbews->priv->cancellable);
- g_mutex_unlock (&cbews->priv->cancellable_lock);
+ 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;
+ }
+ }
- return cancellable;
+ if (update_folder)
+ e_cal_meta_backend_schedule_refresh (E_CAL_META_BACKEND (cbews));
}
static void
-cal_backend_ews_set_cancellable (ECalBackendEws *cbews,
- GCancellable *cancellable)
+ecb_ews_unset_connection (ECalBackendEws *cbews)
{
- GCancellable *old_cancellable;
-
g_return_if_fail (E_IS_CAL_BACKEND_EWS (cbews));
- g_mutex_lock (&cbews->priv->cancellable_lock);
- old_cancellable = cbews->priv->cancellable;
- cbews->priv->cancellable = cancellable;
- g_mutex_unlock (&cbews->priv->cancellable_lock);
+ g_rec_mutex_lock (&cbews->priv->cnc_lock);
+
+ if (cbews->priv->cnc) {
+ g_signal_handlers_disconnect_by_func (cbews->priv->cnc, ecb_ews_server_notification_cb,
cbews);
- if (old_cancellable) {
- g_cancellable_cancel (old_cancellable);
- g_object_unref (old_cancellable);
+ 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_rec_mutex_unlock (&cbews->priv->cnc_lock);
}
-static void
-switch_offline (ECalBackendEws *cbews)
+static icaltimezone *
+ecb_ews_get_timezone (ETimezoneCache *timezone_cache,
+ const gchar *msdn_tzid,
+ const gchar *tzid,
+ const gchar *evo_ews_tzid)
{
- ECalBackendEwsPrivate *priv;
+ icaltimezone *zone = NULL;
+ const gchar *evo_ews_msdn_tzid;
- priv= cbews->priv;
- priv->read_only = TRUE;
+ zone = e_timezone_cache_get_timezone (timezone_cache, tzid);
+ if (zone == NULL)
+ zone = icaltimezone_get_builtin_timezone (tzid);
- if (priv->refresh_timeout) {
- g_source_remove (priv->refresh_timeout);
- priv->refresh_timeout = 0;
- }
+ if (g_strcmp0 (tzid, evo_ews_tzid) == 0)
+ return zone;
- cal_backend_ews_set_cancellable (cbews, NULL);
+ if (evo_ews_tzid != NULL) {
+ evo_ews_msdn_tzid = e_cal_backend_ews_tz_util_get_msdn_equivalent (evo_ews_tzid);
- if (priv->cnc) {
- g_object_unref (priv->cnc);
- priv->cnc = NULL;
+ 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 gboolean
-cal_backend_ews_ensure_connected (ECalBackendEws *cbews,
- GCancellable *cancellable,
- GError **perror)
+static icalparameter *
+ecb_ews_responsetype_to_partstat (const gchar *responsetype)
{
+ icalparameter *param = NULL;
+
+ g_return_val_if_fail (responsetype != NULL, NULL);
+
+ 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;
- GError *local_error = NULL;
+ const gchar *mime_content;
- g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE);
+ g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), NULL);
+ g_return_val_if_fail (E_IS_EWS_ITEM (item), NULL);
- PRIV_LOCK (cbews->priv);
+ timezone_cache = E_TIMEZONE_CACHE (cbews);
- if (cbews->priv->cnc) {
- PRIV_UNLOCK (cbews->priv);
- return TRUE;
- }
+ 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) {
+ 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, utc_zone);
+ 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);
- PRIV_UNLOCK (cbews->priv);
+ /*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,
utc_zone);
+ 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, utc_zone);
+ complete_date.is_date = 1;
+ icalprop = icalproperty_new_completed (complete_date);
+ icalcomponent_add_property (icalcomp, icalprop);
+ }
- ews_settings = cal_backend_ews_get_collection_settings (cbews);
+ /*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);
+ }
- if (e_ews_connection_utils_get_without_password (ews_settings)) {
- e_backend_schedule_authenticate (E_BACKEND (cbews), NULL);
+ icalcomponent_add_component (vcomp, icalcomp);
} else {
- e_backend_credentials_required_sync (E_BACKEND (cbews),
- E_SOURCE_CREDENTIALS_REASON_REQUIRED, NULL, 0, NULL,
- cancellable, &local_error);
- }
+ struct icaltimetype dt;
+ const gchar *tzid;
+ gboolean timezone_set = FALSE;
- if (!local_error)
- return TRUE;
+ mime_content = e_ews_item_get_mime_content (item);
+ vcomp = icalparser_parse_string (mime_content);
- g_propagate_error (perror, local_error);
+ if (!vcomp && mime_content) {
+ const gchar *begin_vcalendar, *end_vcalendar;
- return FALSE;
-}
+ /* 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");
-static void
-e_cal_backend_ews_add_timezone (ECalBackend *backend,
- EDataCal *cal,
- guint32 context,
- GCancellable *cancellable,
- const gchar *tzobj)
-{
- ETimezoneCache *timezone_cache;
- icalcomponent *tz_comp;
- ECalBackendEws *cbews;
- GError *error = NULL;
+ /* 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;
- cbews = (ECalBackendEws *) backend;
- timezone_cache = E_TIMEZONE_CACHE (backend);
+ /* 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;
- e_data_cal_error_if_fail (E_IS_CAL_BACKEND_EWS (cbews), InvalidArg);
- e_data_cal_error_if_fail (tzobj != NULL, InvalidArg);
+ if (begin_vcalendar && !end_vcalendar) {
+ gchar *str;
- tz_comp = icalparser_parse_string (tzobj);
- if (!tz_comp) {
- g_propagate_error (&error, EDC_ERROR (InvalidObject));
- goto exit;
- }
+ str = g_strconcat (mime_content, "\r\n", "END:VCALENDAR", "\r\n", NULL);
+ vcomp = icalparser_parse_string (str);
+ g_free (str);
+ }
+ }
- if (icalcomponent_isa (tz_comp) == ICAL_VTIMEZONE_COMPONENT) {
- icaltimezone *zone;
+ if (!vcomp) {
+ g_warn_if_reached ();
+ return NULL;
+ }
- zone = icaltimezone_new ();
- icaltimezone_set_component (zone, tz_comp);
- e_timezone_cache_add_timezone (timezone_cache, zone);
- icaltimezone_free (zone, 1);
- }
+ 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;
-exit:
- /*FIXME pass tzid here */
- convert_error_to_edc_error (&error);
- e_data_cal_respond_add_timezone (cal, context, error);
-}
+ start_tzid = e_ews_item_get_start_tzid (item);
+ end_tzid = e_ews_item_get_end_tzid (item);
-typedef enum {
- E_EWS_ATTACHMENT_TYPE_NOTHING = 0,
- E_EWS_ATTACHMENT_TYPE_CREATE,
- E_EWS_ATTACHMENT_TYPE_UPDATE
-} EEwsAttachmentType;
-
-typedef struct {
- ECalBackendEws *cbews; /* Create, Remove, Modify, FreeBusy, Attachments, DiscardAlarm */
- GCancellable *cancellable;
- ECalComponent *comp; /* Create, Remove, Modify, FreeBusy, Attachments */
- ECalComponent *extra_comp; /* Modify, Attachments: used as old_comp. Remove: used as parent_comp */
- EDataCal *cal; /* Create, Remove, Modify, FreeBusy, Attachments, DiscardAlarm */
- GSList *users; /* FreeBusy */
- gchar *item_id; /* Accept, Remove, Modify, Attachments, DiscardAlarm */
- gchar *uid; /* Remove */
- gchar *rid; /* Remove */
- EEwsAttachmentType cb_type; /* Attachments */
- ECalObjModType mod; /* Remove */
- guint32 context; /* Create, Remove, Modify, FreeBusy, Attachments, DiscardAlarm */
-} EwsCalendarAsyncData;
+ 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);
-static void
-e_cal_backend_ews_async_data_free (EwsCalendarAsyncData *async_data)
-{
- if (async_data != NULL) {
- g_clear_object (&async_data->cbews);
- g_clear_object (&async_data->cancellable);
- g_clear_object (&async_data->comp);
- g_clear_object (&async_data->extra_comp);
- g_clear_object (&async_data->cal);
-
- g_slist_free_full (async_data->users, g_free);
- g_free (async_data->item_id);
- g_free (async_data->uid);
- g_free (async_data->rid);
-
- g_free (async_data);
- }
-}
+ 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);
-static void
-ews_cal_discard_alarm_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
-{
- EEwsConnection *cnc = E_EWS_CONNECTION (object);
- EwsCalendarAsyncData *edad = user_data;
- GError *error = NULL;
+ /*
+ * 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 (!e_ews_connection_update_items_finish (cnc, res, NULL, &error)) {
- convert_error_to_edc_error (&error);
- }
+ if (start_zone != NULL) {
+ icalcomp = icalcomponent_get_first_component (vcomp, kind);
- convert_error_to_edc_error (&error);
- e_data_cal_respond_discard_alarm (edad->cal, edad->context, error);
+ dt = icalcomponent_get_dtstart (icalcomp);
+ dt = icaltime_convert_to_zone (dt, start_zone);
+ icalcomponent_set_dtstart (icalcomp, dt);
- e_cal_backend_ews_async_data_free (edad);
-}
+ timezone_set = TRUE;
+ e_timezone_cache_add_timezone (timezone_cache, start_zone);
-static void
-e_cal_backend_ews_discard_alarm (ECalBackend *backend,
- EDataCal *cal,
- guint32 context,
- GCancellable *cancellable,
- const gchar *uid,
- const gchar *rid,
- const gchar *auid)
-{
- ECalBackendEws *cbews = (ECalBackendEws *) backend;
- ECalBackendEwsPrivate *priv;
- EwsCalendarAsyncData *edad;
- EwsCalendarConvertData convert_data = { 0 };
- ECalComponent *comp;
- GError *local_error = NULL;
+ if (end_zone != NULL) {
+ dt = icalcomponent_get_dtend (icalcomp);
+ dt = icaltime_convert_to_zone (dt, end_zone);
+ icalcomponent_set_dtend (icalcomp, dt);
- priv = cbews->priv;
+ e_timezone_cache_add_timezone (timezone_cache, end_zone);
+ }
+ }
- PRIV_LOCK (priv);
+ if (!timezone_set)
+ tzid = start_tzid;
+ }
- comp = e_cal_backend_store_get_component (priv->store, uid, NULL);
- if (!comp) {
- e_data_cal_respond_discard_alarm (
- cal, context,
- EDC_ERROR (ObjectNotFound));
- PRIV_UNLOCK (priv);
- return;
- }
+ 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;
- PRIV_UNLOCK (priv);
+ icalcomp = icalcomponent_get_first_component (vcomp, kind);
- if (!cbews->priv->cnc) {
- e_data_cal_respond_discard_alarm (cal, context, EDC_ERROR (RepositoryOffline));
- return;
- }
+ if (!icaltimezone_get_builtin_timezone (tzid) &&
+ icalcomponent_get_uid (icalcomp)) {
+ icalcomponent *vtimezone;
- if (!cal_backend_ews_ensure_connected (cbews, cancellable, &local_error)) {
- convert_error_to_edc_error (&local_error);
- e_data_cal_respond_discard_alarm (cal, context, local_error);
- return;
- }
+ /* Add the timezone */
+ vtimezone = icalcomponent_get_first_component (vcomp,
ICAL_VTIMEZONE_COMPONENT);
+ if (vtimezone != NULL) {
+ icalproperty *prop;
- /* FIXME: Can't there be multiple alarms for each event? Or does
- * Exchange not support that? */
- edad = g_new0 (EwsCalendarAsyncData, 1);
- edad->cbews = g_object_ref (cbews);
- edad->cancellable = cal_backend_ews_ref_cancellable (cbews);
- edad->cal = g_object_ref (cal);
- edad->context = context;
+ new_tzid = g_strconcat ("/evolution/ews/tzid/", icalcomponent_get_uid
(icalcomp), NULL);
- if (e_cal_component_has_recurrences (comp)) {
- gint *index;
+ 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);
- convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_OCCURRENCEITEM;
- e_cal_component_get_sequence (comp, &index);
+ 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);
+ }
+ }
- 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;
+ 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 = icalcomponent_get_dtstart (icalcomp);
+ dt = icaltime_convert_to_zone (dt, zone);
+ icalcomponent_set_dtstart (icalcomp, dt);
+
+ dt = icalcomponent_get_dtend (icalcomp);
+ dt = icaltime_convert_to_zone (dt, zone);
+ icalcomponent_set_dtend (icalcomp, dt);
+ }
+
+ g_free (new_tzid);
}
- } else {
- convert_data.change_type = E_EWS_ITEMCHANGE_TYPE_ITEM;
- convert_data.index = -1;
}
- ews_cal_component_get_item_id (comp, &convert_data.item_id, &convert_data.change_key);
+ /* Vevent or Vtodo */
+ icalcomp = icalcomponent_get_first_component (vcomp, kind);
+ if (icalcomp) {
+ icalproperty *icalprop, *freebusy;
+ const EwsId *item_id;
+ const GSList *l = NULL;
+ const gchar *uid = e_ews_item_get_uid (item);
+ gchar *user_email;
- e_ews_connection_update_items (
- priv->cnc, EWS_PRIORITY_MEDIUM,
- "AlwaysOverwrite", NULL,
- "SendToNone", NULL,
- e_cal_backend_ews_clear_reminder_is_set,
- &convert_data,
- edad->cancellable,
- ews_cal_discard_alarm_cb,
- edad);
-}
+ item_id = e_ews_item_get_id (item);
+ user_email = camel_ews_settings_dup_email (ews_settings);
-static gchar *
-cal_backend_ews_get_builtin_zone_object (const gchar *tzid)
-{
- icalcomponent *icalcomp = NULL, *free_comp = NULL;
- icaltimezone *zone;
- gchar *object = NULL;
+ /* 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;
- zone = icaltimezone_get_builtin_timezone (tzid);
- if (!zone) {
- icalcomp = free_comp = icaltzutil_fetch_timezone (tzid);
- }
+ if (!attendee->mailbox)
+ continue;
- if (zone)
- icalcomp = icaltimezone_get_component (zone);
+ if (g_strcmp0 (attendee->mailbox->routing_type, "EX") == 0)
+ email = e_ews_item_util_strip_ex_address (attendee->mailbox->email);
- if (icalcomp) {
- icalcomponent *clone = icalcomponent_new_clone (icalcomp);
- icalproperty *prop;
+ 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);
- prop = icalcomponent_get_first_property (clone, ICAL_TZID_PROPERTY);
- if (prop) {
- /* change tzid to our, because the component has the buildin tzid */
- icalproperty_set_tzid (prop, tzid);
+ 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);
- object = icalcomponent_as_ical_string_r (clone);
+ 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);
}
- icalcomponent_free (clone);
- }
- if (free_comp)
- icalcomponent_free (free_comp);
+ g_free (user_email);
- return object;
-}
+ /* 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);
+ }
-static void
-e_cal_backend_ews_get_timezone (ECalBackend *backend,
- EDataCal *cal,
- guint32 context,
- GCancellable *cancellable,
- const gchar *tzid)
-{
- ETimezoneCache *timezone_cache;
- icalcomponent *icalcomp;
- icaltimezone *zone;
- gchar *object = NULL;
- GError *error = NULL;
+ break;
+ }
+ }
- timezone_cache = E_TIMEZONE_CACHE (backend);
+ /* 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);
- zone = e_timezone_cache_get_timezone (timezone_cache, tzid);
- if (zone) {
- icalcomp = icaltimezone_get_component (zone);
+ dtend = icalcomponent_get_dtend (icalcomp);
+ dtend.is_date = 1;
+ icalcomponent_set_dtend (icalcomp, dtend);
+ }
+ break;
+ }
+ }
- if (!icalcomp)
- g_propagate_error (&error, e_data_cal_create_error (InvalidObject, NULL));
- else
- object = icalcomponent_as_ical_string_r (icalcomp);
- } else {
- /* TODO Implement in ECalBackend base class */
- /* fallback if tzid contains only the location of timezone */
- gint i, slashes = 0;
+ 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);
+ }
+ }
- for (i = 0; tzid[i]; i++) {
- if (tzid[i] == '/')
- slashes++;
+ /* 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);
+ }
}
- if (slashes == 1)
- object = cal_backend_ews_get_builtin_zone_object (tzid);
+ icalcomponent_set_uid (icalcomp, uid ? uid : item_id->id);
- if (!object) {
- /* 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) {
- object = cal_backend_ews_get_builtin_zone_object (ical_location);
+ 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);
}
}
- if (!object && !error)
- g_propagate_error (&error, e_data_cal_create_error (ObjectNotFound, NULL));
+ icalcomponent_free (vcomp);
- convert_error_to_edc_error (&error);
- e_data_cal_respond_get_timezone (cal, context, error, object);
- g_free (object);
+ 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;
-/* changekey can be NULL if you don't want it. itemid cannot. */
-static void
-ews_cal_component_get_item_id (ECalComponent *comp,
- gchar **itemid,
- gchar **changekey)
-{
- icalproperty *prop;
- gchar *ck = NULL;
- gchar *id = NULL;
+ /* 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);
- prop = icalcomponent_get_first_property (
- e_cal_component_get_icalcomponent (comp),
- ICAL_X_PROPERTY);
- while (prop) {
- const gchar *x_name, *x_val;
+ if (uri)
+ uris = g_slist_append (uris, g_strdup (uri));
+ }
+ }
- x_name = icalproperty_get_x_name (prop);
- x_val = icalproperty_get_x (prop);
- if (!id && !g_ascii_strcasecmp (x_name, "X-EVOLUTION-ITEMID"))
- id = g_strdup (x_val);
- else if (changekey && !ck && !g_ascii_strcasecmp (x_name, "X-EVOLUTION-CHANGEKEY"))
- ck = g_strdup (x_val);
+ e_cal_component_set_attachment_list (res_component, uris);
- prop = icalcomponent_get_next_property (
- e_cal_component_get_icalcomponent (comp),
- ICAL_X_PROPERTY);
+ 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);
+ }
}
- *itemid = id;
- if (changekey)
- *changekey = ck;
+ return res_component;
}
-/* changekey can be NULL if you don't want it. itemid cannot. */
-static void
-ews_cal_component_get_calendar_item_accept_id (ECalComponent *comp,
- gchar **itemid,
- gchar **changekey,
- gchar **mail_id)
+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)
{
- icalproperty *prop;
- gchar *id_item = NULL;
- gchar *id_accept = NULL;
- gchar *ck = NULL;
+ GSList *items = NULL, *link;
+ gboolean success;
- prop = icalcomponent_get_first_property (
- e_cal_component_get_icalcomponent (comp),
- ICAL_X_PROPERTY);
- while (prop) {
- const gchar *x_name, *x_val;
+ g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE);
+ g_return_val_if_fail (out_components != NULL, FALSE);
- 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);
+ 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);
- prop = icalcomponent_get_next_property (
- e_cal_component_get_icalcomponent (comp),
- ICAL_X_PROPERTY);
- }
+ if (!success)
+ return FALSE;
- if (!id_item)
- id_item = g_strdup (id_accept);
+ /* fetch modified occurrences */
+ for (link = items; link; link = g_slist_next (link)) {
+ EEwsItem *item = link->data;
+ const GSList *modified_occurrences;
- *itemid = id_item;
- *mail_id = id_accept;
- if (changekey)
- *changekey = ck;
-}
+ if (!item || e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR)
+ continue;
-static void
-add_comps_to_item_id_hash (ECalBackendEws *cbews)
-{
- ECalBackendEwsPrivate *priv;
- GSList *comps, *l;
+ modified_occurrences = e_ews_item_get_modified_occurrences (item);
+ if (modified_occurrences) {
+ EEwsAdditionalProps *modified_add_props;
- priv = cbews->priv;
+ 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;
- PRIV_LOCK (priv);
+ modified_add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2010);
- comps = e_cal_backend_store_get_components (priv->store);
- for (l = comps; l != NULL; l = g_slist_next (l)) {
- ECalComponent *comp = (ECalComponent *) l->data;
- gchar *item_id = NULL;
+ 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);
- ews_cal_component_get_item_id (comp, &item_id, NULL);
- if (!item_id) {
- const gchar *uid;
+ 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;
+ }
+ }
- /* This should never happen, but sometimes when our
- * use of X- fields has changed it has triggered. Make
- * it cope, and not crash */
- e_cal_component_get_uid (comp, &uid);
- g_warning (
- "EWS calendar item %s had no EWS ItemID!",
- uid);
+ for (link = items; link; link = g_slist_next (link)) {
+ EEwsItem *item = link->data;
+
+ if (!item)
continue;
+
+ 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)));
+ success = FALSE;
+ break;
+ } else {
+ ECalComponent *comp;
+
+ comp = ecb_ews_item_to_component_sync (cbews, item, cancellable, error);
+ if (!comp) {
+ success = FALSE;
+ break;
+ }
+
+ *out_components = g_slist_prepend (*out_components, comp);
}
- g_hash_table_insert (priv->item_id_hash, item_id, comp);
}
- PRIV_UNLOCK (priv);
+ exit:
+ g_slist_free_full (items, g_object_unref);
- g_slist_free (comps);
+ return success;
}
-static gpointer
-handle_notifications_thread (gpointer data)
+static gboolean
+ecb_ews_fetch_items_sync (ECalBackendEws *cbews,
+ const GSList *items, /* EEwsItem * */
+ GSList **out_components, /* ECalComponent * */
+ GCancellable *cancellable,
+ GError **error)
{
- ECalBackendEws *cbews = data;
+ GSList *event_ids = NULL, *task_memo_ids = NULL, *link;
+ gboolean success = TRUE;
- PRIV_LOCK (cbews->priv);
+ 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 (cbews->priv->cnc == NULL)
- goto exit;
+ 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 (cbews->priv->listen_notifications) {
- GSList *folders = NULL;
+ if (event_ids) {
+ EEwsAdditionalProps *add_props;
- if (cbews->priv->subscription_key != 0)
- goto exit;
+ add_props = e_ews_additional_props_new ();
+ if (e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010)) {
+ EEwsExtendedFieldURI *ext_uri;
- folders = g_slist_prepend (folders, cbews->priv->folder_id);
+ add_props->field_uri = g_strdup (GET_ITEMS_SYNC_PROPERTIES_2010);
- e_ews_connection_enable_notifications_sync (
- cbews->priv->cnc,
- folders,
- &cbews->priv->subscription_key);
+ 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);
- g_slist_free (folders);
- } else {
- if (cbews->priv->subscription_key == 0)
- goto exit;
+ 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);
+ }
- e_ews_connection_disable_notifications_sync (
- cbews->priv->cnc,
- cbews->priv->subscription_key);
+ success = ecb_ews_get_items_sync (cbews, event_ids, "IdOnly", add_props, out_components,
cancellable, error);
- cbews->priv->subscription_key = 0;
+ e_ews_additional_props_free (add_props);
}
-exit:
- PRIV_UNLOCK (cbews->priv);
- g_object_unref (cbews);
- return NULL;
+ 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 void
-cbews_listen_notifications_cb (ECalBackendEws *cbews,
- GParamSpec *spec,
- CamelEwsSettings *ews_settings)
+static gboolean
+ecb_ews_freebusy_ecomp_changed (ECalComponent *ecomp,
+ icalcomponent *vevent)
{
- GThread *thread;
+ icalcomponent *icomp;
+ gboolean changed = FALSE;
- PRIV_LOCK (cbews->priv);
- if (cbews->priv->cnc == NULL) {
- PRIV_UNLOCK (cbews->priv);
- return;
- }
+ g_return_val_if_fail (vevent != NULL, FALSE);
- if (!e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010_SP1)) {
- PRIV_UNLOCK (cbews->priv);
- return;
- }
+ if (!ecomp)
+ return TRUE;
- cbews->priv->listen_notifications = !cbews->priv->is_freebusy_calendar &&
camel_ews_settings_get_listen_notifications (ews_settings);
- PRIV_UNLOCK (cbews->priv);
+ 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;
- thread = g_thread_new (NULL, handle_notifications_thread, g_object_ref (cbews));
- g_thread_unref (thread);
+ return changed;
}
-static void
-cbews_server_notification_cb (ECalBackendEws *cbews,
- GSList *events,
- EEwsConnection *cnc)
+static GSList * /* the possibly modified 'in_items' */
+ecb_ews_verify_changes (ECalCache *cal_cache,
+ icalcomponent_kind kind,
+ GSList *in_items, /* EEwsItem * */
+ GCancellable *cancellable)
{
- GSList *l;
- gboolean update_folder = FALSE;
+ GSList *items = NULL, *link;
- g_return_if_fail (cbews != NULL);
- g_return_if_fail (cbews->priv != NULL);
+ g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), in_items);
- for (l = events; l != NULL; l = l->next) {
- EEwsNotificationEvent *event = l->data;
+ 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);
- switch (event->type) {
- case E_EWS_NOTIFICATION_EVENT_CREATED:
- case E_EWS_NOTIFICATION_EVENT_DELETED:
- case E_EWS_NOTIFICATION_EVENT_MODIFIED:
- PRIV_LOCK (cbews->priv);
- if (g_strcmp0 (event->folder_id, cbews->priv->folder_id) == 0)
- update_folder = TRUE;
- PRIV_UNLOCK (cbews->priv);
- break;
- case E_EWS_NOTIFICATION_EVENT_MOVED:
- case E_EWS_NOTIFICATION_EVENT_COPIED:
- PRIV_LOCK (cbews->priv);
- 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;
- PRIV_UNLOCK (cbews->priv);
- break;
- default:
- return;
+ 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);
}
}
- if (update_folder)
- ews_start_sync (cbews);
+ g_slist_free (in_items);
+
+ return items;
}
-static void
-e_cal_backend_ews_open (ECalBackend *backend,
- EDataCal *cal,
- guint32 opid,
- GCancellable *cancellable,
- gboolean only_if_exists)
+static GSList * /* ECalMetaBackendInfo */
+ecb_ews_components_to_infos (ECalMetaBackend *meta_backend,
+ const GSList *components, /* ECalComponent * */
+ icalcomponent_kind kind)
{
- CamelEwsSettings *ews_settings;
- ECalBackendEws *cbews;
- ECalBackendEwsPrivate *priv;
- ESource *source;
- const gchar *cache_dir;
- gboolean need_to_authenticate;
- gboolean ret = TRUE;
- GError *error = NULL;
+ GSList *nfos = NULL, *link;
+ GHashTable *sorted_by_uids; /* gchar * ~> GSList { ECalComponent * } */
+ GHashTableIter iter;
+ gpointer key, value;
- if (e_cal_backend_is_opened (backend))
- return;
+ sorted_by_uids = g_hash_table_new (g_str_hash, g_str_equal);
- cbews = (ECalBackendEws *) backend;
- priv = cbews->priv;
+ for (link = (GSList *) components; link; link = g_slist_next (link)) {
+ ECalComponent *comp = link->data;
+ icalcomponent *icomp;
+ const gchar *uid;
+ GSList *instances;
- cache_dir = e_cal_backend_get_cache_dir (backend);
- source = e_backend_get_source (E_BACKEND (cbews));
- ews_settings = cal_backend_ews_get_collection_settings (cbews);
+ if (!comp)
+ continue;
- e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
+ icomp = e_cal_component_get_icalcomponent (comp);
+ uid = icalcomponent_get_uid (icomp);
- PRIV_LOCK (priv);
+ if (!uid)
+ continue;
- if (!priv->store) {
- ESourceEwsFolder *extension;
- const gchar *extension_name;
+ instances = g_hash_table_lookup (sorted_by_uids, uid);
+ g_hash_table_insert (sorted_by_uids, (gpointer) uid, g_slist_prepend (instances, comp));
+ }
- extension_name = E_SOURCE_EXTENSION_EWS_FOLDER;
- extension = e_source_get_extension (source, extension_name);
- priv->folder_id = e_source_ews_folder_dup_id (extension);
- priv->is_freebusy_calendar = g_strcmp0 (priv->folder_id, "freebusy-calendar") == 0;
+ 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;
- priv->storage_path = g_build_filename (cache_dir, priv->folder_id, NULL);
+ if (!uid || !instances) {
+ g_slist_free (instances);
+ continue;
+ }
- priv->store = e_cal_backend_store_new (
- priv->storage_path,
- E_TIMEZONE_CACHE (backend));
- e_cal_backend_store_load (priv->store);
- add_comps_to_item_id_hash (cbews);
+ /* 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 (priv->default_zone)
- e_cal_backend_store_set_default_timezone (
- priv->store, priv->default_zone);
- }
+ if (!comp)
+ continue;
+
+ if (e_cal_component_is_instance (comp))
+ comp = NULL;
+ }
- need_to_authenticate =
- (priv->cnc == NULL) &&
- (e_backend_is_destination_reachable (E_BACKEND (backend), cancellable, NULL));
+ if (!comp)
+ comp = instances->data;
- PRIV_UNLOCK (priv);
+ if (!comp) {
+ g_slist_free (instances);
+ continue;
+ }
- if (cbews->priv->cnc)
- e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
- else
- e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+ 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 (need_to_authenticate)
- ret = cal_backend_ews_ensure_connected (cbews, cancellable, &error);
+ if (!merged) {
+ g_warn_if_fail (merged != NULL);
+ g_slist_free (instances);
+ continue;
+ }
- if (ret) {
- e_cal_backend_set_writable (backend, !priv->is_freebusy_calendar);
+ nfo = e_cal_meta_backend_info_new (uid, revision, NULL, itemid);
+ nfo->object = icalcomponent_as_ical_string_r (merged);
- PRIV_LOCK (priv);
- if (priv->cnc != NULL) {
- priv->listen_notifications = !priv->is_freebusy_calendar &&
camel_ews_settings_get_listen_notifications (ews_settings);
+ nfos = g_slist_prepend (nfos, nfo);
- if (priv->listen_notifications)
- cbews_listen_notifications_cb (cbews, NULL, ews_settings);
- }
- PRIV_UNLOCK (priv);
+ icalcomponent_free (merged);
+ g_slist_free (instances);
}
- convert_error_to_edc_error (&error);
- e_data_cal_respond_open (cal, opid, error);
+ g_hash_table_destroy (sorted_by_uids);
- g_signal_connect_swapped (
- ews_settings,
- "notify::listen-notifications",
- G_CALLBACK (cbews_listen_notifications_cb),
- cbews);
+ return nfos;
}
static void
-e_cal_backend_ews_get_object (ECalBackend *backend,
- EDataCal *cal,
- guint32 context,
- GCancellable *cancellable,
- const gchar *uid,
- const gchar *rid)
+ecb_ews_extract_item_id (ECalComponent *comp,
+ gchar **out_id,
+ gchar **out_change_key)
{
- ECalBackendEwsPrivate *priv;
- ECalBackendEws *cbews = (ECalBackendEws *) backend;
- gchar *object = NULL;
- GError *error = NULL;
-
- e_data_cal_error_if_fail (E_IS_CAL_BACKEND_EWS (cbews), InvalidArg);
+ icalcomponent *icalcomp;
- priv = cbews->priv;
+ g_return_if_fail (E_IS_CAL_COMPONENT (comp));
- PRIV_LOCK (priv);
+ icalcomp = e_cal_component_get_icalcomponent (comp);
+ g_return_if_fail (icalcomp != NULL);
- if (e_backend_get_online (E_BACKEND (backend))) {
- /* make sure any pending refreshing is done */
- while (priv->refreshing) {
- PRIV_UNLOCK (priv);
- e_flag_wait (priv->refreshing_done);
- PRIV_LOCK (priv);
- }
- }
+ 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");
+}
- /* search the object in the cache */
- if (rid && *rid) {
- ECalComponent *comp;
+static gboolean
+ecb_ews_is_organizer (ECalBackendEws *cbews,
+ ECalComponent *comp)
+{
+ ECalComponentOrganizer organizer;
+ gboolean is_organizer = FALSE;
- comp = e_cal_backend_store_get_component (priv->store, uid, rid);
- if (!comp && e_backend_get_online (E_BACKEND (backend))) {
- /* maybe a meeting invitation, for which the calendar item is not downloaded yet,
- * thus synchronize local cache first */
- ews_start_sync (cbews);
+ g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE);
+ g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);
- PRIV_UNLOCK (priv);
- e_flag_wait (priv->refreshing_done);
- PRIV_LOCK (priv);
+ if (!e_cal_component_has_organizer (comp))
+ return FALSE;
- comp = e_cal_backend_store_get_component (priv->store, uid, rid);
- }
+ organizer.value = NULL;
- if (comp) {
- object = e_cal_component_get_as_string (comp);
+ e_cal_component_get_organizer (comp, &organizer);
+ if (organizer.value) {
+ CamelEwsSettings *ews_settings;
+ const gchar *email = organizer.value;
+ gchar *user_email;
- g_object_unref (comp);
+ ews_settings = ecb_ews_get_collection_settings (cbews);
- if (!object)
- g_propagate_error (&error, EDC_ERROR (ObjectNotFound));
- } else {
- g_propagate_error (&error, EDC_ERROR (ObjectNotFound));
- }
- } else {
- object = e_cal_backend_store_get_components_by_uid_as_ical_string (priv->store, uid);
- if (!object && e_backend_get_online (E_BACKEND (backend))) {
- /* maybe a meeting invitation, for which the calendar item is not downloaded yet,
- * thus synchronize local cache first */
- ews_start_sync (cbews);
+ user_email = camel_ews_settings_dup_email (ews_settings);
- PRIV_UNLOCK (priv);
- e_flag_wait (priv->refreshing_done);
- PRIV_LOCK (priv);
+ if (!g_ascii_strncasecmp (email, "mailto:", 7))
+ email += 7;
- object = e_cal_backend_store_get_components_by_uid_as_ical_string (priv->store, uid);
- }
+ is_organizer = user_email && g_ascii_strcasecmp (email, user_email) == 0;
- if (!object)
- g_propagate_error (&error, EDC_ERROR (ObjectNotFound));
+ g_free (user_email);
}
- PRIV_UNLOCK (priv);
-
- exit:
- convert_error_to_edc_error (&error);
- e_data_cal_respond_get_object (cal, context, error, object);
- g_free (object);
+ return is_organizer;
}
-static void
-cal_backend_ews_get_object_list (ECalBackend *backend,
- const gchar *sexp,
- GSList **objects,
- GError **error)
+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;
- ECalBackendEwsPrivate *priv;
- GSList *components, *l;
- ECalBackendSExp *cbsexp;
- gboolean search_needed = TRUE;
- time_t occur_start = -1, occur_end = -1;
- gboolean prunning_by_time;
-
- cbews = E_CAL_BACKEND_EWS (backend);
- priv = cbews->priv;
-
- if (!strcmp (sexp, "#t"))
- search_needed = FALSE;
-
- cbsexp = e_cal_backend_sexp_new (sexp);
- if (!cbsexp) {
- g_propagate_error (error, EDC_ERROR (InvalidQuery));
- return;
+ 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;
}
- *objects = NULL;
+ ews_settings = ecb_ews_get_collection_settings (cbews);
+ hosturl = camel_ews_settings_dup_hosturl (ews_settings);
+
+ cbews->priv->cnc = e_ews_connection_new (hosturl, ews_settings);
- prunning_by_time = e_cal_backend_sexp_evaluate_occur_times (
- cbsexp, &occur_start, &occur_end);
- components = prunning_by_time ?
- e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
- : e_cal_backend_store_get_components (priv->store);
+ e_binding_bind_property (
+ cbews, "proxy-resolver",
+ cbews->priv->cnc, "proxy-resolver",
+ G_BINDING_SYNC_CREATE);
- for (l = components; l != NULL; l = l->next) {
- ECalComponent *comp = E_CAL_COMPONENT (l->data);
+ *out_auth_result = e_ews_connection_try_credentials_sync (cbews->priv->cnc, credentials, cancellable,
error);
- if (e_cal_backend_get_kind (backend) ==
- icalcomponent_isa (e_cal_component_get_icalcomponent (comp))) {
- if ((!search_needed) ||
- (e_cal_backend_sexp_match_comp (cbsexp, comp, E_TIMEZONE_CACHE (backend)))) {
- *objects = g_slist_append (*objects, e_cal_component_get_as_string (comp));
- }
+ 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 = g_strcmp0 (cbews->priv->folder_id, "freebusy-calendar")
== 0;
+
+ 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_object_unref (cbsexp);
- g_slist_free_full (components, g_object_unref);
+ g_rec_mutex_unlock (&cbews->priv->cnc_lock);
+
+ g_free (hosturl);
+
+ return success;
}
-static void
-e_cal_backend_ews_get_object_list (ECalBackend *backend,
- EDataCal *cal,
- guint32 context,
- GCancellable *cancellable,
- const gchar *sexp)
+static gboolean
+ecb_ews_disconnect_sync (ECalMetaBackend *meta_backend,
+ GCancellable *cancellable,
+ GError **error)
{
- GSList *objects = NULL, *l;
- GError *error = NULL;
+ ECalBackendEws *cbews;
- cal_backend_ews_get_object_list (backend, sexp, &objects, &error);
+ g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE);
- convert_error_to_edc_error (&error);
- e_data_cal_respond_get_object_list (cal, context, error, objects);
- if (objects) {
- for (l = objects; l != NULL; l = g_slist_next (l))
- g_free (l->data);
- g_slist_free (objects);
- }
+ cbews = E_CAL_BACKEND_EWS (meta_backend);
+
+ ecb_ews_unset_connection (cbews);
+
+ return TRUE;
}
static gboolean
-ews_cal_delete_comp (ECalBackendEws *cbews,
- ECalComponent *comp,
- const gchar *item_id)
+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)
{
- ECalBackendEwsPrivate *priv = cbews->priv;
- ECalComponentId *uid;
- gboolean ret;
+ ECalBackendEws *cbews;
+ ECalCache *cal_cache;
+ gboolean success = TRUE;
+ GError *local_error = NULL;
- uid = e_cal_component_get_id (comp);
- ret = e_cal_backend_store_remove_component (priv->store, uid->uid, uid->rid);
- if (!ret)
- goto exit;
+ 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);
- e_cal_backend_notify_component_removed (E_CAL_BACKEND (cbews), uid, comp, NULL);
+ *out_created_objects = NULL;
+ *out_modified_objects = NULL;
+ *out_removed_objects = NULL;
- PRIV_LOCK (priv);
- g_hash_table_remove (priv->item_id_hash, item_id);
- PRIV_UNLOCK (priv);
+ cbews = E_CAL_BACKEND_EWS (meta_backend);
-exit:
- e_cal_component_free_id (uid);
- return ret;
-}
+ cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
+ g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE);
-static void
-ews_cal_append_exdate (ECalBackendEws *cbews,
- ECalComponent *comp,
- const gchar *rid,
- ECalObjModType mod)
-{
- ECalComponent *old_comp;
+ g_rec_mutex_lock (&cbews->priv->cnc_lock);
- old_comp = e_cal_component_clone (comp);
- e_cal_util_remove_instances (e_cal_component_get_icalcomponent (comp), icaltime_from_string (rid),
mod);
+ if (cbews->priv->is_freebusy_calendar) {
+ ESourceEwsFolder *ews_folder;
+ EEWSFreeBusyData fbdata;
+ GSList *free_busy = NULL, *link;
+ gboolean success;
+ time_t today;
- e_cal_backend_notify_component_modified (E_CAL_BACKEND (cbews), old_comp, comp);
+ ews_folder = e_source_get_extension (e_backend_get_source (E_BACKEND (cbews)),
E_SOURCE_EXTENSION_EWS_FOLDER);
- g_object_unref (old_comp);
-}
+ today = time_day_begin (time (NULL));
-static icaltimezone *
-e_cal_backend_ews_get_timezone_from_ical_component (ECalBackend *backend,
- icalcomponent *comp)
-{
- ETimezoneCache *timezone_cache;
- icalproperty *prop = NULL;
- const gchar *tzid = 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));
- timezone_cache = E_TIMEZONE_CACHE (backend);
+ 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);
- prop = icalcomponent_get_first_property (comp, ICAL_DTSTART_PROPERTY);
- if (prop != NULL) {
- icalparameter *param = NULL;
+ if (success) {
+ icaltimezone *utc_zone = icaltimezone_get_utc_timezone ();
+ GSList *comps = NULL;
+ GHashTable *known;
+ GHashTableIter iter;
+ gpointer key;
- param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER);
- if (param != NULL) {
- tzid = icalparameter_get_tzid (param);
- } else {
- struct icaltimetype dtstart;
+ known = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
- dtstart = icalproperty_get_dtstart (prop);
- if (dtstart.is_utc)
- tzid = "UTC";
- }
- }
+ 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 (tzid != NULL)
- return e_timezone_cache_get_timezone (timezone_cache, tzid);
+ if (!comp)
+ continue;
- return NULL;
-}
+ icomp = e_cal_component_get_icalcomponent (comp);
+ if (!icomp)
+ continue;
-static void
-ews_cal_remove_object_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
-{
- EwsCalendarAsyncData *remove_data = user_data;
- GSimpleAsyncResult *simple;
- GError *error = NULL;
+ uid = icalcomponent_get_uid (icomp);
- simple = G_SIMPLE_ASYNC_RESULT (res);
+ if (uid && *uid)
+ g_hash_table_insert (known, g_strdup (uid), g_object_ref
(comp));
+ }
- if (!g_simple_async_result_propagate_error (simple, &error) || error->code ==
EWS_CONNECTION_ERROR_ITEMNOTFOUND) {
- /* remove detached instances first */
- if (remove_data->mod == E_CAL_OBJ_MOD_ALL) {
- ECalBackendEws *cbews = remove_data->cbews;
- GSList *with_detached, *iter;
+ g_slist_free_full (comps, g_object_unref);
+ }
- with_detached = e_cal_backend_store_get_components_by_uid (cbews->priv->store,
remove_data->uid);
- for (iter = with_detached; iter; iter = g_slist_next (iter)) {
- ECalComponent *comp;
- ECalComponentId *id;
+ for (link = free_busy; link; link = g_slist_next (link)) {
+ icalcomponent *fbcomp = link->data;
+ icalproperty *fbprop;
+ icalparameter *param;
+ struct icalperiodtype fb;
+ icalparameter_fbtype fbtype;
- comp = iter->data;
- id = e_cal_component_get_id (comp);
+ if (!fbcomp || icalcomponent_isa (fbcomp) != ICAL_VFREEBUSY_COMPONENT)
+ continue;
- /* notify separately only detached instances - the master object will be
removed below */
- if (id && id->rid && *id->rid &&
- e_cal_backend_store_remove_component (cbews->priv->store, id->uid,
id->rid)) {
- gchar *item_id = NULL;
+ 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;
- e_cal_backend_notify_component_removed (E_CAL_BACKEND (cbews), id,
comp, NULL);
+ param = icalproperty_get_first_parameter (fbprop,
ICAL_FBTYPE_PARAMETER);
+ if (!param)
+ continue;
- ews_cal_component_get_item_id (comp, &item_id, NULL);
- if (item_id) {
- PRIV_LOCK (cbews->priv);
- g_hash_table_remove (cbews->priv->item_id_hash, item_id);
- PRIV_UNLOCK (cbews->priv);
+ fbtype = icalparameter_get_fbtype (param);
- g_free (item_id);
+ 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.start.is_utc = 1;
+ fb.end.zone = utc_zone;
+ fb.end.is_utc = 1;
+
+ 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;
- e_cal_component_free_id (id);
+ *out_removed_objects = g_slist_prepend (*out_removed_objects,
+ e_cal_meta_backend_info_new (uid, NULL, NULL, NULL));
}
- g_slist_free_full (with_detached, g_object_unref);
+ 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);
}
- /* FIXME: This is horrid. Will bite us when we start to delete
- * more than one item at a time... */
- if (remove_data->comp != NULL)
- ews_cal_delete_comp (remove_data->cbews, remove_data->comp, remove_data->item_id);
- if (remove_data->extra_comp != NULL)
- ews_cal_append_exdate (
- remove_data->cbews, remove_data->extra_comp, remove_data->rid,
remove_data->mod);
- }
+ 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;
- convert_error_to_edc_error (&error);
+ add_props = e_ews_additional_props_new ();
+ add_props->field_uri = g_strdup ("item:ItemClass");
- if (remove_data->context)
- e_data_cal_respond_remove_objects (remove_data->cal, remove_data->context, error, NULL, NULL,
NULL);
- else if (error) {
- g_warning ("Remove object error : %s\n", error->message);
- g_clear_error (&error);
- }
+ 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);
- e_cal_backend_ews_async_data_free (remove_data);
-}
+ if (!success &&
+ g_error_matches (local_error, EWS_CONNECTION_ERROR,
EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) {
+ g_clear_error (&local_error);
-static gboolean
-e_cal_backend_ews_is_organizer (ECalBackendEws *cbews,
- ECalComponent *instance_comp,
- ECalComponent *parent_comp)
-{
- ECalComponent *comp;
- gboolean is_organizer = FALSE;
+ e_cal_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL);
- g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE);
+ 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);
+ }
- comp = instance_comp ? instance_comp : parent_comp;
- if (!comp)
- return FALSE;
+ 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;
- PRIV_LOCK (cbews->priv);
+ if (!e_cal_cache_get_ids_with_extra (cal_cache, item_id, &ids, cancellable,
NULL))
+ continue;
- if (e_cal_component_has_organizer (comp)) {
- ECalComponentOrganizer organizer;
+ for (ilink = ids; ilink; ilink = g_slist_next (ilink)) {
+ ECalComponentId *id = ilink->data;
- organizer.value = NULL;
+ /* 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;
+ }
+ }
- e_cal_component_get_organizer (comp, &organizer);
- if (organizer.value) {
- const gchar *email = organizer.value;
+ g_slist_free_full (ids, (GDestroyNotify) e_cal_component_free_id);
+ }
- if (!g_ascii_strncasecmp (email, "mailto:", 7))
- email += 7;
+ g_slist_free_full (components_created, g_object_unref);
+ g_slist_free_full (components_modified, g_object_unref);
- is_organizer = cbews->priv->user_email && g_ascii_strcasecmp (email,
cbews->priv->user_email) == 0;
+ *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);
}
- PRIV_UNLOCK (cbews->priv);
+ g_rec_mutex_unlock (&cbews->priv->cnc_lock);
- return is_organizer;
+ ecb_ews_convert_error_to_edc_error (error);
+ g_clear_object (&cal_cache);
+
+ return success;
}
-static void
-e_cal_backend_ews_remove_object (ECalBackend *backend,
- EDataCal *cal,
- guint32 context,
- GCancellable *cancellable,
- const gchar *uid,
- const gchar *rid,
- ECalObjModType mod)
+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)
{
- EwsCalendarAsyncData *remove_data;
- ECalBackendEws *cbews = (ECalBackendEws *) backend;
- ECalBackendEwsPrivate *priv;
- ECalComponent *comp = NULL, *parent = NULL;
- GError *error = NULL;
- EwsId item_id;
- guint index = 0;
-
- /* There are 3 scenarios where this function is called:
- * 1. An item with no recurrence - rid is NULL. Nothing special here.
- * 2. A modified occurrence of a recurring event - rid isnt NULL. The store will contain the object
which will have to be removed from it.
- * 3. A non modified occurrence of a recurring event - rid isnt NULL. The store will only have a
reference to the master event.
- * This is actually an update event where an exception date will have to be appended to the
master.
- */
- e_data_cal_error_if_fail (E_IS_CAL_BACKEND_EWS (cbews), InvalidArg);
-
- if (!cbews->priv->cnc) {
- e_data_cal_respond_remove_objects (cal, context, EDC_ERROR (RepositoryOffline), NULL, NULL,
NULL);
- return;
- }
+ ECalBackendEws *cbews;
+ GSList *ids, *items = NULL, *components = NULL;
+ gboolean success;
- if (!cal_backend_ews_ensure_connected (cbews, cancellable, &error)) {
- convert_error_to_edc_error (&error);
- e_data_cal_respond_remove_objects (cal, context, error, NULL, NULL, NULL);
- return;
- }
+ 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);
- priv = cbews->priv;
+ cbews = E_CAL_BACKEND_EWS (meta_backend);
- PRIV_LOCK (priv);
+ g_rec_mutex_lock (&cbews->priv->cnc_lock);
- comp = e_cal_backend_store_get_component (priv->store, uid, rid);
+ ids = g_slist_prepend (NULL, (gpointer) (extra && *extra ? extra : uid));
- if (!rid || !*rid)
- rid = NULL;
+ 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);
- if (rid) {
- parent = e_cal_backend_store_get_component (priv->store, uid, NULL);
- if (!parent && !comp) {
- g_warning ("EEE Cant find master component with uid:%s\n", uid);
- g_propagate_error (&error, EDC_ERROR (ObjectNotFound));
- PRIV_UNLOCK (priv);
- goto exit;
- }
- }
-
- if (!comp && !parent) {
- g_warning ("EEE Cant find component with uid:%s & rid:%s\n", uid, rid);
- g_propagate_error (&error, EDC_ERROR (ObjectNotFound));
- PRIV_UNLOCK (priv);
- goto exit;
- }
+ g_slist_free (ids);
- ews_cal_component_get_item_id ((comp ? comp : parent), &item_id.id, &item_id.change_key);
+ if (success && items) {
+ success = ecb_ews_fetch_items_sync (cbews, items, &components, cancellable, error);
- PRIV_UNLOCK (priv);
+ if (components) {
+ const EwsId *ews_id = e_ews_item_get_id (items->data);
- if (!item_id.id) {
- g_propagate_error (
- &error, EDC_ERROR_EX (OtherError,
- "Cannot determine EWS ItemId"));
- goto exit;
- }
+ if (ews_id)
+ *out_extra = g_strdup (ews_id->id);
- if (parent && !comp) {
- index = e_cal_backend_ews_rid_to_index (
- e_cal_backend_ews_get_timezone_from_ical_component (
- backend,
- e_cal_component_get_icalcomponent (parent)),
- rid,
- e_cal_component_get_icalcomponent (parent),
- &error);
-
- if (error != NULL)
- goto exit;
- }
+ if (components->next) {
+ GSList *link;
- remove_data = g_new0 (EwsCalendarAsyncData, 1);
- remove_data->cbews = g_object_ref (cbews);
- remove_data->cancellable = cal_backend_ews_ref_cancellable (cbews);
- remove_data->comp = comp != NULL ? g_object_ref (comp) : NULL;
- remove_data->extra_comp = parent != NULL ? g_object_ref (parent) : NULL;
- remove_data->cal = g_object_ref (cal);
- remove_data->context = context;
- remove_data->item_id = g_strdup (item_id.id);
- remove_data->uid = g_strdup (uid);
- remove_data->rid = (rid ? g_strdup (rid) : NULL);
- remove_data->mod = mod;
-
- e_ews_connection_delete_item (
- priv->cnc, EWS_PRIORITY_MEDIUM, &item_id, index, EWS_HARD_DELETE,
- e_cal_backend_ews_is_organizer (cbews, comp, parent) ? EWS_SEND_TO_ALL_AND_SAVE_COPY :
EWS_SEND_TO_NONE,
- EWS_ALL_OCCURRENCES, remove_data->cancellable,
- ews_cal_remove_object_cb,
- remove_data);
-
- return;
-
-exit:
- if (comp != NULL)
- g_object_unref (comp);
+ *out_component = icalcomponent_new_vcalendar ();
- if (parent != NULL)
- g_object_unref (parent);
+ for (link = components; link; link = g_slist_next (link)) {
+ ECalComponent *comp = link->data;
- convert_error_to_edc_error (&error);
+ if (!comp)
+ continue;
- if (context)
- e_data_cal_respond_remove_objects (cal, context, error, NULL, NULL, NULL);
- else if (error != NULL) {
- g_warning ("Remove object error : %s\n", error->message);
- g_clear_error (&error);
+ 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;
+ }
}
-}
-static void
-e_cal_backend_ews_remove_objects (ECalBackend *backend,
- EDataCal *cal,
- guint32 context,
- GCancellable *cancellable,
- const GSList *ids,
- ECalObjModType mod)
-{
- GError *error = NULL;
- const ECalComponentId *id;
+ if (!components && e_cal_meta_backend_refresh_sync (meta_backend, cancellable, NULL)) {
+ ECalCache *cal_cache;
- if (!ids) {
- if (context) {
- g_propagate_error (&error, EDC_ERROR (InvalidArg));
- e_data_cal_respond_remove_objects (cal, context, error, NULL, NULL, NULL);
- }
- return;
- }
+ 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 (ids->next) {
- if (context) {
- g_propagate_error (&error, EDC_ERROR_EX (UnsupportedMethod, _("EWS does not support
bulk removals")));
- e_data_cal_respond_remove_objects (cal, context, error, NULL, NULL, NULL);
- }
- return;
- }
+ if (!e_cal_cache_get_component_extra (cal_cache, uid, NULL, out_extra,
cancellable, NULL))
+ *out_extra = NULL;
- id = ids->data;
- if (!id) {
- if (context) {
- g_propagate_error (&error, EDC_ERROR (InvalidArg));
- e_data_cal_respond_remove_objects (cal, context, error, NULL, NULL, NULL);
+ g_clear_error (error);
+ }
+ g_object_unref (cal_cache);
}
- return;
}
- e_cal_backend_ews_remove_object (backend, cal, context, cancellable, id->uid, id->rid, mod);
+ g_rec_mutex_unlock (&cbews->priv->cnc_lock);
+
+ ecb_ews_convert_error_to_edc_error (error);
+ g_slist_free_full (components, g_object_unref);
+ g_slist_free_full (items, g_object_unref);
+
+ return success;
}
-static icaltimezone * resolve_tzid (const gchar *tzid, gpointer user_data);
-static void put_component_to_store (ECalBackendEws *cbews,ECalComponent *comp);
+/* 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;
-static void
-e_cal_backend_ews_modify_object (ECalBackend *backend,
- EDataCal *cal,
- guint32 context,
- GCancellable *cancellable,
- const gchar *calobj,
- ECalObjModType mod);
+ if (!comp1 && !comp2)
+ return TRUE;
+ else if (!comp1 || !comp2)
+ return FALSE;
-static void ews_cal_modify_object_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data);
+ icomp1 = e_cal_component_get_icalcomponent (comp1);
+ icomp2 = e_cal_component_get_icalcomponent (comp2);
-static void
-ews_create_attachments_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
-{
- EEwsConnection *cnc = E_EWS_CONNECTION (object);
- EwsCalendarAsyncData *create_data = user_data;
- ECalBackendEwsPrivate *priv = create_data->cbews->priv;
- gchar *change_key;
- GSList *ids, *i;
- GError *error = NULL;
- icalproperty *icalprop;
- icalcomponent *icalcomp;
- icalparameter *icalparam;
- const gchar *comp_uid;
+ if (!icomp1 || !icomp2)
+ return FALSE;
- if (!e_ews_connection_create_attachments_finish (cnc, &change_key, &ids, res, &error)) {
- g_warning ("Error while creating attachments: %s\n", error ? error->message : "Unknown
error");
- if (error != NULL)
- g_clear_error (&error);
+ if (g_strcmp0 (icalcomponent_get_uid (icomp1), icalcomponent_get_uid (icomp2)) != 0)
+ return FALSE;
- e_cal_backend_ews_async_data_free (create_data);
+ if (icalcomponent_count_properties (icomp1, ICAL_ANY_PROPERTY) !=
+ icalcomponent_count_properties (icomp2, ICAL_ANY_PROPERTY))
+ return FALSE;
- return;
- }
+ processed_props = g_hash_table_new (g_direct_hash, g_direct_equal);
- /* get exclusive access to the store */
- e_cal_backend_store_freeze_changes (priv->store);
-
- /* Update change key. id remains the same, but change key changed.*/
- icalcomp = e_cal_component_get_icalcomponent (create_data->comp);
- icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
- while (icalprop) {
- const gchar *x_name;
- x_name = icalproperty_get_x_name (icalprop);
- if (!g_ascii_strcasecmp (x_name, "X-EVOLUTION-CHANGEKEY")) {
- icalproperty_set_value_from_string (icalprop, change_key, "NO");
- break;
- }
- icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
- }
+ 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;
- icalprop = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY);
- i = ids;
- for (; i && icalprop; i = i->next, icalprop = icalcomponent_get_next_property (icalcomp,
ICAL_ATTACH_PROPERTY)) {
- icalparam = icalparameter_new_x (i->data);
- icalparameter_set_xname (icalparam, "X-EWS-ATTACHMENTID");
- icalproperty_add_parameter (icalprop, icalparam);
- g_free (i->data);
- }
+ for (prop2 = icalcomponent_get_first_property (icomp2, kind);
+ prop2;
+ prop2 = icalcomponent_get_next_property (icomp2, kind)) {
+ gchar *str1, *str2;
+ gboolean same;
- e_cal_component_commit_sequence (create_data->comp);
- /* update changes and release access to the store */
- e_cal_backend_store_thaw_changes (priv->store);
+ if (g_hash_table_contains (processed_props, prop2))
+ continue;
- e_cal_component_get_uid (create_data->comp, &comp_uid);
- if (create_data->cb_type == E_EWS_ATTACHMENT_TYPE_UPDATE) {
- const gchar *send_meeting_invitations;
- const gchar *send_or_save;
- EwsCalendarAsyncData *modify_data;
- EwsCalendarConvertData convert_data = { 0 };
+ if (icalproperty_count_parameters (prop1) != icalproperty_count_parameters (prop2))
+ continue;
- modify_data = g_new0 (EwsCalendarAsyncData, 1);
- modify_data->cbews = g_object_ref (create_data->cbews);
- modify_data->cancellable = cal_backend_ews_ref_cancellable (create_data->cbews);
- modify_data->comp = g_object_ref (create_data->comp);
- modify_data->extra_comp = g_object_ref (create_data->extra_comp);
- modify_data->cal = g_object_ref (create_data->cal);
- modify_data->context = create_data->context;
- modify_data->item_id = g_strdup (create_data->item_id);
-
- convert_data.connection = create_data->cbews->priv->cnc;
- convert_data.user_email = create_data->cbews->priv->user_email;
- convert_data.comp = create_data->comp;
- convert_data.old_comp = create_data->extra_comp;
- convert_data.item_id = create_data->item_id;
- convert_data.change_key = change_key;
- convert_data.default_zone = create_data->cbews->priv->default_zone;
-
- if (e_cal_component_has_attendees (create_data->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";
- }
+ str1 = icalproperty_as_ical_string_r (prop1);
+ str2 = icalproperty_as_ical_string_r (prop2);
- e_ews_connection_update_items (
- priv->cnc, EWS_PRIORITY_MEDIUM,
- "AlwaysOverwrite",
- send_or_save,
- send_meeting_invitations,
- priv->folder_id,
- e_cal_backend_ews_convert_component_to_updatexml,
- &convert_data,
- modify_data->cancellable,
- ews_cal_modify_object_cb,
- modify_data);
- } else {
- if (create_data->cb_type == E_EWS_ATTACHMENT_TYPE_CREATE) {
- /*In case we have attendees we have to fake update items,
- * this is the only way to pass attachments in meeting invite mail*/
- if (e_cal_component_has_attendees (create_data->comp)) {
- icalcomponent *icalcomp = e_cal_component_get_icalcomponent
(create_data->comp);
- e_cal_backend_ews_modify_object (
- E_CAL_BACKEND (create_data->cbews),
- create_data->cal,
- 0,
- NULL,
- icalcomponent_as_ical_string (icalcomp),
- E_CAL_OBJ_MOD_ALL);
+ 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;
}
- e_cal_backend_ews_async_data_free (create_data);
- g_slist_free (ids);
+ g_hash_table_destroy (processed_props);
+
+ return equal;
}
+typedef struct _ChangeData {
+ ECalComponent *old_component;
+ ECalComponent *new_component;
+} ChangeData;
+
static void
-ews_create_object_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
+change_data_free (gpointer ptr)
{
- EEwsConnection *cnc = E_EWS_CONNECTION (object);
- EwsCalendarAsyncData *create_data = user_data;
- ECalBackendEws *cbews = create_data->cbews;
- ECalBackendEwsPrivate *priv = cbews->priv;
- GError *error = NULL;
- GSList *ids = NULL, *attachments = NULL, *i, *exceptions = NULL, *items_req = NULL, *items = NULL;
- GSList *new_uids, *new_comps;
- const gchar *comp_uid;
- const EwsId *item_id;
- icalproperty *icalprop;
- icalcomponent *icalcomp;
- guint n_attach;
- EEwsItem *item;
-
- /* get a list of ids from server (single item) */
- if (!e_ews_connection_create_items_finish (cnc, res, &ids, &error)) {
- if (error != NULL) {
- convert_error_to_edc_error (&error);
- e_data_cal_respond_create_objects (create_data->cal, create_data->context, error,
NULL, NULL);
- } else {
- e_data_cal_respond_create_objects (
- create_data->cal, create_data->context, EDC_ERROR_EX (OtherError,
_("Unknown error")), NULL, NULL);
- }
- return;
+ ChangeData *cd = ptr;
+
+ if (cd) {
+ g_clear_object (&cd->old_component);
+ g_clear_object (&cd->new_component);
+ g_free (cd);
}
+}
- item = (EEwsItem *) ids->data;
- item_id = e_ews_item_get_id (item);
- g_slist_free (ids);
+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;
- if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_EVENT) {
- EEwsAdditionalProps *add_props;
- GCancellable *cancellable;
+ 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);
- add_props = e_ews_additional_props_new ();
- add_props->field_uri = g_strdup ("calendar:UID");
+ *out_changed_instances = NULL;
+ *out_removed_instances = NULL;
- cancellable = cal_backend_ews_ref_cancellable (cbews);
- items = g_slist_append (items, item_id->id);
+ 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);
- /* get calender uid from server*/
- e_ews_connection_get_items_sync (
- cnc, EWS_PRIORITY_MEDIUM,
- items,
- "IdOnly",
- add_props,
- FALSE, NULL, E_EWS_BODY_TYPE_TEXT,
- &items_req,
- NULL, NULL, cancellable, &error);
+ for (link = (GSList *) existing_instances; link; link = g_slist_next (link)) {
+ ECalComponent *comp = link->data;
+ ECalComponentId *id = NULL;
- e_ews_additional_props_free (add_props);
- g_clear_object (&cancellable);
-
- if (!res && error != NULL) {
- if (items_req)
- g_slist_free_full (items_req, g_object_unref);
- convert_error_to_edc_error (&error);
- e_data_cal_respond_create_objects (create_data->cal, create_data->context, error,
NULL, NULL);
- return;
- }
+ id = e_cal_component_get_id (comp);
+ if (id)
+ g_hash_table_insert (existing_hash, id, comp);
+ }
- item = (EEwsItem *) items_req->data;
- if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_ERROR) {
- error = g_error_copy (e_ews_item_get_error (item));
- g_slist_free_full (items_req, g_object_unref);
+ 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);
+ }
- convert_error_to_edc_error (&error);
- e_data_cal_respond_create_objects (create_data->cal, create_data->context, error,
NULL, NULL);
- return;
+ g_hash_table_remove (existing_hash, id);
+ e_cal_component_free_id (id);
}
+ }
- item_id = e_ews_item_get_id (item);
+ 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_slist_free (items);
- g_slist_free (items_req);
+ 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);
}
- /* attachments */
- n_attach = e_cal_component_get_num_attachments (create_data->comp);
- if (n_attach > 0) {
- GSList *info_attachments = NULL;
- EwsCalendarAsyncData *attach_data = g_new0 (EwsCalendarAsyncData, 1);
+ for (link = props; link; link = g_slist_next (link)) {
+ EEwsAttachmentInfo *info;
+ icalattach *attach;
+ icalparameter *param;
+ const gchar *stored_filename;
- attach_data->cbews = g_object_ref (create_data->cbews);
- attach_data->cancellable = cal_backend_ews_ref_cancellable (create_data->cbews);
- attach_data->comp = g_object_ref (create_data->comp);
- attach_data->cal = g_object_ref (create_data->cal);
- attach_data->context = create_data->context;
- attach_data->cb_type = 1;
+ prop = link->data;
+ param = icalproperty_get_first_parameter (prop, ICAL_FILENAME_PARAMETER);
+ stored_filename = param ? icalparameter_get_filename (param) : NULL;
- e_cal_component_get_attachment_list (create_data->comp, &attachments);
+ attach = icalproperty_get_attach (prop);
+ if (icalattach_get_is_url (attach)) {
+ const gchar *uri;
- for (i = attachments; i; i = i->next) {
- const gchar *uri = i->data;
- gchar *uri_filename;
- EEwsAttachmentInfo *info;
+ uri = icalattach_get_url (attach);
if (!uri || !*uri)
continue;
@@ -1607,106 +1930,214 @@ ews_create_object_cb (GObject *object,
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;
+ 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;
+ basename = g_path_get_basename (uri_filename);
+ if (basename && *basename && basename[0] != '.' && basename[0] !=
G_DIR_SEPARATOR) {
+ const gchar *uid;
- e_cal_component_get_uid (create_data->comp, &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);
+ 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 (basename);
+ g_free (uri_filename);
}
+ } else {
+ gsize len = -1;
+ guchar *decoded = NULL;
+ const gchar *content;
- g_free (uri_filename);
+ content = (const gchar *) icalattach_get_data (attach);
+ decoded = g_base64_decode (content, &len);
- info_attachments = g_slist_append (info_attachments, info);
- }
+ info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_INLINED);
+ e_ews_attachment_info_set_inlined_data (info, decoded, len);
- e_ews_connection_create_attachments (
- cnc, EWS_PRIORITY_MEDIUM,
- item_id, info_attachments,
- FALSE, attach_data->cancellable,
- ews_create_attachments_cb,
- attach_data);
+ if (stored_filename && *stored_filename)
+ e_ews_attachment_info_set_prefer_filename (info, stored_filename);
- g_slist_free_full (info_attachments, (GDestroyNotify) e_ews_attachment_info_free);
- g_slist_free_full (attachments, g_free);
+ 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);
}
- /* get exclusive access to the store */
- e_cal_backend_store_freeze_changes (priv->store);
+ g_slist_free (props);
- /* set a new ical property containing the change key we got from the exchange server for future use */
- if (e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_EVENT)
- e_cal_component_set_uid (create_data->comp, e_ews_item_get_uid (item));
- else
- e_cal_component_set_uid (create_data->comp, item_id->id);
+ return *out_attachments != NULL;
+}
- icalprop = icalproperty_new_x (item_id->id);
- icalproperty_set_x_name (icalprop, "X-EVOLUTION-ITEMID");
- icalcomp = e_cal_component_get_icalcomponent (create_data->comp);
- icalcomponent_add_property (icalcomp, icalprop);
+static icaltimezone *
+ecb_ews_get_timezone_from_ical_component (ECalBackendEws *cbews,
+ icalcomponent *icalcomp)
+{
+ ETimezoneCache *timezone_cache;
+ icalproperty *prop = NULL;
+ const gchar *tzid = NULL;
- icalprop = icalproperty_new_x (item_id->change_key);
- icalproperty_set_x_name (icalprop, "X-EVOLUTION-CHANGEKEY");
- icalcomp = e_cal_component_get_icalcomponent (create_data->comp);
- icalcomponent_add_property (icalcomp, icalprop);
+ timezone_cache = E_TIMEZONE_CACHE (cbews);
- /* update component internal data */
- e_cal_component_commit_sequence (create_data->comp);
- put_component_to_store (create_data->cbews, create_data->comp);
+ prop = icalcomponent_get_first_property (icalcomp, ICAL_DTSTART_PROPERTY);
+ if (prop != NULL) {
+ icalparameter *param = NULL;
- e_cal_component_get_uid (create_data->comp, &comp_uid);
+ param = icalproperty_get_first_parameter (prop, ICAL_TZID_PARAMETER);
+ if (param) {
+ tzid = icalparameter_get_tzid (param);
+ } else {
+ struct icaltimetype dtstart;
- new_uids = g_slist_append (NULL, (gpointer) comp_uid);
- new_comps = g_slist_append (NULL, create_data->comp);
+ dtstart = icalproperty_get_dtstart (prop);
+ if (dtstart.is_utc)
+ tzid = "UTC";
+ }
+ }
- convert_error_to_edc_error (&error);
- e_data_cal_respond_create_objects (create_data->cal, create_data->context, error, new_uids,
new_comps);
- error = NULL;
+ if (tzid)
+ return e_timezone_cache_get_timezone (timezone_cache, tzid);
- g_slist_free (new_uids);
- g_slist_free (new_comps);
+ return NULL;
+}
- /* notify the backend and the application that a new object was created */
- e_cal_backend_notify_component_created (E_CAL_BACKEND (create_data->cbews), create_data->comp);
+static gboolean
+ecb_ews_remove_item_sync (ECalBackendEws *cbews,
+ ECalCache *cal_cache,
+ const gchar *uid,
+ const gchar *rid,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ECalComponent *comp = NULL, *parent = NULL;
+ EwsId item_id = { 0 };
+ gint index = 0;
+ gboolean success;
- /* place new component in our cache */
- PRIV_LOCK (priv);
- g_hash_table_insert (priv->item_id_hash, g_strdup (item_id->id), g_object_ref (create_data->comp));
- PRIV_UNLOCK (priv);
+ 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);
- /* update changes and release access to the store */
- e_cal_backend_store_thaw_changes (priv->store);
+ if (rid && !*rid)
+ rid = NULL;
- /* Excluded occurrences */
- g_clear_error (&error);
- icalprop = icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY);
- if (icalprop != NULL) {
- icalprop = icalcomponent_get_first_property (icalcomp, ICAL_EXDATE_PROPERTY);
- for (; icalprop; icalprop = icalcomponent_get_next_property (icalcomp, ICAL_EXDATE_PROPERTY))
{
- exceptions = g_slist_prepend (exceptions, g_strdup (icalproperty_get_value_as_string
(icalprop)));
+ 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);
- for (i = exceptions; i; i = i->next) {
- e_cal_backend_ews_remove_object (
- E_CAL_BACKEND (create_data->cbews), create_data->cal, 0, NULL,
- comp_uid, i->data, E_CAL_OBJ_MOD_THIS);
+ if (!item_id.id) {
+ g_propagate_error (error, EDC_ERROR_EX (OtherError, "Cannot determine EWS ItemId"));
+ success = FALSE;
+ } else {
+ if (parent && !comp) {
+ 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 = FALSE;
}
- g_slist_free_full (exceptions, g_free);
+ 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);
}
- e_cal_backend_ews_async_data_free (create_data);
+ 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 {
@@ -1714,7 +2145,9 @@ struct TzidCbData {
ECalBackendEws *cbews;
};
-static void tzid_cb (icalparameter *param, gpointer data)
+static void
+tzid_cb (icalparameter *param,
+ gpointer data)
{
struct TzidCbData *cbd = data;
const gchar *tzid;
@@ -1725,7 +2158,7 @@ static void tzid_cb (icalparameter *param, gpointer data)
if (!tzid)
return;
- zone = resolve_tzid (tzid, cbd->cbews);
+ zone = e_timezone_cache_get_timezone (E_TIMEZONE_CACHE (cbd->cbews), tzid);
if (!zone)
return;
@@ -1737,8 +2170,8 @@ static void tzid_cb (icalparameter *param, gpointer data)
}
static void
-e_cal_backend_ews_pick_all_tzids_out (ECalBackendEws *cbews,
- icalcomponent *icalcomp)
+ecb_ews_pick_all_tzids_out (ECalBackendEws *cbews,
+ icalcomponent *icalcomp)
{
/* pick all the tzids out of the component and resolve
@@ -1747,500 +2180,646 @@ e_cal_backend_ews_pick_all_tzids_out (ECalBackendEws *cbews,
cbd.cbews = cbews;
cbd.comp = icalcomp;
+
icalcomponent_foreach_tzid (icalcomp, tzid_cb, &cbd);
}
-static void
-e_cal_backend_ews_create_objects (ECalBackend *backend,
- EDataCal *cal,
- guint32 context,
- GCancellable *cancellable,
- const GSList *calobjs)
+static gboolean
+ecb_ews_modify_item_sync (ECalBackendEws *cbews,
+ icalcomponent *old_icalcomp,
+ icalcomponent *new_icalcomp,
+ GCancellable *cancellable,
+ GError **error)
{
- EwsCalendarAsyncData *create_data;
- EwsCalendarConvertData convert_data = { 0 };
- EwsFolderId *fid;
- ECalBackendEws *cbews;
- ECalBackendEwsPrivate *priv;
- icalcomponent_kind kind;
+ ECalComponent *comp = NULL, *oldcomp = NULL;
icalcomponent *icalcomp;
- ECalComponent *comp = NULL;
- struct icaltimetype current;
- GError *error = NULL;
- const gchar *send_meeting_invitations, *calobj;
-
- /* sanity check */
- e_data_cal_error_if_fail (E_IS_CAL_BACKEND_EWS (backend), InvalidArg);
- e_data_cal_error_if_fail (calobjs != NULL, InvalidArg);
-
- if (calobjs->next) {
- g_propagate_error (&error, EDC_ERROR_EX (UnsupportedMethod, _("EWS does not support bulk
additions")));
- goto exit;
- }
-
- calobj = calobjs->data;
- e_data_cal_error_if_fail (calobj != NULL && *calobj != '\0', InvalidArg);
+ gchar *itemid = NULL, *changekey = NULL;
+ GSList *added_attachments = NULL, *removed_attachment_ids = NULL;
+ gboolean success = TRUE;
- cbews = E_CAL_BACKEND_EWS (backend);
- priv = cbews->priv;
+ g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE);
+ g_return_val_if_fail (new_icalcomp != NULL, FALSE);
- kind = e_cal_backend_get_kind (E_CAL_BACKEND (backend));
+ icalcomp = icalcomponent_new_clone (new_icalcomp);
- /* make sure we're not offline */
- if (!e_backend_get_online (E_BACKEND (backend)) || !cbews->priv->cnc) {
- g_propagate_error (&error, EDC_ERROR (RepositoryOffline));
- goto exit;
- }
+ ecb_ews_pick_all_tzids_out (cbews, icalcomp);
- if (!cal_backend_ews_ensure_connected (cbews, cancellable, &error)) {
- goto exit;
+ comp = e_cal_component_new_from_icalcomponent (icalcomp);
+ if (!comp) {
+ g_propagate_error (error, EDC_ERROR (InvalidObject));
+ return FALSE;
}
- /* parse ical data */
- comp = e_cal_component_new_from_string (calobj);
- if (comp == NULL) {
- g_propagate_error (&error, EDC_ERROR (InvalidObject));
- goto exit;
+ 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;
}
- icalcomp = e_cal_component_get_icalcomponent (comp);
- /* make sure data was parsed properly */
- if (!icalcomp) {
- g_propagate_error (&error, EDC_ERROR (InvalidObject));
- goto exit;
+ 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));
}
- /* make sure ical data we parse is actually an ical component */
- if (kind != icalcomponent_isa (icalcomp)) {
- icalcomponent_free (icalcomp);
- g_propagate_error (&error, EDC_ERROR (InvalidObject));
- goto exit;
- }
+ ecb_ews_pick_all_tzids_out (cbews, e_cal_component_get_icalcomponent (oldcomp));
- e_ews_clean_icalcomponent (icalcomp);
+ /* In case we have updated attachments we have to run update attachments
+ * before update items so attendees will receive mails with already updated attachments */
- if (!e_ews_connection_satisfies_server_version (cbews->priv->cnc, E_EWS_EXCHANGE_2010))
- e_cal_backend_ews_pick_all_tzids_out (cbews, icalcomp);
+ ecb_ews_get_attach_differences (oldcomp, comp, &removed_attachment_ids, &added_attachments);
- /* prepare new calender component */
- current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
- e_cal_component_set_created (comp, ¤t);
- e_cal_component_set_last_modified (comp, ¤t);
+ /* preform sync delete attachemnt operation*/
+ if (removed_attachment_ids) {
+ g_free (changekey);
+ changekey = NULL;
- create_data = g_new0 (EwsCalendarAsyncData, 1);
- create_data->cbews = g_object_ref (cbews);
- create_data->comp = g_object_ref (comp);
- create_data->cal = g_object_ref (cal);
- create_data->context = context;
+ success = e_ews_connection_delete_attachments_sync (cbews->priv->cnc, EWS_PRIORITY_MEDIUM,
+ removed_attachment_ids, &changekey, cancellable, error);
- convert_data.connection = cbews->priv->cnc;
- convert_data.icalcomp = icalcomp;
- convert_data.default_zone = cbews->priv->default_zone;
-
- /*
- * 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 (comp)) {
- if (e_cal_component_has_attachments (comp))
- 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";
+ g_slist_free_full (removed_attachment_ids, g_free);
}
- fid = e_ews_folder_id_new (priv->folder_id, NULL, FALSE);
+ /* in case we have a new attachments add them before update */
+ if (added_attachments && success) {
+ EwsId item_id;
- e_ews_connection_create_items (
- priv->cnc,
- EWS_PRIORITY_MEDIUM,
- "SaveOnly",
- send_meeting_invitations,
- fid,
- e_cal_backend_ews_convert_calcomp_to_xml,
- &convert_data,
- cancellable,
- ews_create_object_cb,
- create_data);
+ item_id.id = itemid;
+ item_id.change_key = changekey;
- e_ews_folder_id_free (fid);
+ changekey = NULL;
- return;
+ success = e_ews_connection_create_attachments_sync (
+ cbews->priv->cnc, EWS_PRIORITY_MEDIUM,
+ &item_id, added_attachments,
+ FALSE, &changekey, NULL, cancellable, error);
-exit:
- if (comp != NULL)
- g_object_unref (comp);
+ g_free (item_id.change_key);
+ }
- convert_error_to_edc_error (&error);
- e_data_cal_respond_create_objects (cal, context, error, NULL, NULL);
-}
+ 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;
-static void
-ews_cal_modify_object_cb (GObject *object,
- GAsyncResult *res,
- gpointer user_data)
-{
- EEwsConnection *cnc = E_EWS_CONNECTION (object);
- EwsCalendarAsyncData *modify_data = user_data;
- ECalBackendEws *cbews = modify_data->cbews;
- ECalBackendEwsPrivate *priv = cbews->priv;
- GError *error = NULL;
- GSList *ids = NULL;
- const EwsId *item_id;
- icalproperty *icalprop = NULL;
- icalcomponent *icalcomp;
- ECalComponentId *id = NULL;
- const gchar *x_name;
-
- if (!e_ews_connection_update_items_finish (cnc, res, &ids, &error)) {
- convert_error_to_edc_error (&error);
- if (modify_data->context)
- e_data_cal_respond_modify_objects (modify_data->cal, modify_data->context, error,
NULL, NULL);
- else if (error != NULL) {
- g_warning ("Modify object error : %s\n", error->message);
- g_clear_error (&error);
- }
+ item_id.id = itemid;
+ item_id.change_key = changekey;
- goto exit;
- }
+ /* 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;
- e_cal_backend_store_freeze_changes (priv->store);
+ new_rid = icalproperty_get_value_as_string (prop);
- item_id = e_ews_item_get_id ((EEwsItem *) ids->data);
+ 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;
+ }
- /* Update change key. id remains the same, but change key changed.*/
- icalcomp = e_cal_component_get_icalcomponent (modify_data->comp);
- icalprop = icalcomponent_get_first_property (icalcomp, ICAL_X_PROPERTY);
- while (icalprop) {
- x_name = icalproperty_get_x_name (icalprop);
- if (!g_ascii_strcasecmp (x_name, "X-EVOLUTION-CHANGEKEY")) {
- icalproperty_set_value_from_string (icalprop, item_id->change_key, "NO");
- break;
+ if (!old_prop)
+ exceptions = g_slist_prepend (exceptions, prop);
}
- icalprop = icalcomponent_get_next_property (icalcomp, ICAL_X_PROPERTY);
- }
- e_cal_component_commit_sequence (modify_data->comp);
- id = e_cal_component_get_id (modify_data->comp);
- e_cal_backend_store_remove_component (cbews->priv->store, id->uid, id->rid);
- put_component_to_store (cbews, modify_data->comp);
+ exceptions = g_slist_reverse (exceptions);
- if (modify_data->context) {
- GSList *old_components, *new_components;
+ for (link = exceptions; link && success; link = g_slist_next (link)) {
+ guint index;
- e_cal_backend_notify_component_modified (E_CAL_BACKEND (cbews), modify_data->extra_comp,
modify_data->comp);
+ prop = link->data;
- old_components = g_slist_append (NULL, modify_data->extra_comp);
- new_components = g_slist_append (NULL, modify_data->comp);
+ 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);
- e_data_cal_respond_modify_objects (modify_data->cal, modify_data->context, NULL,
old_components, new_components);
+ if (index == 0) {
+ success = FALSE;
+ } else {
+ 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 (old_components);
- g_slist_free (new_components);
+ g_slist_free (exceptions);
}
- ews_start_sync (cbews);
-
- PRIV_LOCK (priv);
- g_hash_table_replace (priv->item_id_hash, g_strdup (modify_data->item_id), g_object_ref
(modify_data->comp));
- PRIV_UNLOCK (priv);
+ if (success) {
+ EwsCalendarConvertData convert_data = { 0 };
+ CamelEwsSettings *ews_settings;
+ const gchar *send_meeting_invitations;
+ const gchar *send_or_save;
- e_cal_backend_store_thaw_changes (priv->store);
+ ews_settings = ecb_ews_get_collection_settings (cbews);
- icalproperty_free (icalprop);
- e_cal_component_free_id (id);
+ convert_data.connection = cbews->priv->cnc;
+ 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 ();
-exit:
- e_cal_backend_ews_async_data_free (modify_data);
-}
+ 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";
+ }
-static void
-e_cal_backend_ews_modify_objects (ECalBackend *backend,
- EDataCal *cal,
- guint32 context,
- GCancellable *cancellable,
- const GSList *calobjs,
- ECalObjModType mod)
-{
- GError *error = NULL;
+ 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);
- if (!calobjs) {
- if (context) {
- g_propagate_error (&error, EDC_ERROR (InvalidArg));
- e_data_cal_respond_modify_objects (cal, context, error, NULL, NULL);
- }
- return;
+ g_free (convert_data.user_email);
}
- if (calobjs->next) {
- if (context) {
- g_propagate_error (&error, EDC_ERROR_EX (UnsupportedMethod, _("EWS does not support
bulk modifications")));
- e_data_cal_respond_modify_objects (cal, context, error, NULL, NULL);
- }
- return;
- }
+ 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);
- e_cal_backend_ews_modify_object (backend, cal, context, cancellable, calobjs->data, mod);
+ return success;
}
-static void
-e_cal_backend_ews_modify_object (ECalBackend *backend,
- EDataCal *cal,
- guint32 context,
- GCancellable *cancellable,
- const gchar *calobj,
- ECalObjModType mod)
+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)
{
- EwsCalendarAsyncData *modify_data;
ECalBackendEws *cbews;
- ECalBackendEwsPrivate *priv;
- icalcomponent_kind kind;
- ECalComponent *comp = NULL, *oldcomp = NULL;
- icalcomponent *icalcomp;
- gchar *itemid = NULL, *changekey = NULL;
- struct icaltimetype current;
- GError *error = NULL;
- GSList *original_attachments = NULL, *modified_attachments = NULL, *added_attachments = NULL,
*removed_attachments = NULL, *removed_attachments_ids = NULL, *i;
- EwsCalendarAsyncData *attach_data;
+ ECalCache *cal_cache;
+ ECalComponent *master = NULL;
+ EwsFolderId *fid;
+ GSList *link;
+ const gchar *uid = NULL;
+ gboolean success = TRUE;
- e_data_cal_error_if_fail (E_IS_CAL_BACKEND_EWS (backend), InvalidArg);
- e_data_cal_error_if_fail (calobj != NULL && *calobj != '\0', InvalidArg);
+ g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (meta_backend), FALSE);
- cbews = E_CAL_BACKEND_EWS (backend);
- priv = cbews->priv;
- kind = e_cal_backend_get_kind (E_CAL_BACKEND (backend));
+ cbews = E_CAL_BACKEND_EWS (meta_backend);
- if (!e_backend_get_online (E_BACKEND (backend)) || !cbews->priv->cnc) {
- g_propagate_error (&error, EDC_ERROR (RepositoryOffline));
- goto exit;
- }
+ for (link = (GSList *) instances; link && !master; link = g_slist_next (link)) {
+ master = link->data;
- if (!cal_backend_ews_ensure_connected (cbews, cancellable, &error)) {
- goto exit;
- }
+ if (!master)
+ continue;
- icalcomp = icalparser_parse_string (calobj);
- if (!icalcomp) {
- g_propagate_error (&error, EDC_ERROR (InvalidObject));
- goto exit;
+ if (e_cal_component_is_instance (master))
+ master = NULL;
}
- if (kind != icalcomponent_isa (icalcomp)) {
- icalcomponent_free (icalcomp);
- g_propagate_error (&error, EDC_ERROR (InvalidObject));
- goto exit;
+
+ if (!master) {
+ g_propagate_error (error, EDC_ERROR (InvalidObject));
+ return FALSE;
}
- e_cal_backend_ews_pick_all_tzids_out (cbews, icalcomp);
+ cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
+ g_return_val_if_fail (cal_cache != NULL, FALSE);
- comp = e_cal_component_new ();
- e_cal_component_set_icalcomponent (comp, icalcomp);
- current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
- e_cal_component_set_last_modified (comp, ¤t);
+ g_rec_mutex_lock (&cbews->priv->cnc_lock);
- ews_cal_component_get_item_id (comp, &itemid, &changekey);
- if (!itemid) {
- g_propagate_error (
- &error, EDC_ERROR_EX (OtherError,
- "Cannot determine EWS ItemId"));
- goto exit;
- }
+ e_cal_component_get_uid (master, &uid);
+ fid = e_ews_folder_id_new (cbews->priv->folder_id, NULL, FALSE);
- PRIV_LOCK (priv);
- oldcomp = g_hash_table_lookup (priv->item_id_hash, itemid);
- if (!oldcomp) {
- g_propagate_error (&error, EDC_ERROR (ObjectNotFound));
- PRIV_UNLOCK (priv);
- goto exit;
- }
- g_object_ref (oldcomp);
- PRIV_UNLOCK (priv);
+ 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;
- e_cal_backend_ews_pick_all_tzids_out (cbews, e_cal_component_get_icalcomponent (oldcomp));
+ if (success)
+ ecb_ews_filter_out_unchanged_instances (instances, existing, &changed_instances,
&removed_instances);
- /*In case we have updated attachments we have to run update attachments
- *before update items so attendees will receive mails with already updated attachments */
+ if (success) {
+ for (link = changed_instances; link && success; link = g_slist_next (link)) {
+ ChangeData *cd = link->data;
- e_cal_component_get_attachment_list (oldcomp, &original_attachments);
- e_cal_component_get_attachment_list (comp, &modified_attachments);
+ if (!cd)
+ continue;
- ewscal_get_attach_differences (original_attachments, modified_attachments, &removed_attachments,
&added_attachments);
- g_slist_free (original_attachments);
- g_slist_free (modified_attachments);
+ success = ecb_ews_modify_item_sync (cbews,
+ e_cal_component_get_icalcomponent (cd->old_component ?
cd->old_component : master),
+ e_cal_component_get_icalcomponent (cd->new_component),
+ cancellable, error);
+ }
- /* preform sync delete attachemnt operation*/
- if (removed_attachments) {
- icalproperty *icalprop;
- GSList *items;
+ for (link = removed_instances; link && success; link = g_slist_next (link)) {
+ ECalComponent *comp = link->data;
+ ECalComponentId *id = NULL;
- /* convert attachment uri to attachment id, should have used a hash table somehow */
- icalcomp = e_cal_component_get_icalcomponent (oldcomp);
- icalprop = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY);
- while (icalprop) {
- const gchar *attachment_url = icalproperty_get_value_as_string (icalprop);
+ if (!comp)
+ continue;
- for (items = removed_attachments; items; items = items->next) {
- if (g_strcmp0 (attachment_url, items->data) == 0) {
- break;
+ id = e_cal_component_get_id (comp);
+
+ if (id) {
+ success = ecb_ews_remove_item_sync (cbews, cal_cache, id->uid,
id->rid, cancellable, error);
+ e_cal_component_free_id (id);
}
}
+ }
+
+ if (success)
+ ecb_ews_extract_item_id (master, out_new_uid, NULL);
- /* not NULL means the attachment was found in removed attachments */
- if (items != NULL)
- removed_attachments_ids = g_slist_append (removed_attachments_ids,
icalproperty_get_parameter_as_string_r (icalprop, "X-EWS-ATTACHMENTID"));
+ 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 {
+ 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));
- icalprop = icalcomponent_get_next_property (icalcomp, ICAL_ATTACH_PROPERTY);
+ 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";
}
- items = NULL;
+ convert_data.connection = cbews->priv->cnc;
+ 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);
- if (removed_attachments_ids) {
- if (e_ews_connection_delete_attachments_sync (
- priv->cnc, EWS_PRIORITY_MEDIUM,
- removed_attachments_ids, &items, cancellable, &error) && items)
- changekey = items->data;
+ ews_id = e_ews_item_get_id (item);
+ }
}
- g_slist_free_full (removed_attachments_ids, g_free);
- g_slist_free (removed_attachments);
- }
+ if (success && item && e_ews_item_get_item_type (item) == E_EWS_ITEM_TYPE_EVENT) {
+ EEwsAdditionalProps *add_props;
+ GSList *items, *items_req = NULL;
- /*in case we have a new attachmetns the update item will be preformed in ews_create_attachments_cb*/
- if (added_attachments) {
- const gchar *old_uid = NULL;
- gint old_uid_len = 0;
- GSList *info_attachments = NULL;
- EwsId *item_id = g_new0 (EwsId, 1);
- item_id->id = itemid;
- item_id->change_key = changekey;
- attach_data = g_new0 (EwsCalendarAsyncData, 1);
-
- attach_data->cbews = g_object_ref (cbews);
- attach_data->comp = g_object_ref (comp);
- attach_data->cb_type = E_EWS_ATTACHMENT_TYPE_UPDATE;
- attach_data->extra_comp = g_object_ref (oldcomp);
- attach_data->cal = g_object_ref (cal);
- attach_data->context = 0;
- attach_data->item_id = itemid;
-
- e_cal_component_get_uid (oldcomp, &old_uid);
- if (old_uid)
- old_uid_len = strlen (old_uid);
-
- for (i = added_attachments; i; i = i->next) {
- EEwsAttachmentInfo *info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_URI);
-
- e_ews_attachment_info_set_uri (info, i->data);
-
- if (old_uid) {
- gchar *filename = g_filename_from_uri (i->data, NULL, NULL);
- if (filename) {
- const gchar *slash = strrchr (filename, G_DIR_SEPARATOR);
- if (slash && g_str_has_prefix (slash + 1, old_uid) &&
- slash[1 + old_uid_len] == '-') {
- e_ews_attachment_info_set_prefer_filename (info, slash + 1 +
old_uid_len + 1);
- }
+ add_props = e_ews_additional_props_new ();
+ add_props->field_uri = g_strdup ("calendar:UID");
+
+ items = g_slist_append (NULL, ews_id->id);
- g_free (filename);
+ /* 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);
}
}
- info_attachments = g_slist_append (info_attachments, info);
+ g_slist_free_full (items_req, g_object_unref);
+ g_slist_free (items);
}
- if (context) {
- convert_error_to_edc_error (&error);
- e_data_cal_respond_modify_objects (cal, context, error, NULL, NULL);
+ /* attachments */
+ if (success && e_cal_component_has_attachments (master) > 0) {
+ GSList *info_attachments = NULL;
+
+ if (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);
+ }
}
- e_ews_connection_create_attachments (
- priv->cnc, EWS_PRIORITY_MEDIUM,
- item_id, info_attachments,
- FALSE, cancellable,
- ews_create_attachments_cb,
- attach_data);
+ if (success && icalcomponent_get_first_property (icalcomp, ICAL_RRULE_PROPERTY)) {
+ GSList *exceptions = NULL;
- g_slist_free_full (info_attachments, (GDestroyNotify) e_ews_attachment_info_free);
- g_slist_free (added_attachments);
- g_free (item_id);
+ /* 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)));
+ }
- } else {
- EwsCalendarConvertData convert_data = { 0 };
- const gchar *send_meeting_invitations;
- const gchar *send_or_save;
+ for (link = exceptions; link && success; link = g_slist_next (link)) {
+ success = ecb_ews_remove_item_sync (cbews, cal_cache, uid, link->data,
cancellable, error);
+ }
- modify_data = g_new0 (EwsCalendarAsyncData, 1);
- modify_data->cbews = g_object_ref (cbews);
- modify_data->comp = g_object_ref (comp);
- modify_data->extra_comp = g_object_ref (oldcomp);
- modify_data->cal = g_object_ref (cal);
- modify_data->context = context;
- modify_data->item_id = itemid;
+ g_slist_free_full (exceptions, g_free);
+ }
- convert_data.connection = cbews->priv->cnc;
- convert_data.user_email = cbews->priv->user_email;
- convert_data.comp = comp;
- convert_data.old_comp = oldcomp;
- convert_data.item_id = itemid;
- convert_data.change_key = changekey;
- convert_data.default_zone = cbews->priv->default_zone;
+ 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);
+ }
- 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";
+ /* 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, NULL, icalcomp, cancellable, error);
}
- e_ews_connection_update_items (
- priv->cnc, EWS_PRIORITY_MEDIUM,
- "AlwaysOverwrite",
- send_or_save,
- send_meeting_invitations,
- priv->folder_id,
- e_cal_backend_ews_convert_component_to_updatexml,
- &convert_data,
- cancellable,
- ews_cal_modify_object_cb,
- modify_data);
+ 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, 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_clear_object (&oldcomp);
+ g_rec_mutex_unlock (&cbews->priv->cnc_lock);
- return;
+ g_clear_object (&cal_cache);
+ e_ews_folder_id_free (fid);
-exit:
- g_clear_object (&oldcomp);
- g_clear_object (&comp);
+ ecb_ews_convert_error_to_edc_error (error);
+
+ 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);
- convert_error_to_edc_error (&error);
- if (context)
- e_data_cal_respond_modify_objects (cal, context, error, NULL, NULL);
- else if (error) {
- g_warning ("Modify object error : %s\n", error->message);
- g_clear_error (&error);
+ 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);
+ g_object_unref (comp);
+
+ return success;
}
static void
-e_ews_receive_objects_no_exchange_mail (ECalBackendEws *cbews,
- icalcomponent *subcomp,
- GSList **ids,
- GCancellable *cancellable,
- GError **error)
+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;
+ }
+
+ 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);
+}
+
+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 = icalcomponent_get_dtstart (vevent);
+ 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.icalcomp = subcomp;
- convert_data.default_zone = cbews->priv->default_zone;
+ convert_data.default_zone = icaltimezone_get_utc_timezone ();
fid = e_ews_folder_id_new (cbews->priv->folder_id, NULL, FALSE);
@@ -2331,9 +2910,9 @@ find_attendee_if_sentby (icalcomponent *ical_comp,
}
static const gchar *
-e_ews_get_current_user_meeting_reponse (ECalBackendEws *cbews,
- icalcomponent *icalcomp,
- const gchar *current_user_mail)
+ecb_ews_get_current_user_meeting_reponse (ECalBackendEws *cbews,
+ icalcomponent *icalcomp,
+ const gchar *current_user_mail)
{
icalproperty *attendee;
const gchar *attendee_str = NULL, *attendee_mail = NULL;
@@ -2415,11 +2994,53 @@ e_ews_get_current_user_meeting_reponse (ECalBackendEws *cbews,
return response;
}
+/* changekey can be NULL if you don't want it. itemid cannot. */
static void
-ews_cal_do_method_request_publish_reply (ECalBackendEws *cbews,
+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,
GCancellable *cancellable,
GError **error)
{
@@ -2433,19 +3054,19 @@ ews_cal_do_method_request_publish_reply (ECalBackendEws *cbews,
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"), cbews->priv->user_email ? cbews->priv->user_email : "NULL");
- return;
+ 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)
- ews_cal_component_get_calendar_item_accept_id (comp, &item_id, &change_key, &mail_id);
+ 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)) {
- e_ews_receive_objects_no_exchange_mail (cbews, subcomp, &ids, cancellable,
&local_error);
+ ecb_ews_receive_objects_no_exchange_mail (cbews, subcomp, &ids, cancellable,
&local_error);
} else {
EwsCalendarConvertData convert_data = { 0 };
@@ -2575,301 +3196,182 @@ ews_cal_do_method_request_publish_reply (ECalBackendEws *cbews,
g_free (mail_id);
g_slist_free_full (ids, g_object_unref);
- /*We have to run sync before any other operations */
- ews_start_sync (cbews);
+ return !local_error;
}
static void
-e_cal_backend_ews_receive_objects (ECalBackend *backend,
- EDataCal *cal,
- guint32 context,
- GCancellable *cancellable,
- const gchar *calobj)
+ecb_ews_receive_objects_sync (ECalBackendSync *sync_backend,
+ EDataCal *cal,
+ GCancellable *cancellable,
+ const gchar *calobj,
+ GError **error)
{
ECalBackendEws *cbews;
- ECalBackendEwsPrivate *priv;
- icalcomponent_kind kind;
+ ECalBackend *cal_backend;
+ CamelEwsSettings *ews_settings;
icalcomponent *icalcomp, *subcomp;
- GError *error = NULL;
- icalproperty_method method;
+ icalcomponent_kind kind;
+ gchar *user_email;
+ gboolean success = TRUE, do_refresh = FALSE;
- cbews = E_CAL_BACKEND_EWS (backend);
- priv = cbews->priv;
+ g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend));
- /* make sure we're not offline */
- if (!e_backend_get_online (E_BACKEND (backend)) || !cbews->priv->cnc) {
- g_propagate_error (&error, EDC_ERROR (RepositoryOffline));
- goto exit;
- }
+ cbews = E_CAL_BACKEND_EWS (sync_backend);
- if (!cal_backend_ews_ensure_connected (cbews, cancellable, &error)) {
- goto exit;
- }
+ if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error))
+ return;
- icalcomp = icalparser_parse_string (calobj);
+ icalcomp = calobj ? icalparser_parse_string (calobj) : NULL;
- /* make sure data was parsed properly */
if (!icalcomp) {
- g_propagate_error (&error, EDC_ERROR (InvalidObject));
- goto exit;
+ g_propagate_error (error, EDC_ERROR (InvalidObject));
+ return;
}
- /* make sure ical data we parse is actually an vcal component */
+ /* 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));
- goto exit;
+ g_propagate_error (error, EDC_ERROR (InvalidObject));
+ return;
}
- kind = e_cal_backend_get_kind (E_CAL_BACKEND (backend));
- method = icalcomponent_get_method (icalcomp);
- subcomp = icalcomponent_get_first_component (icalcomp, kind);
-
- while (subcomp) {
- ECalComponent *comp = e_cal_component_new ();
- const gchar *response_type;
+ cal_backend = E_CAL_BACKEND (cbews);
+ kind = e_cal_backend_get_kind (cal_backend);
- /* duplicate the ical component */
- e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (subcomp));
-
- /*getting a data for meeting request response*/
- response_type = e_ews_get_current_user_meeting_reponse (cbews,
- e_cal_component_get_icalcomponent (comp),
- priv->user_email);
-
- switch (method) {
- case ICAL_METHOD_REQUEST:
- case ICAL_METHOD_PUBLISH:
- case ICAL_METHOD_REPLY:
- ews_cal_do_method_request_publish_reply (
- cbews,
- comp,
- subcomp,
- response_type,
- cancellable,
- &error);
- break;
- case ICAL_METHOD_CANCEL: {
- const gchar *uid = NULL;
- gchar *rid = NULL;
- ECalObjModType mod;
-
- e_cal_component_get_uid (comp, &uid);
- rid = e_cal_component_get_recurid_as_string (comp);
- mod = e_cal_component_is_instance (comp) ? E_CAL_OBJ_MOD_THIS :
E_CAL_OBJ_MOD_ALL;
-
- e_cal_backend_ews_remove_object (backend, cal, 0, cancellable, uid, rid, mod);
- g_free (rid);
- 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
- */
- 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);
-
- e_cal_backend_ews_modify_object (backend, cal, 0, cancellable,
icalcomponent_as_ical_string (subcomp), E_CAL_OBJ_MOD_ALL);
- }
- break;
- default:
- break;
- }
+ ews_settings = ecb_ews_get_collection_settings (cbews);
+ user_email = camel_ews_settings_dup_email (ews_settings);
- g_object_unref (comp);
- subcomp = icalcomponent_get_next_component (icalcomp, kind);
- }
+ 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;
- icalcomponent_free (icalcomp);
+ /* getting a data for meeting request response */
+ response_type = ecb_ews_get_current_user_meeting_reponse (cbews, subcomp, user_email);
-exit:
- convert_error_to_edc_error (&error);
- e_data_cal_respond_receive_objects (cal, context, error);
-}
+ comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (subcomp));
-static const gchar *
-e_cal_get_meeting_cancellation_comment (ECalComponent *comp)
-{
- icalproperty *prop;
- 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 (!g_ascii_strcasecmp (x_name, "X-EVOLUTION-RETRACT-COMMENT"))
- return x_val;
+ success = ecb_ews_do_method_request_publish_reply (cbews, comp, subcomp,
response_type, user_email, cancellable, error);
- prop = icalcomponent_get_next_property (
- e_cal_component_get_icalcomponent (comp),
- ICAL_X_PROPERTY);
- }
+ do_refresh = TRUE;
- return NULL;
-}
-
-static void
-ewscal_send_cancellation_email (ECalBackend *backend,
- EEwsConnection *cnc,
- CamelAddress *from,
- CamelInternetAddress *recipient,
- const gchar *subject,
- const gchar *body,
- const gchar *calobj)
-{
- CamelMimeMessage *message;
- CamelContentType *mime_type;
- GError *error = NULL;
- CamelMultipart *multi;
- CamelMimePart *text_part, *vcal_part;
- gchar *ical_str;
- icalcomponent *vcal, *vevent, *vtz;
- icalproperty *prop;
- icaltimezone *icaltz;
- struct icaltimetype dt;
-
- 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 = icalcomponent_get_dtstart (vevent);
- icaltz = (icaltimezone *)
- (dt.zone ? dt.zone : e_cal_backend_ews_get_timezone_from_ical_component (backend, 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);
+ 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;
- 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);
+ /* getting a data for meeting request response */
+ response_type = ecb_ews_get_current_user_meeting_reponse (cbews, subcomp, user_email);
- 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);
+ if (g_strcmp0 (response_type, "ACCEPTED") == 0) {
+ gchar **split_subject;
+ icalproperty *summary;
- camel_medium_set_content ((CamelMedium *) message, (CamelDataWrapper *) multi);
- g_object_unref (multi);
+ /* 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);
- camel_ews_utils_create_mime_message (cnc, "SendOnly", NULL, message, NULL, from, NULL, NULL, NULL,
NULL, &error);
+ success = ecb_ews_modify_item_sync (cbews, NULL, subcomp, cancellable, error);
- if (error) {
- g_warning ("Failed to send cancellation email: %s", error->message);
- g_clear_error (&error);
+ do_refresh = TRUE;
+ }
+ }
+ break;
+ default:
+ break;
}
- g_object_unref (message);
- icalcomponent_free (vcal);
+ icalcomponent_free (icalcomp);
+ g_free (user_email);
+
+ if (success && do_refresh)
+ e_cal_meta_backend_schedule_refresh (E_CAL_META_BACKEND (cbews));
}
static void
-e_cal_backend_ews_send_objects (ECalBackend *backend,
- EDataCal *cal,
- guint32 context,
- GCancellable *cancellable,
- const gchar *calobj)
+ecb_ews_send_objects_sync (ECalBackendSync *sync_backend,
+ EDataCal *cal,
+ GCancellable *cancellable,
+ const gchar *calobj,
+ GSList **users,
+ gchar **modified_calobj,
+ GError **error)
{
ECalBackendEws *cbews;
- ECalBackendEwsPrivate *priv;
icalcomponent_kind kind;
icalcomponent *icalcomp, *subcomp = NULL;
- GError *error = NULL;
gchar *subcalobj;
+ gboolean success = TRUE;
- cbews = E_CAL_BACKEND_EWS (backend);
- priv = cbews->priv;
+ g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend));
- /* make sure we're not offline */
- if (!e_backend_get_online (E_BACKEND (backend)) || !cbews->priv->cnc) {
- g_propagate_error (&error, EDC_ERROR (RepositoryOffline));
- goto exit;
- }
+ cbews = E_CAL_BACKEND_EWS (sync_backend);
- if (!cal_backend_ews_ensure_connected (cbews, cancellable, &error)) {
- goto exit;
- }
+ if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error))
+ return;
- icalcomp = icalparser_parse_string (calobj);
+ icalcomp = calobj ? icalparser_parse_string (calobj) : NULL;
/* make sure data was parsed properly */
if (!icalcomp) {
- g_propagate_error (&error, EDC_ERROR (InvalidObject));
- goto exit;
+ 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));
- goto exit;
+ g_propagate_error (error, EDC_ERROR (InvalidObject));
+ return;
}
- kind = e_cal_backend_get_kind (E_CAL_BACKEND (backend));
+ 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 (backend));
+ 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) {
- ECalComponent *comp = e_cal_component_new ();
+ 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 ();
- e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (subcomp));
-
- new_body_content = e_cal_get_meeting_cancellation_comment (comp);
+ 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;
+ 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 != NULL;
- prop = icalcomponent_get_next_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;
@@ -2877,1579 +3379,103 @@ e_cal_backend_ews_send_objects (ECalBackend *backend,
subcalobj = icalcomponent_as_ical_string_r (subcomp);
camel_internet_address_add (attendee_addr, icalproperty_get_parameter_as_string
(prop, "CN"), attendee);
- ewscal_send_cancellation_email (backend, priv->cnc, CAMEL_ADDRESS (org_addr),
attendee_addr, subject, new_body_content, subcalobj);
+ 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);
- g_object_unref (comp);
subcomp = icalcomponent_get_next_component (icalcomp, kind);
}
icalcomponent_free (icalcomp);
-exit:
- convert_error_to_edc_error (&error);
- e_data_cal_respond_send_objects (cal, context, error, NULL, calobj);
-}
-
-/* TODO Do not replicate this in every backend */
-static icaltimezone *
-resolve_tzid (const gchar *tzid,
- gpointer user_data)
-{
- ETimezoneCache *timezone_cache;
-
- timezone_cache = E_TIMEZONE_CACHE (user_data);
-
- return e_timezone_cache_get_timezone (timezone_cache, tzid);
+ ecb_ews_convert_error_to_edc_error (error);
}
static void
-put_component_to_store (ECalBackendEws *cbews,
- ECalComponent *comp)
+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)
{
- time_t time_start, time_end;
- ECalBackendEwsPrivate *priv;
-
- priv = cbews->priv;
-
- e_cal_util_get_component_occur_times (
- comp, &time_start, &time_end,
- resolve_tzid, cbews, priv->default_zone,
- e_cal_backend_get_kind (E_CAL_BACKEND (cbews)));
-
- e_cal_backend_store_put_component_with_time_range (priv->store, comp, time_start, time_end);
-}
-
-static void
-ews_get_attachments (ECalBackendEws *cbews,
- EEwsItem *item)
-{
- gboolean has_attachment = FALSE;
- const GSList *attachment_ids, *aid, *l;
- const EwsId *item_id;
- ECalComponent *comp;
- const gchar *uid;
- GSList *uris = NULL, *info_attachments = NULL;
- GCancellable *cancellable;
+ ECalBackendEws *cbews;
+ EEWSFreeBusyData fbdata = { 0 };
+ GSList *freebusy = NULL;
+ gboolean success;
- e_ews_item_has_attachments (item, &has_attachment);
- if (!has_attachment)
- return;
+ g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend));
+ g_return_if_fail (freebusyobjs != NULL);
- item_id = e_ews_item_get_id (item);
- g_return_if_fail (item_id != NULL);
+ cbews = E_CAL_BACKEND_EWS (sync_backend);
- cancellable = cal_backend_ews_ref_cancellable (cbews);
+ *freebusyobjs = NULL;
- PRIV_LOCK (cbews->priv);
- comp = g_hash_table_lookup (cbews->priv->item_id_hash, item_id->id);
- if (!comp) {
- PRIV_UNLOCK (cbews->priv);
- g_clear_object (&cancellable);
- g_warning ("%s: Failed to get component from item_id_hash", G_STRFUNC);
+ if (!e_cal_meta_backend_ensure_connected_sync (E_CAL_META_BACKEND (cbews), cancellable, error))
return;
- }
-
- e_cal_component_get_uid (comp, &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->storage_path,
- TRUE,
- &info_attachments,
- NULL, NULL,
- cancellable,
- NULL)) {
- icalcomponent *icalcomp;
- icalproperty *icalprop;
- icalparameter *icalparam;
- ECalComponentId *id;
- ECalComponent *cache_comp;
-
- 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 (comp, uris);
-
- icalcomp = e_cal_component_get_icalcomponent (comp);
- 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);
- }
-
- id = e_cal_component_get_id (comp);
- if (!id) {
- g_warn_if_reached ();
- } else {
- cache_comp = e_cal_backend_store_get_component (cbews->priv->store, id->uid, id->rid);
- e_cal_component_free_id (id);
-
- put_component_to_store (cbews, comp);
-
- if (cache_comp)
- e_cal_backend_notify_component_modified (E_CAL_BACKEND (cbews), cache_comp,
comp);
- }
-
- g_slist_free_full (uris, g_free);
- g_slist_free_full (info_attachments, (GDestroyNotify) e_ews_attachment_info_free);
- }
-
- PRIV_UNLOCK (cbews->priv);
-
- g_clear_object (&cancellable);
-}
-
-static icaltimezone *
-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 *
-cal_backend_ews_responsetype_to_partstat (const gchar *responsetype)
-{
- icalparameter *param = NULL;
-
- g_return_val_if_fail (responsetype != NULL, NULL);
-
- 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 void
-add_item_to_cache (ECalBackendEws *cbews,
- EEwsItem *item)
-{
- ECalBackendEwsPrivate *priv;
- ETimezoneCache *timezone_cache;
- icalcomponent_kind kind;
- EEwsItemType item_type;
- icalcomponent *icalcomp, *vcomp;
- const gchar *mime_content;
-
- timezone_cache = E_TIMEZONE_CACHE (cbews);
-
- kind = e_cal_backend_get_kind ((ECalBackend *) cbews);
- priv = cbews->priv;
-
- 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,
priv->default_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;
- GCancellable *cancellable;
- GError *error = NULL;
- gboolean includes_last_item;
- gchar *mailtoname;
- 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 */
-
- mailtoname = g_strdup_printf ("mailto:%s", priv->user_email);
- icalprop = icalproperty_new_attendee (mailtoname);
- g_free (mailtoname);
-
- param = icalparameter_new_cn (e_ews_item_get_owner (item));
- icalproperty_add_parameter (icalprop, param);
- icalcomponent_add_property (icalcomp, icalprop);
-
- cancellable = cal_backend_ews_ref_cancellable (cbews);
-
- /* get delegator mail box*/
- e_ews_connection_resolve_names_sync (
- 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);
- g_clear_object (&cancellable);
- }
-
- if (item_type == E_EWS_ITEM_TYPE_TASK) {
- 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, priv->default_zone);
- 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,
priv->default_zone);
- 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, priv->default_zone);
- complete_date.is_date = 1;
- 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 *tzid;
- gboolean timezone_set = FALSE;
-
- mime_content = e_ews_item_get_mime_content (item);
- vcomp = icalparser_parse_string (mime_content);
-
- if (!vcomp && 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) {
- g_warn_if_reached ();
- return;
- }
-
- 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 = get_timezone (
- timezone_cache,
- start_tzid,
- ical_start_tzid != NULL ? ical_start_tzid : start_tzid,
- evo_ews_start_tzid);
- end_zone = 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 = icalcomponent_get_dtstart (icalcomp);
- 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 = icalcomponent_get_dtend (icalcomp);
- 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 = icalcomponent_get_dtstart (icalcomp);
- dt = icaltime_convert_to_zone (dt, zone);
- icalcomponent_set_dtstart (icalcomp, dt);
-
- dt = icalcomponent_get_dtend (icalcomp);
- 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) {
- ECalComponent *comp, *cache_comp = NULL;
- icalproperty *icalprop, *freebusy;
- const EwsId *item_id;
- ECalComponentId *id;
- const GSList *l = NULL;
- const gchar *uid = e_ews_item_get_uid (item);
-
- item_id = e_ews_item_get_id (item);
-
- /* 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 (cbews->priv->user_email && (email || attendee->mailbox->email) &&
e_ews_item_get_my_response_type (item) &&
- g_ascii_strcasecmp (email ? email : attendee->mailbox->email,
cbews->priv->user_email) == 0) {
- param = cal_backend_ews_responsetype_to_partstat
(e_ews_item_get_my_response_type (item));
- } else {
- param = cal_backend_ews_responsetype_to_partstat (attendee->responsetype);
- }
- icalproperty_add_parameter (icalprop, param);
-
- icalcomponent_add_property (icalcomp, icalprop);
- }
-
- /* 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);
- }
- }
-
- /* 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);
-
- icalprop = icalproperty_new_x (item_id->id);
- icalproperty_set_x_name (icalprop, "X-EVOLUTION-ITEMID");
- icalcomponent_add_property (icalcomp, icalprop);
-
- icalprop = icalproperty_new_x (item_id->change_key);
- icalproperty_set_x_name (icalprop, "X-EVOLUTION-CHANGEKEY");
- icalcomponent_add_property (icalcomp, icalprop);
-
- comp = e_cal_component_new ();
- e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp));
-
- /* Categories */
- e_cal_component_set_categories_list (comp, (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 (comp)) {
- GList *alarm_uids, *l;
-
- alarm_uids = e_cal_component_get_alarm_uids (comp);
- for (l = alarm_uids; l != NULL; l = l->next) {
- ECalComponentAlarm *alarm;
- ECalComponentText text;
-
- alarm = e_cal_component_get_alarm (comp, l->data);
- e_cal_component_get_summary (comp, &text);
- e_cal_component_alarm_set_description (alarm, &text);
-
- e_cal_component_alarm_free (alarm);
- }
- cal_obj_uid_list_free (alarm_uids);
- }
-
- id = e_cal_component_get_id (comp);
- cache_comp = e_cal_backend_store_get_component (priv->store, id->uid, id->rid);
- e_cal_component_free_id (id);
-
- put_component_to_store (cbews, comp);
-
- if (!cache_comp) {
- e_cal_backend_notify_component_created (E_CAL_BACKEND (cbews), comp);
- } else {
- e_cal_backend_notify_component_modified (E_CAL_BACKEND (cbews), cache_comp, comp);
- }
-
- PRIV_LOCK (priv);
- g_hash_table_insert (priv->item_id_hash, g_strdup (item_id->id), g_object_ref (comp));
- PRIV_UNLOCK (priv);
-
- g_object_unref (comp);
- }
- icalcomponent_free (vcomp);
-}
-
-static void
-ews_refreshing_inc (ECalBackendEws *cbews)
-{
- PRIV_LOCK (cbews->priv);
- if (!cbews->priv->refreshing)
- e_flag_clear (cbews->priv->refreshing_done);
- cbews->priv->refreshing++;
- PRIV_UNLOCK (cbews->priv);
-}
-
-static void
-ews_refreshing_dec (ECalBackendEws *cbews)
-{
- PRIV_LOCK (cbews->priv);
- if (!cbews->priv->refreshing) {
- e_flag_set (cbews->priv->refreshing_done);
- PRIV_UNLOCK (cbews->priv);
-
- g_warning ("%s: Invalid call, currently not refreshing", G_STRFUNC);
+ /* 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;
}
- cbews->priv->refreshing--;
- if (!cbews->priv->refreshing) {
- e_flag_set (cbews->priv->refreshing_done);
- }
- PRIV_UNLOCK (cbews->priv);
-}
-
-static gboolean
-ews_cal_sync_get_items_sync (ECalBackendEws *cbews,
- const GSList *item_ids,
- const gchar *default_props,
- const EEwsAdditionalProps *add_props)
-{
- ECalBackendEwsPrivate *priv;
- gboolean ret = FALSE;
- GSList *items = NULL, *l;
- GCancellable *cancellable;
- GError *error = NULL;
-
- priv = cbews->priv;
-
- cancellable = cal_backend_ews_ref_cancellable (cbews);
-
- e_ews_connection_get_items_sync (
- priv->cnc,
- EWS_PRIORITY_MEDIUM,
- item_ids,
- default_props,
- add_props,
- FALSE,
- NULL,
- E_EWS_BODY_TYPE_TEXT,
- &items,
- NULL, NULL,
- cancellable,
- &error);
-
- g_clear_object (&cancellable);
-
- if (error != NULL) {
- g_debug ("%s: Unable to get items: %s", G_STRFUNC, error->message);
- g_clear_error (&error);
-
- goto exit;
- }
-
- /* fetch modified occurrences */
- for (l = items; l != NULL; l = g_slist_next (l)) {
- EEwsItem *item = l->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 (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);
- }
-
- ret = ews_cal_sync_get_items_sync (
- cbews, modified_occurrences,
- "IdOnly",
- modified_add_props);
-
- e_ews_additional_props_free (modified_add_props);
-
- if (!ret)
- goto exit;
- }
- }
-
- e_cal_backend_store_freeze_changes (priv->store);
- for (l = items; l != NULL; l = g_slist_next (l)) {
- EEwsItem *item = (EEwsItem *) l->data;
-
- if (!item)
- continue;
-
- if (e_ews_item_get_item_type (item) != E_EWS_ITEM_TYPE_ERROR) {
- add_item_to_cache (cbews, item);
- ews_get_attachments (cbews, item);
- }
- }
- e_cal_backend_store_thaw_changes (priv->store);
- ret = TRUE;
-
-exit:
- g_slist_free_full (items, g_object_unref);
- return ret;
-}
-
-static gboolean
-cal_backend_ews_process_folder_items (ECalBackendEws *cbews,
- const gchar *sync_state,
- GSList *items_created,
- GSList *items_updated,
- GSList *items_deleted)
-{
- ECalBackendEwsPrivate *priv;
- GSList *l[2], *m, *cal_item_ids = NULL, *task_memo_item_ids = NULL;
- gint i;
- gboolean ret = FALSE;
-
- priv = cbews->priv;
-
- l[0] = items_created;
- l[1] = items_updated;
-
- for (i = 0; i < 2; i++) {
- for (; l[i] != NULL; l[i] = g_slist_next (l[i])) {
- EEwsItem *item = (EEwsItem *) l[i]->data;
- EEwsItemType type = e_ews_item_get_item_type (item);
- const EwsId *id;
-
- id = e_ews_item_get_id (item);
- if (type == E_EWS_ITEM_TYPE_EVENT)
- cal_item_ids = g_slist_prepend (cal_item_ids, id->id);
- else if (type == E_EWS_ITEM_TYPE_TASK || type == E_EWS_ITEM_TYPE_MEMO)
- task_memo_item_ids = g_slist_prepend (task_memo_item_ids, id->id);
- }
- }
-
- e_cal_backend_store_freeze_changes (priv->store);
- for (m = items_deleted; m != NULL; m = g_slist_next (m)) {
- gchar *item_id = (gchar *) m->data;
- ECalComponent *comp;
-
- PRIV_LOCK (priv);
- comp = g_hash_table_lookup (priv->item_id_hash, item_id);
- if (comp)
- g_object_ref (comp);
- PRIV_UNLOCK (priv);
-
- if (comp) {
- if (!ews_cal_delete_comp (cbews, comp, item_id)) {
- g_object_unref (comp);
- goto exit;
- }
-
- g_object_unref (comp);
- }
- }
- e_cal_backend_store_thaw_changes (priv->store);
-
-
- if (cal_item_ids) {
- EEwsAdditionalProps *add_props;
-
- add_props = e_ews_additional_props_new ();
- if (e_ews_connection_satisfies_server_version (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);
- }
-
- ews_cal_sync_get_items_sync (
- cbews,
- cal_item_ids,
- "IdOnly",
- add_props);
-
- e_ews_additional_props_free (add_props);
- }
-
- if (task_memo_item_ids) {
- ews_cal_sync_get_items_sync (
- cbews,
- task_memo_item_ids,
- "AllProperties",
- NULL);
- }
- ret = TRUE;
-
-exit:
- g_slist_free (cal_item_ids);
- g_slist_free (task_memo_item_ids);
- return ret;
-}
-
-static void
-cbews_forget_all_components (ECalBackendEws *cbews)
-{
- ECalBackend *backend;
- GSList *ids, *ii;
-
- g_return_if_fail (E_IS_CAL_BACKEND_EWS (cbews));
-
- backend = E_CAL_BACKEND (cbews);
- g_return_if_fail (backend != NULL);
-
- ids = e_cal_backend_store_get_component_ids (cbews->priv->store);
- for (ii = ids; ii; ii = ii->next) {
- ECalComponentId *id = ii->data;
-
- if (!id)
- continue;
-
- e_cal_backend_store_remove_component (cbews->priv->store, id->uid, id->rid);
- e_cal_backend_notify_component_removed (backend, id, NULL, NULL);
- }
-
- g_slist_free_full (ids, (GDestroyNotify) e_cal_component_free_id);
-}
-
-static gboolean
-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 gpointer
-ews_start_sync_thread (gpointer data)
-{
- ECalBackendEws *cbews;
- ECalBackendEwsPrivate *priv;
- GSList *items_created = NULL;
- GSList *items_updated = NULL;
- GSList *items_deleted = NULL;
- gboolean includes_last_item;
- gboolean ret;
- gchar *old_sync_state = NULL;
- gchar *new_sync_state = NULL;
- GCancellable *cancellable;
- GError *error = NULL;
-
- cbews = (ECalBackendEws *) data;
- priv = cbews->priv;
-
- cancellable = cal_backend_ews_ref_cancellable (cbews);
-
- if (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 (priv->cnc, G_PRIORITY_DEFAULT,
- e_ews_cal_utils_prepare_free_busy_request, &fbdata,
- &free_busy, cancellable, &error);
-
- if (success) {
- icaltimezone *utc_zone = icaltimezone_get_utc_timezone ();
- GSList *ids;
- GHashTable *known;
- GHashTableIter iter;
- gpointer key;
-
- known = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-
- ids = e_cal_backend_store_get_component_ids (priv->store);
- for (link = ids; link; link = g_slist_next (link)) {
- ECalComponentId *id = link->data;
-
- if (id && id->uid && *id->uid)
- g_hash_table_insert (known, g_strdup (id->uid), NULL);
- }
- g_slist_free_full (ids, (GDestroyNotify) e_cal_component_free_id);
-
- 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)) {
- 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.start.is_utc = 1;
- fb.end.zone = utc_zone;
- fb.end.is_utc = 1;
-
- 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);
-
- PRIV_LOCK (priv);
- if (g_hash_table_remove (known, icalcomponent_get_uid (vevent))) {
- ECalComponent *ecomp = g_hash_table_lookup
(priv->item_id_hash, icalcomponent_get_uid (vevent));
-
- g_object_ref (ecomp);
-
- PRIV_UNLOCK (priv);
-
- if (ews_freebusy_ecomp_changed (ecomp, vevent)) {
- ECalComponent *new_ecomp;
- gchar *uid = g_strdup (icalcomponent_get_uid
(vevent));
-
- new_ecomp = e_cal_component_new_from_icalcomponent
(vevent);
- if (new_ecomp) {
- PRIV_LOCK (priv);
- g_hash_table_insert (priv->item_id_hash, uid,
g_object_ref (new_ecomp));
- PRIV_UNLOCK (priv);
-
- put_component_to_store (cbews, new_ecomp);
- e_cal_backend_notify_component_modified
(E_CAL_BACKEND (cbews), ecomp, new_ecomp);
-
- g_object_unref (new_ecomp);
- } else {
- g_free (uid);
- }
- } else {
- icalcomponent_free (vevent);
- }
-
- g_clear_object (&ecomp);
- } else {
- ECalComponent *ecomp;
- gchar *uid = g_strdup (icalcomponent_get_uid (vevent));
-
- ecomp = e_cal_component_new_from_icalcomponent (vevent);
- if (ecomp)
- g_hash_table_insert (priv->item_id_hash, uid,
g_object_ref (ecomp));
- else
- g_free (uid);
-
- PRIV_UNLOCK (priv);
-
- if (ecomp) {
- put_component_to_store (cbews, ecomp);
- e_cal_backend_notify_component_created (E_CAL_BACKEND
(cbews), ecomp);
- }
-
- g_clear_object (&ecomp);
- }
- }
- }
+ fbdata.period_start = start;
+ fbdata.period_end = end;
+ fbdata.user_mails = (GSList *) users;
- g_hash_table_iter_init (&iter, known);
- while (g_hash_table_iter_next (&iter, &key, NULL)) {
- ECalComponentId id = { 0 };
+ 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);
- id.uid = key;
- id.rid = NULL;
+ if (success) {
+ GSList *fblink, *ulink;
- if (e_cal_backend_store_remove_component (priv->store, id.uid, id.rid)) {
- e_cal_backend_notify_component_removed (E_CAL_BACKEND (cbews), &id,
NULL, NULL);
+ for (fblink = freebusy, ulink = (GSList *) users;
+ fblink && ulink;
+ fblink = g_slist_next (fblink), ulink = g_slist_next (ulink)) {
+ icalcomponent *icalcomp = fblink->data;
+ gchar *mailto;
- PRIV_LOCK (priv);
- g_hash_table_remove (priv->item_id_hash, id.uid);
- PRIV_UNLOCK (priv);
- }
- }
+ /* add attendee property */
+ mailto = g_strconcat ("mailto:", ulink->data, NULL);
+ icalcomponent_add_property (icalcomp, icalproperty_new_attendee (mailto));
+ g_free (mailto);
- g_hash_table_destroy (known);
- } else if (g_error_matches (error, EWS_CONNECTION_ERROR,
EWS_CONNECTION_ERROR_NOFREEBUSYACCESS)) {
- cbews_forget_all_components (cbews);
- e_cal_backend_notify_error (E_CAL_BACKEND (cbews), error->message);
- g_clear_error (&error);
+ *freebusyobjs = g_slist_prepend (*freebusyobjs, icalcomponent_as_ical_string_r
(icalcomp));
}
- g_slist_free_full (free_busy, (GDestroyNotify) icalcomponent_free);
- g_slist_free_full (fbdata.user_mails, g_free);
- } else {
- old_sync_state = g_strdup (e_cal_backend_store_get_key_value (priv->store, SYNC_KEY));
- do {
- EEwsAdditionalProps *add_props;
- GCancellable *cancellable;
-
- includes_last_item = TRUE;
-
- add_props = e_ews_additional_props_new ();
- add_props->field_uri = g_strdup ("item:ItemClass");
-
- cancellable = cal_backend_ews_ref_cancellable (cbews);
-
- ret = e_ews_connection_sync_folder_items_sync (
- priv->cnc,
- EWS_PRIORITY_MEDIUM,
- old_sync_state,
- priv->folder_id,
- "IdOnly",
- add_props,
- EWS_MAX_FETCH_COUNT,
- &new_sync_state,
- &includes_last_item,
- &items_created,
- &items_updated,
- &items_deleted,
- cancellable,
- &error);
-
- e_ews_additional_props_free (add_props);
- g_clear_object (&cancellable);
- g_free (old_sync_state);
- old_sync_state = NULL;
-
- if (!ret) {
- if (g_error_matches (error, EWS_CONNECTION_ERROR,
EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) {
- g_clear_error (&error);
- e_cal_backend_store_put_key_value (priv->store, SYNC_KEY, NULL);
- cbews_forget_all_components (cbews);
-
- if (!e_ews_connection_sync_folder_items_sync (
- priv->cnc,
- EWS_PRIORITY_MEDIUM,
- NULL,
- priv->folder_id,
- "IdOnly",
- NULL,
- EWS_MAX_FETCH_COUNT,
- &new_sync_state,
- &includes_last_item,
- &items_created,
- &items_updated,
- &items_deleted,
- cancellable,
- &error)) {
- if (!g_error_matches (
- error,
- EWS_CONNECTION_ERROR,
- EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED)) {
- e_cal_backend_set_writable (E_CAL_BACKEND (cbews),
TRUE);
- break;
- }
- }
- } else {
- break;
- }
- }
-
- ret = cal_backend_ews_process_folder_items (
- cbews,
- new_sync_state,
- items_created,
- items_updated,
- items_deleted);
-
- if (!ret)
- break;
-
- g_slist_free_full (items_created, g_object_unref);
- g_slist_free_full (items_updated, g_object_unref);
- g_slist_free_full (items_deleted, g_free);
- items_created = NULL;
- items_updated = NULL;
- items_deleted = NULL;
-
- e_cal_backend_store_put_key_value (priv->store, SYNC_KEY, new_sync_state);
-
- old_sync_state = new_sync_state;
- new_sync_state = NULL;
- } while (!includes_last_item);
+ *freebusyobjs = g_slist_reverse (*freebusyobjs);
}
- ews_refreshing_dec (cbews);
-
- g_clear_object (&cancellable);
+ g_slist_free_full (freebusy, (GDestroyNotify) icalcomponent_free);
- g_slist_free_full (items_created, g_object_unref);
- g_slist_free_full (items_updated, g_object_unref);
- g_slist_free_full (items_deleted, g_free);
-
- if (error != NULL) {
- g_warning ("%s: %s", G_STRFUNC, error->message);
- g_clear_error (&error);
- }
-
- g_free (new_sync_state);
- g_free (old_sync_state);
-
- g_object_unref (cbews);
-
- return NULL;
+ ecb_ews_convert_error_to_edc_error (error);
}
-static gboolean
-ews_start_sync (gpointer data)
-{
- ECalBackendEws *cbews = data;
- GThread *thread;
-
- PRIV_LOCK (cbews->priv);
- if (cbews->priv->refreshing) {
- PRIV_UNLOCK (cbews->priv);
- return TRUE;
- }
-
- ews_refreshing_inc (cbews);
-
- if (!cbews->priv->cnc) {
- ews_refreshing_dec (cbews);
- PRIV_UNLOCK (cbews->priv);
- return FALSE;
- }
- PRIV_UNLOCK (cbews->priv);
-
- /* run the actual operation in thread,
- * to not block main thread of the factory */
- thread = g_thread_new (NULL, ews_start_sync_thread, g_object_ref (cbews));
- g_thread_unref (thread);
-
- return TRUE;
-}
-
-static void
-ews_cal_start_refreshing (ECalBackendEws *cbews)
-{
- ECalBackendEwsPrivate *priv;
-
- priv = cbews->priv;
-
- PRIV_LOCK (priv);
-
- if (!priv->refresh_timeout &&
- e_backend_get_online (E_BACKEND (cbews)) &&
- priv->cnc) {
- ews_start_sync (cbews);
- priv->refresh_timeout = e_named_timeout_add_seconds (
- REFRESH_INTERVAL, (GSourceFunc) ews_start_sync, cbews);
- }
-
- PRIV_UNLOCK (priv);
-}
-
-static void
-e_cal_backend_ews_start_query (ECalBackend *backend,
- EDataCalView *query)
-{
- ECalBackendEws *cbews;
- ECalBackendEwsPrivate *priv;
- GSList *components, *l;
- ECalBackendSExp *cbsexp;
- const gchar *sexp;
- gboolean search_needed = TRUE;
- time_t occur_start = -1, occur_end = -1;
- gboolean prunning_by_time;
- GError *err = NULL;
-
- cbews = E_CAL_BACKEND_EWS (backend);
- priv = cbews->priv;
-
- ews_cal_start_refreshing (cbews);
- cbsexp = e_data_cal_view_get_sexp (query);
- if (!cbsexp) {
- err = EDC_ERROR (InvalidQuery);
- e_data_cal_view_notify_complete (query, err);
- g_error_free (err);
- return;
- }
-
- sexp = e_cal_backend_sexp_text (cbsexp);
- if (!sexp || !strcmp (sexp, "#t"))
- search_needed = FALSE;
-
- prunning_by_time = e_cal_backend_sexp_evaluate_occur_times (
- cbsexp, &occur_start, &occur_end);
- components = prunning_by_time ?
- e_cal_backend_store_get_components_occuring_in_range (priv->store, occur_start, occur_end)
- : e_cal_backend_store_get_components (priv->store);
-
- for (l = components; l != NULL; l = l->next) {
- ECalComponent *comp = E_CAL_COMPONENT (l->data);
-
- if (e_cal_backend_get_kind (backend) ==
- icalcomponent_isa (e_cal_component_get_icalcomponent (comp))) {
- if ((!search_needed) ||
- (e_cal_backend_sexp_match_comp (cbsexp, comp, E_TIMEZONE_CACHE (backend)))) {
- e_data_cal_view_notify_components_added_1 (query, comp);
- }
- }
- }
-
- g_slist_free_full (components, g_object_unref);
- e_data_cal_view_notify_complete (query, NULL);
-}
-
-static void
-e_cal_backend_ews_refresh (ECalBackend *backend,
- EDataCal *cal,
- guint32 context,
- GCancellable *cancellable)
+static gchar *
+ecb_ews_get_backend_property (ECalBackend *cal_backend,
+ const gchar *prop_name)
{
ECalBackendEws *cbews;
- ECalBackendEwsPrivate *priv;
- GError *error = NULL;
-
- cbews = E_CAL_BACKEND_EWS (backend);
- priv = cbews->priv;
-
- /* make sure we're not offline */
- if (!e_backend_get_online (E_BACKEND (backend))) {
- g_propagate_error (&error, EDC_ERROR (RepositoryOffline));
- goto exit;
- }
-
- PRIV_LOCK (priv);
- ews_start_sync (cbews);
- PRIV_UNLOCK (priv);
-
-exit:
- convert_error_to_edc_error (&error);
- e_data_cal_respond_refresh (cal, context, error);
-}
-
-static void
-ews_cal_get_free_busy_cb (GObject *obj,
- GAsyncResult *res,
- gpointer user_data)
-{
- EEwsConnection *cnc = (EEwsConnection *) obj;
- EwsCalendarAsyncData *free_busy_data = user_data;
- GSList *free_busy_sl = NULL, *i;
- GSList *free_busy = NULL, *j;
- GError *error = NULL;
-
- if (!e_ews_connection_get_free_busy_finish (cnc, res, &free_busy_sl, &error)) {
- goto done;
- }
-
- for (i = free_busy_sl, j = free_busy_data->users; i && j; i = i->next, j = j->next) {
- /* add attendee property */
- icalcomponent_add_property ((icalcomponent *) i->data, icalproperty_new_attendee (j->data));
- free_busy = g_slist_append (free_busy, icalcomponent_as_ical_string_r (i->data));
- }
- g_slist_free (free_busy_sl);
-
-done:
- if (free_busy)
- e_data_cal_report_free_busy_data (free_busy_data->cal, free_busy);
- convert_error_to_edc_error (&error);
- e_data_cal_respond_get_free_busy (free_busy_data->cal, free_busy_data->context, error, free_busy);
-
- g_slist_free_full (free_busy, g_free);
- e_cal_backend_ews_async_data_free (free_busy_data);
-}
-
-static void
-e_cal_backend_ews_get_free_busy (ECalBackend *backend,
- EDataCal *cal,
- guint32 context,
- GCancellable *cancellable,
- const GSList *users,
- time_t start,
- time_t end)
-{
- ECalBackendEws *cbews = E_CAL_BACKEND_EWS (backend);
- ECalBackendEwsPrivate *priv = cbews->priv;
- GError *error = NULL;
- EwsCalendarAsyncData *free_busy_data;
- EEWSFreeBusyData fbdata = { 0 };
- GSList *users_copy = NULL;
-
- /* make sure we're not offline */
- if (!e_backend_get_online (E_BACKEND (backend)) || !cbews->priv->cnc) {
- g_propagate_error (&error, EDC_ERROR (RepositoryOffline));
- goto exit;
- }
-
- if (!cal_backend_ews_ensure_connected (cbews, cancellable, &error)) {
- goto exit;
- }
-
- /* 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));
- goto exit;
- }
-
- for (; users; users = users->next)
- users_copy = g_slist_append (users_copy, g_strdup (users->data));
-
- free_busy_data = g_new0 (EwsCalendarAsyncData, 1);
- free_busy_data->cbews = g_object_ref (cbews);
- free_busy_data->cal = g_object_ref (cal);
- free_busy_data->context = context;
- free_busy_data->users = users_copy;
-
- fbdata.period_start = start;
- fbdata.period_end = end;
- fbdata.user_mails = users_copy;
-
- e_ews_connection_get_free_busy (
- priv->cnc,
- EWS_PRIORITY_MEDIUM,
- e_ews_cal_utils_prepare_free_busy_request,
- &fbdata,
- cancellable,
- ews_cal_get_free_busy_cb,
- free_busy_data);
-
- return;
-
-exit:
- convert_error_to_edc_error (&error);
- e_data_cal_respond_get_free_busy (cal, context, error, NULL);
-
-}
-
-static gchar *
-e_cal_backend_ews_get_backend_property (ECalBackend *backend,
- const gchar *prop_name)
-{
+ 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_REFRESH_SUPPORTED,
CAL_STATIC_CAPABILITY_NO_THISANDPRIOR,
CAL_STATIC_CAPABILITY_NO_THISANDFUTURE,
CAL_STATIC_CAPABILITY_NO_CONV_TO_ASSIGN_TASK,
@@ -4459,78 +3485,60 @@ e_cal_backend_ews_get_backend_property (ECalBackend *backend,
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 */
- ECalBackendEws *cbews;
+ CamelEwsSettings *ews_settings;
- cbews = E_CAL_BACKEND_EWS (backend);
+ ews_settings = ecb_ews_get_collection_settings (cbews);
- return g_strdup (cbews->priv->user_email);
+ 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;
- } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_DEFAULT_OBJECT)) {
- ECalComponent *comp;
- gchar *prop_value;
-
- comp = e_cal_component_new ();
-
- switch (e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
- case ICAL_VEVENT_COMPONENT:
- e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
- break;
- case ICAL_VTODO_COMPONENT:
- e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO);
- break;
- case ICAL_VJOURNAL_COMPONENT:
- e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_JOURNAL);
- break;
- default:
- g_object_unref (comp);
- return NULL;
- }
-
- prop_value = e_cal_component_get_as_string (comp);
-
- g_object_unref (comp);
-
- return prop_value;
}
- /* Chain up to parent's get_backend_property() method. */
- return E_CAL_BACKEND_CLASS (e_cal_backend_ews_parent_class)->
- get_backend_property (backend, prop_name);
+ /* 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
-e_cal_backend_ews_notify_online_cb (EBackend *backend,
- GParamSpec *spec)
+ecb_ews_get_timezone_sync (ECalBackendSync *sync_backend,
+ EDataCal *cal,
+ GCancellable *cancellable,
+ const gchar *tzid,
+ gchar **tzobject,
+ GError **error)
{
- ECalBackendEws *cbews;
- ECalBackendEwsPrivate *priv;
+ GError *local_error = NULL;
- cbews = E_CAL_BACKEND_EWS (backend);
- priv = cbews->priv;
+ g_return_if_fail (E_IS_CAL_BACKEND_EWS (sync_backend));
+ g_return_if_fail (tzid != NULL);
+ g_return_if_fail (tzobject != NULL);
- PRIV_LOCK (priv);
+ *tzobject = NULL;
- if (e_backend_get_online (backend)) {
- cal_backend_ews_set_cancellable (cbews, g_cancellable_new ());
- priv->read_only = FALSE;
- } else {
- switch_offline (cbews);
- }
+ 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);
- e_cal_backend_set_writable (E_CAL_BACKEND (backend), !priv->read_only);
+ 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);
+ }
- PRIV_UNLOCK (priv);
+ if (*tzobject)
+ g_clear_error (&local_error);
+ else if (local_error)
+ g_propagate_error (error, local_error);
}
static gboolean
-e_cal_backend_ews_get_destination_address (EBackend *backend,
- gchar **host,
- guint16 *port)
+ecb_ews_get_destination_address (EBackend *backend,
+ gchar **host,
+ guint16 *port)
{
CamelEwsSettings *ews_settings;
SoupURI *soup_uri;
@@ -4545,7 +3553,7 @@ e_cal_backend_ews_get_destination_address (EBackend *backend,
!e_backend_get_source (backend))
return FALSE;
- ews_settings = cal_backend_ews_get_collection_settings (E_CAL_BACKEND_EWS (backend));
+ 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);
@@ -4570,244 +3578,119 @@ e_cal_backend_ews_get_destination_address (EBackend *backend,
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
-e_cal_backend_ews_constructed (GObject *object)
+ecb_ews_constructed (GObject *object)
{
ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object);
- CamelEwsSettings *ews_settings;
+ 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);
- ews_settings = cal_backend_ews_get_collection_settings (cbews);
- g_warn_if_fail (cbews->priv->user_email == NULL);
- cbews->priv->user_email = camel_ews_settings_dup_email (ews_settings);
-}
-
-static void
-e_cal_backend_ews_dispose (GObject *object)
-{
- ECalBackendEws *cbews;
- ECalBackendEwsPrivate *priv;
- CamelEwsSettings *ews_settings;
+ cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbews));
+ g_return_if_fail (cal_cache != NULL);
- g_return_if_fail (object != NULL);
- g_return_if_fail (E_IS_CAL_BACKEND_EWS (object));
+ 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);
- cbews = E_CAL_BACKEND_EWS (object);
- priv = cbews->priv;
+ g_clear_object (&cal_cache);
- ews_settings = cal_backend_ews_get_collection_settings (cbews);
- g_signal_handlers_disconnect_by_func (ews_settings, cbews_listen_notifications_cb, cbews);
+ cbews->priv->attachments_dir = g_build_filename (cache_dirname, "attachments", NULL);
+ g_mkdir_with_parents (cbews->priv->attachments_dir, 0777);
- if (priv->refresh_timeout) {
- g_source_remove (priv->refresh_timeout);
- priv->refresh_timeout = 0;
- }
-
- cal_backend_ews_set_cancellable (cbews, NULL);
+ g_free (cache_dirname);
+}
- if (priv->cnc) {
- g_signal_handlers_disconnect_by_func (priv->cnc, cbews_server_notification_cb, object);
+static void
+ecb_ews_dispose (GObject *object)
+{
+ ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object);
- if (priv->listen_notifications) {
- if (priv->subscription_key != 0) {
- e_ews_connection_disable_notifications_sync (
- priv->cnc,
- priv->subscription_key);
- priv->subscription_key = 0;
- }
+ g_rec_mutex_lock (&cbews->priv->cnc_lock);
- priv->listen_notifications = FALSE;
- }
+ g_clear_object (&cbews->priv->cnc);
- g_object_unref (priv->cnc);
- priv->cnc = NULL;
- }
+ 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);
}
-/* Finalize handler for the file backend */
static void
-e_cal_backend_ews_finalize (GObject *object)
+ecb_ews_finalize (GObject *object)
{
- ECalBackendEws *cbews;
- ECalBackendEwsPrivate *priv;
-
- g_return_if_fail (object != NULL);
- g_return_if_fail (E_IS_CAL_BACKEND_EWS (object));
-
- cbews = E_CAL_BACKEND_EWS (object);
- priv = cbews->priv;
-
- /* Clean up */
- g_rec_mutex_clear (&priv->rec_mutex);
- g_mutex_clear (&priv->cancellable_lock);
-
- if (priv->store) {
- g_object_unref (priv->store);
- priv->store = NULL;
- }
-
- if (priv->folder_id) {
- g_free (priv->folder_id);
- priv->folder_id = NULL;
- }
-
- if (priv->user_email) {
- g_free (priv->user_email);
- priv->user_email = NULL;
- }
-
- if (priv->storage_path) {
- g_free (priv->storage_path);
- priv->storage_path = NULL;
- }
-
- if (priv->default_zone && priv->default_zone != icaltimezone_get_utc_timezone ()) {
- icaltimezone_free (priv->default_zone, 1);
- priv->default_zone = NULL;
- }
+ ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object);
- g_hash_table_destroy (priv->item_id_hash);
+ g_free (cbews->priv->folder_id);
+ g_free (cbews->priv->attachments_dir);
- if (priv->refreshing_done) {
- e_flag_free (priv->refreshing_done);
- priv->refreshing_done = NULL;
- }
+ 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 ESourceAuthenticationResult
-e_cal_backend_ews_authenticate_sync (EBackend *backend,
- const ENamedParameters *credentials,
- gchar **out_certificate_pem,
- GTlsCertificateFlags *out_certificate_errors,
- GCancellable *cancellable,
- GError **error)
+static void
+e_cal_backend_ews_init (ECalBackendEws *cbews)
{
- ECalBackendEws *cal_backend;
- EEwsConnection *connection;
- ESourceAuthenticationResult result;
- CamelEwsSettings *ews_settings;
- gchar *hosturl;
-
- cal_backend = E_CAL_BACKEND_EWS (backend);
- ews_settings = cal_backend_ews_get_collection_settings (cal_backend);
- hosturl = camel_ews_settings_dup_hosturl (ews_settings);
-
- connection = e_ews_connection_new (hosturl, ews_settings);
-
- e_binding_bind_property (
- backend, "proxy-resolver",
- connection, "proxy-resolver",
- G_BINDING_SYNC_CREATE);
-
- result = e_ews_connection_try_credentials_sync (connection, credentials, cancellable, error);
-
- if (result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
-
- PRIV_LOCK (cal_backend->priv);
-
- g_free (cal_backend->priv->user_email);
- cal_backend->priv->user_email = camel_ews_settings_dup_email (ews_settings);
-
- if (cal_backend->priv->cnc != NULL)
- g_object_unref (cal_backend->priv->cnc);
- cal_backend->priv->cnc = g_object_ref (connection);
-
- g_signal_connect_swapped (
- cal_backend->priv->cnc,
- "server-notification",
- G_CALLBACK (cbews_server_notification_cb),
- backend);
-
- PRIV_UNLOCK (cal_backend->priv);
-
- ews_start_sync (cal_backend);
- cbews_listen_notifications_cb (cal_backend, NULL, ews_settings);
- } 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;
- }
-
- g_object_unref (connection);
+ cbews->priv = G_TYPE_INSTANCE_GET_PRIVATE (cbews, E_TYPE_CAL_BACKEND_EWS, ECalBackendEwsPrivate);
- g_free (hosturl);
+ g_rec_mutex_init (&cbews->priv->cnc_lock);
- return result;
+ e_cal_backend_ews_populate_windows_zones ();
}
static void
-e_cal_backend_ews_class_init (ECalBackendEwsClass *class)
+e_cal_backend_ews_class_init (ECalBackendEwsClass *klass)
{
GObjectClass *object_class;
EBackendClass *backend_class;
ECalBackendClass *cal_backend_class;
-
- g_type_class_add_private (class, sizeof (ECalBackendEwsPrivate));
-
- object_class = G_OBJECT_CLASS (class);
- backend_class = E_BACKEND_CLASS (class);
- cal_backend_class = E_CAL_BACKEND_CLASS (class);
-
- object_class->constructed = e_cal_backend_ews_constructed;
- object_class->dispose = e_cal_backend_ews_dispose;
- object_class->finalize = e_cal_backend_ews_finalize;
-
- backend_class->get_destination_address = e_cal_backend_ews_get_destination_address;
- backend_class->authenticate_sync = e_cal_backend_ews_authenticate_sync;
-
- /* Property accessors */
- cal_backend_class->get_backend_property = e_cal_backend_ews_get_backend_property;
-
- cal_backend_class->start_view = e_cal_backend_ews_start_query;
-
- /* Many of these can be moved to Base class */
- cal_backend_class->add_timezone = e_cal_backend_ews_add_timezone;
- cal_backend_class->get_timezone = e_cal_backend_ews_get_timezone;
-
- cal_backend_class->open = e_cal_backend_ews_open;
- cal_backend_class->refresh = e_cal_backend_ews_refresh;
- cal_backend_class->get_object = e_cal_backend_ews_get_object;
- cal_backend_class->get_object_list = e_cal_backend_ews_get_object_list;
-
- cal_backend_class->discard_alarm = e_cal_backend_ews_discard_alarm;
-
- cal_backend_class->create_objects = e_cal_backend_ews_create_objects;
- cal_backend_class->modify_objects = e_cal_backend_ews_modify_objects;
- cal_backend_class->remove_objects = e_cal_backend_ews_remove_objects;
-
- cal_backend_class->receive_objects = e_cal_backend_ews_receive_objects;
- cal_backend_class->send_objects = e_cal_backend_ews_send_objects;
- cal_backend_class->get_free_busy = e_cal_backend_ews_get_free_busy;
-}
-
-static void
-e_cal_backend_ews_init (ECalBackendEws *cbews)
-{
- cbews->priv = G_TYPE_INSTANCE_GET_PRIVATE (cbews, E_TYPE_CAL_BACKEND_EWS, ECalBackendEwsPrivate);
-
- /* create the mutex for thread safety */
- g_rec_mutex_init (&cbews->priv->rec_mutex);
- g_mutex_init (&cbews->priv->cancellable_lock);
- cbews->priv->refreshing_done = e_flag_new ();
- cbews->priv->item_id_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
- cbews->priv->default_zone = icaltimezone_get_utc_timezone ();
- cbews->priv->cancellable = g_cancellable_new ();
-
- g_signal_connect (
- cbews, "notify::online",
- G_CALLBACK (e_cal_backend_ews_notify_online_cb), NULL);
-
- e_cal_backend_ews_populate_windows_zones ();
+ 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.h b/src/calendar/e-cal-backend-ews.h
index 48480f3..4537eeb 100644
--- a/src/calendar/e-cal-backend-ews.h
+++ b/src/calendar/e-cal-backend-ews.h
@@ -35,31 +35,19 @@ G_BEGIN_DECLS
typedef struct _ECalBackendEws ECalBackendEws;
typedef struct _ECalBackendEwsClass ECalBackendEwsClass;
-
typedef struct _ECalBackendEwsPrivate ECalBackendEwsPrivate;
struct _ECalBackendEws {
- ECalBackend backend;
-
- /* Private data */
+ ECalMetaBackend parent_object;
ECalBackendEwsPrivate *priv;
};
struct _ECalBackendEwsClass {
- ECalBackendClass parent_class;
+ ECalMetaBackendClass parent_class;
};
GType e_cal_backend_ews_get_type (void);
-const EEwsConnection *
- e_cal_backend_ews_get_connection (ECalBackendEws *cbews);
-
-const icaltimezone *
- e_cal_backend_ews_get_default_zone (ECalBackendEws *cbews);
-
-const gchar *
- e_cal_backend_ews_get_user_email (ECalBackendEws *cbews);
-
G_END_DECLS
-#endif
+#endif /* E_CAL_BACKEND_EWS_H */
diff --git a/src/server/e-ews-connection.c b/src/server/e-ews-connection.c
index aa5f22e..48517ca 100644
--- a/src/server/e-ews-connection.c
+++ b/src/server/e-ews-connection.c
@@ -1973,6 +1973,7 @@ e_ews_attachment_info_free (EEwsAttachmentInfo *info)
}
g_free (info->prefer_filename);
+ g_free (info->id);
g_free (info);
}
@@ -2067,7 +2068,6 @@ e_ews_attachment_info_set_filename (EEwsAttachmentInfo *info,
info->data.inlined.filename = g_strdup (filename);
}
-
const gchar *
e_ews_attachment_info_get_uri (EEwsAttachmentInfo *info)
{
@@ -2088,6 +2088,26 @@ e_ews_attachment_info_set_uri (EEwsAttachmentInfo *info,
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 */
/**
@@ -3478,13 +3498,13 @@ ews_soup_got_chunk (SoupMessage *msg,
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 : %d ", errno);
+ "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 : %d ", errno);
+ "Failed to open the cache file '%s': %s", data->cache_filename, g_strerror (errno));
}
}
@@ -6983,9 +7003,7 @@ ews_handle_root_item_id_param (ESoapParameter *subparam,
if (attspara == NULL)
return;
- async_data->items = g_slist_append (
- async_data->items,
- e_soap_parameter_get_property (attspara, "RootItemChangeKey"));
+ async_data->sync_state = e_soap_parameter_get_property (attspara, "RootItemChangeKey");
}
static void
@@ -7085,7 +7103,7 @@ e_ews_connection_delete_attachments (EEwsConnection *cnc,
gboolean
e_ews_connection_delete_attachments_finish (EEwsConnection *cnc,
GAsyncResult *result,
- GSList **parents_ids,
+ gchar **new_change_key,
GError **error)
{
GSimpleAsyncResult *simple;
@@ -7103,10 +7121,10 @@ e_ews_connection_delete_attachments_finish (EEwsConnection *cnc,
if (g_simple_async_result_propagate_error (simple, error))
return FALSE;
- if (parents_ids)
- *parents_ids = async_data->items;
+ if (new_change_key)
+ *new_change_key = async_data->sync_state;
else
- g_slist_free_full (async_data->items, g_free);
+ g_free (async_data->sync_state);
return TRUE;
}
@@ -7115,7 +7133,7 @@ gboolean
e_ews_connection_delete_attachments_sync (EEwsConnection *cnc,
gint pri,
const GSList *attachments_ids,
- GSList **parents_ids,
+ gchar **new_change_key,
GCancellable *cancellable,
GError **error)
{
@@ -7134,7 +7152,7 @@ e_ews_connection_delete_attachments_sync (EEwsConnection *cnc,
result = e_async_closure_wait (closure);
ret = e_ews_connection_delete_attachments_finish (
- cnc, result, parents_ids, error);
+ cnc, result, new_change_key, error);
e_async_closure_free (closure);
diff --git a/src/server/e-ews-connection.h b/src/server/e-ews-connection.h
index 7d472dc..b596bc7 100644
--- a/src/server/e-ews-connection.h
+++ b/src/server/e-ews-connection.h
@@ -954,13 +954,13 @@ void e_ews_connection_delete_attachments
gboolean e_ews_connection_delete_attachments_finish
(EEwsConnection *cnc,
GAsyncResult *result,
- GSList **parents_ids,
+ gchar **new_change_key,
GError **error);
gboolean e_ews_connection_delete_attachments_sync
(EEwsConnection *cnc,
gint pri,
const GSList *attachments_ids,
- GSList **parents_ids,
+ gchar **new_change_key,
GCancellable *cancellable,
GError **error);
diff --git a/src/server/e-ews-item.h b/src/server/e-ews-item.h
index 832057a..88a540e 100644
--- a/src/server/e-ews-item.h
+++ b/src/server/e-ews-item.h
@@ -129,6 +129,7 @@ typedef struct {
gchar *uri;
} data;
gchar *prefer_filename;
+ gchar *id;
} EEwsAttachmentInfo;
typedef enum {
@@ -296,6 +297,10 @@ void e_ews_attachment_info_set_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);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]