[evolution-ews/wip/mcrha/office365: 34/50] Add user contacts (address book) read (get_changes())
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-ews/wip/mcrha/office365: 34/50] Add user contacts (address book) read (get_changes())
- Date: Mon, 3 Aug 2020 15:24:08 +0000 (UTC)
commit 104c8efaade29bd3c02620a7b00189c0284f8dbd
Author: Milan Crha <mcrha redhat com>
Date: Mon Jul 13 19:02:22 2020 +0200
Add user contacts (address book) read (get_changes())
src/Office365/addressbook/e-book-backend-o365.c | 1390 ++++++++++++++++++++++-
src/Office365/camel/camel-o365-folder.c | 6 +-
src/Office365/camel/camel-o365-store.c | 6 +-
src/Office365/camel/camel-o365-transport.c | 2 +-
src/Office365/common/e-o365-connection.c | 246 +++-
src/Office365/common/e-o365-connection.h | 27 +-
src/Office365/common/e-o365-json-utils.c | 678 ++++++++++-
src/Office365/common/e-o365-json-utils.h | 184 ++-
src/Office365/registry/e-o365-backend.c | 6 +-
9 files changed, 2427 insertions(+), 118 deletions(-)
---
diff --git a/src/Office365/addressbook/e-book-backend-o365.c b/src/Office365/addressbook/e-book-backend-o365.c
index d60003ba..bddd8d6a 100644
--- a/src/Office365/addressbook/e-book-backend-o365.c
+++ b/src/Office365/addressbook/e-book-backend-o365.c
@@ -17,6 +17,9 @@
#include "evolution-ews-config.h"
+#include <string.h>
+#include <time.h>
+
#include <glib.h>
#include <glib/gi18n-lib.h>
@@ -27,6 +30,8 @@
#include <libedata-book/libedata-book.h>
#include "common/camel-o365-settings.h"
+#include "common/e-o365-connection.h"
+#include "common/e-source-o365-folder.h"
#include "e-book-backend-o365.h"
@@ -39,20 +44,1159 @@
#define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
#endif
-#define d(x)
-
#define EC_ERROR_EX(_code,_msg) e_client_error_create (_code, _msg)
#define EBC_ERROR_EX(_code,_msg) e_book_client_error_create (_code, _msg)
#define EBB_O365_DATA_VERSION 1
#define EBB_O365_DATA_VERSION_KEY "o365-data-version"
+#define LOCK(_bb) g_rec_mutex_lock (&_bb->priv->property_lock)
+#define UNLOCK(_bb) g_rec_mutex_unlock (&_bb->priv->property_lock)
+
struct _EBookBackendO365Private {
GRecMutex property_lock;
+ EO365Connection *cnc;
+ gchar *folder_id;
};
G_DEFINE_TYPE_WITH_PRIVATE (EBookBackendO365, e_book_backend_o365, E_TYPE_BOOK_META_BACKEND)
+static void
+ebb_o365_contact_get_string_attribute (EO365Contact *o365_contact,
+ EContact *inout_contact,
+ EContactField field_id,
+ const gchar * (*o365_get_func) (EO365Contact *contact))
+{
+ e_contact_set (inout_contact, field_id, o365_get_func (o365_contact));
+}
+
+static void
+ebb_o365_contact_add_string_attribute (EContact *new_contact,
+ EContact *old_contact,
+ EContactField field_id,
+ JsonBuilder *builder,
+ void (* o365_add_func) (JsonBuilder *builder,
+ const gchar *value))
+{
+ const gchar *new_value, *old_value;
+
+ g_return_if_fail (o365_add_func != NULL);
+
+ new_value = e_contact_get_const (new_contact, field_id);
+ old_value = old_contact ? e_contact_get_const (old_contact, field_id) : NULL;
+
+ if (g_strcmp0 (new_value, old_value) != 0)
+ o365_add_func (builder, new_value);
+}
+
+static gboolean
+ebb_o365_contact_get_rev (EBookBackendO365 *bbo365,
+ EO365Contact *o365_contact,
+ EContact *inout_contact,
+ EContactField field_id,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gchar time_string[100] = { 0 };
+ struct tm stm;
+ time_t value;
+
+ value = e_o365_contact_get_last_modified_date_time (o365_contact);
+
+ if (value <= (time_t) 0)
+ value = time (NULL);
+
+ gmtime_r (&value, &stm);
+ strftime (time_string, 100, "%Y-%m-%dT%H:%M:%SZ", &stm);
+
+ e_contact_set (inout_contact, field_id, time_string);
+
+ return TRUE;
+}
+
+static gboolean
+ebb_o365_contact_get_birthday (EBookBackendO365 *bbo365,
+ EO365Contact *o365_contact,
+ EContact *inout_contact,
+ EContactField field_id,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ time_t value;
+
+ value = e_o365_contact_get_birthday (o365_contact);
+
+ if (value > (time_t) 0) {
+ EContactDate dt;
+ struct tm stm;
+
+ gmtime_r (&value, &stm);
+
+ dt.year = stm.tm_year + 1900;
+ dt.month = stm.tm_mon + 1;
+ dt.day = stm.tm_mday;
+
+ e_contact_set (inout_contact, field_id, &dt);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+ebb_o365_contact_add_birthday (EBookBackendO365 *bbo365,
+ EContact *new_contact,
+ EContact *old_contact,
+ EContactField field_id,
+ JsonBuilder *builder,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EContactDate *old_dt = NULL;
+ EContactDate *new_dt = NULL;
+
+ new_dt = e_contact_get (new_contact, field_id);
+ old_dt = old_contact ? e_contact_get (old_contact, field_id) : NULL;
+
+ if (!e_contact_date_equal (new_dt, old_dt)) {
+ if (new_dt) {
+ GDateTime *gdt;
+
+ gdt = g_date_time_new_local (new_dt->year, new_dt->month, new_dt->day, 0, 0, 0.0);
+
+ if (gdt) {
+ e_o365_contact_add_birthday (builder, g_date_time_to_unix (gdt));
+
+ g_date_time_unref (gdt);
+ } else {
+ e_o365_contact_add_birthday (builder, (time_t) 0);
+ }
+ } else {
+ e_o365_contact_add_birthday (builder, (time_t) 0);
+ }
+ }
+
+ e_contact_date_free (new_dt);
+ e_contact_date_free (old_dt);
+
+ return TRUE;
+}
+
+static gboolean
+ebb_o365_contact_get_address (EBookBackendO365 *bbo365,
+ EO365Contact *o365_contact,
+ EContact *inout_contact,
+ EContactField field_id,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EO365PhysicalAddress *phys_address = NULL;
+
+ if (field_id == E_CONTACT_ADDRESS_WORK)
+ phys_address = e_o365_contact_get_business_address (o365_contact);
+ else if (field_id == E_CONTACT_ADDRESS_HOME)
+ phys_address = e_o365_contact_get_home_address (o365_contact);
+ else if (field_id == E_CONTACT_ADDRESS_OTHER)
+ phys_address = e_o365_contact_get_other_address (o365_contact);
+ else
+ g_warning ("%s: Uncaught field '%s'", G_STRFUNC, e_contact_field_name (field_id));
+
+ if (phys_address) {
+ EContactAddress addr;
+
+ memset (&addr, 0, sizeof (EContactAddress));
+
+ addr.locality = (gchar *) e_o365_physical_address_get_city (phys_address);
+ addr.country = (gchar *) e_o365_physical_address_get_country_or_region (phys_address);
+ addr.code = (gchar *) e_o365_physical_address_get_postal_code (phys_address);
+ addr.region = (gchar *) e_o365_physical_address_get_state (phys_address);
+ addr.street = (gchar *) e_o365_physical_address_get_street (phys_address);
+
+ if (addr.locality || addr.country || addr.code || addr.region || addr.street)
+ e_contact_set (inout_contact, field_id, &addr);
+ else
+ e_contact_set (inout_contact, field_id, NULL);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+ebb_o365_contact_address_equal (const EContactAddress *addr1,
+ const EContactAddress *addr2)
+{
+ if (!addr1 && !addr2)
+ return TRUE;
+
+ if ((addr1 && !addr2) || (!addr1 && addr2))
+ return FALSE;
+
+ return /* g_strcmp0 (addr1->address_format, addr2->address_format) == 0 && */
+ g_strcmp0 (addr1->po, addr2->po) == 0 &&
+ g_strcmp0 (addr1->ext, addr2->ext) == 0 &&
+ g_strcmp0 (addr1->street, addr2->street) == 0 &&
+ g_strcmp0 (addr1->locality, addr2->locality) == 0 &&
+ g_strcmp0 (addr1->region, addr2->region) == 0 &&
+ g_strcmp0 (addr1->code, addr2->code) == 0 &&
+ g_strcmp0 (addr1->country, addr2->country) == 0;
+}
+
+static gboolean
+ebb_o365_contact_add_address (EBookBackendO365 *bbo365,
+ EContact *new_contact,
+ EContact *old_contact,
+ EContactField field_id,
+ JsonBuilder *builder,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EContactAddress *new_addr, *old_addr;
+
+ new_addr = e_contact_get (new_contact, field_id);
+ old_addr = old_contact ? e_contact_get (old_contact, field_id) : NULL;
+
+ if (!ebb_o365_contact_address_equal (new_addr, old_addr)) {
+ void (* add_func) (JsonBuilder *builder,
+ const gchar *city,
+ const gchar *country_or_region,
+ const gchar *postal_code,
+ const gchar *state,
+ const gchar *street) = NULL;
+
+ if (field_id == E_CONTACT_ADDRESS_WORK)
+ add_func = e_o365_contact_add_business_address;
+ else if (field_id == E_CONTACT_ADDRESS_HOME)
+ add_func = e_o365_contact_add_home_address;
+ else if (field_id == E_CONTACT_ADDRESS_OTHER)
+ add_func = e_o365_contact_add_other_address;
+ else
+ g_warning ("%s: Uncaught field '%s'", G_STRFUNC, e_contact_field_name (field_id));
+
+ if (add_func) {
+ if (new_addr) {
+ add_func (builder, new_addr->locality, new_addr->country, new_addr->code,
new_addr->region, new_addr->street);
+ } else {
+ add_func (builder, NULL, NULL, NULL, NULL, NULL);
+ }
+ }
+ }
+
+ e_contact_address_free (new_addr);
+ e_contact_address_free (old_addr);
+
+ return TRUE;
+}
+
+static gboolean
+ebb_o365_string_values_equal (GSList *new_values, /* const gchar * */
+ GSList *old_values) /* const gchar * */
+{
+ GHashTable *values;
+ GSList *link;
+ gboolean equal = TRUE;
+
+ if (g_slist_length (new_values) != g_slist_length (old_values))
+ return FALSE;
+
+ values = g_hash_table_new (g_str_hash, g_str_equal);
+
+ for (link = new_values; link; link = g_slist_next (link)) {
+ gchar *value = link->data;
+
+ if (value)
+ g_hash_table_add (values, value);
+ }
+
+ for (link = old_values; link && equal; link = g_slist_next (link)) {
+ const gchar *value = link->data;
+
+ if (value)
+ equal = g_hash_table_remove (values, value);
+ }
+
+ equal = equal && !g_hash_table_size (values);
+
+ g_hash_table_destroy (values);
+
+ return equal;
+}
+
+static gboolean
+ebb_o365_string_list_values_equal (GList *new_values, /* const gchar * */
+ GList *old_values) /* const gchar * */
+{
+ GHashTable *values;
+ GList *link;
+ gboolean equal = TRUE;
+
+ if (g_list_length (new_values) != g_list_length (old_values))
+ return FALSE;
+
+ values = g_hash_table_new (g_str_hash, g_str_equal);
+
+ for (link = new_values; link; link = g_list_next (link)) {
+ gchar *value = link->data;
+
+ if (value)
+ g_hash_table_add (values, value);
+ }
+
+ for (link = old_values; link && equal; link = g_list_next (link)) {
+ const gchar *value = link->data;
+
+ if (value)
+ equal = g_hash_table_remove (values, value);
+ }
+
+ equal = equal && !g_hash_table_size (values);
+
+ g_hash_table_destroy (values);
+
+ return equal;
+}
+
+static gboolean
+ebb_o365_contact_get_phone (EBookBackendO365 *bbo365,
+ EO365Contact *o365_contact,
+ EContact *inout_contact,
+ EContactField field_id,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ JsonArray *values = NULL;
+ const gchar *type_val = NULL;
+
+ if (field_id == E_CONTACT_PHONE_BUSINESS) {
+ values = e_o365_contact_get_business_phones (o365_contact);
+ type_val = "WORK";
+ } else if (field_id == E_CONTACT_PHONE_HOME) {
+ values = e_o365_contact_get_home_phones (o365_contact);
+ type_val = "HOME";
+ } else {
+ g_warning ("%s: Uncaught field '%s'", G_STRFUNC, e_contact_field_name (field_id));
+ }
+
+ if (values) {
+ EVCard *vcard = E_VCARD (inout_contact);
+ guint ii, len;
+
+ len = json_array_get_length (values);
+
+ for (ii = 0; ii < len; ii++) {
+ const gchar *str = json_array_get_string_element (values, ii);
+
+ if (str && *str) {
+ EVCardAttributeParam *param;
+ EVCardAttribute *attr;
+
+ attr = e_vcard_attribute_new (NULL, EVC_TEL);
+ param = e_vcard_attribute_param_new (EVC_TYPE);
+
+ e_vcard_attribute_add_param_with_value (attr, param, type_val);
+ e_vcard_add_attribute_with_value (vcard, attr, str);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static GSList * /* gchar * */
+ebb_o365_extract_phones (EContact *contact,
+ const gchar *only_type) /* NULL for anything but known types */
+{
+ GSList *phones = NULL;
+ GList *attrs, *link;
+
+ if (!contact)
+ return NULL;
+
+ attrs = e_vcard_get_attributes (E_VCARD (contact));
+
+ for (link = attrs; link; link = g_list_next (link)) {
+ EVCardAttribute *attr = link->data;
+ gboolean use_it = FALSE;
+
+ if (!attr || !e_vcard_attribute_get_name (attr) ||
+ g_ascii_strcasecmp (e_vcard_attribute_get_name (attr), EVC_TEL) != 0)
+ continue;
+
+ if (only_type) {
+ use_it = e_vcard_attribute_has_type (attr, only_type);
+ } else {
+ use_it = !e_vcard_attribute_has_type (attr, "WORK");
+ }
+
+ if (use_it)
+ phones = g_slist_prepend (phones, e_vcard_attribute_get_value (attr));
+ }
+
+ return g_slist_reverse (phones);
+}
+
+static gboolean
+ebb_o365_contact_add_phone (EBookBackendO365 *bbo365,
+ EContact *new_contact,
+ EContact *old_contact,
+ EContactField field_id,
+ JsonBuilder *builder,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ void (* begin_func) (JsonBuilder *builder) = NULL;
+ void (* end_func) (JsonBuilder *builder) = NULL;
+ void (* add_func) (JsonBuilder *builder, const gchar *value) = NULL;
+ const gchar *type_val = NULL;
+ GSList *new_values, *old_values;
+
+ if (field_id == E_CONTACT_PHONE_BUSINESS) {
+ begin_func = e_o365_contact_begin_business_phones;
+ end_func = e_o365_contact_end_business_phones;
+ add_func = e_o365_contact_add_business_phone;
+ type_val = "WORK";
+ } else if (field_id == E_CONTACT_PHONE_HOME) {
+ begin_func = e_o365_contact_begin_home_phones;
+ end_func = e_o365_contact_end_home_phones;
+ add_func = e_o365_contact_add_home_phone;
+ type_val = NULL; /* everythign else is treated as "HOME" phone */
+ } else {
+ g_warning ("%s: Uncaught field '%s'", G_STRFUNC, e_contact_field_name (field_id));
+ }
+
+ new_values = ebb_o365_extract_phones (new_contact, type_val);
+ old_values = ebb_o365_extract_phones (old_contact, type_val);
+
+ if (!ebb_o365_string_values_equal (new_values, old_values)) {
+ GSList *link;
+
+ begin_func (builder);
+
+ for (link = new_values; link; link = g_slist_next (link)) {
+ const gchar *value = link->data;
+
+ add_func (builder, value);
+ }
+
+ end_func (builder);
+ }
+
+ g_slist_free_full (new_values, g_free);
+ g_slist_free_full (old_values, g_free);
+
+ return TRUE;
+}
+
+static gboolean
+ebb_o365_contact_get_categories (EBookBackendO365 *bbo365,
+ EO365Contact *o365_contact,
+ EContact *inout_contact,
+ EContactField field_id,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ JsonArray *values;
+
+ values = e_o365_contact_get_categories (o365_contact);
+
+ if (values) {
+ GString *categories_str = NULL;
+ guint ii, len;
+
+ len = json_array_get_length (values);
+
+ for (ii = 0; ii < len; ii++) {
+ const gchar *str = json_array_get_string_element (values, ii);
+
+ if (str && *str) {
+ if (!categories_str) {
+ categories_str = g_string_new (str);
+ } else {
+ g_string_append_c (categories_str, ',');
+ g_string_append (categories_str, str);
+ }
+ }
+ }
+
+ if (categories_str) {
+ e_contact_set (inout_contact, field_id, categories_str->str);
+ g_string_free (categories_str, TRUE);
+ }
+ }
+
+ return TRUE;
+}
+
+static GSList *
+ebb_o365_extract_categories (EContact *contact,
+ EContactField field_id)
+{
+ GSList *categories = NULL;
+ const gchar *str;
+
+ if (!contact)
+ return NULL;
+
+ str = e_contact_get_const (contact, field_id);
+
+ if (str && *str) {
+ gchar **split_str;
+ gint ii;
+
+ split_str = g_strsplit (str, ",", -1);
+
+ for (ii = 0; split_str && split_str[ii]; ii++) {
+ gchar *item = split_str[ii];
+
+ if (item && *item)
+ categories = g_slist_prepend (categories, item);
+ else
+ g_free (item);
+
+ split_str[ii] = NULL;
+ }
+
+ g_free (split_str);
+ }
+
+ return g_slist_reverse (categories);
+}
+
+static gboolean
+ebb_o365_contact_add_categories (EBookBackendO365 *bbo365,
+ EContact *new_contact,
+ EContact *old_contact,
+ EContactField field_id,
+ JsonBuilder *builder,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GSList *new_values, *old_values;
+
+ new_values = ebb_o365_extract_categories (new_contact, field_id);
+ old_values = ebb_o365_extract_categories (old_contact, field_id);
+
+ if (!ebb_o365_string_values_equal (new_values, old_values)) {
+ GSList *link;
+
+ e_o365_contact_begin_categories (builder);
+
+ for (link = new_values; link; link = g_slist_next (link)) {
+ const gchar *value = link->data;
+
+ e_o365_contact_add_category (builder, value);
+ }
+
+ e_o365_contact_end_categories (builder);
+ }
+
+ g_slist_free_full (new_values, g_free);
+ g_slist_free_full (old_values, g_free);
+
+ return TRUE;
+}
+
+static gboolean
+ebb_o365_contact_get_emails (EBookBackendO365 *bbo365,
+ EO365Contact *o365_contact,
+ EContact *inout_contact,
+ EContactField field_id,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ JsonArray *values;
+
+ values = e_o365_contact_get_categories (o365_contact);
+
+ if (values) {
+ EVCard *vcard = E_VCARD (inout_contact);
+ guint ii, len;
+
+ len = json_array_get_length (values);
+
+ for (ii = 0; ii < len; ii++) {
+ const gchar *str = json_array_get_string_element (values, ii);
+
+ if (str && *str) {
+ EVCardAttribute *attr;
+
+ attr = e_vcard_attribute_new (NULL, EVC_EMAIL);
+
+ e_vcard_add_attribute_with_value (vcard, attr, str);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+ebb_o365_parse_qp_email (const gchar *string,
+ gchar **name,
+ gchar **email)
+{
+ struct _camel_header_address *address;
+ gboolean res = FALSE;
+
+ address = camel_header_address_decode (string, "UTF-8");
+
+ if (address) {
+ /* report success only when we have filled both name and email address */
+ if (address->type == CAMEL_HEADER_ADDRESS_NAME && address->name && *address->name &&
address->v.addr && *address->v.addr) {
+ *name = g_strdup (address->name);
+ *email = g_strdup (address->v.addr);
+ res = TRUE;
+ }
+
+ camel_header_address_unref (address);
+ }
+
+ if (!res) {
+ CamelInternetAddress *addr = camel_internet_address_new ();
+ const gchar *const_name = NULL, *const_email = NULL;
+
+ if (camel_address_unformat (CAMEL_ADDRESS (addr), string) == 1 &&
+ camel_internet_address_get (addr, 0, &const_name, &const_email) &&
+ const_name && *const_name && const_email && *const_email) {
+ *name = g_strdup (const_name);
+ *email = g_strdup (const_email);
+ res = TRUE;
+ }
+
+ g_clear_object (&addr);
+ }
+
+ return res;
+}
+
+static gboolean
+ebb_o365_contact_add_emails (EBookBackendO365 *bbo365,
+ EContact *new_contact,
+ EContact *old_contact,
+ EContactField field_id,
+ JsonBuilder *builder,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GList *new_values, *old_values;
+
+ new_values = e_contact_get (new_contact, field_id);
+ old_values = old_contact ? e_contact_get (old_contact, field_id) : NULL;
+
+ if (!ebb_o365_string_list_values_equal (new_values, old_values)) {
+ GList *link;
+
+ e_o365_contact_begin_email_addresses (builder);
+
+ for (link = new_values; link; link = g_list_next (link)) {
+ const gchar *value = link->data;
+ gchar *name = NULL, *address = NULL;
+
+ if (ebb_o365_parse_qp_email (value, &name, &address))
+ e_o365_add_email_address (builder, name, address);
+
+ g_free (name);
+ g_free (address);
+ }
+
+ e_o365_contact_end_email_addresses (builder);
+ }
+
+ g_list_free_full (new_values, g_free);
+ g_list_free_full (old_values, g_free);
+
+ return TRUE;
+}
+
+static gboolean
+ebb_o365_contact_add_file_as (EBookBackendO365 *bbo365,
+ EContact *new_contact,
+ EContact *old_contact,
+ EContactField field_id,
+ JsonBuilder *builder,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const gchar *new_value;
+
+ ebb_o365_contact_add_string_attribute (new_contact, old_contact, field_id, builder,
e_o365_contact_add_file_as);
+
+ new_value = e_contact_get_const (new_contact, E_CONTACT_FILE_AS);
+
+ /* Set it always, to not be overwritten by server re-calculations on other property changes */
+ e_o365_contact_add_display_name (builder, new_value);
+
+ return TRUE;
+}
+
+static gboolean
+ebb_o365_contact_get_im_addresses (EBookBackendO365 *bbo365,
+ EO365Contact *o365_contact,
+ EContact *inout_contact,
+ EContactField field_id,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ JsonArray *values;
+
+ values = e_o365_contact_get_im_addresses (o365_contact);
+
+ if (values) {
+ EVCard *vcard = E_VCARD (inout_contact);
+ const gchar *field_name = e_contact_field_name (field_id);
+ guint ii, len;
+
+ len = json_array_get_length (values);
+
+ for (ii = 0; ii < len; ii++) {
+ const gchar *str = json_array_get_string_element (values, ii);
+
+ if (str && *str) {
+ EVCardAttribute *attr;
+
+ attr = e_vcard_attribute_new (NULL, field_name);
+
+ e_vcard_add_attribute_with_value (vcard, attr, str);
+ }
+ }
+ }
+
+ return TRUE;
+}
+
+static GSList * /* gchar * */
+ebb_o365_extract_im_addresses (EContact *contact)
+{
+ GSList *ims = NULL;
+ GList *attrs, *link;
+
+ if (!contact)
+ return NULL;
+
+ attrs = e_vcard_get_attributes (E_VCARD (contact));
+
+ for (link = attrs; link; link = g_list_next (link)) {
+ EVCardAttribute *attr = link->data;
+ const gchar *name;
+
+ if (!attr)
+ continue;
+
+ name = e_vcard_attribute_get_name (attr);
+
+ if (!name || (
+ g_ascii_strcasecmp (name, EVC_X_GOOGLE_TALK) != 0 &&
+ g_ascii_strcasecmp (name, EVC_X_SKYPE) != 0 &&
+ g_ascii_strcasecmp (name, EVC_X_GADUGADU) != 0 &&
+ g_ascii_strcasecmp (name, EVC_X_AIM) != 0 &&
+ g_ascii_strcasecmp (name, EVC_X_GROUPWISE) != 0 &&
+ g_ascii_strcasecmp (name, EVC_X_JABBER) != 0 &&
+ g_ascii_strcasecmp (name, EVC_X_YAHOO) != 0 &&
+ g_ascii_strcasecmp (name, EVC_X_MSN) != 0 &&
+ g_ascii_strcasecmp (name, EVC_X_ICQ) != 0))
+ continue;
+
+ ims = g_slist_prepend (ims, e_vcard_attribute_get_value (attr));
+ }
+
+ return g_slist_reverse (ims);
+}
+
+static gboolean
+ebb_o365_contact_add_im_addresses (EBookBackendO365 *bbo365,
+ EContact *new_contact,
+ EContact *old_contact,
+ EContactField field_id,
+ JsonBuilder *builder,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GSList *new_values, *old_values;
+
+ new_values = ebb_o365_extract_im_addresses (new_contact);
+ old_values = ebb_o365_extract_im_addresses (old_contact);
+
+ if (!ebb_o365_string_values_equal (new_values, old_values)) {
+ GSList *link;
+
+ e_o365_contact_begin_im_addresses (builder);
+
+ for (link = new_values; link; link = g_slist_next (link)) {
+ const gchar *value = link->data;
+
+ if (value && *value)
+ e_o365_contact_add_im_address (builder, value);
+ }
+
+ e_o365_contact_end_im_addresses (builder);
+ }
+
+ g_slist_free_full (new_values, g_free);
+ g_slist_free_full (old_values, g_free);
+
+ return TRUE;
+}
+
+static gboolean
+ebb_o365_contact_get_middle_name (EBookBackendO365 *bbo365,
+ EO365Contact *o365_contact,
+ EContact *inout_contact,
+ EContactField field_id,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const gchar *value;
+
+ value = e_o365_contact_get_middle_name (o365_contact);
+
+ if (value && *value) {
+ EContactName *name = e_contact_get (inout_contact, field_id);
+ gchar *prev;
+
+ if (!name)
+ name = e_contact_name_new ();
+
+ prev = name->additional;
+ name->additional = (gchar *) value;
+
+ e_contact_set (inout_contact, field_id, name);
+
+ name->additional = prev;
+ e_contact_name_free (name);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+ebb_o365_contact_add_middle_name (EBookBackendO365 *bbo365,
+ EContact *new_contact,
+ EContact *old_contact,
+ EContactField field_id,
+ JsonBuilder *builder,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EContactName *new_value, *old_value;
+
+ new_value = e_contact_get (new_contact, field_id);
+ old_value = old_contact ? e_contact_get (old_contact, field_id) : NULL;
+
+ if (!(new_value && old_value && g_strcmp0 (new_value->additional, old_value->additional) == 0))
+ e_o365_contact_add_middle_name (builder, new_value ? new_value->additional : NULL);
+
+ e_contact_name_free (new_value);
+ e_contact_name_free (old_value);
+
+ return TRUE;
+}
+
+static gboolean
+ebb_o365_contact_get_title (EBookBackendO365 *bbo365,
+ EO365Contact *o365_contact,
+ EContact *inout_contact,
+ EContactField field_id,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ const gchar *value;
+
+ value = e_o365_contact_get_middle_name (o365_contact);
+
+ if (value && *value) {
+ EContactName *name = e_contact_get (inout_contact, field_id);
+ gchar *prev;
+
+ if (!name)
+ name = e_contact_name_new ();
+
+ prev = name->prefixes;
+ name->prefixes = (gchar *) value;
+
+ e_contact_set (inout_contact, field_id, name);
+
+ name->additional = prev;
+ e_contact_name_free (name);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+ebb_o365_contact_add_title (EBookBackendO365 *bbo365,
+ EContact *new_contact,
+ EContact *old_contact,
+ EContactField field_id,
+ JsonBuilder *builder,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EContactName *new_value, *old_value;
+
+ new_value = e_contact_get (new_contact, field_id);
+ old_value = old_contact ? e_contact_get (old_contact, field_id) : NULL;
+
+ if (!(new_value && old_value && g_strcmp0 (new_value->prefixes, old_value->prefixes) == 0))
+ e_o365_contact_add_middle_name (builder, new_value ? new_value->prefixes : NULL);
+
+ e_contact_name_free (new_value);
+ e_contact_name_free (old_value);
+
+ return TRUE;
+}
+
+static gboolean
+ebb_o365_contact_get_photo (EBookBackendO365 *bbo365,
+ EO365Contact *o365_contact,
+ EContact *inout_contact,
+ EContactField field_id,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GByteArray *photo_data = NULL;
+ GError *local_error = NULL;
+
+ LOCK (bbo365);
+
+ if (e_o365_connection_get_contact_photo_sync (cnc, NULL, bbo365->priv->folder_id,
+ e_o365_contact_get_id (o365_contact), &photo_data, cancellable, &local_error) &&
+ photo_data && photo_data->len) {
+ EContactPhoto *photo;
+
+ photo = e_contact_photo_new ();
+ e_contact_photo_set_inlined (photo, photo_data->data, photo_data->len);
+ e_contact_photo_set_mime_type (photo, "image/jpeg");
+ e_contact_set (inout_contact, field_id, photo);
+ e_contact_photo_free (photo);
+ }
+
+ UNLOCK (bbo365);
+
+ if (photo_data)
+ g_byte_array_unref (photo_data);
+ g_clear_error (&local_error);
+
+ /* Even it could fail, ignore it and read as many contacts as possible, rather than stop on the first
error */
+ return TRUE;
+}
+
+static gboolean
+ebb_o365_contact_photo_equal (EContactPhoto *photo1,
+ EContactPhoto *photo2)
+{
+ const guchar *data1, *data2;
+ gsize len1 = 0, len2 = 0;
+
+ if (!photo1 && !photo2)
+ return TRUE;
+
+ if ((photo1 && !photo2) || (!photo1 && photo2))
+ return FALSE;
+
+ data1 = e_contact_photo_get_inlined (photo1, &len1);
+ data2 = e_contact_photo_get_inlined (photo2, &len2);
+
+ if (!data1 && !data2)
+ return TRUE;
+
+ return len1 == len2 &&
+ memcmp (data1, data2, len1) == 0;
+}
+
+static gboolean
+ebb_o365_contact_add_photo (EBookBackendO365 *bbo365,
+ EContact *new_contact,
+ EContact *old_contact,
+ EContactField field_id,
+ JsonBuilder *builder,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EContactPhoto *new_value, *old_value;
+
+ new_value = e_contact_get (new_contact, field_id);
+ old_value = old_contact ? e_contact_get (old_contact, field_id) : NULL;
+
+ if (!ebb_o365_contact_photo_equal (new_value, old_value)) {
+ GByteArray *jpeg_photo = NULL, tmp;
+ GError *local_error = NULL;
+
+ if (new_value) {
+ gsize len = 0;
+
+ tmp.data = (guchar *) e_contact_photo_get_inlined (new_value, &len);
+
+ if (len && tmp.data) {
+ tmp.len = len;
+ jpeg_photo = &tmp;
+ }
+ }
+
+ LOCK (bbo365);
+
+ if (!e_o365_connection_update_contact_photo_sync (cnc, NULL, bbo365->priv->folder_id,
+ e_contact_get_const (new_contact, E_CONTACT_UID), jpeg_photo, cancellable,
&local_error)) {
+ g_warning ("%s: Failed to store photo for '%s': %s", G_STRFUNC, (const gchar *)
e_contact_get_const (new_contact, E_CONTACT_UID),
+ local_error ? local_error->message : "Unknown error");
+ }
+
+ UNLOCK (bbo365);
+
+ g_clear_error (&local_error);
+ }
+
+ e_contact_photo_free (new_value);
+ e_contact_photo_free (old_value);
+
+ return TRUE;
+}
+
+#define STRING_FIELD(fldid, getfn, addfn) { fldid, FALSE, getfn, NULL, addfn, NULL }
+#define COMPLEX_FIELD(fldid, getfn, addfn) { fldid, FALSE, NULL, getfn, NULL, addfn }
+#define COMPLEX_FIELD_2(fldid, getfn, addfn) { fldid, TRUE, NULL, getfn, NULL, addfn }
+#define COMPLEX_ADDFN(fldid, getfn, addfn) { fldid, FALSE, getfn, NULL, NULL, addfn }
+
+struct _mappings {
+ EContactField field_id;
+ gboolean add_in_second_go;
+ const gchar * (* o365_get_func) (EO365Contact *o365_contact);
+ gboolean (* get_func) (EBookBackendO365 *bbo365,
+ EO365Contact *o365_contact,
+ EContact *inout_contact,
+ EContactField field_id,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error);
+ void (* o365_add_func) (JsonBuilder *builder,
+ const gchar *value);
+ gboolean (* add_func) (EBookBackendO365 *bbo365,
+ EContact *new_contact,
+ EContact *old_contact, /* nullable */
+ EContactField field_id,
+ JsonBuilder *builder,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error);
+} mappings[] = {
+ STRING_FIELD (E_CONTACT_UID, e_o365_contact_get_id, NULL),
+ COMPLEX_FIELD (E_CONTACT_REV, ebb_o365_contact_get_rev, NULL),
+ STRING_FIELD (E_CONTACT_ASSISTANT, e_o365_contact_get_assistant_name,
e_o365_contact_add_assistant_name),
+ COMPLEX_FIELD (E_CONTACT_BIRTH_DATE, ebb_o365_contact_get_birthday,
ebb_o365_contact_add_birthday),
+ COMPLEX_FIELD (E_CONTACT_ADDRESS_WORK, ebb_o365_contact_get_address,
ebb_o365_contact_add_address),
+ STRING_FIELD (E_CONTACT_HOMEPAGE_URL, e_o365_contact_get_business_home_page,
e_o365_contact_add_business_home_page),
+ COMPLEX_FIELD (E_CONTACT_PHONE_BUSINESS, ebb_o365_contact_get_phone,
ebb_o365_contact_add_phone),
+ COMPLEX_FIELD (E_CONTACT_CATEGORIES, ebb_o365_contact_get_categories,
ebb_o365_contact_add_categories),
+ STRING_FIELD (E_CONTACT_ORG, e_o365_contact_get_company_name,
e_o365_contact_add_company_name),
+ STRING_FIELD (E_CONTACT_ORG_UNIT, e_o365_contact_get_department,
e_o365_contact_add_department),
+ COMPLEX_FIELD (E_CONTACT_EMAIL, ebb_o365_contact_get_emails,
ebb_o365_contact_add_emails),
+ COMPLEX_ADDFN (E_CONTACT_FILE_AS, e_o365_contact_get_file_as,
ebb_o365_contact_add_file_as),
+ /* STRING_FIELD (???, e_o365_contact_get_generation,
e_o365_contact_add_generation), */
+ STRING_FIELD (E_CONTACT_GIVEN_NAME, e_o365_contact_get_given_name,
e_o365_contact_add_given_name),
+ COMPLEX_FIELD (E_CONTACT_ADDRESS_HOME, ebb_o365_contact_get_address,
ebb_o365_contact_add_address),
+ COMPLEX_FIELD (E_CONTACT_PHONE_HOME, ebb_o365_contact_get_phone,
ebb_o365_contact_add_phone),
+ COMPLEX_FIELD (E_CONTACT_IM_MSN, ebb_o365_contact_get_im_addresses,
ebb_o365_contact_add_im_addresses),
+ /* STRING_FIELD (???, e_o365_contact_get_initials,
e_o365_contact_add_initials), */
+ STRING_FIELD (E_CONTACT_TITLE, e_o365_contact_get_job_title,
e_o365_contact_add_job_title),
+ STRING_FIELD (E_CONTACT_MANAGER, e_o365_contact_get_manager,
e_o365_contact_add_manager),
+ COMPLEX_FIELD (E_CONTACT_NAME, ebb_o365_contact_get_middle_name,
ebb_o365_contact_add_middle_name),
+ STRING_FIELD (E_CONTACT_PHONE_MOBILE, e_o365_contact_get_mobile_phone,
e_o365_contact_add_mobile_phone),
+ STRING_FIELD (E_CONTACT_NICKNAME, e_o365_contact_get_nick_name,
e_o365_contact_add_nick_name),
+ STRING_FIELD (E_CONTACT_OFFICE, e_o365_contact_get_office_location,
e_o365_contact_add_office_location),
+ COMPLEX_FIELD (E_CONTACT_ADDRESS_OTHER, ebb_o365_contact_get_address,
ebb_o365_contact_add_address),
+ STRING_FIELD (E_CONTACT_NOTE, e_o365_contact_get_personal_notes,
e_o365_contact_add_personal_notes),
+ STRING_FIELD (E_CONTACT_ROLE, e_o365_contact_get_profession,
e_o365_contact_add_profession),
+ STRING_FIELD (E_CONTACT_SPOUSE, e_o365_contact_get_spouse_name,
e_o365_contact_add_spouse_name),
+ STRING_FIELD (E_CONTACT_FAMILY_NAME, e_o365_contact_get_surname,
e_o365_contact_add_surname),
+ COMPLEX_FIELD (E_CONTACT_NAME, ebb_o365_contact_get_title,
ebb_o365_contact_add_title),
+ /* STRING_FIELD (???, e_o365_contact_get_yomi_company_name,
e_o365_contact_add_yomi_company_name), */
+ /* STRING_FIELD (???, e_o365_contact_get_yomi_given_name,
e_o365_contact_add_yomi_given_name), */
+ /* STRING_FIELD (???, e_o365_contact_get_yomi_surname,
e_o365_contact_add_yomi_surname), */
+ COMPLEX_FIELD_2 (E_CONTACT_PHOTO, ebb_o365_contact_get_photo,
ebb_o365_contact_add_photo)
+};
+
+static gchar *
+ebb_o365_json_contact_to_vcard_string (EBookBackendO365 *bbo365,
+ EO365Contact *o365_contact,
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EContact *contact;
+ gchar *object = NULL;
+ gint ii;
+ gboolean success = TRUE;
+
+ g_return_val_if_fail (o365_contact != NULL, NULL);
+
+ contact = e_contact_new ();
+
+ for (ii = 0; success && ii < G_N_ELEMENTS (mappings); ii++) {
+ if (mappings[ii].o365_add_func) {
+ ebb_o365_contact_get_string_attribute (o365_contact, contact, mappings[ii].field_id,
mappings[ii].o365_get_func);
+ } else if (mappings[ii].get_func) {
+ success = mappings[ii].get_func (bbo365, o365_contact, contact,
mappings[ii].field_id, cnc, cancellable, error);
+ }
+ }
+
+ if (success)
+ object = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
+
+ g_clear_object (&contact);
+
+ return object;
+}
+
+static JsonBuilder *
+ebb_o365_contact_to_json (EBookBackendO365 *bbo365,
+ EContact *new_contact,
+ EContact *old_contact, /* nullable */
+ EO365Connection *cnc,
+ GCancellable *cancellable,
+ GError **error)
+{
+ JsonBuilder *builder;
+ gint ii;
+ gboolean success = TRUE;
+
+ g_return_val_if_fail (new_contact != NULL, NULL);
+
+ builder = json_builder_new_immutable ();
+ e_o365_json_begin_object_member (builder, NULL);
+
+ for (ii = 0; success && ii < G_N_ELEMENTS (mappings); ii++) {
+ if (mappings[ii].o365_add_func) {
+ ebb_o365_contact_add_string_attribute (new_contact, old_contact,
mappings[ii].field_id, builder, mappings[ii].o365_add_func);
+ } else if (!mappings[ii].add_in_second_go && mappings[ii].add_func) {
+ success = mappings[ii].add_func (bbo365, new_contact, old_contact,
mappings[ii].field_id, builder, cnc, cancellable, error);
+ }
+ }
+
+ e_o365_json_end_object_member (builder);
+
+ if (!success)
+ g_clear_object (&builder);
+
+ return builder;
+}
+
static void
ebb_o365_convert_error_to_client_error (GError **perror)
{
@@ -104,22 +1248,29 @@ ebb_o365_maybe_disconnect_sync (EBookBackendO365 *bbo365,
}
}
-static void
-ebb_o365_unset_connection (EBookBackendO365 *bbo365,
- gboolean is_disconnect)
+static gboolean
+ebb_o365_unset_connection_sync (EBookBackendO365 *bbo365,
+ gboolean is_disconnect,
+ GCancellable *cancellable,
+ GError **error)
{
- g_return_if_fail (E_IS_BOOK_BACKEND_O365 (bbo365));
+ gboolean success = TRUE;
- g_rec_mutex_lock (&bbo365->priv->property_lock);
+ g_return_val_if_fail (E_IS_BOOK_BACKEND_O365 (bbo365), FALSE);
- /*if (bbo365->priv->cnc) {
+ LOCK (bbo365);
+
+ if (bbo365->priv->cnc) {
if (is_disconnect)
- e_o365_connection_set_disconnected_flag (bbo365->priv->cnc, TRUE);
+ success = e_o365_connection_disconnect_sync (bbo365->priv->cnc, cancellable, error);
}
- g_clear_object (&bbo365->priv->cnc);*/
+ g_clear_object (&bbo365->priv->cnc);
+ g_clear_pointer (&bbo365->priv->folder_id, g_free);
+
+ UNLOCK (bbo365);
- g_rec_mutex_unlock (&bbo365->priv->property_lock);
+ return success;
}
static gboolean
@@ -139,17 +1290,63 @@ ebb_o365_connect_sync (EBookMetaBackend *meta_backend,
bbo365 = E_BOOK_BACKEND_O365 (meta_backend);
- g_rec_mutex_lock (&bbo365->priv->property_lock);
+ LOCK (bbo365);
- /*if (bbo365->priv->cnc)*/ {
- g_rec_mutex_unlock (&bbo365->priv->property_lock);
+ if (bbo365->priv->cnc) {
+ UNLOCK (bbo365);
*out_auth_result = E_SOURCE_AUTHENTICATION_ACCEPTED;
return TRUE;
+ } else {
+ EBackend *backend;
+ ESourceRegistry *registry;
+ ESource *source;
+ EO365Connection *cnc;
+ ESourceO365Folder *o365_folder_extension;
+ CamelO365Settings *o365_settings;
+ gchar *folder_id;
+
+ backend = E_BACKEND (bbo365);
+ source = e_backend_get_source (backend);
+ registry = e_book_backend_get_registry (E_BOOK_BACKEND (bbo365));
+ o365_settings = camel_o365_settings_get_from_backend (backend, registry);
+ g_warn_if_fail (o365_settings != NULL);
+
+ o365_folder_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_O365_FOLDER);
+ folder_id = e_source_o365_folder_dup_id (o365_folder_extension);
+
+ if (folder_id) {
+ cnc = e_o365_connection_new_for_backend (backend, registry, source, o365_settings);
+
+ *out_auth_result = e_o365_connection_authenticate_sync (cnc, NULL,
E_O365_FOLDER_KIND_CONTACTS, folder_id,
+ out_certificate_pem, out_certificate_errors, cancellable, error);
+
+ if (*out_auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
+ bbo365->priv->cnc = g_object_ref (cnc);
+
+ g_warn_if_fail (bbo365->priv->folder_id == NULL);
+
+ g_free (bbo365->priv->folder_id);
+ bbo365->priv->folder_id = folder_id;
+
+ folder_id = NULL;
+ success = TRUE;
+
+ e_book_backend_set_writable (E_BOOK_BACKEND (bbo365), TRUE);
+ }
+ } else {
+ *out_auth_result = E_SOURCE_AUTHENTICATION_ERROR;
+ g_propagate_error (error, EC_ERROR_EX (E_CLIENT_ERROR_OTHER_ERROR, _("Folder ID is
not set")));
+ }
+
+ g_clear_object (&cnc);
+ g_free (folder_id);
}
- g_rec_mutex_unlock (&bbo365->priv->property_lock);
+ UNLOCK (bbo365);
+
+ ebb_o365_convert_error_to_client_error (error);
return success;
}
@@ -161,7 +1358,71 @@ ebb_o365_disconnect_sync (EBookMetaBackend *meta_backend,
{
g_return_val_if_fail (E_IS_BOOK_BACKEND_O365 (meta_backend), FALSE);
- ebb_o365_unset_connection (E_BOOK_BACKEND_O365 (meta_backend), TRUE);
+ return ebb_o365_unset_connection_sync (E_BOOK_BACKEND_O365 (meta_backend), TRUE, cancellable, error);
+}
+
+typedef struct _ObjectsDeltaData {
+ EBookBackendO365 *bbo365;
+ ECache *cache;
+ GSList **out_created_objects;
+ GSList **out_modified_objects;
+ GSList **out_removed_objects;
+} ObjectsDeltaData;
+
+static gboolean
+ebb_o365_get_objects_delta_cb (EO365Connection *cnc,
+ const GSList *results, /* JsonObject * - the returned objects from the server
*/
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ObjectsDeltaData *odd = user_data;
+ GSList *link;
+
+ g_return_val_if_fail (odd != NULL, FALSE);
+
+ for (link = (GSList *) results; link && !g_cancellable_is_cancelled (cancellable); link =
g_slist_next (link)) {
+ EO365Contact *contact = link->data;
+ const gchar *id;
+
+ if (!contact)
+ continue;
+
+ id = e_o365_contact_get_id (contact);
+
+ if (!id)
+ continue;
+
+ if (e_o365_delta_is_removed_object (contact)) {
+ *(odd->out_removed_objects) = g_slist_prepend (*(odd->out_removed_objects),
+ e_book_meta_backend_info_new (id, NULL, NULL, NULL));
+ } else {
+ GSList **out_slist;
+ gchar *object;
+
+ if (e_cache_contains (odd->cache, id, E_CACHE_INCLUDE_DELETED))
+ out_slist = odd->out_modified_objects;
+ else
+ out_slist = odd->out_created_objects;
+
+ object = ebb_o365_json_contact_to_vcard_string (odd->bbo365, contact, cnc,
cancellable, error);
+
+ if (!g_cancellable_is_cancelled (cancellable))
+ g_warn_if_fail (object != NULL);
+
+ if (object) {
+ EBookMetaBackendInfo *nfo;
+
+ nfo = e_book_meta_backend_info_new (id,
+ e_o365_contact_get_change_key (contact),
+ object, NULL);
+
+ nfo->extra = object; /* assumes ownership, to avoid unnecessary re-allocation
*/
+
+ *out_slist = g_slist_prepend (*out_slist, nfo);
+ }
+ }
+ }
return TRUE;
}
@@ -180,8 +1441,9 @@ ebb_o365_get_changes_sync (EBookMetaBackend *meta_backend,
{
EBookBackendO365 *bbo365;
EBookCache *book_cache;
- gboolean success = TRUE;
- /*GError *local_error = NULL;*/
+ ObjectsDeltaData odd;
+ gboolean success;
+ GError *local_error = NULL;
g_return_val_if_fail (E_IS_BOOK_BACKEND_O365 (meta_backend), FALSE);
g_return_val_if_fail (out_new_sync_tag != NULL, FALSE);
@@ -199,9 +1461,48 @@ ebb_o365_get_changes_sync (EBookMetaBackend *meta_backend,
book_cache = e_book_meta_backend_ref_cache (meta_backend);
g_return_val_if_fail (E_IS_BOOK_CACHE (book_cache), FALSE);
- g_rec_mutex_lock (&bbo365->priv->property_lock);
+ odd.bbo365 = bbo365;
+ odd.cache = E_CACHE (book_cache);
+ odd.out_created_objects = out_created_objects;
+ odd.out_modified_objects = out_modified_objects;
+ odd.out_removed_objects = out_removed_objects;
+
+ LOCK (bbo365);
+
+ success = e_o365_connection_get_objects_delta_sync (bbo365->priv->cnc, NULL,
+ E_O365_FOLDER_KIND_CONTACTS, bbo365->priv->folder_id, NULL, last_sync_tag, 0,
+ ebb_o365_get_objects_delta_cb, &odd,
+ out_new_sync_tag, cancellable, &local_error);
+
+ if (e_o365_connection_util_delta_token_failed (local_error)) {
+ GSList *known_uids = NULL, *link;
- g_rec_mutex_unlock (&bbo365->priv->property_lock);
+ g_clear_error (&local_error);
+
+ if (e_book_cache_search_uids (book_cache, NULL, &known_uids, cancellable, error)) {
+ for (link = known_uids; link; link = g_slist_next (link)) {
+ const gchar *uid = link->data;
+
+ if (uid) {
+ *out_removed_objects = g_slist_prepend (*out_removed_objects,
+ e_book_meta_backend_info_new (uid, NULL, NULL, NULL));
+ }
+ }
+ }
+
+ e_cache_remove_all (E_CACHE (book_cache), cancellable, NULL);
+
+ g_slist_free_full (known_uids, g_free);
+
+ success = e_o365_connection_get_objects_delta_sync (bbo365->priv->cnc, NULL,
+ E_O365_FOLDER_KIND_CONTACTS, bbo365->priv->folder_id, NULL, NULL, 0,
+ ebb_o365_get_objects_delta_cb, &odd,
+ out_new_sync_tag, cancellable, &local_error);
+ } else if (local_error) {
+ g_propagate_error (error, local_error);
+ }
+
+ UNLOCK (bbo365);
ebb_o365_convert_error_to_client_error (error);
ebb_o365_maybe_disconnect_sync (bbo365, error, cancellable);
@@ -229,9 +1530,9 @@ ebb_o365_load_contact_sync (EBookMetaBackend *meta_backend,
bbo365 = E_BOOK_BACKEND_O365 (meta_backend);
- g_rec_mutex_lock (&bbo365->priv->property_lock);
+ LOCK (bbo365);
- g_rec_mutex_unlock (&bbo365->priv->property_lock);
+ UNLOCK (bbo365);
ebb_o365_convert_error_to_client_error (error);
ebb_o365_maybe_disconnect_sync (bbo365, error, cancellable);
@@ -252,6 +1553,7 @@ ebb_o365_save_contact_sync (EBookMetaBackend *meta_backend,
GError **error)
{
EBookBackendO365 *bbo365;
+ EContact *tmp_contact = NULL;
gboolean success = FALSE;
g_return_val_if_fail (E_IS_BOOK_BACKEND_O365 (meta_backend), FALSE);
@@ -261,13 +1563,22 @@ ebb_o365_save_contact_sync (EBookMetaBackend *meta_backend,
bbo365 = E_BOOK_BACKEND_O365 (meta_backend);
- g_rec_mutex_lock (&bbo365->priv->property_lock);
+ LOCK (bbo365);
+
+ if (e_vcard_get_attribute (E_VCARD (contact), EVC_PHOTO)) {
+ tmp_contact = e_contact_duplicate (contact);
+ contact = tmp_contact;
+
+ e_contact_inline_local_photos (contact, NULL);
+ }
- g_rec_mutex_unlock (&bbo365->priv->property_lock);
+ UNLOCK (bbo365);
ebb_o365_convert_error_to_client_error (error);
ebb_o365_maybe_disconnect_sync (bbo365, error, cancellable);
+ g_clear_object (&tmp_contact);
+
return success;
}
@@ -288,9 +1599,9 @@ ebb_o365_remove_contact_sync (EBookMetaBackend *meta_backend,
bbo365 = E_BOOK_BACKEND_O365 (meta_backend);
- g_rec_mutex_lock (&bbo365->priv->property_lock);
+ LOCK (bbo365);
- g_rec_mutex_unlock (&bbo365->priv->property_lock);
+ UNLOCK (bbo365);
ebb_o365_convert_error_to_client_error (error);
ebb_o365_maybe_disconnect_sync (bbo365, error, cancellable);
@@ -355,41 +1666,24 @@ ebb_o365_get_backend_property (EBookBackend *book_backend,
} else if (g_str_equal (prop_name, E_BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
GString *buffer;
gchar *fields;
- /*gint ii;*/
+ gint ii;
buffer = g_string_sized_new (1024);
- /*for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) {
- if (mappings[ii].element_type != ELEMENT_TYPE_SIMPLE)
- continue;
-
+ for (ii = 0; ii < G_N_ELEMENTS (mappings); ii++) {
if (buffer->len > 0)
g_string_append_c (buffer, ',');
+
g_string_append (buffer, e_contact_field_name (mappings[ii].field_id));
}
- for (ii = 0; ii < G_N_ELEMENTS (phone_field_map); ii++) {
- if (buffer->len > 0)
- g_string_append_c (buffer, ',');
- g_string_append (buffer, e_contact_field_name (phone_field_map[ii].field));
- }*/
-
fields = g_strjoin (
",",
buffer->str,
- e_contact_field_name (E_CONTACT_FULL_NAME),
- e_contact_field_name (E_CONTACT_NICKNAME),
- e_contact_field_name (E_CONTACT_FAMILY_NAME),
e_contact_field_name (E_CONTACT_EMAIL_1),
e_contact_field_name (E_CONTACT_EMAIL_2),
e_contact_field_name (E_CONTACT_EMAIL_3),
- e_contact_field_name (E_CONTACT_ADDRESS_WORK),
- e_contact_field_name (E_CONTACT_ADDRESS_HOME),
- e_contact_field_name (E_CONTACT_ADDRESS_OTHER),
- e_contact_field_name (E_CONTACT_ANNIVERSARY),
- e_contact_field_name (E_CONTACT_BIRTH_DATE),
- e_contact_field_name (E_CONTACT_NOTE),
- e_contact_field_name (E_CONTACT_PHOTO),
+ e_contact_field_name (E_CONTACT_EMAIL_4),
NULL);
g_string_free (buffer, TRUE);
@@ -425,7 +1719,7 @@ e_book_backend_o365_dispose (GObject *object)
{
EBookBackendO365 *bbo365 = E_BOOK_BACKEND_O365 (object);
- ebb_o365_unset_connection (bbo365, FALSE);
+ ebb_o365_unset_connection_sync (bbo365, FALSE, NULL, NULL);
/* Chain up to parent's method. */
G_OBJECT_CLASS (e_book_backend_o365_parent_class)->dispose (object);
diff --git a/src/Office365/camel/camel-o365-folder.c b/src/Office365/camel/camel-o365-folder.c
index b0c89027..48ea5b94 100644
--- a/src/Office365/camel/camel-o365-folder.c
+++ b/src/Office365/camel/camel-o365-folder.c
@@ -1045,11 +1045,11 @@ o365_folder_refresh_info_sync (CamelFolder *folder,
sdd.changes = NULL;
sdd.removed_uids = NULL;
- success = e_o365_connection_get_mail_messages_delta_sync (cnc, NULL, folder_id,
O365_FETCH_SUMMARY_PROPERTIES,
+ success = e_o365_connection_get_objects_delta_sync (cnc, NULL, E_O365_FOLDER_KIND_MAIL, folder_id,
O365_FETCH_SUMMARY_PROPERTIES,
curr_delta_link, 0, o365_folder_got_summary_messages_cb, &sdd,
&new_delta_link, cancellable, &local_error);
- if (curr_delta_link && g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
+ if (curr_delta_link && e_o365_connection_util_delta_token_failed (local_error)) {
g_clear_error (&local_error);
g_clear_pointer (&curr_delta_link, g_free);
@@ -1057,7 +1057,7 @@ o365_folder_refresh_info_sync (CamelFolder *folder,
o365_folder_forget_all_mails (o365_folder);
- success = e_o365_connection_get_mail_messages_delta_sync (cnc, NULL, folder_id,
O365_FETCH_SUMMARY_PROPERTIES,
+ success = e_o365_connection_get_objects_delta_sync (cnc, NULL, E_O365_FOLDER_KIND_MAIL,
folder_id, O365_FETCH_SUMMARY_PROPERTIES,
NULL, 0, o365_folder_got_summary_messages_cb, &sdd,
&new_delta_link, cancellable, &local_error);
}
diff --git a/src/Office365/camel/camel-o365-store.c b/src/Office365/camel/camel-o365-store.c
index a5c4af67..4142b23a 100644
--- a/src/Office365/camel/camel-o365-store.c
+++ b/src/Office365/camel/camel-o365-store.c
@@ -597,7 +597,7 @@ o365_store_authenticate_sync (CamelService *service,
if (!cnc)
return CAMEL_AUTHENTICATION_ERROR;
- switch (e_o365_connection_authenticate_sync (cnc, cancellable, error)) {
+ switch (e_o365_connection_authenticate_sync (cnc, NULL, E_O365_FOLDER_KIND_MAIL, NULL, NULL, NULL,
cancellable, error)) {
case E_SOURCE_AUTHENTICATION_ERROR:
case E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED:
default:
@@ -1269,9 +1269,7 @@ o365_store_get_folder_info_sync (CamelStore *store,
success = e_o365_connection_get_folders_delta_sync (cnc, NULL,
E_O365_FOLDER_KIND_MAIL, NULL, old_delta_link, 0,
camel_o365_got_folders_delta_cb, &fdd, &new_delta_link, cancellable,
&local_error);
- if (old_delta_link && *old_delta_link && (
- g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)
||
- g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_BAD_REQUEST)))
{
+ if (old_delta_link && *old_delta_link &&
e_o365_connection_util_delta_token_failed (local_error)) {
g_clear_pointer (&old_delta_link, g_free);
g_clear_error (&local_error);
diff --git a/src/Office365/camel/camel-o365-transport.c b/src/Office365/camel/camel-o365-transport.c
index 6e8b0071..7149f975 100644
--- a/src/Office365/camel/camel-o365-transport.c
+++ b/src/Office365/camel/camel-o365-transport.c
@@ -238,7 +238,7 @@ o365_transport_authenticate_sync (CamelService *service,
if (!cnc)
return CAMEL_AUTHENTICATION_ERROR;
- switch (e_o365_connection_authenticate_sync (cnc, cancellable, error)) {
+ switch (e_o365_connection_authenticate_sync (cnc, NULL, E_O365_FOLDER_KIND_MAIL, NULL, NULL, NULL,
cancellable, error)) {
case E_SOURCE_AUTHENTICATION_ERROR:
case E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED:
default:
diff --git a/src/Office365/common/e-o365-connection.c b/src/Office365/common/e-o365-connection.c
index ef5be174..0f05eef4 100644
--- a/src/Office365/common/e-o365-connection.c
+++ b/src/Office365/common/e-o365-connection.c
@@ -718,6 +718,13 @@ e_o365_connection_init (EO365Connection *cnc)
G_BINDING_DEFAULT);
}
+gboolean
+e_o365_connection_util_delta_token_failed (const GError *error)
+{
+ return g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED) ||
+ g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_BAD_REQUEST);
+}
+
EO365Connection *
e_o365_connection_new (ESource *source,
CamelO365Settings *settings)
@@ -1362,6 +1369,39 @@ e_o365_read_no_response_cb (EO365Connection *cnc,
return TRUE;
}
+static gboolean
+e_o365_read_to_byte_array_cb (EO365Connection *cnc,
+ SoupMessage *message,
+ GInputStream *raw_data_stream,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GByteArray **out_byte_array = user_data;
+ gchar buffer[4096];
+ gssize n_read;
+
+ g_return_val_if_fail (message != NULL, FALSE);
+ g_return_val_if_fail (out_byte_array != NULL, FALSE);
+
+ if (!*out_byte_array) {
+ goffset content_length;
+
+ content_length = soup_message_headers_get_content_length (message->response_headers);
+
+ if (!content_length || content_length > 65536)
+ content_length = 65535;
+
+ *out_byte_array = g_byte_array_sized_new (content_length);
+ }
+
+ while (n_read = g_input_stream_read (raw_data_stream, buffer, sizeof (buffer), cancellable, error),
n_read > 0) {
+ g_byte_array_append (*out_byte_array, (const guint8 *) buffer, n_read);
+ }
+
+ return !n_read;
+}
+
typedef struct _EO365ResponseData {
EO365ConnectionJsonFunc json_func;
gpointer func_user_data;
@@ -1476,6 +1516,10 @@ o365_connection_new_soup_message (const gchar *method,
soup_message_headers_append (message->request_headers, "Connection", "Close");
soup_message_headers_append (message->request_headers, "User-Agent", "Evolution-O365/"
VERSION);
+ /* Disable caching for proxies (RFC 4918, section 10.4.5) */
+ soup_message_headers_append (message->request_headers, "Cache-Control", "no-cache");
+ soup_message_headers_append (message->request_headers, "Pragma", "no-cache");
+
if ((csm_flags & CSM_DISABLE_RESPONSE) != 0)
soup_message_headers_append (message->request_headers, "Prefer", "return=minimal");
} else {
@@ -1511,26 +1555,52 @@ e_o365_connection_get_ssl_error_details (EO365Connection *cnc,
ESourceAuthenticationResult
e_o365_connection_authenticate_sync (EO365Connection *cnc,
+ const gchar *user_override,
+ EO365FolderKind kind,
+ const gchar *folder_id,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
GCancellable *cancellable,
GError **error)
{
ESourceAuthenticationResult result = E_SOURCE_AUTHENTICATION_ERROR;
- EO365ResponseData rd;
SoupMessage *message;
+ JsonObject *object = NULL;
gchar *uri;
+ const gchar *resource = NULL;
gboolean success;
- GSList *folders = NULL;
GError *local_error = NULL;
g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), result);
/* Just pick an inexpensive operation */
- uri = e_o365_connection_construct_uri (cnc, TRUE, NULL, E_O365_API_V1_0, NULL,
- "mailFolders",
- NULL,
+ switch (kind) {
+ case E_O365_FOLDER_KIND_UNKNOWN:
+ case E_O365_FOLDER_KIND_MAIL:
+ resource = "mailFolders";
+
+ if (!folder_id || !*folder_id)
+ folder_id = "inbox";
+ break;
+ case E_O365_FOLDER_KIND_CONTACTS:
+ resource = "contactFolders";
+
+ if (!folder_id || !*folder_id)
+ folder_id = "contacts";
+ break;
+ default:
+ g_warn_if_reached ();
+
+ resource = "mailFolders";
+ folder_id = "inbox";
+ break;
+ }
+
+ uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+ resource,
+ folder_id,
NULL,
"$select", "displayName",
- "$top", "1",
NULL);
message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
@@ -1543,12 +1613,7 @@ e_o365_connection_authenticate_sync (EO365Connection *cnc,
g_free (uri);
- memset (&rd, 0, sizeof (EO365ResponseData));
-
- rd.read_only_once = TRUE;
- rd.out_items = &folders;
-
- success = o365_connection_send_request_sync (cnc, message, e_o365_read_valued_response_cb, NULL, &rd,
cancellable, &local_error);
+ success = o365_connection_send_request_sync (cnc, message, e_o365_read_json_object_response_cb, NULL,
&object, cancellable, &local_error);
if (success) {
result = E_SOURCE_AUTHENTICATION_ACCEPTED;
@@ -1558,6 +1623,9 @@ e_o365_connection_authenticate_sync (EO365Connection *cnc,
local_error->code = G_IO_ERROR_CANCELLED;
} else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED;
+
+ if (out_certificate_pem || out_certificate_errors)
+ e_o365_connection_get_ssl_error_details (cnc, out_certificate_pem,
out_certificate_errors);
} else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
ESoupAuthBearer *bearer;
@@ -1588,7 +1656,9 @@ e_o365_connection_authenticate_sync (EO365Connection *cnc,
}
}
- g_slist_free_full (folders, (GDestroyNotify) json_object_unref);
+ if (object)
+ json_object_unref (object);
+
g_clear_object (&message);
g_clear_error (&local_error);
@@ -1756,7 +1826,7 @@ e_o365_connection_set_json_body (SoupMessage *message,
data = json_generator_to_data (generator, &data_length);
- soup_message_headers_append (message->request_headers, "Content-Type", "application/json");
+ soup_message_headers_set_content_type (message->request_headers, "application/json", NULL);
if (data)
soup_message_body_append_take (message->request_body, (guchar *) data, data_length);
@@ -2510,20 +2580,22 @@ e_o365_connection_rename_mail_folder_sync (EO365Connection *cnc,
return success;
}
-/* https://docs.microsoft.com/en-us/graph/api/message-delta?view=graph-rest-1.0&tabs=http */
+/* https://docs.microsoft.com/en-us/graph/api/message-delta?view=graph-rest-1.0&tabs=http
+ https://docs.microsoft.com/en-us/graph/api/contact-delta?view=graph-rest-1.0&tabs=http */
gboolean
-e_o365_connection_get_mail_messages_delta_sync (EO365Connection *cnc,
- const gchar *user_override, /* for which user, NULL to use
the account user */
- const gchar *folder_id, /* folder ID to get delta messages in
*/
- const gchar *select, /* properties to select, nullable */
- const gchar *delta_link, /* previous delta link */
- guint max_page_size, /* 0 for default by the server */
- EO365ConnectionJsonFunc func, /* function to call with each
result set */
- gpointer func_user_data, /* user data passed into the 'func'
*/
- gchar **out_delta_link,
- GCancellable *cancellable,
- GError **error)
+e_o365_connection_get_objects_delta_sync (EO365Connection *cnc,
+ const gchar *user_override, /* for which user, NULL to use the
account user */
+ EO365FolderKind kind,
+ const gchar *folder_id, /* folder ID to get delta messages in */
+ const gchar *select, /* properties to select, nullable */
+ const gchar *delta_link, /* previous delta link */
+ guint max_page_size, /* 0 for default by the server */
+ EO365ConnectionJsonFunc func, /* function to call with each result
set */
+ gpointer func_user_data, /* user data passed into the 'func' */
+ gchar **out_delta_link,
+ GCancellable *cancellable,
+ GError **error)
{
EO365ResponseData rd;
SoupMessage *message = NULL;
@@ -2538,12 +2610,29 @@ e_o365_connection_get_mail_messages_delta_sync (EO365Connection *cnc,
message = o365_connection_new_soup_message (SOUP_METHOD_GET, delta_link, CSM_DEFAULT, NULL);
if (!message) {
+ const gchar *kind_str = NULL, *kind_path_str = NULL;
gchar *uri;
+ switch (kind) {
+ case E_O365_FOLDER_KIND_CONTACTS:
+ kind_str = "contactFolders";
+ kind_path_str = "contacts";
+ break;
+ case E_O365_FOLDER_KIND_MAIL:
+ kind_str = "mailFolders";
+ kind_path_str = "messages";
+ break;
+ default:
+ g_warn_if_reached ();
+ break;
+ }
+
+ g_return_val_if_fail (kind_str != NULL && kind_path_str != NULL, FALSE);
+
uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
- "mailFolders",
+ kind_str,
folder_id,
- "messages",
+ kind_path_str,
"", "delta",
"$select", select,
NULL);
@@ -2604,11 +2693,9 @@ e_o365_connection_get_mail_message_sync (EO365Connection *cnc,
g_return_val_if_fail (func != NULL, FALSE);
uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
- /*"mailFolders",
- folder_id,*/
"messages",
- "", message_id,
- "", "$value",
+ message_id,
+ "$value",
NULL);
message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
@@ -3193,3 +3280,98 @@ e_o365_connection_get_contacts_folder_sync (EO365Connection *cnc,
return success;
}
+
+/* https://docs.microsoft.com/en-us/graph/api/profilephoto-get?view=graph-rest-1.0 */
+
+gboolean
+e_o365_connection_get_contact_photo_sync (EO365Connection *cnc,
+ const gchar *user_override, /* for which user, NULL to use the
account user */
+ const gchar *folder_id,
+ const gchar *contact_id,
+ GByteArray **out_photo,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupMessage *message;
+ gboolean success;
+ gchar *uri;
+
+ g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+ g_return_val_if_fail (folder_id != NULL, FALSE);
+ g_return_val_if_fail (contact_id != NULL, FALSE);
+ g_return_val_if_fail (out_photo != NULL, FALSE);
+
+ uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+ "contactFolders",
+ folder_id,
+ "contacts",
+ "", contact_id,
+ "", "photo",
+ "", "$value",
+ NULL);
+
+ message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
+
+ if (!message) {
+ g_free (uri);
+
+ return FALSE;
+ }
+
+ g_free (uri);
+
+ success = o365_connection_send_request_sync (cnc, message, NULL, e_o365_read_to_byte_array_cb,
out_photo, cancellable, error);
+
+ g_clear_object (&message);
+
+ return success;
+}
+
+/* https://docs.microsoft.com/en-us/graph/api/profilephoto-update?view=graph-rest-1.0&tabs=http */
+
+gboolean
+e_o365_connection_update_contact_photo_sync (EO365Connection *cnc,
+ const gchar *user_override, /* for which user, NULL to use the
account user */
+ const gchar *folder_id,
+ const gchar *contact_id,
+ const GByteArray *jpeg_photo, /* nullable, to remove the photo */
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupMessage *message;
+ gboolean success;
+ gchar *uri;
+
+ g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+
+ uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+ "contactFolders",
+ folder_id,
+ "contacts",
+ "", contact_id,
+ "", "photo",
+ "", "$value",
+ NULL);
+
+ message = o365_connection_new_soup_message (SOUP_METHOD_PUT, uri, CSM_DEFAULT, error);
+
+ if (!message) {
+ g_free (uri);
+
+ return FALSE;
+ }
+
+ g_free (uri);
+
+ soup_message_headers_set_content_type (message->request_headers, "image/jpeg", NULL);
+ soup_message_headers_set_content_length (message->request_headers, jpeg_photo ? jpeg_photo->len : 0);
+
+ if (jpeg_photo)
+ soup_message_body_append (message->request_body, SOUP_MEMORY_STATIC, jpeg_photo->data,
jpeg_photo->len);
+
+ success = o365_connection_send_request_sync (cnc, message, NULL, e_o365_read_no_response_cb, NULL,
cancellable, error);
+
+ g_clear_object (&message);
+
+ return success;
+}
diff --git a/src/Office365/common/e-o365-connection.h b/src/Office365/common/e-o365-connection.h
index a19fbb20..f49529eb 100644
--- a/src/Office365/common/e-o365-connection.h
+++ b/src/Office365/common/e-o365-connection.h
@@ -85,6 +85,9 @@ struct _EO365ConnectionClass {
GObjectClass parent_class;
};
+gboolean e_o365_connection_util_delta_token_failed
+ (const GError *error);
+
GType e_o365_connection_get_type (void) G_GNUC_CONST;
EO365Connection *
@@ -126,6 +129,11 @@ gboolean e_o365_connection_get_ssl_error_details
ESourceAuthenticationResult
e_o365_connection_authenticate_sync
(EO365Connection *cnc,
+ const gchar *user_override,
+ EO365FolderKind kind,
+ const gchar *folder_id,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
GCancellable *cancellable,
GError **error);
gboolean e_o365_connection_disconnect_sync
@@ -216,9 +224,10 @@ gboolean e_o365_connection_rename_mail_folder_sync
EO365MailFolder **out_mail_folder,
GCancellable *cancellable,
GError **error);
-gboolean e_o365_connection_get_mail_messages_delta_sync
+gboolean e_o365_connection_get_objects_delta_sync
(EO365Connection *cnc,
const gchar *user_override, /* for which user, NULL to use
the account user */
+ EO365FolderKind kind,
const gchar *folder_id, /* folder ID to get delta messages
in */
const gchar *select, /* properties to select, nullable */
const gchar *delta_link, /* previous delta link */
@@ -300,6 +309,22 @@ gboolean e_o365_connection_get_contacts_folder_sync
EO365Folder **out_folder,
GCancellable *cancellable,
GError **error);
+gboolean e_o365_connection_get_contact_photo_sync
+ (EO365Connection *cnc,
+ const gchar *user_override, /* for which user, NULL to use
the account user */
+ const gchar *folder_id,
+ const gchar *contact_id,
+ GByteArray **out_photo,
+ GCancellable *cancellable,
+ GError **error);
+gboolean e_o365_connection_update_contact_photo_sync
+ (EO365Connection *cnc,
+ const gchar *user_override, /* for which user, NULL to use
the account user */
+ const gchar *folder_id,
+ const gchar *contact_id,
+ const GByteArray *jpeg_photo, /* nullable, to remove the
photo */
+ GCancellable *cancellable,
+ GError **error);
G_END_DECLS
diff --git a/src/Office365/common/e-o365-json-utils.c b/src/Office365/common/e-o365-json-utils.c
index a3cdb79f..bb509252 100644
--- a/src/Office365/common/e-o365-json-utils.c
+++ b/src/Office365/common/e-o365-json-utils.c
@@ -245,6 +245,30 @@ e_o365_json_add_string_member (JsonBuilder *builder,
json_builder_add_string_value (builder, value ? value : "");
}
+void
+e_o365_json_add_nonempty_string_member (JsonBuilder *builder,
+ const gchar *member_name,
+ const gchar *value)
+{
+ g_return_if_fail (member_name && *member_name);
+
+ if (value && *value)
+ e_o365_json_add_string_member (builder, member_name, value);
+}
+
+void
+e_o365_json_add_nonempty_or_null_string_member (JsonBuilder *builder,
+ const gchar *member_name,
+ const gchar *value)
+{
+ g_return_if_fail (member_name && *member_name);
+
+ if (value && *value)
+ e_o365_json_add_string_member (builder, member_name, value);
+ else
+ e_o365_json_add_null_member (builder, member_name);
+}
+
time_t
e_o365_get_date_time_offset_member (JsonObject *object,
const gchar *member_name)
@@ -276,7 +300,7 @@ e_o365_add_date_time_offset_member (JsonBuilder *builder,
GDateTime *dt;
gchar *value_str;
- if ((time_t) value <= 0) {
+ if (value <= (time_t) 0) {
e_o365_json_add_null_member (builder, member_name);
return;
}
@@ -443,11 +467,8 @@ e_o365_add_recipient (JsonBuilder *builder,
e_o365_json_begin_object_member (builder, member_name);
e_o365_json_begin_object_member (builder, "emailAddress");
- if (name && *name)
- e_o365_json_add_string_member (builder, "name", name);
-
- if (address && *address)
- e_o365_json_add_string_member (builder, "address", address);
+ e_o365_json_add_nonempty_string_member (builder, "name", name);
+ e_o365_json_add_nonempty_string_member (builder, "address", address);
e_o365_json_end_object_member (builder); /* emailAddress */
e_o365_json_end_object_member (builder); /* member_name */
@@ -483,9 +504,7 @@ e_o365_add_date_time (JsonBuilder *builder,
e_o365_json_begin_object_member (builder, member_name);
e_o365_add_date_time_offset_member (builder, "dateTime", date_time);
-
- if (zone && *zone)
- e_o365_json_add_string_member (builder, "timeZone", zone);
+ e_o365_json_add_nonempty_string_member (builder, "timeZone", zone);
e_o365_json_end_object_member (builder);
}
@@ -906,8 +925,7 @@ void
e_o365_mail_message_add_internet_message_id (JsonBuilder *builder,
const gchar *message_id)
{
- if (message_id && *message_id)
- e_o365_json_add_string_member (builder, "internetMessageId", message_id);
+ e_o365_json_add_nonempty_string_member (builder, "internetMessageId", message_id);
}
gboolean
@@ -1037,8 +1055,7 @@ void
e_o365_mail_message_add_subject (JsonBuilder *builder,
const gchar *subject)
{
- if (subject)
- e_o365_json_add_string_member (builder, "subject", subject);
+ e_o365_json_add_nonempty_string_member (builder, "subject", subject);
}
JsonArray * /* EO365Recipient * */
@@ -1213,3 +1230,638 @@ e_o365_file_attachment_add_content_id (JsonBuilder *builder,
{
e_o365_json_add_string_member (builder, "contentId", value);
}
+
+/* https://docs.microsoft.com/en-us/graph/api/resources/emailaddress?view=graph-rest-1.0 */
+
+const gchar *
+e_o365_email_address_get_name (EO365EmailAddress *email)
+{
+ return e_o365_json_get_string_member (email, "name", NULL);
+}
+
+const gchar *
+e_o365_email_address_get_address (EO365EmailAddress *email)
+{
+ return e_o365_json_get_string_member (email, "address", NULL);
+}
+
+void
+e_o365_add_email_address (JsonBuilder *builder,
+ const gchar *name,
+ const gchar *address)
+{
+ g_return_if_fail ((name && *name) || (address && *address));
+
+ e_o365_json_begin_object_member (builder, NULL);
+
+ e_o365_json_add_nonempty_string_member (builder, "name", name);
+ e_o365_json_add_nonempty_string_member (builder, "address", address);
+
+ e_o365_json_end_object_member (builder); /* unnamed object */
+}
+
+/* https://docs.microsoft.com/en-us/graph/api/resources/physicaladdress?view=graph-rest-1.0 */
+
+const gchar *
+e_o365_physical_address_get_city (EO365PhysicalAddress *address)
+{
+ return e_o365_json_get_string_member (address, "city", NULL);
+}
+
+const gchar *
+e_o365_physical_address_get_country_or_region (EO365PhysicalAddress *address)
+{
+ return e_o365_json_get_string_member (address, "countryOrRegion", NULL);
+}
+
+const gchar *
+e_o365_physical_address_get_postal_code (EO365PhysicalAddress *address)
+{
+ return e_o365_json_get_string_member (address, "postalCode", NULL);
+}
+
+const gchar *
+e_o365_physical_address_get_state (EO365PhysicalAddress *address)
+{
+ return e_o365_json_get_string_member (address, "state", NULL);
+}
+
+const gchar *
+e_o365_physical_address_get_street (EO365PhysicalAddress *address)
+{
+ return e_o365_json_get_string_member (address, "street", NULL);
+}
+
+void
+e_o365_add_physical_address (JsonBuilder *builder,
+ const gchar *member_name,
+ const gchar *city,
+ const gchar *country_or_region,
+ const gchar *postal_code,
+ const gchar *state,
+ const gchar *street)
+{
+ if ((city && *city) ||
+ (country_or_region && *country_or_region) ||
+ (postal_code && *postal_code) ||
+ (state && *state) ||
+ (street && *street)) {
+ e_o365_json_begin_object_member (builder, member_name);
+ e_o365_json_add_nonempty_string_member (builder, "city", city);
+ e_o365_json_add_nonempty_string_member (builder, "countryOrRegion", country_or_region);
+ e_o365_json_add_nonempty_string_member (builder, "postalCode", postal_code);
+ e_o365_json_add_nonempty_string_member (builder, "state", state);
+ e_o365_json_add_nonempty_string_member (builder, "street", street);
+ e_o365_json_end_object_member (builder);
+ } else {
+ e_o365_json_add_null_member (builder, member_name);
+ }
+}
+
+/* https://docs.microsoft.com/en-us/graph/api/resources/contact?view=graph-rest-1.0 */
+
+const gchar *
+e_o365_contact_get_id (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "id", NULL);
+}
+
+const gchar *
+e_o365_contact_get_parent_folder_id (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "parentFolderId", NULL);
+}
+
+const gchar *
+e_o365_contact_get_change_key (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "changeKey", NULL);
+}
+
+time_t
+e_o365_contact_get_created_date_time (EO365Contact *contact)
+{
+ return e_o365_get_date_time_offset_member (contact, "createdDateTime");
+}
+
+time_t
+e_o365_contact_get_last_modified_date_time (EO365Contact *contact)
+{
+ return e_o365_get_date_time_offset_member (contact, "lastModifiedDateTime");
+}
+
+const gchar *
+e_o365_contact_get_assistant_name (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "assistantName", NULL);
+}
+
+void
+e_o365_contact_add_assistant_name (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "assistantName", value);
+}
+
+time_t
+e_o365_contact_get_birthday (EO365Contact *contact)
+{
+ return e_o365_get_date_time_offset_member (contact, "birthday");
+}
+
+void
+e_o365_contact_add_birthday (JsonBuilder *builder,
+ time_t value)
+{
+ e_o365_add_date_time_offset_member (builder, "birthday", value);
+}
+
+EO365PhysicalAddress *
+e_o365_contact_get_business_address (EO365Contact *contact)
+{
+ return e_o365_json_get_object_member (contact, "businessAddress");
+}
+
+void
+e_o365_contact_add_business_address (JsonBuilder *builder,
+ const gchar *city,
+ const gchar *country_or_region,
+ const gchar *postal_code,
+ const gchar *state,
+ const gchar *street)
+{
+ e_o365_add_physical_address (builder, "businessAddress", city, country_or_region, postal_code, state,
street);
+}
+
+const gchar *
+e_o365_contact_get_business_home_page (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "businessHomePage", NULL);
+}
+
+void
+e_o365_contact_add_business_home_page (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "businessHomePage", value);
+}
+
+JsonArray * /* const gchar * */
+e_o365_contact_get_business_phones (EO365Contact *contact)
+{
+ return e_o365_json_get_array_member (contact, "businessPhones");
+}
+
+void
+e_o365_contact_begin_business_phones (JsonBuilder *builder)
+{
+ e_o365_json_begin_array_member (builder, "businessPhones");
+}
+
+void
+e_o365_contact_end_business_phones (JsonBuilder *builder)
+{
+ e_o365_json_end_array_member (builder);
+}
+
+void
+e_o365_contact_add_business_phone (JsonBuilder *builder,
+ const gchar *value)
+{
+ g_return_if_fail (value && *value);
+
+ json_builder_add_string_value (builder, value);
+}
+
+JsonArray * /* const gchar * */
+e_o365_contact_get_categories (EO365Contact *contact)
+{
+ return e_o365_json_get_array_member (contact, "categories");
+}
+
+void
+e_o365_contact_begin_categories (JsonBuilder *builder)
+{
+ e_o365_json_begin_array_member (builder, "categories");
+}
+
+void
+e_o365_contact_end_categories (JsonBuilder *builder)
+{
+ e_o365_json_end_array_member (builder);
+}
+
+void
+e_o365_contact_add_category (JsonBuilder *builder,
+ const gchar *category)
+{
+ g_return_if_fail (category && *category);
+
+ json_builder_add_string_value (builder, category);
+}
+
+JsonArray * /* const gchar * */
+e_o365_contact_get_children (EO365Contact *contact)
+{
+ return e_o365_json_get_array_member (contact, "children");
+}
+
+void
+e_o365_contact_begin_children (JsonBuilder *builder)
+{
+ e_o365_json_begin_array_member (builder, "children");
+}
+
+void
+e_o365_contact_end_children (JsonBuilder *builder)
+{
+ e_o365_json_end_array_member (builder);
+}
+
+void
+e_o365_contact_add_child (JsonBuilder *builder,
+ const gchar *value)
+{
+ g_return_if_fail (value && *value);
+
+ json_builder_add_string_value (builder, value);
+}
+
+const gchar *
+e_o365_contact_get_company_name (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "companyName", NULL);
+}
+
+void
+e_o365_contact_add_company_name (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "companyName", value);
+}
+
+const gchar *
+e_o365_contact_get_department (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "department", NULL);
+}
+
+void
+e_o365_contact_add_department (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "department", value);
+}
+
+const gchar *
+e_o365_contact_get_display_name (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "displayName", NULL);
+}
+
+void
+e_o365_contact_add_display_name (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "displayName", value);
+}
+
+JsonArray * /* EO365EmailAddress * */
+e_o365_contact_get_email_addresses (EO365Contact *contact)
+{
+ return e_o365_json_get_array_member (contact, "emailAddresses");
+}
+
+void
+e_o365_contact_begin_email_addresses (JsonBuilder *builder)
+{
+ e_o365_json_begin_array_member (builder, "emailAddresses");
+}
+
+void
+e_o365_contact_end_email_addresses (JsonBuilder *builder)
+{
+ e_o365_json_end_array_member (builder);
+}
+
+const gchar *
+e_o365_contact_get_file_as (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "fileAs", NULL);
+}
+
+void
+e_o365_contact_add_file_as (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "fileAs", value);
+}
+
+const gchar *
+e_o365_contact_get_generation (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "generation", NULL);
+}
+
+void
+e_o365_contact_add_generation (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "generation", value);
+}
+
+const gchar *
+e_o365_contact_get_given_name (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "givenName", NULL);
+}
+
+void
+e_o365_contact_add_given_name (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "givenName", value);
+}
+
+EO365PhysicalAddress *
+e_o365_contact_get_home_address (EO365Contact *contact)
+{
+ return e_o365_json_get_object_member (contact, "homeAddress");
+}
+
+void
+e_o365_contact_add_home_address (JsonBuilder *builder,
+ const gchar *city,
+ const gchar *country_or_region,
+ const gchar *postal_code,
+ const gchar *state,
+ const gchar *street)
+{
+ e_o365_add_physical_address (builder, "homeAddress", city, country_or_region, postal_code, state,
street);
+}
+
+JsonArray * /* const gchar * */
+e_o365_contact_get_home_phones (EO365Contact *contact)
+{
+ return e_o365_json_get_array_member (contact, "homePhones");
+}
+
+void
+e_o365_contact_begin_home_phones (JsonBuilder *builder)
+{
+ e_o365_json_begin_array_member (builder, "homePhones");
+}
+
+void
+e_o365_contact_end_home_phones (JsonBuilder *builder)
+{
+ e_o365_json_end_array_member (builder);
+}
+
+void
+e_o365_contact_add_home_phone (JsonBuilder *builder,
+ const gchar *value)
+{
+ g_return_if_fail (value && *value);
+
+ json_builder_add_string_value (builder, value);
+}
+
+JsonArray * /* const gchar * */
+e_o365_contact_get_im_addresses (EO365Contact *contact)
+{
+ return e_o365_json_get_array_member (contact, "imAddresses");
+}
+
+void
+e_o365_contact_begin_im_addresses (JsonBuilder *builder)
+{
+ e_o365_json_begin_array_member (builder, "imAddresses");
+}
+
+void
+e_o365_contact_end_im_addresses (JsonBuilder *builder)
+{
+ e_o365_json_end_array_member (builder);
+}
+
+void
+e_o365_contact_add_im_address (JsonBuilder *builder,
+ const gchar *value)
+{
+ g_return_if_fail (value && *value);
+
+ json_builder_add_string_value (builder, value);
+}
+
+const gchar *
+e_o365_contact_get_initials (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "initials", NULL);
+}
+
+void
+e_o365_contact_add_initials (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "initials", value);
+}
+
+const gchar *
+e_o365_contact_get_job_title (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "jobTitle", NULL);
+}
+
+void
+e_o365_contact_add_job_title (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "jobTitle", value);
+}
+
+const gchar *
+e_o365_contact_get_manager (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "manager", NULL);
+}
+
+void
+e_o365_contact_add_manager (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "manager", value);
+}
+
+const gchar *
+e_o365_contact_get_middle_name (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "middleName", NULL);
+}
+
+void
+e_o365_contact_add_middle_name (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "middleName", value);
+}
+
+const gchar *
+e_o365_contact_get_mobile_phone (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "mobilePhone", NULL);
+}
+
+void
+e_o365_contact_add_mobile_phone (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "mobilePhone", value);
+}
+
+const gchar *
+e_o365_contact_get_nick_name (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "nickName", NULL);
+}
+
+void
+e_o365_contact_add_nick_name (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "nickName", value);
+}
+
+const gchar *
+e_o365_contact_get_office_location (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "officeLocation", NULL);
+}
+
+void
+e_o365_contact_add_office_location (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "officeLocation", value);
+}
+
+EO365PhysicalAddress *
+e_o365_contact_get_other_address (EO365Contact *contact)
+{
+ return e_o365_json_get_object_member (contact, "otherAddress");
+}
+
+void
+e_o365_contact_add_other_address (JsonBuilder *builder,
+ const gchar *city,
+ const gchar *country_or_region,
+ const gchar *postal_code,
+ const gchar *state,
+ const gchar *street)
+{
+ e_o365_add_physical_address (builder, "otherAddress", city, country_or_region, postal_code, state,
street);
+}
+
+const gchar *
+e_o365_contact_get_personal_notes (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "personalNotes", NULL);
+}
+
+void
+e_o365_contact_add_personal_notes (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "personalNotes", value);
+}
+
+const gchar *
+e_o365_contact_get_profession (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "profession", NULL);
+}
+
+void
+e_o365_contact_add_profession (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "profession", value);
+}
+
+const gchar *
+e_o365_contact_get_spouse_name (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "spouseName", NULL);
+}
+
+void
+e_o365_contact_add_spouse_name (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "spouseName", value);
+}
+
+const gchar *
+e_o365_contact_get_surname (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "surname", NULL);
+}
+
+void
+e_o365_contact_add_surname (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "surname", value);
+}
+
+const gchar *
+e_o365_contact_get_title (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "title", NULL);
+}
+
+void
+e_o365_contact_add_title (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "title", value);
+}
+
+const gchar *
+e_o365_contact_get_yomi_company_name (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "yomiCompanyName", NULL);
+}
+
+void
+e_o365_contact_add_yomi_company_name (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "yomiCompanyName", value);
+}
+
+const gchar *
+e_o365_contact_get_yomi_given_name (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "yomiGivenName", NULL);
+}
+
+void
+e_o365_contact_add_yomi_given_name (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "yomiGivenName", value);
+}
+
+const gchar *
+e_o365_contact_get_yomi_surname (EO365Contact *contact)
+{
+ return e_o365_json_get_string_member (contact, "yomiSurname", NULL);
+}
+
+void
+e_o365_contact_add_yomi_surname (JsonBuilder *builder,
+ const gchar *value)
+{
+ e_o365_json_add_nonempty_or_null_string_member (builder, "yomiSurname", value);
+}
diff --git a/src/Office365/common/e-o365-json-utils.h b/src/Office365/common/e-o365-json-utils.h
index 0f766d6f..2cd13d1c 100644
--- a/src/Office365/common/e-o365-json-utils.h
+++ b/src/Office365/common/e-o365-json-utils.h
@@ -23,6 +23,21 @@
G_BEGIN_DECLS
+/* Just for better readability */
+#define EO365Attachment JsonObject
+#define EO365Category JsonObject
+#define EO365Contact JsonObject
+#define EO365DateTimeWithZone JsonObject
+#define EO365EmailAddress JsonObject
+#define EO365Folder JsonObject
+#define EO365FollowupFlag JsonObject
+#define EO365InternetMessageHeader JsonObject
+#define EO365ItemBody JsonObject
+#define EO365MailFolder JsonObject
+#define EO365MailMessage JsonObject
+#define EO365PhysicalAddress JsonObject
+#define EO365Recipient JsonObject
+
typedef enum _EO365AttachmentDataType {
E_O365_ATTACHMENT_DATA_TYPE_NOT_SET,
E_O365_ATTACHMENT_DATA_TYPE_UNKNOWN,
@@ -61,18 +76,6 @@ typedef enum _EO365ItemBodyContentTypeType {
E_O365_ITEM_BODY_CONTENT_TYPE_HTML
} EO365ItemBodyContentTypeType;
-/* Just for better readability */
-#define EO365Attachment JsonObject
-#define EO365Category JsonObject
-#define EO365DateTimeWithZone JsonObject
-#define EO365Folder JsonObject
-#define EO365FollowupFlag JsonObject
-#define EO365InternetMessageHeader JsonObject
-#define EO365ItemBody JsonObject
-#define EO365MailFolder JsonObject
-#define EO365MailMessage JsonObject
-#define EO365Recipient JsonObject
-
JsonArray * e_o365_json_get_array_member (JsonObject *object,
const gchar *member_name);
void e_o365_json_begin_array_member (JsonBuilder *builder,
@@ -112,6 +115,13 @@ const gchar * e_o365_json_get_string_member (JsonObject *object,
void e_o365_json_add_string_member (JsonBuilder *builder,
const gchar *member_name,
const gchar *value);
+void e_o365_json_add_nonempty_string_member (JsonBuilder *builder,
+ const gchar *member_name,
+ const gchar *value);
+void e_o365_json_add_nonempty_or_null_string_member
+ (JsonBuilder *builder,
+ const gchar *member_name,
+ const gchar *value);
time_t e_o365_get_date_time_offset_member (JsonObject *object,
const gchar *member_name);
@@ -306,6 +316,156 @@ const gchar * e_o365_file_attachment_get_content_id (EO365Attachment *attachment
void e_o365_file_attachment_add_content_id (JsonBuilder *builder,
const gchar *value);
+const gchar * e_o365_email_address_get_name (EO365EmailAddress *email);
+const gchar * e_o365_email_address_get_address (EO365EmailAddress *email);
+void e_o365_add_email_address (JsonBuilder *builder,
+ const gchar *name,
+ const gchar *address);
+const gchar * e_o365_physical_address_get_city (EO365PhysicalAddress *address);
+const gchar * e_o365_physical_address_get_country_or_region
+ (EO365PhysicalAddress *address);
+const gchar * e_o365_physical_address_get_postal_code (EO365PhysicalAddress *address);
+const gchar * e_o365_physical_address_get_state (EO365PhysicalAddress *address);
+const gchar * e_o365_physical_address_get_street (EO365PhysicalAddress *address);
+void e_o365_add_physical_address (JsonBuilder *builder,
+ const gchar *member_name,
+ const gchar *city,
+ const gchar *country_or_region,
+ const gchar *postal_code,
+ const gchar *state,
+ const gchar *street);
+
+const gchar * e_o365_contact_get_id (EO365Contact *contact);
+const gchar * e_o365_contact_get_parent_folder_id (EO365Contact *contact);
+const gchar * e_o365_contact_get_change_key (EO365Contact *contact);
+time_t e_o365_contact_get_created_date_time (EO365Contact *contact);
+time_t e_o365_contact_get_last_modified_date_time
+ (EO365Contact *contact);
+const gchar * e_o365_contact_get_assistant_name (EO365Contact *contact);
+void e_o365_contact_add_assistant_name (JsonBuilder *builder,
+ const gchar *value);
+time_t e_o365_contact_get_birthday (EO365Contact *contact);
+void e_o365_contact_add_birthday (JsonBuilder *builder,
+ time_t value);
+EO365PhysicalAddress *
+ e_o365_contact_get_business_address (EO365Contact *contact);
+void e_o365_contact_add_business_address (JsonBuilder *builder,
+ const gchar *city,
+ const gchar *country_or_region,
+ const gchar *postal_code,
+ const gchar *state,
+ const gchar *street);
+const gchar * e_o365_contact_get_business_home_page (EO365Contact *contact);
+void e_o365_contact_add_business_home_page (JsonBuilder *builder,
+ const gchar *value);
+JsonArray * e_o365_contact_get_business_phones (EO365Contact *contact); /* const gchar * */
+void e_o365_contact_begin_business_phones (JsonBuilder *builder);
+void e_o365_contact_end_business_phones (JsonBuilder *builder);
+void e_o365_contact_add_business_phone (JsonBuilder *builder,
+ const gchar *value);
+JsonArray * e_o365_contact_get_categories (EO365Contact *contact); /* const gchar * */
+void e_o365_contact_begin_categories (JsonBuilder *builder);
+void e_o365_contact_end_categories (JsonBuilder *builder);
+void e_o365_contact_add_category (JsonBuilder *builder,
+ const gchar *category);
+JsonArray * e_o365_contact_get_children (EO365Contact *contact); /* const gchar * */
+void e_o365_contact_begin_children (JsonBuilder *builder);
+void e_o365_contact_end_children (JsonBuilder *builder);
+void e_o365_contact_add_child (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_company_name (EO365Contact *contact);
+void e_o365_contact_add_company_name (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_department (EO365Contact *contact);
+void e_o365_contact_add_department (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_display_name (EO365Contact *contact);
+void e_o365_contact_add_display_name (JsonBuilder *builder,
+ const gchar *value);
+JsonArray * e_o365_contact_get_email_addresses (EO365Contact *contact); /* EO365EmailAddress * */
+void e_o365_contact_begin_email_addresses (JsonBuilder *builder);
+void e_o365_contact_end_email_addresses (JsonBuilder *builder);
+const gchar * e_o365_contact_get_file_as (EO365Contact *contact);
+void e_o365_contact_add_file_as (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_generation (EO365Contact *contact);
+void e_o365_contact_add_generation (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_given_name (EO365Contact *contact);
+void e_o365_contact_add_given_name (JsonBuilder *builder,
+ const gchar *value);
+EO365PhysicalAddress *
+ e_o365_contact_get_home_address (EO365Contact *contact);
+void e_o365_contact_add_home_address (JsonBuilder *builder,
+ const gchar *city,
+ const gchar *country_or_region,
+ const gchar *postal_code,
+ const gchar *state,
+ const gchar *street);
+JsonArray * e_o365_contact_get_home_phones (EO365Contact *contact); /* const gchar * */
+void e_o365_contact_begin_home_phones (JsonBuilder *builder);
+void e_o365_contact_end_home_phones (JsonBuilder *builder);
+void e_o365_contact_add_home_phone (JsonBuilder *builder,
+ const gchar *value);
+JsonArray * e_o365_contact_get_im_addresses (EO365Contact *contact); /* const gchar * */
+void e_o365_contact_begin_im_addresses (JsonBuilder *builder);
+void e_o365_contact_end_im_addresses (JsonBuilder *builder);
+void e_o365_contact_add_im_address (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_initials (EO365Contact *contact);
+void e_o365_contact_add_initials (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_job_title (EO365Contact *contact);
+void e_o365_contact_add_job_title (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_manager (EO365Contact *contact);
+void e_o365_contact_add_manager (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_middle_name (EO365Contact *contact);
+void e_o365_contact_add_middle_name (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_mobile_phone (EO365Contact *contact);
+void e_o365_contact_add_mobile_phone (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_nick_name (EO365Contact *contact);
+void e_o365_contact_add_nick_name (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_office_location (EO365Contact *contact);
+void e_o365_contact_add_office_location (JsonBuilder *builder,
+ const gchar *value);
+EO365PhysicalAddress *
+ e_o365_contact_get_other_address (EO365Contact *contact);
+void e_o365_contact_add_other_address (JsonBuilder *builder,
+ const gchar *city,
+ const gchar *country_or_region,
+ const gchar *postal_code,
+ const gchar *state,
+ const gchar *street);
+const gchar * e_o365_contact_get_personal_notes (EO365Contact *contact);
+void e_o365_contact_add_personal_notes (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_profession (EO365Contact *contact);
+void e_o365_contact_add_profession (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_spouse_name (EO365Contact *contact);
+void e_o365_contact_add_spouse_name (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_surname (EO365Contact *contact);
+void e_o365_contact_add_surname (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_title (EO365Contact *contact);
+void e_o365_contact_add_title (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_yomi_company_name (EO365Contact *contact);
+void e_o365_contact_add_yomi_company_name (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_yomi_given_name (EO365Contact *contact);
+void e_o365_contact_add_yomi_given_name (JsonBuilder *builder,
+ const gchar *value);
+const gchar * e_o365_contact_get_yomi_surname (EO365Contact *contact);
+void e_o365_contact_add_yomi_surname (JsonBuilder *builder,
+ const gchar *value);
+
G_END_DECLS
#endif /* E_O365_JSON_UTILS_H */
diff --git a/src/Office365/registry/e-o365-backend.c b/src/Office365/registry/e-o365-backend.c
index d1b1c3c7..80fe6482 100644
--- a/src/Office365/registry/e-o365-backend.c
+++ b/src/Office365/registry/e-o365-backend.c
@@ -306,9 +306,7 @@ o365_backend_sync_folders_thread (GTask *task,
success = e_o365_connection_get_folders_delta_sync (cnc, NULL, E_O365_FOLDER_KIND_CONTACTS, NULL,
old_delta_link, 0,
o365_backend_got_contact_folders_delta_cb, o365_backend, &new_delta_link, cancellable,
&error);
- if (old_delta_link && *old_delta_link && (
- g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED) ||
- g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_BAD_REQUEST))) {
+ if (old_delta_link && *old_delta_link && e_o365_connection_util_delta_token_failed (error)) {
g_clear_pointer (&old_delta_link, g_free);
g_clear_error (&error);
@@ -642,7 +640,7 @@ o365_backend_authenticate_sync (EBackend *backend,
cnc = e_o365_connection_new (e_backend_get_source (backend), o365_settings);
- result = e_o365_connection_authenticate_sync (cnc, cancellable, error);
+ result = e_o365_connection_authenticate_sync (cnc, NULL, E_O365_FOLDER_KIND_UNKNOWN, NULL,
out_certificate_pem, out_certificate_errors, cancellable, error);
if (result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
e_collection_backend_authenticate_children (E_COLLECTION_BACKEND (backend), credentials);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]