e_mail_formatter_parse_html_mnemonics (const gchar *label,
                                       gchar **out_access_key)
{
	const gchar *pos = NULL;
	GString *html_label = NULL;

	g_return_val_if_fail (label != NULL, NULL);

	if (out_access_key != NULL)
		*out_access_key = NULL;

	if (!g_utf8_validate (label, -1, NULL)) {
		gchar *res = g_strdup (label);

		g_return_val_if_fail (g_utf8_validate (label, -1, NULL), res);

		return res;
	}

	pos = strstr (label, "_");
	if (pos != NULL) {
		gunichar uk;

		html_label = g_string_new ("");
		g_string_append_len (html_label, label, pos - label);

		pos++;
		uk = g_utf8_get_char (pos);

		pos = g_utf8_next_char (pos);

		g_string_append (html_label, "<u>");
		g_string_append_unichar (html_label, uk);
		g_string_append (html_label, "</u>");
		g_string_append (html_label, pos);

		if (out_access_key != NULL && uk != 0) {
			gchar ukstr[10];
			gint len;

			len = g_unichar_to_utf8 (g_unichar_toupper (uk), ukstr);
			if (len > 0)
				*out_access_key = g_strndup (ukstr, len);
		}

	} else {
		html_label = g_string_new (label);
	}

	return g_string_free (html_label, FALSE);
}


e_mail_part_get_validity_flags (EMailPart *part)
{
	EMailPartValidityFlags flags = 0;
	GList *head, *link;

	g_return_val_if_fail (E_IS_MAIL_PART (part), 0);

	head = g_queue_peek_head_link (&part->validities);

	for (link = head; link != NULL; link = g_list_next (link)) {
		EMailPartValidityPair *pair = link->data;

		if (pair != NULL)
			flags |= pair->validity_type;
	}

	return flags;
}


e_mail_parser_parse_part_as (EMailParser *parser,
                             CamelMimePart *part,
                             GString *part_id,
                             const gchar *mime_type,
                             GCancellable *cancellable,
                             GQueue *out_mail_parts)
{
	GQueue *parsers;
	GList *iter;
	gboolean mime_part_handled = FALSE;

	parsers = e_mail_parser_get_parsers (parser, mime_type);

	if (parsers == NULL) {
		e_mail_parser_wrap_as_attachment (
			parser, part, part_id, out_mail_parts);
		return TRUE;
	}

	for (iter = parsers->head; iter; iter = iter->next) {
		EMailParserExtension *extension;

		extension = iter->data;
		if (!extension)
			continue;

		mime_part_handled = e_mail_parser_extension_parse (
			extension, parser, part, part_id,
			cancellable, out_mail_parts);

		if (mime_part_handled)
			break;
	}

	return mime_part_handled;
}


mail_parser_parse_thread (GSimpleAsyncResult *simple,
                          GObject *source_object,
                          GCancellable *cancellable)
{
	EMailPartList *part_list;

	part_list = g_simple_async_result_get_op_res_gpointer (simple);

	mail_parser_run (
		E_MAIL_PARSER (source_object),
		part_list, cancellable);
}


e_ews_backend_sync_folders_finish (EEwsBackend *backend,
                                   GAsyncResult *result,
                                   GError **error)
{
	GSimpleAsyncResult *simple;

	g_return_val_if_fail (
		g_simple_async_result_is_valid (
		result, G_OBJECT (backend),
		e_ews_backend_sync_folders), FALSE);

	simple = G_SIMPLE_ASYNC_RESULT (result);

	/* Assume success unless a GError is set. */
	return !g_simple_async_result_propagate_error (simple, error);
}


ews_backend_dup_resource_id (ECollectionBackend *backend,
                             ESource *child_source)
{
	ESourceEwsFolder *extension;
	const gchar *extension_name;

	extension_name = E_SOURCE_EXTENSION_EWS_FOLDER;
	extension = e_source_get_extension (child_source, extension_name);

	return e_source_ews_folder_dup_id (extension);
}


e_ews_connection_query_auth_methods_finish (EEwsConnection *cnc,
					    GAsyncResult *result,
					    GSList **auth_methods,
					    GError **error)
{
	GSimpleAsyncResult *simple;
	EwsAsyncData *async_data;

	g_return_val_if_fail (cnc != NULL, FALSE);
	g_return_val_if_fail (auth_methods != NULL, FALSE);
	g_return_val_if_fail (
		g_simple_async_result_is_valid (
		result, G_OBJECT (cnc), e_ews_connection_query_auth_methods),
		FALSE);

	simple = G_SIMPLE_ASYNC_RESULT (result);
	async_data = g_simple_async_result_get_op_res_gpointer (simple);

	if (GPOINTER_TO_INT (g_object_get_data (G_OBJECT (simple), EWS_OBJECT_KEY_AUTHS_GATHERED)) != 1 &&
	    g_simple_async_result_propagate_error (simple, error))
		return FALSE;

	*auth_methods = g_slist_reverse (async_data->items);

	return TRUE;
}


e_book_backend_ews_class_init (EBookBackendEwsClass *klass)
{
	GObjectClass *object_class;
	EBackendClass *backend_class;
	EBookBackendClass *book_backend_class;
	EBookMetaBackendClass *book_meta_backend_class;

	g_type_class_add_private (klass, sizeof (EBookBackendEwsPrivate));

	book_meta_backend_class = E_BOOK_META_BACKEND_CLASS (klass);
	book_meta_backend_class->backend_module_filename = "libebookbackendews.so";
	book_meta_backend_class->backend_factory_type_name = "EBookBackendEwsFactory";
	book_meta_backend_class->connect_sync = ebb_ews_connect_sync;
	book_meta_backend_class->disconnect_sync = ebb_ews_disconnect_sync;
	book_meta_backend_class->get_changes_sync = ebb_ews_get_changes_sync;
	book_meta_backend_class->load_contact_sync = ebb_ews_load_contact_sync;
	book_meta_backend_class->save_contact_sync = ebb_ews_save_contact_sync;
	book_meta_backend_class->remove_contact_sync = ebb_ews_remove_contact_sync;
	book_meta_backend_class->search_sync = ebb_ews_search_sync;
	book_meta_backend_class->search_uids_sync = ebb_ews_search_uids_sync;

	book_backend_class = E_BOOK_BACKEND_CLASS (klass);
	book_backend_class->get_backend_property = ebb_ews_get_backend_property;

	backend_class = E_BACKEND_CLASS (klass);
	backend_class->get_destination_address = ebb_ews_get_destination_address;

	object_class = G_OBJECT_CLASS (klass);
	object_class->constructed = e_book_backend_ews_constructed;
	object_class->dispose = e_book_backend_ews_dispose;
	object_class->finalize = e_book_backend_ews_finalize;
}


action_global_subscribe_foreign_folder_cb (GtkAction *action,
					   EShellView *shell_view)
{
	EShell *shell;
	EShellBackend *shell_backend;
	EShellWindow *shell_window;
	EClientCache *client_cache;
	CamelSession *session = NULL;

	g_return_if_fail (E_IS_SHELL_VIEW (shell_view));

	shell_window = e_shell_view_get_shell_window (shell_view);
	shell = e_shell_window_get_shell (shell_window);
	shell_backend = e_shell_get_backend_by_name (shell, "mail");

	if (shell_backend)
		g_object_get (G_OBJECT (shell_backend), "session", &session, NULL);

	if (!session)
		return;

	client_cache = e_shell_get_client_cache (shell);

	e_ews_subscribe_foreign_folder (GTK_WINDOW (shell_window), session, NULL, client_cache);

	g_object_unref (session);
}


autodiscover_cancelled_cb (GCancellable *cancellable,
                           EEwsConnection *cnc)
{
	ews_connection_schedule_abort (cnc);
}


mail_config_ews_autodiscover_set_property (GObject *object,
                                           guint property_id,
                                           const GValue *value,
                                           GParamSpec *pspec)
{
	switch (property_id) {
		case PROP_BACKEND:
			mail_config_ews_autodiscover_set_backend (
				E_MAIL_CONFIG_EWS_AUTODISCOVER (object),
				g_value_get_object (value));
			return;
	}

	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}


e_ews_connection_delete_attachments_sync (EEwsConnection *cnc,
                                          gint pri,
                                          const GSList *attachments_ids,
					  gchar **new_change_key,
                                          GCancellable *cancellable,
                                          GError **error)
{
	EAsyncClosure *closure;
	GAsyncResult *result;
	gboolean ret;

	g_return_val_if_fail (cnc != NULL, FALSE);

	closure = e_async_closure_new ();

	e_ews_connection_delete_attachments (
		cnc, pri, attachments_ids, cancellable,
		e_async_closure_callback, closure);

	result = e_async_closure_wait (closure);

	ret = e_ews_connection_delete_attachments_finish (
		cnc, result, new_change_key, error);

	e_async_closure_free (closure);

	return ret;
}


ebb_ews_convert_dl_to_updatexml_cb (ESoapMessage *msg,
				    gpointer user_data,
				    GError **error)
{
	ConvertData *cd = user_data;
	EContact *old_contact = cd->old_contact;
	EContact *new_contact = cd->new_contact;
	gchar *change_key = NULL;

	if (!cd->change_key) {
		change_key = e_vcard_util_dup_x_attribute (E_VCARD (old_contact), X_EWS_CHANGEKEY);
		if (!change_key)
			change_key = e_contact_get (old_contact, E_CONTACT_REV);
	}

	e_ews_message_start_item_change (msg, E_EWS_ITEMCHANGE_TYPE_ITEM,
		e_contact_get_const (old_contact, E_CONTACT_UID),
		cd->change_key ? cd->change_key : change_key,
		0);
	e_ews_message_start_set_item_field (msg, "Members", "distributionlist", "DistributionList");
	ebb_ews_write_dl_members (msg, new_contact);
	e_ews_message_end_set_item_field (msg);
	e_ews_message_end_item_change (msg);

	g_free (change_key);

	return TRUE;
}


ews_connection_schedule_cancel_message (EEwsConnection *cnc,
                                        SoupMessage *message)
{
	EwsScheduleData *sd;
	GSource *source;

	g_return_if_fail (E_IS_EWS_CONNECTION (cnc));
	g_return_if_fail (SOUP_IS_MESSAGE (message));

	sd = g_new0 (EwsScheduleData, 1);
	sd->cnc = g_object_ref (cnc);
	sd->message = g_object_ref (message);
	sd->op = EWS_SCHEDULE_OP_CANCEL;

	source = g_idle_source_new ();
	g_source_set_priority (source, G_PRIORITY_DEFAULT);
	g_source_set_callback (source, ews_connection_scheduled_cb, sd, NULL);
	g_source_attach (source, cnc->priv->soup_context);
	g_source_unref (source);
}


folder_sizes_tree_populate (GtkTreeStore *store,
			    CamelFolderInfo *folder_info,
			    GtkTreeIter *parent,
			    FolderSizeDialogData *fsd)
{
	while (folder_info != NULL) {
		GtkTreeIter iter;
		const gchar *icon_name;
		const gchar *folder_size;

		icon_name = em_folder_utils_get_icon_name (folder_info->flags);
		if (g_strcmp0 (icon_name, "folder") == 0) {
			CamelFolder *folder;

			folder = camel_store_get_folder_sync (
				CAMEL_STORE (fsd->ews_store), folder_info->full_name, 0, NULL, NULL);

			if (folder != NULL) {
				if (em_utils_folder_is_drafts (fsd->registry, folder))
					icon_name = "accessories-text-editor";

				g_object_unref (folder);
			}
		}

		folder_size = g_hash_table_lookup (fsd->folder_sizes, folder_info->full_name);

		gtk_tree_store_append (store, &iter, parent);
		gtk_tree_store_set (store, &iter,
				COL_FOLDER_ICON, icon_name,
				COL_FOLDER_NAME, folder_info->display_name,
				COL_FOLDER_SIZE, folder_size,
				COL_FOLDER_FLAGS, folder_info->flags,
				-1);

		if (folder_info->child != NULL)
			folder_sizes_tree_populate (store, folder_info->child, &iter, fsd);

		folder_info = folder_info->next;
	}
}


ebews_set_im_changes (EBookBackendEws *bbews,
		      ESoapMessage *message,
		      EContact *new,
		      EContact *old,
		      gchar **out_new_change_key,
		      GCancellable *cancellable,
		      GError **error)
{

}


ebews_populate_emails_ex (EBookBackendEws *bbews,
			  EContact *contact,
			  EEwsItem *item,
			  gboolean require_smtp_prefix,
			  gboolean use_primary_address)
{
	if (set_email_address (contact, E_CONTACT_EMAIL_1, item, "EmailAddress1", require_smtp_prefix) && use_primary_address)
		return;

	if (set_email_address (contact, E_CONTACT_EMAIL_2, item, "EmailAddress2", require_smtp_prefix) && use_primary_address)
		return;

	if (set_email_address (contact, E_CONTACT_EMAIL_3, item, "EmailAddress3", require_smtp_prefix) && use_primary_address)
		return;
}


ecb_ews_get_destination_address (EBackend *backend,
				 gchar **host,
				 guint16 *port)
{
	CamelEwsSettings *ews_settings;
	SoupURI *soup_uri;
	gchar *host_url;
	gboolean result = FALSE;

	g_return_val_if_fail (port != NULL, FALSE);
	g_return_val_if_fail (host != NULL, FALSE);

	/* Sanity checking */
	if (!e_cal_backend_get_registry (E_CAL_BACKEND (backend)) ||
	    !e_backend_get_source (backend))
		return FALSE;

	ews_settings = ecb_ews_get_collection_settings (E_CAL_BACKEND_EWS (backend));
	g_return_val_if_fail (ews_settings != NULL, FALSE);

	host_url = camel_ews_settings_dup_hosturl (ews_settings);
	g_return_val_if_fail (host_url != NULL, FALSE);

	soup_uri = soup_uri_new (host_url);
	if (soup_uri) {
		*host = g_strdup (soup_uri_get_host (soup_uri));
		*port = soup_uri_get_port (soup_uri);

		result = *host && **host;
		if (!result) {
			g_free (*host);
			*host = NULL;
		}

		soup_uri_free (soup_uri);
	}

	g_free (host_url);

	return result;
}


camel_ews_store_get_ooo_alert_state (const CamelEwsStore *ews_store)
{
	g_return_val_if_fail (
			CAMEL_IS_EWS_STORE (ews_store),
			CAMEL_EWS_STORE_OOO_ALERT_STATE_UNKNOWN);

	return ews_store->priv->ooo_alert_state;
}


ews_backend_populate (ECollectionBackend *backend)
{
	ESource *source;
	EEwsBackend *ews_backend = E_EWS_BACKEND (backend);

	source = e_backend_get_source (E_BACKEND (backend));

	ews_backend->priv->need_update_folders = TRUE;

	if (!ews_backend->priv->source_changed_id) {
		ews_backend->priv->source_changed_id = g_signal_connect (
			source, "changed",
			G_CALLBACK (ews_backend_source_changed_cb), ews_backend);
	}

	/* do not do anything, if account is disabled */
	if (!e_source_get_enabled (source))
		return;

	ews_backend_add_gal_source (ews_backend);

	if (e_backend_get_online (E_BACKEND (backend))) {
		CamelEwsSettings *ews_settings;

		ews_settings = ews_backend_get_settings (ews_backend);

		if (e_ews_connection_utils_get_without_password (ews_settings)) {
			e_backend_schedule_authenticate (E_BACKEND (backend), NULL);
		} else {
			e_backend_credentials_required_sync (E_BACKEND (backend),
				E_SOURCE_CREDENTIALS_REASON_REQUIRED, NULL, 0, NULL,
				NULL, NULL);
		}
	} else {
		ews_backend_claim_old_resources (backend);
	}
}


e_ews_connection_queue_request (EEwsConnection *cnc,
                                ESoapMessage *msg,
                                EEwsResponseCallback cb,
                                gint pri,
                                GCancellable *cancellable,
                                GSimpleAsyncResult *simple)
{
	EwsNode *node;

	g_return_if_fail (cnc != NULL);
	g_return_if_fail (cb != NULL);
	g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (simple));

	node = ews_node_new ();
	node->msg = msg;
	node->pri = pri;
	node->cb = cb;
	node->cnc = cnc;
	node->simple = g_object_ref (simple);

	QUEUE_LOCK (cnc);
	cnc->priv->jobs = g_slist_insert_sorted (cnc->priv->jobs, (gpointer *) node, (GCompareFunc) comp_func);
	QUEUE_UNLOCK (cnc);

	if (cancellable) {
		node->cancellable = g_object_ref (cancellable);
		if (g_cancellable_is_cancelled (cancellable))
			ews_cancel_request (cancellable, node);
		else
			node->cancel_handler_id = g_cancellable_connect (
				cancellable,
				G_CALLBACK (ews_cancel_request),
				(gpointer) node, NULL);
	}

	ews_trigger_next_request (cnc);
}


ebb_ews_build_restriction (const gchar *query,
			   gchar **auto_comp_str)
{
	ESExpResult *r;
	ESExp *sexp;
	EBookBackendEwsSExpData *sdata;
	gboolean autocompletion = FALSE;
	gint i;

	*auto_comp_str = NULL;

	sexp = e_sexp_new ();
	sdata = g_new0 (EBookBackendEwsSExpData, 1);
	sdata->is_autocompletion = FALSE;

	for (i = 0; i < G_N_ELEMENTS (symbols); i++) {
		e_sexp_add_function (
			sexp, 0, (gchar *) symbols[i].name,
			symbols[i].func,
			sdata);
	}

	e_sexp_input_text (sexp, query, strlen (query));
	e_sexp_parse (sexp);

	r = e_sexp_eval (sexp);
	if (r) {
		autocompletion = sdata->is_autocompletion;
		if (autocompletion)
			*auto_comp_str = sdata->auto_comp_str;
		else
			g_free (sdata->auto_comp_str);
	}

	e_sexp_result_free (sexp, r);
	g_object_unref (sexp);
	g_free (sdata);

	return autocompletion && *auto_comp_str;
}


ews_get_recurring_date_transition (ESoapParameter *node)
{
	ESoapParameter *param;
	EEwsCalendarRecurringDateTransition *recurring_date_transition = NULL;
	EEwsCalendarTo *to = NULL;
	gchar *time_offset = NULL;
	gchar *month = NULL;
	gchar *day = NULL;
	gboolean success = FALSE;

	to = ews_get_to (node);
	if (to == NULL)
		goto exit;

	param = e_soap_parameter_get_first_child_by_name (node, "TimeOffset");
	if (param != NULL)
		time_offset = e_soap_parameter_get_string_value (param);

	if (time_offset == NULL)
		goto exit;

	param = e_soap_parameter_get_first_child_by_name (node, "Month");
	if (param != NULL)
		month = e_soap_parameter_get_string_value (param);

	if (month == NULL)
		goto exit;

	param = e_soap_parameter_get_first_child_by_name (node, "Day");
	if (param != NULL)
		day = e_soap_parameter_get_string_value (param);

	if (day == NULL)
		goto exit;

	success = TRUE;

exit:
	if (success) {
		recurring_date_transition = e_ews_calendar_recurring_date_transition_new ();
		recurring_date_transition->to = to;
		recurring_date_transition->time_offset = time_offset;
		recurring_date_transition->month = month;
		recurring_date_transition->day = day;
	} else {
		e_ews_calendar_to_free (to);
		g_free (time_offset);
		g_free (month);
		g_free (day);
	}

	return recurring_date_transition;
}


e_ews_connection_utils_prepare_auth_method (SoupSession *soup_session,
					    EwsAuthType auth_method)
{
	/* We used to disable Basic auth to avoid it getting in the way of
	 * our GSSAPI hacks. But leave it enabled in the case where NTLM is
	 * enabled, which is the default configuration. It's a useful fallback
	 * which people may be relying on. */
	if (auth_method == EWS_AUTH_TYPE_GSSAPI) {
		soup_session_add_feature_by_type (soup_session, E_SOUP_TYPE_AUTH_NEGOTIATE);
		soup_session_remove_feature_by_type (soup_session, SOUP_TYPE_AUTH_BASIC);
	} else if (auth_method == EWS_AUTH_TYPE_OAUTH2) {
		soup_session_add_feature_by_type (soup_session, E_TYPE_SOUP_AUTH_BEARER);
		soup_session_remove_feature_by_type (soup_session, SOUP_TYPE_AUTH_BASIC);
	} else if (auth_method == EWS_AUTH_TYPE_NTLM) {
		soup_session_add_feature_by_type (soup_session, SOUP_TYPE_AUTH_NTLM);
	}
}


e_ews_attachment_info_get_mime_type (EEwsAttachmentInfo *info)
{
	g_return_val_if_fail (info != NULL, NULL);
	g_return_val_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_INLINED, NULL);

	return info->data.inlined.mime_type;
}


ebb_ews_unset_connection (EBookBackendEws *bbews)
{
	g_return_if_fail (E_IS_BOOK_BACKEND_EWS (bbews));

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

	if (bbews->priv->cnc) {
		e_ews_connection_set_disconnected_flag (bbews->priv->cnc, TRUE);

		g_signal_handlers_disconnect_by_func (bbews->priv->cnc, ebb_ews_server_notification_cb, bbews);

		if (bbews->priv->subscription_key != 0) {
			e_ews_connection_disable_notifications_sync (
				bbews->priv->cnc,
				bbews->priv->subscription_key);
			bbews->priv->subscription_key = 0;
		}
	}

	g_clear_object (&bbews->priv->cnc);

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


e_ews_connection_set_bearer_auth (EEwsConnection *cnc,
				  ESoupAuthBearer *bearer_auth)
{
	g_return_if_fail (E_IS_EWS_CONNECTION (cnc));
	if (bearer_auth)
		g_return_if_fail (E_IS_SOUP_AUTH_BEARER (bearer_auth));

	g_mutex_lock (&cnc->priv->property_lock);

	if (bearer_auth != cnc->priv->bearer_auth) {
		g_clear_object (&cnc->priv->bearer_auth);
		cnc->priv->bearer_auth = bearer_auth;

		if (cnc->priv->bearer_auth)
			g_object_ref (cnc->priv->bearer_auth);
	}

	g_mutex_unlock (&cnc->priv->property_lock);
}


e_ews_connection_empty_folder (EEwsConnection *cnc,
			       gint pri,
			       const gchar *folder_id,
			       gboolean is_distinguished_id,
			       const gchar *delete_type,
			       gboolean delete_subfolders,
			       GCancellable *cancellable,
			       GAsyncReadyCallback callback,
			       gpointer user_data)
{
	ESoapMessage *msg;
	GSimpleAsyncResult *simple;
	EwsAsyncData *async_data;

	g_return_if_fail (cnc != NULL);

	msg = e_ews_message_new_with_header (
			cnc->priv->settings,
			cnc->priv->uri,
			cnc->priv->impersonate_user,
			"EmptyFolder",
			"DeleteType",
			delete_type,
			cnc->priv->version,
			E_EWS_EXCHANGE_2010,
			FALSE,
			TRUE);

	e_soap_message_add_attribute (msg, "DeleteSubFolders", delete_subfolders ? "true" : "false", NULL, NULL);

	e_soap_message_start_element (msg, "FolderIds", "messages", NULL);

	e_soap_message_start_element (
			msg,
			is_distinguished_id ? "DistinguishedFolderId" : "FolderId",
			NULL,
			NULL);
	e_soap_message_add_attribute (msg, "Id", folder_id, NULL, NULL);

	/* This element is required for delegate access */
	if (is_distinguished_id && cnc->priv->email) {
		e_soap_message_start_element (msg, "Mailbox", NULL, NULL);
		e_ews_message_write_string_parameter(
				msg, "EmailAddress", NULL, cnc->priv->email);
		e_soap_message_end_element (msg);
	}

	e_soap_message_end_element (msg); /* </DistinguishedFolderId> || </FolderId> */

	e_soap_message_end_element (msg); /* </FolderIds> */

	e_ews_message_write_footer (msg);

	simple = g_simple_async_result_new (
		G_OBJECT (cnc), callback, user_data,
		e_ews_connection_empty_folder);

	async_data = g_new0 (EwsAsyncData, 1);
	g_simple_async_result_set_op_res_gpointer (
		simple, async_data, (GDestroyNotify) async_data_free);

	e_ews_connection_queue_request (
		cnc, msg, empty_folder_response_cb,
		pri, cancellable, simple);

	g_object_unref (simple);
}


sync_xxx_response_cb (ESoapParameter *subparam,
                      EwsAsyncData *async_data,
                      ItemParser parser,
                      const gchar *last_tag,
                      const gchar *delete_id_tag)
{
	ESoapParameter *node;
	gchar *new_sync_state = NULL, *value, *last;
	GSList *items_created = NULL, *items_updated = NULL, *items_deleted = NULL;
	gboolean includes_last_item;

	node = e_soap_parameter_get_first_child_by_name (subparam, "SyncState");
	new_sync_state = e_soap_parameter_get_string_value (node);

	node = e_soap_parameter_get_first_child_by_name (subparam, last_tag);
	last = e_soap_parameter_get_string_value (node);
	/*
	 * Set the includes_last_item to TRUE as default.
	 * It can avoid an infinite loop in caller, when, for some reason,
	 * we don't receive the last_tag property from the server.
	 */
	includes_last_item = g_strcmp0 (last, "false") != 0;
	g_free (last);

	node = e_soap_parameter_get_first_child_by_name (subparam, "Changes");

	if (node) {
		ESoapParameter *subparam1;
		for (subparam1 = e_soap_parameter_get_first_child_by_name (node, "Create");
		     subparam1 != NULL;
		     subparam1 = e_soap_parameter_get_next_child_by_name (subparam1, "Create")) {
			EEwsFolder *folder;

			folder = parser (subparam1);
			if (folder)
				items_created = g_slist_append (items_created, folder);
		}

		for (subparam1 = e_soap_parameter_get_first_child_by_name (node, "Update");
		     subparam1 != NULL;
		     subparam1 = e_soap_parameter_get_next_child_by_name (subparam1, "Update")) {
			EEwsFolder *folder;

			folder = parser (subparam1);
			if (folder)
				items_updated = g_slist_append (items_updated, folder);
		}
		  /* Exchange 2007SP1 introduced <ReadFlagChange> which is basically identical
		   * to <Update>; no idea why they thought it was a good idea. */
		for (subparam1 = e_soap_parameter_get_first_child_by_name (node, "ReadFlagChange");
		     subparam1 != NULL;
		     subparam1 = e_soap_parameter_get_next_child_by_name (subparam1, "ReadFlagChange")) {
			EEwsFolder *folder;

			folder = parser (subparam1);
			if (folder)
				items_updated = g_slist_append (items_updated, folder);
		}

		for (subparam1 = e_soap_parameter_get_first_child_by_name (node, "Delete");
		     subparam1 != NULL;
		     subparam1 = e_soap_parameter_get_next_child_by_name (subparam1, "Delete")) {
			ESoapParameter *folder_param;

			folder_param = e_soap_parameter_get_first_child_by_name (subparam1, delete_id_tag);
			value = e_soap_parameter_get_property (folder_param, "Id");
			items_deleted = g_slist_append (items_deleted, value);
		}
	}

	async_data->items_created = items_created;
	async_data->items_updated = items_updated;
	async_data->items_deleted = items_deleted;
	async_data->sync_state = new_sync_state;
	async_data->includes_last_item = includes_last_item;
}


ews_refresh_finfo (CamelEwsStore *ews_store)
{
	if (!camel_offline_store_get_online (CAMEL_OFFLINE_STORE (ews_store)))
		return FALSE;

	if (!ews_store->priv->updates_cancellable)
		ews_store->priv->updates_cancellable = g_cancellable_new ();

	run_update_thread (ews_store, TRUE, ews_store->priv->updates_cancellable);

	return TRUE;
}


e_ews_connection_delete_item (EEwsConnection *cnc,
                              gint pri,
                              EwsId *item_id,
                              guint index,
                              EwsDeleteType delete_type,
                              EwsSendMeetingCancellationsType send_cancels,
                              EwsAffectedTaskOccurrencesType affected_tasks,
                              GCancellable *cancellable,
                              GAsyncReadyCallback callback,
                              gpointer user_data)
{
	ESoapMessage *msg;
	GSimpleAsyncResult *simple;
	EwsAsyncData *async_data;
	gchar buffer[32];

	g_return_if_fail (cnc != NULL);

	msg = e_ews_message_new_with_header (
			cnc->priv->settings,
			cnc->priv->uri,
			cnc->priv->impersonate_user,
			"DeleteItem",
			"DeleteType",
			ews_delete_type_to_str (delete_type),
			cnc->priv->version,
			E_EWS_EXCHANGE_2007_SP1,
			FALSE,
			TRUE);

	if (send_cancels)
		e_soap_message_add_attribute (
			msg, "SendMeetingCancellations",
			ews_send_cancels_to_str (send_cancels), NULL, NULL);

	if (affected_tasks != EWS_NONE_OCCURRENCES)
		e_soap_message_add_attribute (
			msg, "AffectedTaskOccurrences",
			ews_affected_tasks_to_str (affected_tasks), NULL, NULL);

	e_soap_message_start_element (msg, "ItemIds", "messages", NULL);

	if (index) {
		e_soap_message_start_element (msg, "OccurrenceItemId", NULL, NULL);
		e_soap_message_add_attribute (msg, "RecurringMasterId", item_id->id, NULL, NULL);
		if (item_id->change_key)
			e_soap_message_add_attribute (msg, "ChangeKey", item_id->change_key, NULL, NULL);
		snprintf (buffer, 32, "%u", index);
		e_soap_message_add_attribute (msg, "InstanceIndex", buffer, NULL, NULL);
		e_soap_message_end_element (msg);
	} else {
		e_soap_message_start_element (msg, "ItemId", NULL, NULL);
		e_soap_message_add_attribute (msg, "Id", item_id->id, NULL, NULL);
		if (item_id->change_key)
			e_soap_message_add_attribute (msg, "ChangeKey", item_id->change_key, NULL, NULL);
		e_soap_message_end_element (msg);
	}

	e_soap_message_end_element (msg);

	e_ews_message_write_footer (msg);

	simple = g_simple_async_result_new (
		G_OBJECT (cnc), callback, user_data,
		e_ews_connection_delete_items);

	async_data = g_new0 (EwsAsyncData, 1);
	g_simple_async_result_set_op_res_gpointer (
		simple, async_data, (GDestroyNotify) async_data_free);

	e_ews_connection_queue_request (
		cnc, msg, delete_item_response_cb,
		pri, cancellable, simple);

	g_object_unref (simple);
}


ews_store_dispose (GObject *object)
{
	CamelEwsStore *ews_store;
	CamelEwsSettings *ews_settings;

	ews_store = CAMEL_EWS_STORE (object);

	ews_settings = CAMEL_EWS_SETTINGS (camel_service_ref_settings (CAMEL_SERVICE (ews_store)));
	g_signal_handlers_disconnect_by_func (ews_settings, camel_ews_store_listen_notifications_cb, ews_store);
	g_signal_handlers_disconnect_by_func (ews_settings, camel_ews_store_check_all_cb, ews_store);
	g_object_unref (ews_settings);

	if (ews_store->summary != NULL) {
		camel_ews_store_summary_save (ews_store->summary, NULL);
		g_object_unref (ews_store->summary);
		ews_store->summary = NULL;
	}

	g_mutex_lock (&ews_store->priv->connection_lock);
	ews_store_unset_connection_locked (ews_store);
	g_mutex_unlock (&ews_store->priv->connection_lock);

	g_slist_free_full (ews_store->priv->update_folder_names, g_free);
	ews_store->priv->update_folder_names = NULL;

	g_slist_free_full (ews_store->priv->public_folders, g_object_unref);
	ews_store->priv->public_folders = NULL;

	/* Chain up to parent's dispose() method. */
	G_OBJECT_CLASS (camel_ews_store_parent_class)->dispose (object);
}


set_photo (EBookBackendEws *bbews,
	   const EwsId *item_id,
	   EContact *contact,
	   EContactPhoto *photo,
	   gchar **new_change_key,
	   GCancellable *cancellable,
	   GError **error)
{
	EEwsAttachmentInfo *info;
	EwsId *id = NULL;
	GSList *files = NULL;
	const guchar *data;
	gsize len;

	if (!item_id) {
		id = g_new0 (EwsId, 1);
		id->id = e_contact_get (contact, E_CONTACT_UID);
		id->change_key = e_vcard_util_dup_x_attribute (E_VCARD (contact), X_EWS_CHANGEKEY);
		if (!id->change_key)
			id->change_key = e_contact_get (contact, E_CONTACT_REV);

		item_id = id;
	}

	data = e_contact_photo_get_inlined (photo, &len);

	info = e_ews_attachment_info_new (E_EWS_ATTACHMENT_INFO_TYPE_INLINED);
	e_ews_attachment_info_set_inlined_data (info, data, len);
	e_ews_attachment_info_set_mime_type (info, "image/jpeg");
	e_ews_attachment_info_set_filename (info, "ContactPicture.jpg");

	files = g_slist_append (files, info);

	e_ews_connection_create_attachments_sync (
			bbews->priv->cnc,
			EWS_PRIORITY_MEDIUM,
			item_id,
			files,
			TRUE,
			new_change_key,
			NULL,
			cancellable,
			error);

	if (id) {
		g_free (id->change_key);
		g_free (id->id);
		g_free (id);
	}

	g_slist_free_full (files, (GDestroyNotify) e_ews_attachment_info_free);
}


ews_connection_get_property (GObject *object,
                             guint property_id,
                             GValue *value,
                             GParamSpec *pspec)
{
	switch (property_id) {
		case PROP_PASSWORD:
			g_value_take_string (
				value,
				e_ews_connection_dup_password (
				E_EWS_CONNECTION (object)));
			return;

		case PROP_PROXY_RESOLVER:
			g_value_take_object (
				value,
				e_ews_connection_ref_proxy_resolver (
				E_EWS_CONNECTION (object)));
			return;

		case PROP_SETTINGS:
			g_value_take_object (
				value,
				e_ews_connection_ref_settings (
				E_EWS_CONNECTION (object)));
			return;

		case PROP_SOURCE:
			g_value_set_object (
				value,
				e_ews_connection_get_source (
				E_EWS_CONNECTION (object)));
			return;
	}

	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}


e_ews_notification_event_free (EEwsNotificationEvent *event)
{
	if (event != NULL) {
		g_free (event->folder_id);
		g_free (event->old_folder_id);
		g_free (event);
	}
}


ecb_ews_get_rsvp (icalproperty *attendee,
		  gboolean *out_rsvp_requested)
{
	if (out_rsvp_requested)
		*out_rsvp_requested = FALSE;
	else
		return;

	if (attendee) {
		icalparameter *rsvp;

		rsvp = icalproperty_get_first_parameter (attendee, ICAL_RSVP_PARAMETER);
		if (rsvp) {
			*out_rsvp_requested = icalparameter_get_rsvp (rsvp) == ICAL_RSVP_TRUE;
		}
	}
}


delete_item_response_cb (ESoapResponse *response,
                         GSimpleAsyncResult *simple)
{
	ESoapParameter *param;
	ESoapParameter *subparam;
	GError *error = NULL;

	param = e_soap_response_get_first_parameter_by_name (
		response, "ResponseMessages", &error);

	/* Sanity check */
	g_return_if_fail (
		(param != NULL && error == NULL) ||
		(param == NULL && error != NULL));

	if (error != NULL) {
		g_simple_async_result_take_error (simple, error);
		return;
	}

	subparam = e_soap_parameter_get_first_child (param);

	while (subparam != NULL) {
		if (!ews_get_response_status (subparam, &error)) {
			g_simple_async_result_take_error (simple, error);
			return;
		}

		subparam = e_soap_parameter_get_next_child (subparam);
	}
}


e_ews_connection_get_items_sync (EEwsConnection *cnc,
                                 gint pri,
                                 const GSList *ids,
                                 const gchar *default_props,
				 const EEwsAdditionalProps *add_props,
                                 gboolean include_mime,
                                 const gchar *mime_directory,
				 EEwsBodyType body_type,
                                 GSList **items,
                                 ESoapProgressFn progress_fn,
                                 gpointer progress_data,
                                 GCancellable *cancellable,
                                 GError **error)
{
	EAsyncClosure *closure;
	GAsyncResult *result;
	gboolean success;

	g_return_val_if_fail (cnc != NULL, FALSE);

	closure = e_async_closure_new ();

	e_ews_connection_get_items (
		cnc, pri,ids, default_props,
		add_props, include_mime,
		mime_directory, body_type, progress_fn,
		progress_data, cancellable,
		e_async_closure_callback, closure);

	result = e_async_closure_wait (closure);

	success = e_ews_connection_get_items_finish (
		cnc, result, items, error);

	e_async_closure_free (closure);

	return success;
}


ews_backend_finalize (GObject *object)
{
	EEwsBackendPrivate *priv;

	priv = E_EWS_BACKEND_GET_PRIVATE (object);

	g_hash_table_destroy (priv->folders);
	g_mutex_clear (&priv->folders_lock);

	g_free (priv->oal_selected);

	g_free (priv->sync_state);
	g_mutex_clear (&priv->sync_state_lock);

	g_mutex_clear (&priv->connection_lock);

	e_named_parameters_free (priv->credentials);

	/* Chain up to parent's finalize() method. */
	G_OBJECT_CLASS (e_ews_backend_parent_class)->finalize (object);
}


ews_get_transitions_groups_list (ESoapParameter *node)
{
	ESoapParameter *param;
	GSList *transitions_groups = NULL;

	for (param = e_soap_parameter_get_first_child_by_name (node, "TransitionsGroup");
	     param != NULL;
	     param = e_soap_parameter_get_next_child_by_name (param, "TransitionsGroup")) {
		EEwsCalendarTransitionsGroup *tg;

		tg = ews_get_transitions_group (param);
		if (tg != NULL) {
			transitions_groups = g_slist_prepend (transitions_groups, tg);
		} else {
			g_slist_free_full (transitions_groups, (GDestroyNotify) e_ews_calendar_transitions_group_free);
			return NULL;
		}
	}

	transitions_groups = g_slist_reverse (transitions_groups);
	return transitions_groups;
}


ews_urls_free_content (EwsUrls *urls)
{
	if (!urls)
		return;

	if (urls->as_url)
		xmlFree (urls->as_url);
	urls->as_url = NULL;

	if (urls->oab_url)
		xmlFree (urls->oab_url);
	urls->oab_url = NULL;
}


ecb_ews_is_organizer (ECalBackendEws *cbews,
		      ECalComponent *comp)
{
	ECalComponentOrganizer organizer;
	gboolean is_organizer = FALSE;

	g_return_val_if_fail (E_IS_CAL_BACKEND_EWS (cbews), FALSE);
	g_return_val_if_fail (E_IS_CAL_COMPONENT (comp), FALSE);

	if (!e_cal_component_has_organizer (comp))
		return FALSE;

	organizer.value = NULL;

	e_cal_component_get_organizer (comp, &organizer);
	if (organizer.value) {
		CamelEwsSettings *ews_settings;
		const gchar *email = organizer.value;
		gchar *user_email;

		ews_settings = ecb_ews_get_collection_settings (cbews);

		user_email = camel_ews_settings_dup_email (ews_settings);

		if (!g_ascii_strncasecmp (email, "mailto:", 7))
			email += 7;

		is_organizer = user_email && g_ascii_strcasecmp (email, user_email) == 0;

		g_free (user_email);
	}

	return is_organizer;
}


e_ews_backend_ref_connection (EEwsBackend *backend,
                              GCancellable *cancellable,
                              GAsyncReadyCallback callback,
                              gpointer user_data)
{
	GSimpleAsyncResult *simple;

	g_return_if_fail (E_IS_EWS_BACKEND (backend));

	simple = g_simple_async_result_new (
		G_OBJECT (backend), callback,
		user_data, e_ews_backend_ref_connection);

	g_simple_async_result_run_in_thread (
		simple, ews_backend_ref_connection_thread,
		G_PRIORITY_DEFAULT, cancellable);

	g_object_unref (simple);
}


ebews_populate_uid (EBookBackendEws *bbews,
		    EContact *contact,
                    EEwsItem *item,
		    GCancellable *cancellable,
		    GError **error)
{
	const EwsId *id;

	id = e_ews_item_get_id (item);
	if (id) {
		e_contact_set (contact, E_CONTACT_UID, id->id);
		ebews_populate_rev (contact, item);
		e_vcard_util_set_x_attribute (E_VCARD (contact), X_EWS_CHANGEKEY, id->change_key);
	}
}


camel_ews_store_maybe_disconnect (CamelEwsStore *store,
                                  const GError *error)
{
	CamelService *service;

	g_return_if_fail (store != NULL);

	if (!error)
		return;

	service = CAMEL_SERVICE (store);

	if (camel_service_get_connection_status (service) != CAMEL_SERVICE_CONNECTED)
		return;

	if (g_error_matches (error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_NORESPONSE) ||
	    g_error_matches (error, EWS_CONNECTION_ERROR, EWS_CONNECTION_ERROR_AUTHENTICATION_FAILED))
		camel_service_disconnect_sync (service, FALSE, NULL, NULL);
}


ews_get_recurring_day_transition (ESoapParameter *node)
{
	ESoapParameter *param;
	EEwsCalendarRecurringDayTransition *recurring_day_transition = NULL;
	EEwsCalendarTo *to = NULL;
	gchar *time_offset = NULL;
	gchar *month = NULL;
	gchar *day_of_week = NULL;
	gchar *occurrence = NULL;
	gboolean success = FALSE;

	to = ews_get_to (node);
	if (to == NULL)
		goto exit;

	param = e_soap_parameter_get_first_child_by_name (node, "TimeOffset");
	if (param != NULL)
		time_offset = e_soap_parameter_get_string_value (param);

	if (time_offset == NULL)
		goto exit;

	param = e_soap_parameter_get_first_child_by_name (node, "Month");
	if (param != NULL)
		month = e_soap_parameter_get_string_value (param);

	if (month == NULL)
		goto exit;

	param = e_soap_parameter_get_first_child_by_name (node, "DayOfWeek");
	if (param != NULL)
		day_of_week = e_soap_parameter_get_string_value (param);

	if (day_of_week == NULL)
		goto exit;

	param = e_soap_parameter_get_first_child_by_name (node, "Occurrence");
	if (param != NULL)
		occurrence = e_soap_parameter_get_string_value (param);

	if (occurrence == NULL)
		goto exit;

	success = TRUE;

exit:
	if (success) {
		recurring_day_transition = e_ews_calendar_recurring_day_transition_new ();
		recurring_day_transition->to = to;
		recurring_day_transition->time_offset = time_offset;
		recurring_day_transition->month = month;
		recurring_day_transition->day_of_week = day_of_week;
		recurring_day_transition->occurrence = occurrence;
	} else {
		e_ews_calendar_to_free (to);
		g_free (time_offset);
		g_free (month);
		g_free (day_of_week);
		g_free (occurrence);
	}

	return recurring_day_transition;
}


e_ews_connection_set_folder_permissions_sync (EEwsConnection *cnc,
                                              gint pri,
                                              EwsFolderId *folder_id,
                                              EEwsFolderType folder_type,
                                              const GSList *permissions,
                                              GCancellable *cancellable,
                                              GError **error)
{
	EAsyncClosure *closure;
	GAsyncResult *result;
	gboolean success;

	g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE);
	g_return_val_if_fail (folder_id != NULL, FALSE);
	g_return_val_if_fail (permissions != NULL, FALSE);

	closure = e_async_closure_new ();

	e_ews_connection_set_folder_permissions (
		cnc, pri, folder_id, folder_type, permissions, cancellable,
		e_async_closure_callback, closure);

	result = e_async_closure_wait (closure);

	success = e_ews_connection_set_folder_permissions_finish (
		cnc, result, error);

	e_async_closure_free (closure);

	return success;
}


e_ews_attachment_info_set_filename (EEwsAttachmentInfo *info,
				    const gchar *filename)
{
	g_return_if_fail (info != NULL);
	g_return_if_fail (info->type == E_EWS_ATTACHMENT_INFO_TYPE_INLINED);

	g_free (info->data.inlined.filename);
	info->data.inlined.filename = g_strdup (filename);
}


e_ews_connection_find_folder_sync (EEwsConnection *cnc,
				   gint pri,
				   const EwsFolderId *fid,
				   gboolean *includes_last_item,
				   GSList **folders,
				   GCancellable *cancellable,
				   GError **error)
{
	EAsyncClosure *closure;
	GAsyncResult *result;
	gboolean success;

	g_return_val_if_fail (cnc != NULL, FALSE);

	closure = e_async_closure_new ();

	e_ews_connection_find_folder (cnc, pri, fid, cancellable, e_async_closure_callback, closure);

	result = e_async_closure_wait (closure);

	success = e_ews_connection_find_folder_finish (
		cnc, result, includes_last_item, folders, error);

	e_async_closure_free (closure);

	return success;
}


ews_connection_utils_maybe_prepare_bearer_auth (EEwsConnection *cnc,
						SoupMessage *message,
						GCancellable *cancellable)
{
	ESource *source;
	ESoupAuthBearer *using_bearer_auth;
	gchar *auth_method = NULL;
	gboolean success;
	GError *local_error = NULL;

	g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE);

	source = e_ews_connection_get_source (cnc);
	if (!source)
		return TRUE;

	if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
		ESourceAuthentication *extension;

		extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
		auth_method = e_source_authentication_dup_method (extension);
	} else {
		CamelEwsSettings *ews_settings;

		ews_settings = e_ews_connection_ref_settings (cnc);
		if (ews_settings) {
			if (camel_ews_settings_get_auth_mechanism (ews_settings) == EWS_AUTH_TYPE_OAUTH2)
				auth_method = g_strdup ("OAuth2");

			g_object_unref (ews_settings);
		}

		if (!auth_method)
			return TRUE;
	}

	if (g_strcmp0 (auth_method, "OAuth2") != 0 &&
	    g_strcmp0 (auth_method, "Office365") != 0 &&
	    !e_oauth2_services_is_oauth2_alias_static (auth_method)) {
		g_free (auth_method);
		return TRUE;
	}

	g_free (auth_method);

	using_bearer_auth = e_ews_connection_ref_bearer_auth (cnc);
	if (using_bearer_auth) {
		success = ews_connection_utils_setup_bearer_auth (cnc, message, FALSE, using_bearer_auth, cancellable, &local_error);
		g_clear_object (&using_bearer_auth);
	} else {
		SoupAuth *soup_auth;
		SoupURI *soup_uri;

		soup_uri = message ? soup_message_get_uri (message) : NULL;
		if (soup_uri && soup_uri->host && *soup_uri->host) {
			soup_uri = soup_uri_copy_host (soup_uri);
		} else {
			soup_uri = NULL;
		}

		g_warn_if_fail (soup_uri != NULL);

		if (!soup_uri) {
			soup_message_set_status_full (message, SOUP_STATUS_MALFORMED, "Cannot get host from message");
			return FALSE;
		}

		soup_auth = g_object_new (E_TYPE_SOUP_AUTH_BEARER, SOUP_AUTH_HOST, soup_uri->host, NULL);

		success = ews_connection_utils_setup_bearer_auth (cnc, message, FALSE, E_SOUP_AUTH_BEARER (soup_auth), cancellable, &local_error);
		if (success)
			e_ews_connection_set_bearer_auth (cnc, E_SOUP_AUTH_BEARER (soup_auth));

		g_object_unref (soup_auth);
		soup_uri_free (soup_uri);
	}

	if (!success) {
		if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
			soup_message_set_status (message, SOUP_STATUS_CANCELLED);
		else if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED) ||
			 g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
			soup_message_set_status_full (message, SOUP_STATUS_UNAUTHORIZED, local_error->message);
		else
			soup_message_set_status_full (message, SOUP_STATUS_MALFORMED, local_error ? local_error->message : _("Unknown error"));
	}

	g_clear_error (&local_error);

	return success;
}


ews_config_lookup_constructed (GObject *object)
{
	EConfigLookup *config_lookup;

	/* Chain up to parent's method. */
	G_OBJECT_CLASS (e_ews_config_lookup_parent_class)->constructed (object);

	config_lookup = E_CONFIG_LOOKUP (e_extension_get_extensible (E_EXTENSION (object)));

	e_config_lookup_register_worker (config_lookup, E_CONFIG_LOOKUP_WORKER (object));
}


e_ews_connection_enable_notifications_sync (EEwsConnection *cnc,
					    GSList *folders,
					    guint *subscription_key)
{
	GSList *new_folders = NULL, *l;
	gint subscriptions_size;

	g_return_if_fail (cnc != NULL);
	g_return_if_fail (cnc->priv != NULL);
	g_return_if_fail (cnc->priv->version >= E_EWS_EXCHANGE_2010_SP1);
	g_return_if_fail (folders != NULL);

	NOTIFICATION_LOCK (cnc);
	subscriptions_size = g_hash_table_size (cnc->priv->subscriptions);

	if (subscriptions_size == G_MAXUINT - 1)
		goto exit;

	if (subscriptions_size > 0) {
		e_ews_notification_stop_listening_sync (cnc->priv->notification);

		g_clear_object (&cnc->priv->notification);

		g_slist_free_full (cnc->priv->subscribed_folders, g_free);
		cnc->priv->subscribed_folders = NULL;
	}

	while (g_hash_table_contains (cnc->priv->subscriptions, GINT_TO_POINTER (notification_key))) {
		notification_key++;
		if (notification_key == 0)
			notification_key++;
	}

	for (l = folders; l != NULL; l = l->next)
		new_folders = g_slist_prepend (new_folders, g_strdup (l->data));

	g_hash_table_insert (cnc->priv->subscriptions, GINT_TO_POINTER (notification_key), new_folders);
	new_folders = NULL;

	g_hash_table_foreach (cnc->priv->subscriptions, ews_connection_build_subscribed_folders_list, cnc);

	cnc->priv->notification = e_ews_notification_new (cnc);

	e_ews_notification_start_listening_sync (cnc->priv->notification, cnc->priv->subscribed_folders);

exit:
	*subscription_key = notification_key;
	notification_key++;
	if (notification_key == 0)
		notification_key++;

	NOTIFICATION_UNLOCK (cnc);
}


e_ews_connection_remove_delegate (EEwsConnection *cnc,
                                  gint pri,
                                  const gchar *mail_id,
                                  const GSList *delegate_ids, /* EwsUserId * */
                                  GCancellable *cancellable,
                                  GAsyncReadyCallback callback,
                                  gpointer user_data)
{
	ESoapMessage *msg;
	GSimpleAsyncResult *simple;
	EwsAsyncData *async_data;
	const GSList *iter;

	g_return_if_fail (cnc != NULL);
	g_return_if_fail (delegate_ids != NULL);

	msg = e_ews_message_new_with_header (
			cnc->priv->settings,
			cnc->priv->uri,
			cnc->priv->impersonate_user,
			"RemoveDelegate",
			NULL,
			NULL,
			cnc->priv->version,
			E_EWS_EXCHANGE_2007_SP1,
			FALSE,
			TRUE);

	e_soap_message_start_element (msg, "Mailbox", "messages", NULL);
	e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, mail_id ? mail_id : cnc->priv->email);
	e_soap_message_end_element (msg);

	e_soap_message_start_element (msg, "UserIds", "messages", NULL);
	for (iter = delegate_ids; iter; iter = iter->next) {
		const EwsUserId *user_id = iter->data;

		if (!user_id)
			continue;

		e_soap_message_start_element (msg, "UserId", NULL, NULL);
		e_ews_message_write_string_parameter (msg, "PrimarySmtpAddress", NULL, user_id->primary_smtp);
		e_soap_message_end_element (msg); /* UserId */
	}

	e_soap_message_end_element (msg); /* UserIds */

	e_ews_message_write_footer (msg);

	simple = g_simple_async_result_new (
		G_OBJECT (cnc), callback, user_data,
		e_ews_connection_remove_delegate);

	async_data = g_new0 (EwsAsyncData, 1);
	g_simple_async_result_set_op_res_gpointer (
		simple, async_data, (GDestroyNotify) async_data_free);

	e_ews_connection_queue_request (
		cnc, msg, update_delegate_response_cb,
		pri, cancellable, simple);

	g_object_unref (simple);
}


e_ews_connection_get_folder_info (EEwsConnection *cnc,
                                  gint pri,
                                  const gchar *mail_id,
                                  const EwsFolderId *folder_id,
                                  GCancellable *cancellable,
                                  GAsyncReadyCallback callback,
                                  gpointer user_data)
{
	ESoapMessage *msg;
	GSimpleAsyncResult *simple;
	EwsAsyncData *async_data;

	g_return_if_fail (cnc != NULL);
	g_return_if_fail (folder_id != NULL);

	msg = e_ews_message_new_with_header (
			cnc->priv->settings,
			cnc->priv->uri,
			cnc->priv->impersonate_user,
			"GetFolder",
			NULL,
			NULL,
			cnc->priv->version,
			E_EWS_EXCHANGE_2007_SP1,
			FALSE,
			TRUE);

	e_soap_message_start_element (msg, "FolderShape", "messages", NULL);
	e_ews_message_write_string_parameter (msg, "BaseShape", NULL, "Default");
	e_soap_message_start_element (msg, "AdditionalProperties", NULL, NULL);
	e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", "folder:FolderClass");
	e_ews_message_write_string_parameter_with_attribute (msg, "FieldURI", NULL, NULL, "FieldURI", "folder:ParentFolderId");
	e_soap_message_end_element (msg); /* AdditionalProperties */
	e_soap_message_end_element (msg); /* FolderShape */

	e_soap_message_start_element (msg, "FolderIds", "messages", NULL);
	e_ews_folder_id_append_to_msg (msg, mail_id, folder_id);
	e_soap_message_end_element (msg);

	e_ews_message_write_footer (msg);

	simple = g_simple_async_result_new (
		G_OBJECT (cnc), callback, user_data,
		e_ews_connection_get_folder_info);

	async_data = g_new0 (EwsAsyncData, 1);
	g_simple_async_result_set_op_res_gpointer (
		simple, async_data, (GDestroyNotify) async_data_free);

	e_ews_connection_queue_request (
		cnc, msg, get_folder_info_response_cb,
		pri, cancellable, simple);

	g_object_unref (simple);
}


e_ews_config_lookup_result_init (EEwsConfigLookupResult *ews_result)
{
}


e_ews_connection_update_folder (EEwsConnection *cnc,
                                gint pri,
                                EEwsRequestCreationCallback create_cb,
                                gpointer create_user_data,
                                GCancellable *cancellable,
                                GAsyncReadyCallback callback,
                                gpointer user_data)
{
	ESoapMessage *msg;
	GSimpleAsyncResult *simple;
	EwsAsyncData *async_data;
	gboolean success;
	GError *local_error = NULL;

	g_return_if_fail (cnc != NULL);

	msg = e_ews_message_new_with_header (
			cnc->priv->settings,
			cnc->priv->uri,
			cnc->priv->impersonate_user,
			"UpdateFolder",
			NULL,
			NULL,
			cnc->priv->version,
			E_EWS_EXCHANGE_2007_SP1,
			FALSE,
			TRUE);

	e_soap_message_start_element (msg, "FolderChanges", "messages", NULL);

	success = create_cb (msg, create_user_data, &local_error);

	e_soap_message_end_element (msg); /* FolderChanges */

	e_ews_message_write_footer (msg);

	simple = g_simple_async_result_new (
		G_OBJECT (cnc), callback, user_data,
		e_ews_connection_update_folder);

	async_data = g_new0 (EwsAsyncData, 1);
	g_simple_async_result_set_op_res_gpointer (
		simple, async_data, (GDestroyNotify) async_data_free);

	if (success) {
		e_ews_connection_queue_request (
			cnc, msg, update_folder_response_cb,
			pri, cancellable, simple);
	} else {
		if (local_error)
			g_simple_async_result_take_error (simple, local_error);
		g_simple_async_result_complete_in_idle (simple);
		g_clear_object (&msg);
	}

	g_object_unref (simple);
}


e_ews_additional_props_new (void)
{
	return g_new0 (EEwsAdditionalProps, 1);
}


e_ews_connection_get_ssl_error_details (EEwsConnection *cnc,
					gchar **out_certificate_pem,
					GTlsCertificateFlags *out_certificate_errors)
{
	g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), FALSE);
	g_return_val_if_fail (out_certificate_pem != NULL, FALSE);
	g_return_val_if_fail (out_certificate_errors != NULL, FALSE);

	g_mutex_lock (&cnc->priv->property_lock);
	if (!cnc->priv->ssl_info_set) {
		g_mutex_unlock (&cnc->priv->property_lock);
		return FALSE;
	}

	*out_certificate_pem = g_strdup (cnc->priv->ssl_certificate_pem);
	*out_certificate_errors = cnc->priv->ssl_certificate_errors;

	g_mutex_unlock (&cnc->priv->property_lock);

	return TRUE;
}


ews_discover_server_version (EEwsConnection *cnc,
			     ESoapResponse *response)
{
	ESoapParameter *param;
	gchar *version;

	g_return_if_fail (cnc != NULL);

	if (cnc->priv->version != E_EWS_EXCHANGE_UNKNOWN)
		return;

	param = e_soap_response_get_first_parameter_by_name (
		response, "ServerVersionInfo", NULL);
	if (!param)
		return;

	version = e_soap_parameter_get_property (param, "Version");

	e_ews_connection_set_server_version_from_string (cnc, version);

	g_free (version);
}


ews_affected_tasks_to_str (EwsAffectedTaskOccurrencesType affected_tasks)
{
	switch (affected_tasks) {
		case EWS_NONE_OCCURRENCES:
			return NULL;
		case EWS_ALL_OCCURRENCES:
			return "AllOccurrences";
		case EWS_SPECIFIED_OCCURRENCE_ONLY:
			return "SpecifiedOccurrenceOnly";
	}
	return NULL;
}


e_ews_attachment_info_get_prefer_filename (EEwsAttachmentInfo *info)
{
	g_return_val_if_fail (info != NULL, NULL);

	return info->prefer_filename;
}


e_ews_connection_get_delegate (EEwsConnection *cnc,
                               gint pri,
                               const gchar *mail_id,
                               gboolean include_permissions,
                               GCancellable *cancellable,
                               GAsyncReadyCallback callback,
                               gpointer user_data)
{
	ESoapMessage *msg;
	GSimpleAsyncResult *simple;
	EwsAsyncData *async_data;

	g_return_if_fail (cnc != NULL);

	msg = e_ews_message_new_with_header (
			cnc->priv->settings,
			cnc->priv->uri,
			cnc->priv->impersonate_user,
			"GetDelegate",
			"IncludePermissions",
			include_permissions ? "true" : "false",
			cnc->priv->version,
			E_EWS_EXCHANGE_2007_SP1,
			FALSE,
			TRUE);

	e_soap_message_start_element (msg, "Mailbox", "messages", NULL);

	e_ews_message_write_string_parameter (msg, "EmailAddress", NULL, mail_id ? mail_id : cnc->priv->email);

	e_soap_message_end_element (msg);

	e_ews_message_write_footer (msg);

	simple = g_simple_async_result_new (
		G_OBJECT (cnc), callback, user_data,
		e_ews_connection_get_delegate);

	async_data = g_new0 (EwsAsyncData, 1);
	g_simple_async_result_set_op_res_gpointer (
		simple, async_data, (GDestroyNotify) async_data_free);

	e_ews_connection_queue_request (
		cnc, msg, get_delegate_response_cb,
		pri, cancellable, simple);

	g_object_unref (simple);
}


camel_ews_store_server_notification_cb (CamelEwsStore *ews_store,
					GSList *events,
					EEwsConnection *cnc)
{
	GSList *l;
	gboolean update_folder = FALSE;
	gboolean update_folder_list = FALSE;
	GHashTable *folder_ids;

	g_return_if_fail (ews_store != NULL);
	g_return_if_fail (ews_store->priv != NULL);

	folder_ids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);

	for (l = events; l != NULL; l = l->next) {
		EEwsNotificationEvent *event = l->data;

		switch (event->type) {
			case E_EWS_NOTIFICATION_EVENT_CREATED:
			case E_EWS_NOTIFICATION_EVENT_DELETED:
			case E_EWS_NOTIFICATION_EVENT_MODIFIED:
				UPDATE_LOCK (ews_store);
				if (event->is_item) {
					update_folder = TRUE;
					if (!g_hash_table_lookup (folder_ids, event->folder_id))
						g_hash_table_insert (
							folder_ids, g_strdup (event->folder_id), GINT_TO_POINTER (1));
				} else {
					update_folder_list = TRUE;
				}
				UPDATE_UNLOCK (ews_store);
				break;
			case E_EWS_NOTIFICATION_EVENT_MOVED:
			case E_EWS_NOTIFICATION_EVENT_COPIED:
				UPDATE_LOCK (ews_store);
				if (event->is_item) {
					update_folder = TRUE;
					if (!g_hash_table_lookup (folder_ids, event->old_folder_id))
						g_hash_table_insert (
							folder_ids, g_strdup (event->old_folder_id), GINT_TO_POINTER (1));

					if (!g_hash_table_lookup (folder_ids, event->folder_id))
						g_hash_table_insert (
							folder_ids, g_strdup (event->folder_id), GINT_TO_POINTER (1));
				} else {
					update_folder_list = TRUE;
				}
				UPDATE_UNLOCK (ews_store);
				break;
			default:
				break;
		}
	}

	if (update_folder)
		schedule_folder_update (ews_store, folder_ids);
	if (update_folder_list)
		schedule_folder_list_update (ews_store);

	g_hash_table_destroy (folder_ids);
}


e_ews_connection_sync_folder_items_finish (EEwsConnection *cnc,
                                           GAsyncResult *result,
                                           gchar **new_sync_state,
                                           gboolean *includes_last_item,
                                           GSList **items_created,
                                           GSList **items_updated,
                                           GSList **items_deleted,
                                           GError **error)
{
	GSimpleAsyncResult *simple;
	EwsAsyncData *async_data;

	g_return_val_if_fail (cnc != NULL, FALSE);
	g_return_val_if_fail (
		g_simple_async_result_is_valid (
		result, G_OBJECT (cnc), e_ews_connection_sync_folder_items),
		FALSE);

	simple = G_SIMPLE_ASYNC_RESULT (result);
	async_data = g_simple_async_result_get_op_res_gpointer (simple);

	if (g_simple_async_result_propagate_error (simple, error))
		return FALSE;

	*new_sync_state = async_data->sync_state;
	*includes_last_item = async_data->includes_last_item;
	*items_created = async_data->items_created;
	*items_updated = async_data->items_updated;
	*items_deleted = async_data->items_deleted;

	return TRUE;
}


ebews_set_anniversary (ESoapMessage *message,
                       EContact *contact)
{

}


e_ews_config_utils_is_online (void)
{
	EShell *shell;

	shell = e_shell_get_default ();

	return shell && e_shell_get_online (shell);
}


ecb_ews_finalize (GObject *object)
{
	ECalBackendEws *cbews = E_CAL_BACKEND_EWS (object);

	g_free (cbews->priv->folder_id);
	g_free (cbews->priv->attachments_dir);

	g_rec_mutex_clear (&cbews->priv->cnc_lock);

	e_cal_backend_ews_unref_windows_zones ();

	/* Chain up to parent's method. */
	G_OBJECT_CLASS (e_cal_backend_ews_parent_class)->finalize (object);
}


e_ews_connection_dup_password (EEwsConnection *cnc)
{
	const gchar *protected;
	gchar *duplicate;

	g_return_val_if_fail (E_IS_EWS_CONNECTION (cnc), NULL);

	g_mutex_lock (&cnc->priv->property_lock);

	protected = e_ews_connection_get_password (cnc);
	duplicate = g_strdup (protected);

	g_mutex_unlock (&cnc->priv->property_lock);

	return duplicate;
}


e_ews_connection_get_oal_list (EEwsConnection *cnc,
                               GCancellable *cancellable,
                               GAsyncReadyCallback callback,
                               gpointer user_data)
{
	GSimpleAsyncResult *simple;
	SoupMessage *soup_message;
	struct _oal_req_data *data;
	GError *error = NULL;

	g_return_if_fail (E_IS_EWS_CONNECTION (cnc));

	soup_message = e_ews_get_msg_for_url (cnc, cnc->priv->settings, cnc->priv->uri, NULL, &error);

	simple = g_simple_async_result_new (
		G_OBJECT (cnc), callback, user_data,
		e_ews_connection_get_oal_list);

	if (!soup_message) {
		g_simple_async_result_take_error (simple, error);
		g_simple_async_result_complete_in_idle (simple);
		return;
	}

	data = g_slice_new0 (struct _oal_req_data);
	data->cnc = g_object_ref (cnc);
	data->soup_message = soup_message;  /* the session owns this */

	if (G_IS_CANCELLABLE (cancellable)) {
		data->cancellable = g_object_ref (cancellable);
		data->cancel_id = g_cancellable_connect (
			data->cancellable,
			G_CALLBACK (ews_cancel_msg),
			data, (GDestroyNotify) NULL);
	}

	g_simple_async_result_set_op_res_gpointer (
		simple, data, (GDestroyNotify) oal_req_data_free);

	ews_connection_schedule_queue_message (cnc, soup_message, oal_response_cb, simple);
}


sync_folders_closure_free (SyncFoldersClosure *closure)
{
	g_object_unref (closure->backend);

	/* List of EEwsFolder instances. */
	g_slist_free_full (closure->folders_created, g_object_unref);

	/* List of folder ID strings. */
	g_slist_free_full (closure->folders_deleted, g_free);

	/* List of EEwsFolder instances. */
	g_slist_free_full (closure->folders_updated, g_object_unref);

	g_slice_free (SyncFoldersClosure, closure);
}


folder_list_update_cb (gpointer user_data)
{
	struct ScheduleUpdateData *sud = user_data;

	g_return_val_if_fail (sud != NULL, FALSE);

	if (g_cancellable_is_cancelled (sud->cancellable))
		return FALSE;

	g_return_val_if_fail (sud->ews_store != NULL, FALSE);
	g_return_val_if_fail (sud->ews_store->priv != NULL, FALSE);

	UPDATE_LOCK (sud->ews_store);
	if (sud->expected_id != sud->ews_store->priv->update_folder_list_id)
		goto exit;

	sud->ews_store->priv->update_folder_list_id = 0;

	if (!g_cancellable_is_cancelled (sud->cancellable))
		run_update_thread (sud->ews_store, TRUE, sud->cancellable);

exit:
	UPDATE_UNLOCK (sud->ews_store);

	return FALSE;
}


ews_store_get_property (GObject *object,
                        guint property_id,
                        GValue *value,
                        GParamSpec *pspec)
{
	switch (property_id) {
		case PROP_HAS_OOO_SET:
			g_value_set_boolean (
				value,
				camel_ews_store_get_has_ooo_set (
				CAMEL_EWS_STORE (object)));
			return;
		case PROP_OOO_ALERT_STATE:
			g_value_set_enum (
				value,
				camel_ews_store_get_ooo_alert_state (
				CAMEL_EWS_STORE (object)));
			return;
		case PROP_CONNECTABLE:
			g_value_take_object (
				value,
				camel_network_service_ref_connectable (
					CAMEL_NETWORK_SERVICE (object)));
			return;
		case PROP_HOST_REACHABLE:
			g_value_set_boolean (
				value,
				camel_network_service_get_host_reachable (
					CAMEL_NETWORK_SERVICE (object)));
			return;
	}

	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}


ebews_populate_birth_date (EBookBackendEws *bbews,
			   EContact *contact,
			   EEwsItem *item,
			   GCancellable *cancellable,
			   GError **error)
{
	time_t bdate;
	GDate date;
	EContactDate edate;

	bdate = e_ews_item_get_birthday (item);

	if (bdate) {
		g_date_clear (&date, 1);
		g_date_set_time_t (&date, bdate);

		edate.year = date.year;
		edate.month = date.month;
		edate.day = date.day;

		if (g_date_valid (&date))
			e_contact_set (contact, E_CONTACT_BIRTH_DATE, &edate);
	}
}


ebews_set_givenname_changes (EBookBackendEws *bbews,
			     ESoapMessage *message,
			     EContact *new,
			     EContact *old,
			     gchar **out_new_change_key,
			     GCancellable *cancellable,
			     GError **error)
{
	/* Does nothing, the "GivenName" is filled by the "FullName" code */
}


ews_backend_create_resource_sync (ECollectionBackend *backend,
                                  ESource *source,
                                  GCancellable *cancellable,
                                  GError **error)
{
	EEwsConnection *connection = NULL;
	EwsFolderId *out_folder_id = NULL;
	EEwsFolderType folder_type = E_EWS_FOLDER_TYPE_UNKNOWN;
	const gchar *extension_name;
	const gchar *parent_folder_id = NULL;
	gchar *folder_name;
	gboolean success = FALSE;

	extension_name = E_SOURCE_EXTENSION_EWS_FOLDER;
	if (e_source_has_extension (source, extension_name)) {
		ESourceEwsFolder *extension;

		/* foreign and public folders are just added */
		extension = e_source_get_extension (source, extension_name);
		if (e_source_ews_folder_get_foreign (extension) ||
		    e_source_ews_folder_get_public (extension))
			success = TRUE;
	}

	if (!success) {
		connection = e_ews_backend_ref_connection_sync (E_EWS_BACKEND (backend), NULL, NULL, NULL, cancellable, error);
		if (connection == NULL)
			return FALSE;

		extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
		if (e_source_has_extension (source, extension_name)) {
			folder_type = E_EWS_FOLDER_TYPE_CONTACTS;
			parent_folder_id = "contacts";
		}

		extension_name = E_SOURCE_EXTENSION_CALENDAR;
		if (e_source_has_extension (source, extension_name)) {
			folder_type = E_EWS_FOLDER_TYPE_CALENDAR;
			parent_folder_id = "calendar";
		}

		extension_name = E_SOURCE_EXTENSION_TASK_LIST;
		if (e_source_has_extension (source, extension_name)) {
			folder_type = E_EWS_FOLDER_TYPE_TASKS;
			parent_folder_id = "tasks";
		}

		/* FIXME No support for memo lists. */

		if (parent_folder_id == NULL) {
			g_set_error (
				error, G_IO_ERROR,
				G_IO_ERROR_INVALID_ARGUMENT,
				_("Could not determine a suitable folder "
				"class for a new folder named “%s”"),
				e_source_get_display_name (source));
			goto exit;
		}

		folder_name = e_source_dup_display_name (source);

		success = e_ews_connection_create_folder_sync (
			connection, EWS_PRIORITY_MEDIUM,
			parent_folder_id, TRUE,
			folder_name, folder_type,
			&out_folder_id, cancellable, error);

		g_free (folder_name);

		/* Sanity check */
		g_warn_if_fail (
			(success && out_folder_id != NULL) ||
			(!success && out_folder_id == NULL));

		if (out_folder_id != NULL) {
			ESourceEwsFolder *extension;
			const gchar *extension_name;

			extension_name = E_SOURCE_EXTENSION_EWS_FOLDER;
			extension = e_source_get_extension (source, extension_name);
			e_source_ews_folder_set_id (
				extension, out_folder_id->id);
			e_source_ews_folder_set_change_key (
				extension, out_folder_id->change_key);

			e_ews_folder_id_free (out_folder_id);
		}
	}

	if (success) {
		ESourceRegistryServer *server;
		ESource *parent_source;
		const gchar *cache_dir;
		const gchar *parent_uid;

		/* Configure the source as a collection member. */
		parent_source = e_backend_get_source (E_BACKEND (backend));
		parent_uid = e_source_get_uid (parent_source);
		e_source_set_parent (source, parent_uid);

		/* Changes should be written back to the cache directory. */
		cache_dir = e_collection_backend_get_cache_dir (backend);
		e_server_side_source_set_write_directory (
			E_SERVER_SIDE_SOURCE (source), cache_dir);

		/* Set permissions for clients. */
		e_server_side_source_set_writable (E_SERVER_SIDE_SOURCE (source), TRUE);
		e_server_side_source_set_remote_deletable (E_SERVER_SIDE_SOURCE (source), TRUE);

		server = e_collection_backend_ref_server (backend);
		e_source_registry_server_add_source (server, source);
		g_object_unref (server);
	}

 exit:
	if (connection)
		g_object_unref (connection);

	return success;
}


ews_node_new ()
{
	EwsNode *node;

	node = g_new0 (EwsNode, 1);
	return node;
}


camel_ews_store_ensure_unique_path (CamelEwsStore *ews_store,
                                    gchar **ppath)
{
	gboolean done;
	guint counter = 0;
	gchar *base_path = NULL;

	g_return_if_fail (CAMEL_IS_EWS_STORE (ews_store));
	g_return_if_fail (ews_store->summary != NULL);
	g_return_if_fail (ppath != NULL);
	g_return_if_fail (*ppath != NULL);

	done = FALSE;
	while (!done) {
		gchar *fid;

		done = TRUE;

		fid = camel_ews_store_summary_get_folder_id_from_name (ews_store->summary, *ppath);
		if (fid) {
			g_free (fid);

			done = FALSE;
			counter++;
			if (!counter) {
				g_debug ("%s: Counter overflow", G_STRFUNC);
				break;
			}

			if (!base_path)
				base_path = *ppath;
			else
				g_free (*ppath);

			*ppath = g_strdup_printf ("%s_%u", base_path, counter);
		}
	}

	g_free (base_path);
}


ebb_ews_contacts_append_dl (EBookBackendEws *bbews,
			    EEwsItem *item,
			    const EwsId *id,
			    const gchar *d_name,
			    GSList *members,
			    GSList **contacts,
			    GCancellable *cancellable,
			    GError **error)
{
	EContact *contact;
	EVCardAttribute *attr;

	contact = ebb_ews_get_dl_info (bbews, item, id, d_name, members, cancellable, error);
	if (contact == NULL)
		return FALSE;

	attr = e_vcard_attribute_new (NULL, "X-EWS-KIND");
	e_vcard_add_attribute_with_value (E_VCARD (contact), attr, "DT_DISTLIST");

	*contacts = g_slist_prepend (*contacts, contact);

	return TRUE;
}


ebb_ews_gal_filter_contact (goffset offset,
			    const gchar *sha1,
			    gpointer user_data,
			    GError **error)
{
	struct _db_data *data = (struct _db_data *) user_data;
	gchar *uid;

	/* Is there an existing identical record, with the same SHA1? */
	uid = g_hash_table_lookup (data->sha1s, sha1);
	if (!uid)
		return TRUE;

	/* Remove it from the hash tables so it doesn't get deleted at the end. */
	g_hash_table_remove (data->sha1s, sha1);
	g_hash_table_remove (data->uids, uid);
	data->unchanged++;

	/* Don't bother to parse and process this record. */
	return FALSE;
}


get_property_as_uint32 (xmlNodePtr node_ptr,
                        const gchar *name)
{
	gchar *s;
	guint32 val = -1;

	s = get_property (node_ptr, name);
	if (s)
		sscanf (s,"%"G_GUINT32_FORMAT, &val);
	g_free (s);

	return val;
}


e_ews_connection_delete_folder_sync (EEwsConnection *cnc,
                                     gint pri,
                                     const gchar *folder_id,
                                     gboolean is_distinguished_id,
                                     const gchar *delete_type,
                                     GCancellable *cancellable,
                                     GError **error)
{
	EAsyncClosure *closure;
	GAsyncResult *result;
	gboolean success;

	g_return_val_if_fail (cnc != NULL, FALSE);

	closure = e_async_closure_new ();

	e_ews_connection_delete_folder (
		cnc, pri, folder_id,
		is_distinguished_id,
		delete_type,
		cancellable,
		e_async_closure_callback, closure);

	result = e_async_closure_wait (closure);

	success = e_ews_connection_delete_folder_finish (cnc, result, error);

	e_async_closure_free (closure);

	return success;
}


e_ews_connection_get_server_time_zones_finish (EEwsConnection *cnc,
					       GAsyncResult *result,
					       GSList **tzds, /*EEwsCalendarTimeZoneDefinition */
					       GError **error)
{
	GSimpleAsyncResult *simple;
	EwsAsyncData *async_data;

	g_return_val_if_fail (cnc != NULL, FALSE);
	g_return_val_if_fail (
		g_simple_async_result_is_valid (result, G_OBJECT (cnc), e_ews_connection_get_server_time_zones),
		FALSE);

	simple = G_SIMPLE_ASYNC_RESULT (result);
	async_data = g_simple_async_result_get_op_res_gpointer (simple);

	if (g_simple_async_result_propagate_error (simple, error))
		return FALSE;

	if (async_data->tzds == NULL)
		return FALSE;

	if (tzds != NULL)
		*tzds = async_data->tzds;
	else
		g_slist_free_full (
			async_data->tzds,
			(GDestroyNotify) e_ews_calendar_time_zone_definition_free);

	return TRUE;
}


camel_ews_store_init (CamelEwsStore *ews_store)
{
	ews_store->priv = CAMEL_EWS_STORE_GET_PRIVATE (ews_store);

	ews_store->priv->last_refresh_time = time (NULL) - (FINFO_REFRESH_INTERVAL + 10);
	ews_store->priv->updates_cancellable = NULL;
	ews_store->priv->update_folder_names = NULL;
	ews_store->priv->subscription_key = 0;
	ews_store->priv->update_folder_id = 0;
	ews_store->priv->update_folder_list_id = 0;
	ews_store->priv->password_expires_in_days = -1;
	g_mutex_init (&ews_store->priv->get_finfo_lock);
	g_mutex_init (&ews_store->priv->connection_lock);
	g_rec_mutex_init (&ews_store->priv->update_lock);
}


e_ews_connection_add_delegate_finish (EEwsConnection *cnc,
                                      GAsyncResult *result,
                                      GError **error)
{
	GSimpleAsyncResult *simple;

	g_return_val_if_fail (cnc != NULL, FALSE);
	g_return_val_if_fail (
		g_simple_async_result_is_valid (
		result, G_OBJECT (cnc), e_ews_connection_add_delegate),
		FALSE);

	simple = G_SIMPLE_ASYNC_RESULT (result);

	return !g_simple_async_result_propagate_error (simple, error);
}


e_util_utf8_make_valid (const gchar *str)
{
	if (!str)
		return g_strdup ("");

	return e_util_utf8_data_make_valid (str, strlen (str));
}


e_util_unref_in_thread (gpointer object)
{
	GThread *thread;
	GError *error = NULL;

	if (!object)
		return;

	g_return_if_fail (G_IS_OBJECT (object));

	thread = g_thread_try_new (NULL, unref_object_in_thread, object, &error);
	if (thread) {
		g_thread_unref (thread);
	} else {
		g_warning ("%s: Failed to run thread: %s", G_STRFUNC, error ? error->message : "Unknown error");
		g_object_unref (object);
	}

	g_clear_error (&error);
}


e_util_gthread_id (GThread *thread)
{
#if GLIB_SIZEOF_VOID_P == 8
	/* 64-bit Windows */
	return (guint64) thread;
#else
	return (gint) thread;
#endif
}


stripped_char (gunichar ch)
{
	gunichar decomp[4];
	gunichar retval;
	GUnicodeType utype;
	gsize dlen;

	utype = g_unichar_type (ch);

	switch (utype) {
	case G_UNICODE_CONTROL:
	case G_UNICODE_FORMAT:
	case G_UNICODE_UNASSIGNED:
	case G_UNICODE_SPACING_MARK:
		/* Ignore those */
		return 0;
	default:
		/* Convert to lowercase */
		ch = g_unichar_tolower (ch);
		/* falls through */
	case G_UNICODE_LOWERCASE_LETTER:
		if ((dlen = g_unichar_fully_decompose (ch, FALSE, decomp, 4))) {
			retval = decomp[0];
			return retval;
		}
		break;
	}

	return 0;
}


e_async_closure_free (EAsyncClosure *closure)
{
	g_return_if_fail (closure != NULL);

	g_main_context_pop_thread_default (closure->context);

	g_main_loop_unref (closure->loop);
	g_main_context_unref (closure->context);

	g_mutex_lock (&closure->lock);
	g_clear_object (&closure->result);
	g_mutex_unlock (&closure->lock);
	g_mutex_clear (&closure->lock);

	g_slice_free (EAsyncClosure, closure);
}


e_binding_transform_enum_value_to_nick (GBinding *binding,
                                        const GValue *source_value,
                                        GValue *target_value,
                                        gpointer not_used)
{
	GEnumClass *enum_class;
	GEnumValue *enum_value;
	gint value;
	gboolean success = FALSE;

	g_return_val_if_fail (G_IS_BINDING (binding), FALSE);

	enum_class = g_type_class_peek (G_VALUE_TYPE (source_value));
	g_return_val_if_fail (G_IS_ENUM_CLASS (enum_class), FALSE);

	value = g_value_get_enum (source_value);
	enum_value = g_enum_get_value (enum_class, value);
	if (enum_value != NULL) {
		g_value_set_string (target_value, enum_value->value_nick);
		success = TRUE;
	}

	return success;
}


e_weak_ref_new (gpointer object)
{
	GWeakRef *weak_ref;

	weak_ref = g_slice_new0 (GWeakRef);
	g_weak_ref_init (weak_ref, object);

	return weak_ref;
}


static void skb_warn_bad_offload(const struct sk_buff *skb)
{
	static const netdev_features_t null_features = 0;
	struct net_device *dev = skb->dev;
	const char *driver = "";

	if (!net_ratelimit())
		return;

	if (dev && dev->dev.parent)
		driver = dev_driver_string(dev->dev.parent);

	WARN(1, "%s: caps=(%pNF, %pNF) len=%d data_len=%d gso_size=%d "
	     "gso_type=%d ip_summed=%d\n",
	     driver, dev ? &dev->features : &null_features,
	     skb->sk ? &skb->sk->sk_route_caps : &null_features,
	     skb->len, skb->data_len, skb_shinfo(skb)->gso_size,
	     skb_shinfo(skb)->gso_type, skb->ip_summed);


static void __cleanup(struct ioat_dma_chan *ioat, dma_addr_t phys_complete)
{
	struct ioat_chan_common *chan = &ioat->base;
	struct list_head *_desc, *n;
	struct dma_async_tx_descriptor *tx;

	dev_dbg(to_dev(chan), "%s: phys_complete: %llx\n",
		 __func__, (unsigned long long) phys_complete);
	list_for_each_safe(_desc, n, &ioat->used_desc) {
		struct ioat_desc_sw *desc;

		prefetch(n);
		desc = list_entry(_desc, typeof(*desc), node);
		tx = &desc->txd;
		/*
		 * Incoming DMA requests may use multiple descriptors,
		 * due to exceeding xfercap, perhaps. If so, only the
		 * last one will have a cookie, and require unmapping.
		 */
		dump_desc_dbg(ioat, desc);
		if (tx->cookie) {
			dma_cookie_complete(tx);
			dma_descriptor_unmap(tx);
			ioat->active -= desc->hw->tx_cnt;
			if (tx->callback) {
				tx->callback(tx->callback_param);
				tx->callback = NULL;
			}
		}

		if (tx->phys != phys_complete) {
			/*
			 * a completed entry, but not the last, so clean
			 * up if the client is done with the descriptor
			 */
			if (async_tx_test_ack(tx))
				list_move_tail(&desc->node, &ioat->free_desc);
		} else {
			/*
			 * last used desc. Do not remove, so we can
			 * append from it.
			 */

			/* if nothing else is pending, cancel the
			 * completion timeout
			 */
			if (n == &ioat->used_desc) {
				dev_dbg(to_dev(chan),
					"%s cancel completion timeout\n",
					__func__);
				clear_bit(IOAT_COMPLETION_PENDING, &chan->state);
			}

			/* TODO check status bits? */
			break;
		}
	}

	chan->last_completion = phys_complete;
}


static int tcp_match_skb_to_sack(struct sock *sk, struct sk_buff *skb,
				  u32 start_seq, u32 end_seq)
{
	int err;
	bool in_sack;
	unsigned int pkt_len;
	unsigned int mss;

	in_sack = !after(start_seq, TCP_SKB_CB(skb)->seq) &&
		  !before(end_seq, TCP_SKB_CB(skb)->end_seq);

	if (tcp_skb_pcount(skb) > 1 && !in_sack &&
	    after(TCP_SKB_CB(skb)->end_seq, start_seq)) {
		mss = tcp_skb_mss(skb);
		in_sack = !after(start_seq, TCP_SKB_CB(skb)->seq);

		if (!in_sack) {
			pkt_len = start_seq - TCP_SKB_CB(skb)->seq;
			if (pkt_len < mss)
				pkt_len = mss;
		} else {
			pkt_len = end_seq - TCP_SKB_CB(skb)->seq;
			if (pkt_len < mss)
				return -EINVAL;
		}

		/* Round if necessary so that SACKs cover only full MSSes
		 * and/or the remaining small portion (if present)
		 */
		if (pkt_len > mss) {
			unsigned int new_len = (pkt_len / mss) * mss;
			if (!in_sack && new_len < pkt_len) {
				new_len += mss;
				if (new_len > skb->len)
					return 0;
			}
			pkt_len = new_len;
		}
		err = tcp_fragment(sk, skb, pkt_len, mss);
		if (err < 0)
			return err;
	}

	return in_sack;
}


static void get_openreq6(struct seq_file *seq,
			 const struct sock *sk, struct request_sock *req, int i, kuid_t uid)
{
	int ttd = req->expires - jiffies;
	const struct in6_addr *src = &inet_rsk(req)->ir_v6_loc_addr;
	const struct in6_addr *dest = &inet_rsk(req)->ir_v6_rmt_addr;

	if (ttd < 0)
		ttd = 0;

	seq_printf(seq,
		   "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
		   "%02X %08X:%08X %02X:%08lX %08X %5u %8d %d %d %pK\n",
		   i,
		   src->s6_addr32[0], src->s6_addr32[1],
		   src->s6_addr32[2], src->s6_addr32[3],
		   inet_rsk(req)->ir_num,
		   dest->s6_addr32[0], dest->s6_addr32[1],
		   dest->s6_addr32[2], dest->s6_addr32[3],
		   ntohs(inet_rsk(req)->ir_rmt_port),
		   TCP_SYN_RECV,
		   0, 0, /* could print option size, but that is af dependent. */
		   1,   /* timers active (only the expire timer) */
		   jiffies_to_clock_t(ttd),
		   req->num_timeout,
		   from_kuid_munged(seq_user_ns(seq), uid),
		   0,  /* non standard timer */
		   0, /* open_requests have no inode */
		   0, req);
}


static bool tcp_try_undo_partial(struct sock *sk, const int acked,
				 const int prior_unsacked)
{
	struct tcp_sock *tp = tcp_sk(sk);

	if (tp->undo_marker && tcp_packet_delayed(tp)) {
		/* Plain luck! Hole if filled with delayed
		 * packet, rather than with a retransmit.
		 */
		tcp_update_reordering(sk, tcp_fackets_out(tp) + acked, 1);

		/* We are getting evidence that the reordering degree is higher
		 * than we realized. If there are no retransmits out then we
		 * can undo. Otherwise we clock out new packets but do not
		 * mark more packets lost or retransmit more.
		 */
		if (tp->retrans_out) {
			tcp_cwnd_reduction(sk, prior_unsacked, 0);
			return true;
		}

		if (!tcp_any_retrans_done(sk))
			tp->retrans_stamp = 0;

		DBGUNDO(sk, "partial recovery");
		tcp_undo_cwnd_reduction(sk, true);
		NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_TCPPARTIALUNDO);
		tcp_try_keep_open(sk);
		return true;
	}
	return false;
}


static void ioat2_restart_channel(struct ioat2_dma_chan *ioat)
{
	struct ioat_chan_common *chan = &ioat->base;
	dma_addr_t phys_complete;

	ioat2_quiesce(chan, 0);
	if (ioat_cleanup_preamble(chan, &phys_complete))
		__cleanup(ioat, phys_complete);

	__ioat2_restart_chan(ioat);
}


static void ioat2_start_null_desc(struct ioat2_dma_chan *ioat)
{
	spin_lock_bh(&ioat->prep_lock);
	__ioat2_start_null_desc(ioat);
	spin_unlock_bh(&ioat->prep_lock);
}


static inline bool is_dma_copy_aligned(struct dma_device *dev, size_t off1,
				       size_t off2, size_t len)
{
	return dmaengine_check_align(dev->copy_align, off1, off2, len);
}


__dma_device_satisfies_mask(struct dma_device *device,
			    const dma_cap_mask_t *want)
{
	dma_cap_mask_t has;

	bitmap_and(has.bits, want->bits, device->cap_mask.bits,
		DMA_TX_TYPE_END);
	return bitmap_equal(want->bits, has.bits, DMA_TX_TYPE_END);
}


struct dma_chan *dma_find_channel(enum dma_transaction_type tx_type)
{
	return this_cpu_read(channel_table[tx_type]->chan);
}


static inline void sk_filter_uncharge(struct sock *sk, struct sk_filter *fp)
{
	atomic_sub(sk_filter_size(fp->len), &sk->sk_omem_alloc);
	sk_filter_release(fp);
}


static inline u32 __tcp_set_rto(const struct tcp_sock *tp)
{
	return (tp->srtt >> 3) + tp->rttvar;
}


static void ioat1_reset_channel(struct ioat_dma_chan *ioat)
{
	struct ioat_chan_common *chan = &ioat->base;
	void __iomem *reg_base = chan->reg_base;
	u32 chansts, chanerr;

	dev_warn(to_dev(chan), "reset\n");
	chanerr = readl(reg_base + IOAT_CHANERR_OFFSET);
	chansts = *chan->completion & IOAT_CHANSTS_STATUS;
	if (chanerr) {
		dev_err(to_dev(chan),
			"chan%d, CHANSTS = 0x%08x CHANERR = 0x%04x, clearing\n",
			chan_num(chan), chansts, chanerr);
		writel(chanerr, reg_base + IOAT_CHANERR_OFFSET);
	}

	/*
	 * whack it upside the head with a reset
	 * and wait for things to settle out.
	 * force the pending count to a really big negative
	 * to make sure no one forces an issue_pending
	 * while we're waiting.
	 */

	ioat->pending = INT_MIN;
	writeb(IOAT_CHANCMD_RESET,
	       reg_base + IOAT_CHANCMD_OFFSET(chan->device->version));
	set_bit(IOAT_RESET_PENDING, &chan->state);
	mod_timer(&chan->timer, jiffies + RESET_DELAY);
}


static inline void tcp_synq_overflow(struct sock *sk)
{
	tcp_sk(sk)->rx_opt.ts_recent_stamp = jiffies;
}


int ioat1_dma_probe(struct ioatdma_device *device, int dca)
{
	struct pci_dev *pdev = device->pdev;
	struct dma_device *dma;
	int err;

	device->intr_quirk = ioat1_intr_quirk;
	device->enumerate_channels = ioat1_enumerate_channels;
	device->self_test = ioat_dma_self_test;
	device->timer_fn = ioat1_timer_event;
	device->cleanup_fn = ioat1_cleanup_event;
	dma = &device->common;
	dma->device_prep_dma_memcpy = ioat1_dma_prep_memcpy;
	dma->device_issue_pending = ioat1_dma_memcpy_issue_pending;
	dma->device_alloc_chan_resources = ioat1_dma_alloc_chan_resources;
	dma->device_free_chan_resources = ioat1_dma_free_chan_resources;
	dma->device_tx_status = ioat_dma_tx_status;

	err = ioat_probe(device);
	if (err)
		return err;
	err = ioat_register(device);
	if (err)
		return err;
	ioat_kobject_add(device, &ioat1_ktype);

	if (dca)
		device->dca = ioat_dca_init(pdev, device->reg_base);

	return err;
}


static unsigned int tcp_xmit_size_goal(struct sock *sk, u32 mss_now,
				       int large_allowed)
{
	struct tcp_sock *tp = tcp_sk(sk);
	u32 xmit_size_goal, old_size_goal;

	xmit_size_goal = mss_now;

	if (large_allowed && sk_can_gso(sk)) {
		u32 gso_size, hlen;

		/* Maybe we should/could use sk->sk_prot->max_header here ? */
		hlen = inet_csk(sk)->icsk_af_ops->net_header_len +
		       inet_csk(sk)->icsk_ext_hdr_len +
		       tp->tcp_header_len;

		/* Goal is to send at least one packet per ms,
		 * not one big TSO packet every 100 ms.
		 * This preserves ACK clocking and is consistent
		 * with tcp_tso_should_defer() heuristic.
		 */
		gso_size = sk->sk_pacing_rate / (2 * MSEC_PER_SEC);
		gso_size = max_t(u32, gso_size,
				 sysctl_tcp_min_tso_segs * mss_now);

		xmit_size_goal = min_t(u32, gso_size,
				       sk->sk_gso_max_size - 1 - hlen);

		xmit_size_goal = tcp_bound_to_half_wnd(tp, xmit_size_goal);

		/* We try hard to avoid divides here */
		old_size_goal = tp->xmit_size_goal_segs * mss_now;

		if (likely(old_size_goal <= xmit_size_goal &&
			   old_size_goal + mss_now > xmit_size_goal)) {
			xmit_size_goal = old_size_goal;
		} else {
			tp->xmit_size_goal_segs =
				min_t(u16, xmit_size_goal / mss_now,
				      sk->sk_gso_max_segs);
			xmit_size_goal = tp->xmit_size_goal_segs * mss_now;
		}
	}

	return max(xmit_size_goal, mss_now);
}


static void get_tcp6_sock(struct seq_file *seq, struct sock *sp, int i)
{
	const struct in6_addr *dest, *src;
	__u16 destp, srcp;
	int timer_active;
	unsigned long timer_expires;
	const struct inet_sock *inet = inet_sk(sp);
	const struct tcp_sock *tp = tcp_sk(sp);
	const struct inet_connection_sock *icsk = inet_csk(sp);

	dest  = &sp->sk_v6_daddr;
	src   = &sp->sk_v6_rcv_saddr;
	destp = ntohs(inet->inet_dport);
	srcp  = ntohs(inet->inet_sport);

	if (icsk->icsk_pending == ICSK_TIME_RETRANS) {
		timer_active	= 1;
		timer_expires	= icsk->icsk_timeout;
	} else if (icsk->icsk_pending == ICSK_TIME_PROBE0) {
		timer_active	= 4;
		timer_expires	= icsk->icsk_timeout;
	} else if (timer_pending(&sp->sk_timer)) {
		timer_active	= 2;
		timer_expires	= sp->sk_timer.expires;
	} else {
		timer_active	= 0;
		timer_expires = jiffies;
	}

	seq_printf(seq,
		   "%4d: %08X%08X%08X%08X:%04X %08X%08X%08X%08X:%04X "
		   "%02X %08X:%08X %02X:%08lX %08X %5u %8d %lu %d %pK %lu %lu %u %u %d\n",
		   i,
		   src->s6_addr32[0], src->s6_addr32[1],
		   src->s6_addr32[2], src->s6_addr32[3], srcp,
		   dest->s6_addr32[0], dest->s6_addr32[1],
		   dest->s6_addr32[2], dest->s6_addr32[3], destp,
		   sp->sk_state,
		   tp->write_seq-tp->snd_una,
		   (sp->sk_state == TCP_LISTEN) ? sp->sk_ack_backlog : (tp->rcv_nxt - tp->copied_seq),
		   timer_active,
		   jiffies_delta_to_clock_t(timer_expires - jiffies),
		   icsk->icsk_retransmits,
		   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sp)),
		   icsk->icsk_probes_out,
		   sock_i_ino(sp),
		   atomic_read(&sp->sk_refcnt), sp,
		   jiffies_to_clock_t(icsk->icsk_rto),
		   jiffies_to_clock_t(icsk->icsk_ack.ato),
		   (icsk->icsk_ack.quick << 1) | icsk->icsk_ack.pingpong,
		   tp->snd_cwnd,
		   tcp_in_initial_slowstart(tp) ? -1 : tp->snd_ssthresh
		   );
}


desc_get_errstat(struct ioat2_dma_chan *ioat, struct ioat_ring_ent *desc)
{
	struct ioat_dma_descriptor *hw = desc->hw;

	switch (hw->ctl_f.op) {
	case IOAT_OP_PQ_VAL:
	case IOAT_OP_PQ_VAL_16S:
	{
		struct ioat_pq_descriptor *pq = desc->pq;

		/* check if there's error written */
		if (!pq->dwbes_f.wbes)
			return;

		/* need to set a chanerr var for checking to clear later */

		if (pq->dwbes_f.p_val_err)
			*desc->result |= SUM_CHECK_P_RESULT;

		if (pq->dwbes_f.q_val_err)
			*desc->result |= SUM_CHECK_Q_RESULT;

		return;
	}
	default:
		return;
	}
}


static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key,
			       __be32 daddr, __be32 saddr, const struct tcphdr *th)
{
	struct tcp_md5sig_pool *hp;
	struct hash_desc *desc;

	hp = tcp_get_md5sig_pool();
	if (!hp)
		goto clear_hash_noput;
	desc = &hp->md5_desc;

	if (crypto_hash_init(desc))
		goto clear_hash;
	if (tcp_v4_md5_hash_pseudoheader(hp, daddr, saddr, th->doff << 2))
		goto clear_hash;
	if (tcp_md5_hash_header(hp, th))
		goto clear_hash;
	if (tcp_md5_hash_key(hp, key))
		goto clear_hash;
	if (crypto_hash_final(desc, md5_hash))
		goto clear_hash;

	tcp_put_md5sig_pool();
	return 0;

clear_hash:
	tcp_put_md5sig_pool();
clear_hash_noput:
	memset(md5_hash, 0, 16);
	return 1;
}


static int netif_rx_internal(struct sk_buff *skb)
{
	int ret;

	/* if netpoll wants it, pretend we never saw it */
	if (netpoll_rx(skb))
		return NET_RX_DROP;

	net_timestamp_check(netdev_tstamp_prequeue, skb);

	trace_netif_rx(skb);
#ifdef CONFIG_RPS
	if (static_key_false(&rps_needed)) {
		struct rps_dev_flow voidflow, *rflow = &voidflow;
		int cpu;

		preempt_disable();
		rcu_read_lock();

		cpu = get_rps_cpu(skb->dev, skb, &rflow);
		if (cpu < 0)
			cpu = smp_processor_id();

		ret = enqueue_to_backlog(skb, cpu, &rflow->last_qtail);

		rcu_read_unlock();
		preempt_enable();
	} else
#endif
	{
		unsigned int qtail;
		ret = enqueue_to_backlog(skb, get_cpu(), &qtail);
		put_cpu();
	}
	return ret;


unsigned int tcp_poll(struct file *file, struct socket *sock, poll_table *wait)
{
	unsigned int mask;
	struct sock *sk = sock->sk;
	const struct tcp_sock *tp = tcp_sk(sk);

	sock_rps_record_flow(sk);

	sock_poll_wait(file, sk_sleep(sk), wait);
	if (sk->sk_state == TCP_LISTEN)
		return inet_csk_listen_poll(sk);

	/* Socket is not locked. We are protected from async events
	 * by poll logic and correct handling of state changes
	 * made by other threads is impossible in any case.
	 */

	mask = 0;

	/*
	 * POLLHUP is certainly not done right. But poll() doesn't
	 * have a notion of HUP in just one direction, and for a
	 * socket the read side is more interesting.
	 *
	 * Some poll() documentation says that POLLHUP is incompatible
	 * with the POLLOUT/POLLWR flags, so somebody should check this
	 * all. But careful, it tends to be safer to return too many
	 * bits than too few, and you can easily break real applications
	 * if you don't tell them that something has hung up!
	 *
	 * Check-me.
	 *
	 * Check number 1. POLLHUP is _UNMASKABLE_ event (see UNIX98 and
	 * our fs/select.c). It means that after we received EOF,
	 * poll always returns immediately, making impossible poll() on write()
	 * in state CLOSE_WAIT. One solution is evident --- to set POLLHUP
	 * if and only if shutdown has been made in both directions.
	 * Actually, it is interesting to look how Solaris and DUX
	 * solve this dilemma. I would prefer, if POLLHUP were maskable,
	 * then we could set it on SND_SHUTDOWN. BTW examples given
	 * in Stevens' books assume exactly this behaviour, it explains
	 * why POLLHUP is incompatible with POLLOUT.	--ANK
	 *
	 * NOTE. Check for TCP_CLOSE is added. The goal is to prevent
	 * blocking on fresh not-connected or disconnected socket. --ANK
	 */
	if (sk->sk_shutdown == SHUTDOWN_MASK || sk->sk_state == TCP_CLOSE)
		mask |= POLLHUP;
	if (sk->sk_shutdown & RCV_SHUTDOWN)
		mask |= POLLIN | POLLRDNORM | POLLRDHUP;

	/* Connected or passive Fast Open socket? */
	if (sk->sk_state != TCP_SYN_SENT &&
	    (sk->sk_state != TCP_SYN_RECV || tp->fastopen_rsk != NULL)) {
		int target = sock_rcvlowat(sk, 0, INT_MAX);

		if (tp->urg_seq == tp->copied_seq &&
		    !sock_flag(sk, SOCK_URGINLINE) &&
		    tp->urg_data)
			target++;

		/* Potential race condition. If read of tp below will
		 * escape above sk->sk_state, we can be illegally awaken
		 * in SYN_* states. */
		if (tp->rcv_nxt - tp->copied_seq >= target)
			mask |= POLLIN | POLLRDNORM;

		if (!(sk->sk_shutdown & SEND_SHUTDOWN)) {
			if (sk_stream_is_writeable(sk)) {
				mask |= POLLOUT | POLLWRNORM;
			} else {  /* send SIGIO later */
				set_bit(SOCK_ASYNC_NOSPACE,
					&sk->sk_socket->flags);
				set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);

				/* Race breaker. If space is freed after
				 * wspace test but before the flags are set,
				 * IO signal will be lost.
				 */
				if (sk_stream_is_writeable(sk))
					mask |= POLLOUT | POLLWRNORM;
			}
		} else
			mask |= POLLOUT | POLLWRNORM;

		if (tp->urg_data & TCP_URG_VALID)
			mask |= POLLPRI;
	}
	/* This barrier is coupled with smp_wmb() in tcp_reset() */
	smp_rmb();
	if (sk->sk_err)
		mask |= POLLERR;

	return mask;
}


static void chan_dev_release(struct device *dev)
{
	struct dma_chan_dev *chan_dev;

	chan_dev = container_of(dev, typeof(*chan_dev), device);
	if (atomic_dec_and_test(chan_dev->idr_ref)) {
		mutex_lock(&dma_list_mutex);
		idr_remove(&dma_idr, chan_dev->dev_id);
		mutex_unlock(&dma_list_mutex);
		kfree(chan_dev->idr_ref);
	}
	kfree(chan_dev);
}


dmaengine_get_unmap_data(struct device *dev, int nr, gfp_t flags)
{
	return NULL;
}


static int dccp_msghdr_parse(struct msghdr *msg, struct sk_buff *skb)
{
	struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg);

	/*
	 * Assign an (opaque) qpolicy priority value to skb->priority.
	 *
	 * We are overloading this skb field for use with the qpolicy subystem.
	 * The skb->priority is normally used for the SO_PRIORITY option, which
	 * is initialised from sk_priority. Since the assignment of sk_priority
	 * to skb->priority happens later (on layer 3), we overload this field
	 * for use with queueing priorities as long as the skb is on layer 4.
	 * The default priority value (if nothing is set) is 0.
	 */
	skb->priority = 0;

	for (; cmsg != NULL; cmsg = CMSG_NXTHDR(msg, cmsg)) {

		if (!CMSG_OK(msg, cmsg))
			return -EINVAL;

		if (cmsg->cmsg_level != SOL_DCCP)
			continue;

		if (cmsg->cmsg_type <= DCCP_SCM_QPOLICY_MAX &&
		    !dccp_qpolicy_param_ok(skb->sk, cmsg->cmsg_type))
			return -EINVAL;

		switch (cmsg->cmsg_type) {
		case DCCP_SCM_PRIORITY:
			if (cmsg->cmsg_len != CMSG_LEN(sizeof(__u32)))
				return -EINVAL;
			skb->priority = *(__u32 *)CMSG_DATA(cmsg);
			break;
		default:
			return -EINVAL;
		}
	}
	return 0;
}


static struct ip_options_rcu *tcp_v4_save_options(struct sk_buff *skb)
{
	const struct ip_options *opt = &(IPCB(skb)->opt);
	struct ip_options_rcu *dopt = NULL;

	if (opt && opt->optlen) {
		int opt_size = sizeof(*dopt) + opt->optlen;

		dopt = kmalloc(opt_size, GFP_ATOMIC);
		if (dopt) {
			if (ip_options_echo(&dopt->opt, skb)) {
				kfree(dopt);
				dopt = NULL;
			}
		}
	}
	return dopt;
}


void ioat_kobject_add(struct ioatdma_device *device, struct kobj_type *type)
{
	struct dma_device *dma = &device->common;
	struct dma_chan *c;

	list_for_each_entry(c, &dma->channels, device_node) {
		struct ioat_chan_common *chan = to_chan_common(c);
		struct kobject *parent = &c->dev->device.kobj;
		int err;

		err = kobject_init_and_add(&chan->kobj, type, parent, "quickdata");
		if (err) {
			dev_warn(to_dev(chan),
				 "sysfs init error (%d), continuing...\n", err);
			kobject_put(&chan->kobj);
			set_bit(IOAT_KOBJ_INIT_FAIL, &chan->state);
		}
	}
}


void netdev_freemem(struct net_device *dev)
{
	char *addr = (char *)dev - dev->padded;

	if (is_vmalloc_addr(addr))
		vfree(addr);
	else
		kfree(addr);


static inline int dmaengine_slave_config(struct dma_chan *chan,
					  struct dma_slave_config *config)
{
	return dmaengine_device_control(chan, DMA_SLAVE_CONFIG,
			(unsigned long)config);
}


static void tcp_fin(struct sock *sk)
{
	struct tcp_sock *tp = tcp_sk(sk);
	const struct dst_entry *dst;

	inet_csk_schedule_ack(sk);

	sk->sk_shutdown |= RCV_SHUTDOWN;
	sock_set_flag(sk, SOCK_DONE);

	switch (sk->sk_state) {
	case TCP_SYN_RECV:
	case TCP_ESTABLISHED:
		/* Move to CLOSE_WAIT */
		tcp_set_state(sk, TCP_CLOSE_WAIT);
		dst = __sk_dst_get(sk);
		if (!dst || !dst_metric(dst, RTAX_QUICKACK))
			inet_csk(sk)->icsk_ack.pingpong = 1;
		break;

	case TCP_CLOSE_WAIT:
	case TCP_CLOSING:
		/* Received a retransmission of the FIN, do
		 * nothing.
		 */
		break;
	case TCP_LAST_ACK:
		/* RFC793: Remain in the LAST-ACK state. */
		break;

	case TCP_FIN_WAIT1:
		/* This case occurs when a simultaneous close
		 * happens, we must ack the received FIN and
		 * enter the CLOSING state.
		 */
		tcp_send_ack(sk);
		tcp_set_state(sk, TCP_CLOSING);
		break;
	case TCP_FIN_WAIT2:
		/* Received a FIN -- send ACK and enter TIME_WAIT. */
		tcp_send_ack(sk);
		tcp_time_wait(sk, TCP_TIME_WAIT, 0);
		break;
	default:
		/* Only TCP_LISTEN and TCP_CLOSE are left, in these
		 * cases we should never reach this piece of code.
		 */
		pr_err("%s: Impossible, sk->sk_state=%d\n",
		       __func__, sk->sk_state);
		break;
	}

	/* It _is_ possible, that we have something out-of-order _after_ FIN.
	 * Probably, we should reset in this case. For now drop them.
	 */
	__skb_queue_purge(&tp->out_of_order_queue);
	if (tcp_is_sack(tp))
		tcp_sack_reset(&tp->rx_opt);
	sk_mem_reclaim(sk);

	if (!sock_flag(sk, SOCK_DEAD)) {
		sk->sk_state_change(sk);

		/* Do not send POLL_HUP for half duplex close. */
		if (sk->sk_shutdown == SHUTDOWN_MASK ||
		    sk->sk_state == TCP_CLOSE)
			sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_HUP);
		else
			sk_wake_async(sk, SOCK_WAKE_WAITD, POLL_IN);
	}
}


static dma_cookie_t ioat2_tx_submit_unlock(struct dma_async_tx_descriptor *tx)
{
	struct dma_chan *c = tx->chan;
	struct ioat2_dma_chan *ioat = to_ioat2_chan(c);
	struct ioat_chan_common *chan = &ioat->base;
	dma_cookie_t cookie;

	cookie = dma_cookie_assign(tx);
	dev_dbg(to_dev(&ioat->base), "%s: cookie: %d\n", __func__, cookie);

	if (!test_and_set_bit(IOAT_CHAN_ACTIVE, &chan->state))
		mod_timer(&chan->timer, jiffies + COMPLETION_TIMEOUT);

	/* make descriptor updates visible before advancing ioat->head,
	 * this is purposefully not smp_wmb() since we are also
	 * publishing the descriptor updates to a dma device
	 */
	wmb();

	ioat->head += ioat->produce;

	ioat2_update_pending(ioat);
	spin_unlock_bh(&ioat->prep_lock);

	return cookie;
}


void ioat_init_channel(struct ioatdma_device *device, struct ioat_chan_common *chan, int idx)
{
	struct dma_device *dma = &device->common;
	struct dma_chan *c = &chan->common;
	unsigned long data = (unsigned long) c;

	chan->device = device;
	chan->reg_base = device->reg_base + (0x80 * (idx + 1));
	spin_lock_init(&chan->cleanup_lock);
	chan->common.device = dma;
	dma_cookie_init(&chan->common);
	list_add_tail(&chan->common.device_node, &dma->channels);
	device->idx[idx] = chan;
	init_timer(&chan->timer);
	chan->timer.function = device->timer_fn;
	chan->timer.data = data;
	tasklet_init(&chan->cleanup_task, device->cleanup_fn, data);
}


static inline bool dmaf_p_disabled_continue(enum dma_ctrl_flags flags)
{
	enum dma_ctrl_flags mask = DMA_PREP_CONTINUE | DMA_PREP_PQ_DISABLE_P;

	return (flags & mask) == mask;
}


int sock_recv_errqueue(struct sock *sk, struct msghdr *msg, int len,
		       int level, int type)
{
	struct sock_exterr_skb *serr;
	struct sk_buff *skb, *skb2;
	int copied, err;

	err = -EAGAIN;
	skb = skb_dequeue(&sk->sk_error_queue);
	if (skb == NULL)
		goto out;

	copied = skb->len;
	if (copied > len) {
		msg->msg_flags |= MSG_TRUNC;
		copied = len;
	}
	err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, copied);
	if (err)
		goto out_free_skb;

	sock_recv_timestamp(msg, sk, skb);

	serr = SKB_EXT_ERR(skb);
	put_cmsg(msg, level, type, sizeof(serr->ee), &serr->ee);

	msg->msg_flags |= MSG_ERRQUEUE;
	err = copied;

	/* Reset and regenerate socket error */
	spin_lock_bh(&sk->sk_error_queue.lock);
	sk->sk_err = 0;
	if ((skb2 = skb_peek(&sk->sk_error_queue)) != NULL) {
		sk->sk_err = SKB_EXT_ERR(skb2)->ee.ee_errno;
		spin_unlock_bh(&sk->sk_error_queue.lock);
		sk->sk_error_report(sk);
	} else
		spin_unlock_bh(&sk->sk_error_queue.lock);

out_free_skb:
	kfree_skb(skb);
out:
	return err;
}


static inline u64 ioat_chansts_to_addr(u64 status)
{
	return status & IOAT_CHANSTS_COMPLETED_DESCRIPTOR_ADDR;
}


static void rollback_registered_many(struct list_head *head)
{
	struct net_device *dev, *tmp;
	LIST_HEAD(close_head);

	BUG_ON(dev_boot_phase);
	ASSERT_RTNL();

	list_for_each_entry_safe(dev, tmp, head, unreg_list) {
		/* Some devices call without registering
		 * for initialization unwind. Remove those
		 * devices and proceed with the remaining.
		 */
		if (dev->reg_state == NETREG_UNINITIALIZED) {
			pr_debug("unregister_netdevice: device %s/%p never was registered\n",
				 dev->name, dev);

			WARN_ON(1);
			list_del(&dev->unreg_list);
			continue;
		}
		dev->dismantle = true;
		BUG_ON(dev->reg_state != NETREG_REGISTERED);
	}

	/* If device is running, close it first. */
	list_for_each_entry(dev, head, unreg_list)
		list_add_tail(&dev->close_list, &close_head);
	dev_close_many(&close_head);

	list_for_each_entry(dev, head, unreg_list) {
		/* And unlink it from device chain. */
		unlist_netdevice(dev);

		dev->reg_state = NETREG_UNREGISTERING;
	}

	synchronize_net();

	list_for_each_entry(dev, head, unreg_list) {
		/* Shutdown queueing discipline. */
		dev_shutdown(dev);

		/* Notify protocols, that we are about to destroy
		   this device. They should clean all the things.
		*/
		call_netdevice_notifiers(NETDEV_UNREGISTER, dev);

		if (!dev->rtnl_link_ops ||
		    dev->rtnl_link_state == RTNL_LINK_INITIALIZED)
			rtmsg_ifinfo(RTM_DELLINK, dev, ~0U, GFP_KERNEL);

		/*
		 *	Flush the unicast and multicast chains
		 */
		dev_uc_flush(dev);
		dev_mc_flush(dev);

		if (dev->netdev_ops->ndo_uninit)
			dev->netdev_ops->ndo_uninit(dev);

		/* Notifier chain MUST detach us all upper devices. */
		WARN_ON(netdev_has_any_upper_dev(dev));

		/* Remove entries from kobject tree */
		netdev_unregister_kobject(dev);
#ifdef CONFIG_XPS
		/* Remove XPS queueing entries */
		netif_reset_xps_queues_gt(dev, 0);
#endif
	}

	synchronize_net();

	list_for_each_entry(dev, head, unreg_list)
		dev_put(dev);