static void
e_book_backend_webdav_remove_contacts (EBookBackend *backend,
                                       EDataBook *book,
                                       guint32 opid,
                                       GCancellable *cancellable,
                                       const GSList *id_list)
{
	EBookBackendWebdav        *webdav      = E_BOOK_BACKEND_WEBDAV (backend);
	EBookBackendWebdavPrivate *priv        = webdav->priv;
	gchar                     *uid         = id_list->data;
	GSList                     deleted_ids = {NULL,};
	guint                      status;

	if (!e_backend_get_online (E_BACKEND (backend))) {
		e_data_book_respond_remove_contacts (
			book, opid,
			EDB_ERROR (REPOSITORY_OFFLINE), NULL);
		return;
	}

	/* We make the assumption that the ID list we're passed is always exactly one element long, since we haven't specified "bulk-removes"
	 * in our static capability list. */
	if (id_list->next != NULL) {
		e_data_book_respond_remove_contacts (
			book, opid,
			EDB_ERROR_EX (NOT_SUPPORTED,
			_("The backend does not support bulk removals")),
			NULL);
		return;
	}

	status = delete_contact (webdav, uid, cancellable);
	if (status != 204) {
		if (status == 401 || status == 407) {
			e_data_book_respond_remove_contacts (
				book, opid,
				webdav_handle_auth_request (webdav), NULL);
		} else {
			g_warning ("DELETE failed with HTTP status %d", status);
			e_data_book_respond_remove_contacts (
				book, opid,
				e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
				_("DELETE failed with HTTP status %d"), status),
				NULL);
		}
		return;
	}

	g_mutex_lock (&priv->cache_lock);
	e_book_backend_cache_remove_contact (priv->cache, uid);
	g_mutex_unlock (&priv->cache_lock);

	deleted_ids.data = uid;
	e_data_book_respond_remove_contacts (book, opid, EDB_ERROR (SUCCESS), &deleted_ids);
}
static void
e_book_backend_webdav_modify_contacts (EBookBackend *backend,
                                       EDataBook *book,
                                       guint32 opid,
                                       GCancellable *cancellable,
                                       const GSList *vcards)
{
	EBookBackendWebdav        *webdav  = E_BOOK_BACKEND_WEBDAV (backend);
	EBookBackendWebdavPrivate *priv    = webdav->priv;
	EContact                  *contact;
	GSList                     modified_contacts = {NULL,};
	const gchar                *uid;
	const gchar                *etag;
	guint status;
	gchar *status_reason = NULL;
	const gchar *vcard = vcards->data;

	if (!e_backend_get_online (E_BACKEND (backend))) {
		e_data_book_respond_create_contacts (
			book, opid,
			EDB_ERROR (REPOSITORY_OFFLINE), NULL);
		return;
	}

	/* We make the assumption that the vCard list we're passed is always exactly one element long, since we haven't specified "bulk-modifies"
	 * in our static capability list. This is because there is no clean way to roll back changes in case of an error. */
	if (vcards->next != NULL) {
		e_data_book_respond_modify_contacts (
			book, opid,
			EDB_ERROR_EX (
				NOT_SUPPORTED,
				_("The backend does not support bulk modifications")),
			NULL);
		return;
	}

	/* modify contact */
	contact = e_contact_new_from_vcard (vcard);
	status = upload_contact (webdav, contact, &status_reason, cancellable);
	if (status != 201 && status != 204) {
		g_object_unref (contact);
		if (status == 401 || status == 407) {
			e_data_book_respond_modify_contacts (book, opid, webdav_handle_auth_request (webdav), NULL);
			g_free (status_reason);
			return;
		}
		/* data changed on server while we were editing */
		if (status == 412) {
			/* too bad no special error code in evolution for this... */
			e_data_book_respond_modify_contacts (book, opid,
				e_data_book_create_error_fmt (
				E_DATA_BOOK_STATUS_OTHER_ERROR,
				_("Contact on server changed -> not modifying")),
				NULL);
			g_free (status_reason);
			return;
		}

		e_data_book_respond_modify_contacts (book, opid,
			e_data_book_create_error_fmt (
			E_DATA_BOOK_STATUS_OTHER_ERROR,
			_("Modify contact failed with HTTP status: %d (%s)"),
			status, status_reason),
			NULL);
		g_free (status_reason);
		return;
	}

	g_free (status_reason);

	uid = e_contact_get_const (contact, E_CONTACT_UID);
	g_mutex_lock (&priv->cache_lock);
	e_book_backend_cache_remove_contact (priv->cache, uid);

	etag = e_contact_get_const (contact, E_CONTACT_REV);

	/* PUT request didn't return an etag? try downloading to get one */
	if (etag == NULL || (etag[0] == 'W' && etag[1] == '/')) {
		EContact *new_contact;

		g_warning ("Server didn't return etag for modified address resource");
		new_contact = download_contact (webdav, uid, cancellable);
		if (new_contact != NULL) {
			contact = new_contact;
		}
	}
	e_book_backend_cache_add_contact (priv->cache, contact);
	g_mutex_unlock (&priv->cache_lock);

	modified_contacts.data = contact;
	e_data_book_respond_modify_contacts (book, opid, EDB_ERROR (SUCCESS), &modified_contacts);

	g_object_unref (contact);
}
static void
e_book_backend_webdav_create_contacts (EBookBackend *backend,
                                       EDataBook *book,
                                       guint32 opid,
                                       GCancellable *cancellable,
                                       const GSList *vcards)
{
	EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (backend);
	EBookBackendWebdavPrivate *priv   = webdav->priv;
	EContact                  *contact;
	gchar                     *uid;
	guint                      status;
	gchar                     *status_reason = NULL;
	const gchar               *vcard = (const gchar *) vcards->data;
	GSList                     added_contacts = {NULL,};

	/* We make the assumption that the vCard list we're passed is always exactly one element long, since we haven't specified "bulk-adds"
	 * in our static capability list. This is because there is no clean way to roll back changes in case of an error. */
	if (vcards->next != NULL) {
		e_data_book_respond_create_contacts (
			book, opid,
			EDB_ERROR_EX (NOT_SUPPORTED,
			_("The backend does not support bulk additions")),
			NULL);
		return;
	}

	if (!e_backend_get_online (E_BACKEND (backend))) {
		e_data_book_respond_create_contacts (book, opid, EDB_ERROR (REPOSITORY_OFFLINE), NULL);
		return;
	}

	/* do 3 rand() calls to construct a unique ID... poor way but should be
	 * good enough for us */
	uid = g_strdup_printf (
		"%s%08X-%08X-%08X.vcf",
		priv->uri, rand (), rand (), rand ());

	contact = e_contact_new_from_vcard_with_uid (vcard, uid);

	/* kill revision field (might have been set by some other backend) */
	e_contact_set (contact, E_CONTACT_REV, NULL);

	status = upload_contact (webdav, contact, &status_reason, cancellable);
	if (status != 201 && status != 204) {
		g_object_unref (contact);
		if (status == 401 || status == 407) {
			e_data_book_respond_create_contacts (book, opid, webdav_handle_auth_request (webdav), NULL);
		} else {
			e_data_book_respond_create_contacts (
				book, opid,
				e_data_book_create_error_fmt (
				E_DATA_BOOK_STATUS_OTHER_ERROR,
				_("Create resource '%s' failed with HTTP status: %d (%s)"),
				uid, status, status_reason),
					NULL);
		}
		g_free (uid);
		g_free (status_reason);
		return;
	}

	g_free (status_reason);

	/* PUT request didn't return an etag? try downloading to get one */
	if (e_contact_get_const (contact, E_CONTACT_REV) == NULL) {
		const gchar *new_uid;
		EContact *new_contact;

		g_warning ("Server didn't return etag for new address resource");
		new_uid = e_contact_get_const (contact, E_CONTACT_UID);
		new_contact = download_contact (webdav, new_uid, cancellable);
		g_object_unref (contact);

		if (new_contact == NULL) {
			e_data_book_respond_create_contacts (
				book, opid,
				EDB_ERROR (OTHER_ERROR), NULL);
			g_free (uid);
			return;
		}
		contact = new_contact;
	}

	g_mutex_lock (&priv->cache_lock);
	e_book_backend_cache_add_contact (priv->cache, contact);
	g_mutex_unlock (&priv->cache_lock);

	added_contacts.data = contact;
	e_data_book_respond_create_contacts (book, opid, EDB_ERROR (SUCCESS), &added_contacts);

	g_object_unref (contact);
	g_free (uid);
}
static void
e_book_backend_webdav_open (EBookBackend *backend,
                            EDataBook *book,
                            guint opid,
                            GCancellable *cancellable,
                            gboolean only_if_exists)
{
	EBookBackendWebdav        *webdav = E_BOOK_BACKEND_WEBDAV (backend);
	EBookBackendWebdavPrivate *priv   = webdav->priv;
	ESourceAuthentication     *auth_extension;
	ESourceOffline            *offline_extension;
	ESourceWebdav             *webdav_extension;
	ESource                   *source;
	const gchar               *extension_name;
	const gchar               *cache_dir;
	gchar                     *filename;
	SoupSession               *session;
	SoupURI                   *suri;
	GError                    *error = NULL;

	/* will try fetch ctag for the first time, if it fails then sets this to FALSE */
	priv->supports_getctag = TRUE;

	source = e_backend_get_source (E_BACKEND (backend));
	cache_dir = e_book_backend_get_cache_dir (backend);

	extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
	auth_extension = e_source_get_extension (source, extension_name);

	extension_name = E_SOURCE_EXTENSION_OFFLINE;
	offline_extension = e_source_get_extension (source, extension_name);

	extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
	webdav_extension = e_source_get_extension (source, extension_name);

	priv->marked_for_offline =
		e_source_offline_get_stay_synchronized (offline_extension);

	if (!e_backend_get_online (E_BACKEND (backend)) && !priv->marked_for_offline ) {
		e_book_backend_respond_opened (backend, book, opid, EDB_ERROR (OFFLINE_UNAVAILABLE));
		return;
	}

	suri = e_source_webdav_dup_soup_uri (webdav_extension);

	priv->uri = soup_uri_to_string (suri, FALSE);
	if (!priv->uri || !*priv->uri) {
		g_free (priv->uri);
		priv->uri = NULL;
		soup_uri_free (suri);
		e_book_backend_respond_opened (backend, book, opid, EDB_ERROR_EX (OTHER_ERROR, _("Cannot transform SoupURI to string")));
		return;
	}

	g_mutex_lock (&priv->cache_lock);

	/* make sure the priv->uri ends with a forward slash */
	if (priv->uri[strlen (priv->uri) - 1] != '/') {
		gchar *tmp = priv->uri;
		priv->uri = g_strconcat (tmp, "/", NULL);
		g_free (tmp);
	}

	if (!priv->cache) {
		filename = g_build_filename (cache_dir, "cache.xml", NULL);
		priv->cache = e_book_backend_cache_new (filename);
		g_free (filename);
	}
	g_mutex_unlock (&priv->cache_lock);

	session = soup_session_sync_new ();
	g_object_set (
		session,
		SOUP_SESSION_TIMEOUT, 90,
		SOUP_SESSION_SSL_STRICT, TRUE,
		SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
		NULL);

	e_source_webdav_unset_temporary_ssl_trust (webdav_extension);

	g_signal_connect (
		session, "authenticate",
		G_CALLBACK (soup_authenticate), webdav);

	priv->session = session;
	priv->proxy = e_proxy_new ();
	e_proxy_setup_proxy (priv->proxy);
	g_signal_connect (
		priv->proxy, "changed",
		G_CALLBACK (proxy_settings_changed), priv);
	proxy_settings_changed (priv->proxy, priv);
	webdav_debug_setup (priv->session);

	e_book_backend_notify_online (backend, TRUE);
	e_book_backend_notify_readonly (backend, FALSE);

	if (e_source_authentication_required (auth_extension))
		e_backend_authenticate_sync (
			E_BACKEND (backend),
			E_SOURCE_AUTHENTICATOR (backend),
			cancellable, &error);

	soup_uri_free (suri);

	/* This function frees the GError passed to it. */
	e_book_backend_respond_opened (backend, book, opid, error);
}
static void
ebbm_contacts_modify_contacts (EBookBackendMAPI *ebma, GCancellable *cancellable, const GSList *vcards, GSList **modified_contacts, GError **error)
{
	EBookBackendMAPIContacts *ebmac;
	EBookBackendMAPIContactsPrivate *priv;
	EMapiConnection *conn;
	EMapiCreateitemData mcd;
	EContact *contact;
	GError *mapi_error = NULL;
	mapi_id_t mid;

	e_mapi_return_data_book_error_if_fail (ebma != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
	e_mapi_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_MAPI_CONTACTS (ebma), E_DATA_BOOK_STATUS_INVALID_ARG);
	e_mapi_return_data_book_error_if_fail (vcards != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
	e_mapi_return_data_book_error_if_fail (modified_contacts != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);

	ebmac = E_BOOK_BACKEND_MAPI_CONTACTS (ebma);
	e_mapi_return_data_book_error_if_fail (ebmac != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);

	priv = ebmac->priv;
	e_mapi_return_data_book_error_if_fail (priv != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);

	if (vcards->next != NULL) {
		g_propagate_error (error, EDB_ERROR_EX (NOT_SUPPORTED, _("The backend does not support bulk modifications")));
		return;
	}

	e_book_backend_mapi_lock_connection (ebma);

	conn = e_book_backend_mapi_get_connection (ebma, cancellable, &mapi_error);
	if (!conn) {
		e_book_backend_mapi_unlock_connection (ebma);

		if (!mapi_error)
			g_propagate_error (error, EDB_ERROR (REPOSITORY_OFFLINE));
		else
			mapi_error_to_edb_error (error, mapi_error, E_DATA_BOOK_STATUS_REPOSITORY_OFFLINE, NULL);
		g_clear_error (&mapi_error);

		return;
	}

	contact = e_contact_new_from_vcard (vcards->data);
	if (!contact) {
		g_propagate_error (error, EDB_ERROR (REPOSITORY_OFFLINE));
		e_book_backend_mapi_unlock_connection (ebma);
		return;
	}

	e_book_backend_mapi_get_db (ebma, &mcd.db);
	mcd.contact = contact;

	if (e_mapi_util_mapi_id_from_string (e_contact_get_const (contact, E_CONTACT_UID), &mid)) {
		mapi_object_t obj_folder;
		gboolean status;

		status = ebbm_contacts_open_folder (ebmac, conn, &obj_folder, cancellable, &mapi_error);

		if (status) {
			status = e_mapi_connection_modify_object (conn, &obj_folder, mid,
								  ebbm_contact_to_object, &mcd,
								  cancellable, &mapi_error);

			e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
		}

		e_book_backend_mapi_maybe_disconnect (ebma, mapi_error);
		if (!status) {
			mapi_error_to_edb_error (error, mapi_error, E_DATA_BOOK_STATUS_OTHER_ERROR, _("Failed to modify item on a server"));
			if (mapi_error)
				g_error_free (mapi_error);

			g_object_unref (contact);
		} else {
			*modified_contacts = g_slist_append (NULL, contact);
		}
	} else {
		g_debug ("%s: Failed to decode MID from '%s'", G_STRFUNC, (const gchar *) e_contact_get_const (contact, E_CONTACT_UID));
	}

	e_book_backend_mapi_unlock_connection (ebma);
}
static void
ebbm_contacts_create_contacts (EBookBackendMAPI *ebma, GCancellable *cancellable, const GSList *vcards, GSList **added_contacts, GError **error)
{
	EBookBackendMAPIContacts *ebmac;
	EBookBackendMAPIContactsPrivate *priv;
	EMapiConnection *conn;
	EMapiCreateitemData mcd;
	GError *mapi_error = NULL;
	mapi_id_t mid = 0;
	mapi_object_t obj_folder;
	gboolean status;
	gchar *id;
	EContact *contact;

	e_mapi_return_data_book_error_if_fail (ebma != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
	e_mapi_return_data_book_error_if_fail (E_IS_BOOK_BACKEND_MAPI_CONTACTS (ebma), E_DATA_BOOK_STATUS_INVALID_ARG);
	e_mapi_return_data_book_error_if_fail (vcards != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);
	e_mapi_return_data_book_error_if_fail (added_contacts != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);

	ebmac = E_BOOK_BACKEND_MAPI_CONTACTS (ebma);
	e_mapi_return_data_book_error_if_fail (ebmac != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);

	priv = ebmac->priv;
	e_mapi_return_data_book_error_if_fail (priv != NULL, E_DATA_BOOK_STATUS_INVALID_ARG);

	if (vcards->next) {
		g_propagate_error (error, EDB_ERROR_EX (NOT_SUPPORTED, _("The backend does not support bulk additions")));
		return;
	}

	e_book_backend_mapi_lock_connection (ebma);

	conn = e_book_backend_mapi_get_connection (ebma, cancellable, &mapi_error);
	if (!conn) {
		e_book_backend_mapi_unlock_connection (ebma);

		if (!mapi_error)
			g_propagate_error (error, EDB_ERROR (REPOSITORY_OFFLINE));
		else
			mapi_error_to_edb_error (error, mapi_error, E_DATA_BOOK_STATUS_REPOSITORY_OFFLINE, NULL);
		g_clear_error (&mapi_error);

		return;
	}

	contact = e_contact_new_from_vcard (vcards->data);
	if (!contact) {
		g_propagate_error (error, EDB_ERROR (REPOSITORY_OFFLINE));
		e_book_backend_mapi_unlock_connection (ebma);
		return;
	}

	e_book_backend_mapi_get_db (ebma, &mcd.db);
	mcd.contact = contact;

	status = ebbm_contacts_open_folder (ebmac, conn, &obj_folder, cancellable, &mapi_error);

	if (status) {
		e_mapi_connection_create_object (conn, &obj_folder, E_MAPI_CREATE_FLAG_NONE,
						 ebbm_contact_to_object, &mcd,
						 &mid, cancellable, &mapi_error);
		e_mapi_connection_close_folder (conn, &obj_folder, cancellable, &mapi_error);
	}

	e_book_backend_mapi_maybe_disconnect (ebma, mapi_error);
	e_book_backend_mapi_unlock_connection (ebma);

	if (!mid) {
		mapi_error_to_edb_error (error, mapi_error, E_DATA_BOOK_STATUS_OTHER_ERROR, _("Failed to create item on a server"));

		if (mapi_error)
			g_error_free (mapi_error);

		g_object_unref (contact);
		return;
	}

	id = e_mapi_util_mapi_id_to_string (mid);

	/* UID of the contact is nothing but the concatenated string of hex id of folder and the message.*/
	e_contact_set (contact, E_CONTACT_UID, id);
	e_contact_set (contact, E_CONTACT_BOOK_UID, e_book_backend_mapi_get_book_uid (ebma));

	g_free (id);

	*added_contacts = g_slist_append (NULL, contact);
}