ebb_ews_get_changes_sync (EBookMetaBackend *meta_backend,
			  const gchar *last_sync_tag,
			  gboolean is_repeat,
			  gchar **out_new_sync_tag,
			  gboolean *out_repeat,
			  GSList **out_created_objects,
			  GSList **out_modified_objects,
			  GSList **out_removed_objects,
			  GCancellable *cancellable,
			  GError **error)
{
	EBookBackendEws *bbews;
	EBookCache *book_cache;
	gboolean success = TRUE;
	GError *local_error = NULL;

	g_return_val_if_fail (E_IS_BOOK_BACKEND_EWS (meta_backend), FALSE);
	g_return_val_if_fail (out_new_sync_tag != NULL, FALSE);
	g_return_val_if_fail (out_repeat != NULL, FALSE);
	g_return_val_if_fail (out_created_objects != NULL, FALSE);
	g_return_val_if_fail (out_modified_objects != NULL, FALSE);
	g_return_val_if_fail (out_removed_objects != NULL, FALSE);

	*out_created_objects = NULL;
	*out_modified_objects = NULL;
	*out_removed_objects = NULL;

	bbews = E_BOOK_BACKEND_EWS (meta_backend);

	book_cache = e_book_meta_backend_ref_cache (meta_backend);
	g_return_val_if_fail (E_IS_BOOK_CACHE (book_cache), FALSE);

	g_rec_mutex_lock (&bbews->priv->cnc_lock);

	if (bbews->priv->is_gal) {
		CamelEwsSettings *ews_settings;
		gchar *oab_url;

		ews_settings = ebb_ews_get_collection_settings (bbews);
		oab_url = camel_ews_settings_dup_oaburl (ews_settings);

		if (oab_url && *oab_url &&
		    camel_ews_settings_get_oab_offline (ews_settings)) {
			EEwsConnection *oab_cnc;
			GSList *full_l = NULL, *deltas = NULL, *link;
			EwsOALDetails *full = NULL;
			gchar *password, *etag = NULL;
			gint sequence;

			sequence = e_cache_get_key_int (E_CACHE (book_cache), "gal-sequence", NULL);
			if (sequence == -1)
				sequence = 0;

			oab_cnc = e_ews_connection_new_for_backend (E_BACKEND (bbews), e_book_backend_get_registry (E_BOOK_BACKEND (bbews)), oab_url, ews_settings);

			e_binding_bind_property (
				bbews, "proxy-resolver",
				oab_cnc, "proxy-resolver",
				G_BINDING_SYNC_CREATE);

			password = e_ews_connection_dup_password (bbews->priv->cnc);
			e_ews_connection_set_password (oab_cnc, password);
			e_util_safe_free_string (password);

			d (printf ("Ewsgal: Fetching oal full details file\n"));
			if (!e_ews_connection_get_oal_detail_sync (oab_cnc, bbews->priv->folder_id, NULL, last_sync_tag, &full_l, &etag, cancellable, &local_error)) {
				if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_NOT_MODIFIED)) {
					g_clear_error (&local_error);
				} else {
					success = FALSE;
				}
			}

			if (success && full_l) {
				guint32 delta_size = 0;

				for (link = full_l; link; link = g_slist_next (link)) {
					EwsOALDetails *det = link->data;

					/* Throw away anything older than we already have */
					if (det->seq <= sequence) {
						ews_oal_details_free (det);
					} else if (!g_strcmp0 (det->type, "Full")) {
						if (full)
							ews_oal_details_free (full);
						full = det;
					} else if (sequence > 0 && !g_strcmp0 (det->type, "Diff")) {
						delta_size += det->size;
						deltas = g_slist_insert_sorted (deltas, det, det_sort_func);
					} else {
						ews_oal_details_free (det);
					}
				}

				g_slist_free (full_l);
				full_l = NULL;

				/* If the deltas would be bigger, just download the new full file */
				if (full && delta_size > full->size) {
					g_slist_free_full (deltas, (GDestroyNotify) ews_oal_details_free);
					deltas = NULL;
				}
			}

			if (full) {
				gchar *uncompressed_filename;

				uncompressed_filename = ebb_ews_download_gal (bbews, book_cache, full, deltas, sequence, cancellable, &local_error);
				if (!uncompressed_filename) {
					success = FALSE;
				} else {
					d (printf ("Ewsgal: Removing old gal\n"));
					/* remove old_gal_file */
					ebb_ews_remove_old_gal_file (book_cache);

					d (printf ("Ewsgal: Check for changes in GAL\n"));
					success = ebb_ews_check_gal_changes (bbews, book_cache, uncompressed_filename,
						out_created_objects, out_modified_objects, out_removed_objects, cancellable, &local_error);

					if (success) {
						if (e_cache_set_key (E_CACHE (book_cache), "oab-filename", uncompressed_filename, NULL)) {
							/* Don't let it get deleted */
							g_free (uncompressed_filename);
							uncompressed_filename = NULL;
						}

						e_cache_set_key_int (E_CACHE (book_cache), "gal-sequence", full->seq, NULL);

						d (printf ("Ewsgal: sync successfully completed\n"));
					}

					ews_oal_details_free (full);
				}

				if (uncompressed_filename) {
					/* preserve  the oab file once we are able to decode the differential updates */
					g_unlink (uncompressed_filename);
					g_free (uncompressed_filename);
				}
			}

			g_slist_free_full (full_l, (GDestroyNotify) ews_oal_details_free);
			g_slist_free_full (deltas, (GDestroyNotify) ews_oal_details_free);
			g_clear_object (&oab_cnc);

			if (success)
				*out_new_sync_tag = etag;
			else
				g_free (etag);

			if (local_error) {
				g_prefix_error (&local_error, "%s", _("Failed to update GAL:"));
				g_propagate_error (error, local_error);
			}
		}

		g_free (oab_url);
	} else {
		GSList *items_created = NULL, *items_modified = NULL, *items_deleted = NULL, *link;
		gboolean includes_last_item = TRUE;

		success = e_ews_connection_sync_folder_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM,
			last_sync_tag, bbews->priv->folder_id, "IdOnly", NULL, EWS_MAX_FETCH_COUNT,
			out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted,
			cancellable, &local_error);

		if (!success &&
		    g_error_matches (local_error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_INVALIDSYNCSTATEDATA)) {
			g_clear_error (&local_error);

			e_book_meta_backend_empty_cache_sync (meta_backend, cancellable, NULL);

			success = e_ews_connection_sync_folder_items_sync (bbews->priv->cnc, EWS_PRIORITY_MEDIUM,
				NULL, bbews->priv->folder_id, "IdOnly", NULL, EWS_MAX_FETCH_COUNT,
				out_new_sync_tag, &includes_last_item, &items_created, &items_modified, &items_deleted,
				cancellable, &local_error);
		}

		if (success) {
			GSList *contacts_created = NULL, *contacts_modified = NULL;

			/* The sync state doesn't cover changes made by save_contact_sync(),
			   thus verify the changes, instead of re-donwloading the contacts again */
			items_created = ebb_ews_verify_changes (book_cache, items_created, cancellable);
			items_modified = ebb_ews_verify_changes (book_cache, items_modified, cancellable);

			if (items_created) {
				success = ebb_ews_fetch_items_sync (bbews, items_created, &contacts_created, cancellable, error);
				if (success)
					*out_created_objects = ebb_ews_contacts_to_infos (contacts_created, bbews->priv->is_gal);
			}

			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, bbews->priv->is_gal);
			}

			for (link = items_deleted; link; link = g_slist_next (link)) {
				const gchar *uid = link->data;

				*out_removed_objects = g_slist_prepend (*out_removed_objects,
					e_book_meta_backend_info_new (uid, NULL, NULL, NULL));
			}

			g_slist_free_full (contacts_created, g_object_unref);
			g_slist_free_full (contacts_modified, g_object_unref);

			*out_repeat = !includes_last_item;
		} else if (local_error) {
			g_propagate_error (error, local_error);
		}

		g_slist_free_full (items_created, g_object_unref);
		g_slist_free_full (items_modified, g_object_unref);
		g_slist_free_full (items_deleted, g_free);
	}

	g_rec_mutex_unlock (&bbews->priv->cnc_lock);

	ebb_ews_convert_error_to_edb_error (error);
	ebb_ews_maybe_disconnect_sync (bbews, error, cancellable);

	g_clear_object (&book_cache);

	return success;
}