/*
 * Copyright (c) 2010-2019 Belledonne Communications SARL.
 *
 * This file is part of Liblinphone.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "linphone/account_creator.h"
#include "linphone/core.h"
#include "linphone/lpconfig.h"

#include "c-wrapper/c-wrapper.h"
#include "dial-plan/dial-plan.h"

#ifdef HAVE_FLEXIAPI
#include "FlexiAPIClient.hh"
#endif

#include "bctoolbox/crypto.h"
#include "bctoolbox/regex.h"

// TODO: From coreapi. Remove me later.
#include "private.h"

// =============================================================================

using namespace LinphonePrivate;

BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneAccountCreatorCbs);

BELLE_SIP_INSTANCIATE_VPTR(LinphoneAccountCreatorCbs, belle_sip_object_t,
	NULL, // destroy
	NULL, // clone
	NULL, // marshal
	FALSE
);

/************************** Start Account Creator Cbs **************************/

LinphoneAccountCreatorCbs * linphone_account_creator_cbs_new(void) {
	return belle_sip_object_new(LinphoneAccountCreatorCbs);
}

LinphoneAccountCreatorCbs * linphone_account_creator_cbs_ref(LinphoneAccountCreatorCbs *cbs) {
	belle_sip_object_ref(cbs);
	return cbs;
}

void linphone_account_creator_cbs_unref(LinphoneAccountCreatorCbs *cbs) {
	belle_sip_object_unref(cbs);
}

void *linphone_account_creator_cbs_get_user_data(const LinphoneAccountCreatorCbs *cbs) {
	return cbs->user_data;
}

void linphone_account_creator_cbs_set_user_data(LinphoneAccountCreatorCbs *cbs, void *ud) {
	cbs->user_data = ud;
}

LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_create_account(const LinphoneAccountCreatorCbs *cbs) {
	return cbs->create_account_response_cb;
}

void linphone_account_creator_cbs_set_create_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
	cbs->create_account_response_cb = cb;
}

LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_delete_account(const LinphoneAccountCreatorCbs *cbs) {
	return cbs->delete_account_response_cb;
}

void linphone_account_creator_cbs_set_delete_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
	cbs->delete_account_response_cb = cb;
}

LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_account_exist(const LinphoneAccountCreatorCbs *cbs) {
	return cbs->is_account_exist_response_cb;
}

void linphone_account_creator_cbs_set_is_account_exist(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
	cbs->is_account_exist_response_cb = cb;
}

LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_confirmation_key(const LinphoneAccountCreatorCbs *cbs) {
	return cbs->confirmation_key_response_cb;
}

void linphone_account_creator_cbs_set_confirmation_key(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
	cbs->confirmation_key_response_cb = cb;
}

LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_activate_account(const LinphoneAccountCreatorCbs *cbs) {
	return cbs->activate_account_response_cb;
}

void linphone_account_creator_cbs_set_activate_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
	cbs->activate_account_response_cb = cb;
}

LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_send_token(const LinphoneAccountCreatorCbs *cbs) {
	return cbs->send_token_cb;
}

void linphone_account_creator_cbs_set_send_token(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
	cbs->send_token_cb = cb;
}

LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_account_activated(const LinphoneAccountCreatorCbs *cbs) {
	return cbs->is_account_activated_response_cb;
}

void linphone_account_creator_cbs_set_is_account_activated(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
	cbs->is_account_activated_response_cb = cb;
}

LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_link_account(const LinphoneAccountCreatorCbs *cbs) {
	return cbs->link_account_response_cb;
}

void linphone_account_creator_cbs_set_link_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
	cbs->link_account_response_cb = cb;
}

LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_activate_alias(const LinphoneAccountCreatorCbs *cbs) {
	return cbs->activate_alias_response_cb;
}

void linphone_account_creator_cbs_set_activate_alias(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
	cbs->activate_alias_response_cb = cb;
}

LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_alias_used(const LinphoneAccountCreatorCbs *cbs) {
	return cbs->is_alias_used_response_cb;
}

void linphone_account_creator_cbs_set_is_alias_used(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
	cbs->is_alias_used_response_cb = cb;
}

LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_is_account_linked(const LinphoneAccountCreatorCbs *cbs) {
	return cbs->is_account_linked_response_cb;
}

void linphone_account_creator_cbs_set_is_account_linked(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
	cbs->is_account_linked_response_cb = cb;
}

LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_recover_account(const LinphoneAccountCreatorCbs *cbs) {
	return cbs->recover_account_response_cb;
}

void linphone_account_creator_cbs_set_recover_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
	cbs->recover_account_response_cb = cb;
}

LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_update_account(const LinphoneAccountCreatorCbs *cbs) {
	return cbs->update_account_response_cb;
}

void linphone_account_creator_cbs_set_update_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
	cbs->update_account_response_cb = cb;
}

LinphoneAccountCreatorCbsStatusCb linphone_account_creator_cbs_get_login_linphone_account(const LinphoneAccountCreatorCbs *cbs) {
	return cbs->login_linphone_account_response_cb;
}

void linphone_account_creator_cbs_set_login_linphone_account(LinphoneAccountCreatorCbs *cbs, LinphoneAccountCreatorCbsStatusCb cb) {
	cbs->login_linphone_account_response_cb = cb;
}

/************************** End Account Creator Cbs **************************/

/************************** Start Account Creator Linphone **************************/

LinphoneAccountCreatorStatus linphone_account_creator_constructor_linphone_xmlrpc(LinphoneAccountCreator *creator) {
	LinphoneAddress *addr;
	const char *identity = linphone_config_get_default_string(linphone_core_get_config(creator->core), "proxy", "reg_identity", NULL);
	const char *proxy = linphone_config_get_default_string(linphone_core_get_config(creator->core), "proxy", "reg_proxy", NULL);
	const char *route = linphone_config_get_default_string(linphone_core_get_config(creator->core), "proxy", "reg_route", NULL);
	const char *realm = linphone_config_get_default_string(linphone_core_get_config(creator->core), "proxy", "realm", NULL);

	if (!creator->proxy_cfg) creator->proxy_cfg = linphone_core_create_proxy_config(creator->core);
	linphone_proxy_config_set_realm(creator->proxy_cfg, realm ? realm : "sip.linphone.org");
	linphone_proxy_config_set_route(creator->proxy_cfg, route ? route : "sip.linphone.org");
	linphone_proxy_config_set_server_addr(creator->proxy_cfg, proxy ? proxy : "sip.linphone.org");
	addr = linphone_address_new(identity ? identity : "sip:username@sip.linphone.org");
	linphone_proxy_config_set_identity_address(creator->proxy_cfg, addr);
	linphone_address_unref(addr);

	return LinphoneAccountCreatorStatusRequestOk;
}

/****************** START OF ACCOUNT USED SECTION *****************************/
static void _is_account_exist_response_cb(LinphoneXmlRpcRequest *request) {
	LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
	LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed;

	const char* resp = linphone_xml_rpc_request_get_string_response(request);
	if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) {
		status = (strcmp(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == 0) ? LinphoneAccountCreatorStatusAccountNotExist : (
						(strcmp(resp, "ERROR_ALIAS_DOESNT_EXIST") == 0) ? LinphoneAccountCreatorStatusAccountExist :
													LinphoneAccountCreatorStatusAccountExistWithAlias);
		if (status == LinphoneAccountCreatorStatusAccountExistWithAlias) {
			set_string(&creator->phone_number, resp, FALSE);
		}
	}

	if (creator->cbs->is_account_exist_response_cb != NULL) {
		creator->cbs->is_account_exist_response_cb(creator, status, resp);
	}
	NOTIFY_IF_EXIST_ACCOUNT_CREATOR(is_account_exist, creator, status, resp)
}

LinphoneAccountCreatorStatus linphone_account_creator_is_account_exist_linphone_xmlrpc(LinphoneAccountCreator *creator) {
	LinphoneXmlRpcRequest *request = NULL;
	if (!creator->username && !creator->phone_number) {
		if (creator->cbs->is_account_exist_response_cb != NULL) {
			creator->cbs->is_account_exist_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters");
		}
		NOTIFY_IF_EXIST_ACCOUNT_CREATOR(is_account_exist, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters")
		return LinphoneAccountCreatorStatusMissingArguments;
	}

	fill_domain_and_algorithm_if_needed(creator);

	LinphoneXmlRpcSession* xmlrpc_session = (LinphoneXmlRpcSession*)belle_sip_object_data_get(
		BELLE_SIP_OBJECT(creator),
		"xmlrpc_session"
	);

	if (xmlrpc_session) {
		ms_debug("Account creator: is_account_exist (%s=%s, domain=%s)",
			(creator->username) ? "username" : "phone number",
			(creator->username) ? creator->username : creator->phone_number,
			_get_domain(creator));

		request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "get_phone_number_for_account");
		linphone_xml_rpc_request_add_string_arg(request, creator->username ? creator->username : creator->phone_number);
		linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator));
		linphone_xml_rpc_request_set_user_data(request, creator);
		linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _is_account_exist_response_cb);
		linphone_xml_rpc_session_send_request(xmlrpc_session, request);
		linphone_xml_rpc_request_unref(request);

		return LinphoneAccountCreatorStatusRequestOk;
	}
	return LinphoneAccountCreatorStatusRequestFailed;
}
/****************** END OF CREATE ACCOUNT USED SECTION ************************/

/****************** START OF CREATE ACCOUNT SECTION ***************************/
static void _create_account_cb_custom(LinphoneXmlRpcRequest *request) {
	LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
	LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed;

	const char* resp = linphone_xml_rpc_request_get_string_response(request);
	if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) {
		status = (strcmp(resp, "OK") == 0) ? LinphoneAccountCreatorStatusAccountCreated
		: (strcmp(resp, "ERROR_CANNOT_SEND_SMS") == 0) ? LinphoneAccountCreatorStatusPhoneNumberInvalid
		: (strcmp(resp, "ERROR_MAX_SMS_EXCEEDED") == 0) ? LinphoneAccountCreatorStatusPhoneNumberOverused
		: (strcmp(resp, "ERROR_ACCOUNT_ALREADY_IN_USE") == 0) ? LinphoneAccountCreatorStatusAccountExist
		: (strcmp(resp, "ERROR_ALIAS_ALREADY_IN_USE") == 0) ? LinphoneAccountCreatorStatusAccountExistWithAlias
		: (strcmp(resp, "ERROR_ALGO_NOT_SUPPORTED") == 0) ? LinphoneAccountCreatorStatusAlgoNotSupported
		: LinphoneAccountCreatorStatusAccountNotCreated;
	}

	if (creator->cbs->create_account_response_cb != NULL) {
		creator->cbs->create_account_response_cb(creator, status, resp);
	}
	NOTIFY_IF_EXIST_ACCOUNT_CREATOR(create_account, creator, status, resp)
}

static LinphoneXmlRpcRequest * _create_account_with_phone_custom(LinphoneAccountCreator *creator) {
	LinphoneXmlRpcRequest *request;
	if (!creator->phone_number) {
		return NULL;
	}
	ms_debug("Account creator: create_account_with_phone (phone number=%s, username=%s, domain=%s, language=%s, algo=%s)",
		creator->phone_number,
		(creator->username) ? creator->username : creator->phone_number,
		_get_domain(creator),
		creator->language,
		creator->algorithm);

	request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "create_phone_account");
	linphone_xml_rpc_request_add_string_arg(request, creator->phone_number);
	linphone_xml_rpc_request_add_string_arg(request, creator->username ? creator->username : creator->phone_number);
	linphone_xml_rpc_request_add_string_arg(request, creator->password ?
		ha1_for_passwd(creator->username ? creator->username : creator->phone_number, _get_domain(creator), creator->password, creator->algorithm) : "");
	linphone_xml_rpc_request_add_string_arg(request, linphone_core_get_user_agent(creator->core));
	linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator));
	linphone_xml_rpc_request_add_string_arg(request, creator->language);
	linphone_xml_rpc_request_add_string_arg(request, creator->algorithm);
	return request;
}

static LinphoneXmlRpcRequest * _create_account_with_email_custom(LinphoneAccountCreator *creator) {
	LinphoneXmlRpcRequest *request;
	if (!creator->username || !creator->email || !creator->password) {
		return NULL;
	}
	ms_debug("Account creator: create_account_with_email (username=%s, email=%s, domain=%s, algo=%s)",
		creator->username,
		creator->email,
		_get_domain(creator),
		creator->algorithm);

	request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "create_email_account");
	linphone_xml_rpc_request_add_string_arg(request, creator->username);
	linphone_xml_rpc_request_add_string_arg(request, creator->email);
	linphone_xml_rpc_request_add_string_arg(request,
		ha1_for_passwd(creator->username ? creator->username : creator->phone_number, _get_domain(creator), creator->password, creator->algorithm));
	linphone_xml_rpc_request_add_string_arg(request, linphone_core_get_user_agent(creator->core));
	linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator));
	linphone_xml_rpc_request_add_string_arg(request, creator->algorithm);
	return request;
}

LinphoneAccountCreatorStatus linphone_account_creator_create_account_linphone_xmlrpc(LinphoneAccountCreator *creator) {
	LinphoneXmlRpcRequest *request = NULL;
	fill_domain_and_algorithm_if_needed(creator);
	char *identity = _get_identity(creator);

	if (!identity || (!(request = _create_account_with_phone_custom(creator))
		&& !(request = _create_account_with_email_custom(creator)))) {
		if (creator->cbs->create_account_response_cb != NULL) {
			creator->cbs->create_account_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters");
		}
		NOTIFY_IF_EXIST_ACCOUNT_CREATOR(create_account, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters")
		if (identity) ms_free(identity);
		return LinphoneAccountCreatorStatusMissingArguments;
	}

	LinphoneXmlRpcSession* xmlrpc_session = (LinphoneXmlRpcSession*)belle_sip_object_data_get(
		BELLE_SIP_OBJECT(creator),
		"xmlrpc_session"
	);

	if (xmlrpc_session) {
		linphone_xml_rpc_request_set_user_data(request, creator);
		linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _create_account_cb_custom);
		linphone_xml_rpc_session_send_request(xmlrpc_session, request);
		linphone_xml_rpc_request_unref(request);
		ms_free(identity);
		return LinphoneAccountCreatorStatusRequestOk;
	}

	ms_free(identity);
	return LinphoneAccountCreatorStatusRequestFailed;
}

static void _delete_linphone_account_response_cb(LinphoneXmlRpcRequest *request) {
	LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
	LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed;

	const char* resp = linphone_xml_rpc_request_get_string_response(request);
	if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) {
		status = (strcmp(resp, "OK") == 0) ? LinphoneAccountCreatorStatusAccountCreated
		: (strcmp(resp, "ERROR_USERNAME_PARAMETER_NOT_FOUND") == 0) ? LinphoneAccountCreatorStatusMissingArguments
		: (strcmp(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == 0) ? LinphoneAccountCreatorStatusAccountNotExist
		: (strcmp(resp, "ERROR_ALGO_NOT_SUPPORTED") == 0) ? LinphoneAccountCreatorStatusAlgoNotSupported
		: (strstr(resp, "ERROR_") == 0) ? LinphoneAccountCreatorStatusRequestFailed
		: LinphoneAccountCreatorStatusAccountNotCreated;
	}

	if (creator->cbs->delete_account_response_cb != NULL) {
		creator->cbs->delete_account_response_cb(creator, status, resp);
	}
	NOTIFY_IF_EXIST_ACCOUNT_CREATOR(delete_account, creator, status, resp)
}

LinphoneAccountCreatorStatus linphone_account_creator_delete_account_linphone_xmlrpc(LinphoneAccountCreator *creator) {
	if ((!creator->username && !creator->phone_number) || !creator->password || !creator->proxy_cfg) {
		if (creator->cbs->delete_account_response_cb != NULL) {
			creator->cbs->delete_account_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters");
		}
		NOTIFY_IF_EXIST_ACCOUNT_CREATOR(delete_account, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters")
		return LinphoneAccountCreatorStatusMissingArguments;
	}

	fill_domain_and_algorithm_if_needed(creator);
	ms_debug("Account creator: delete_account (username=%s, password=%s, domain=%s, algo=%s)",
		creator->username,
		creator->password,
		linphone_proxy_config_get_domain(creator->proxy_cfg),
		creator->algorithm);

	LinphoneXmlRpcSession* xmlrpc_session = (LinphoneXmlRpcSession*)belle_sip_object_data_get(
		BELLE_SIP_OBJECT(creator),
		"xmlrpc_session"
	);

	LinphoneXmlRpcRequest *request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "delete_account");
	linphone_xml_rpc_request_add_string_arg(request, creator->username ? creator->username : creator->phone_number);
	linphone_xml_rpc_request_add_string_arg(request, creator->password);
	linphone_xml_rpc_request_add_string_arg(request, linphone_proxy_config_get_domain(creator->proxy_cfg));
	linphone_xml_rpc_request_add_string_arg(request, creator->algorithm);
	linphone_xml_rpc_request_set_user_data(request, creator);
	linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _delete_linphone_account_response_cb);
	linphone_xml_rpc_session_send_request(xmlrpc_session, request);
	linphone_xml_rpc_request_unref(request);

	return LinphoneAccountCreatorStatusRequestOk;
}

/****************** END OF CREATE ACCOUNT SECTION *****************************/

/****************** START OF VALIDATE ACCOUNT SECTION *************************/

static void _login_account_confirmation_key_cb_custom(LinphoneXmlRpcRequest *request) {
	LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
	LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed;

	if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusFailed) {
		const char* resp = linphone_xml_rpc_request_get_string_response(request);
		if (resp != NULL) {
			if (strcmp(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == 0) {
				status = LinphoneAccountCreatorStatusAccountNotExist;
			} else if (strcmp(resp, "ERROR_KEY_DOESNT_MATCH") == 0) {
				status = LinphoneAccountCreatorStatusWrongActivationCode;
			} else if (strcmp(resp, "ERROR_ALGO_NOT_SUPPORTED") == 0) {
				status = LinphoneAccountCreatorStatusAlgoNotSupported;
			} else {
				status = LinphoneAccountCreatorStatusUnexpectedError;
			}
		}
	} else if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) {
		status = LinphoneAccountCreatorStatusRequestOk;
		/*const bctbx_map_t *resp = linphone_xml_rpc_request_get_string_struct_response(request);
		const auto &it = bctbx_map_cchar_find_key(resp, "password");
		if (!bctbx_iterator_equals(it, bctbx_map_cchar_end(resp))) {
			const char *ha1 = (const char *)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it));
			set_string(&creator->ha1, ha1, FALSE);
		}
		const auto &it2 = bctbx_map_cchar_find_key(resp, "algorithm");
		if (!bctbx_iterator_equals(it2, bctbx_map_cchar_end(resp))) {
			const char *algo = (const char *)bctbx_pair_cchar_get_second(bctbx_iterator_cchar_get_pair(it2));
			if (algo) {
				if (creator->algorithm) {
					if (strcmp(algo, creator->algorithm) != 0) {
						ms_warning("Asked for password hashed using %s, got algorithm %s", creator->algorithm, algo);
					} else {
						ms_debug("Got password hashed using %s that we requested", algo);
					}
				} else {
					ms_debug("Account creator wasn't configured for a specific alogithm, got %s", algo);
				}
				set_string(&creator->algorithm, algo, FALSE);
			} else {
				ms_error("Couldn't get algorithm from struct response !");
			}
		}*/
		const bctbx_list_t *list = linphone_xml_rpc_request_get_list_response(request);
		const char *ha1 = (char *)bctbx_list_get_data(list);
		set_string(&creator->ha1, ha1, FALSE);
		if (bctbx_list_size(list) > 1) {
			const char *algo = (char *)bctbx_list_get_data(bctbx_list_next(list));
			if (algo) {
				if (creator->algorithm) {
					if (strcmp(algo, creator->algorithm) != 0) {
						ms_warning("Asked for password hashed using %s, got algorithm %s", creator->algorithm, algo);
					} else {
						ms_debug("Got password hashed using %s that we requested", algo);
					}
				} else {
					ms_debug("Account creator wasn't configured for a specific alogithm, got %s", algo);
				}
				set_string(&creator->algorithm, algo, FALSE);
			} else {
				ms_error("Couldn't get algorithm from struct response !");
			}
		}
	}

	const char *content = linphone_xml_rpc_request_get_content(request);
	if (creator->cbs->login_linphone_account_response_cb != NULL) {
		creator->cbs->login_linphone_account_response_cb(creator, status, content);
	}
	NOTIFY_IF_EXIST_ACCOUNT_CREATOR(login_linphone_account, creator, status, content)
}

LinphoneAccountCreatorStatus linphone_account_creator_login_linphone_account_linphone_xmlrpc(LinphoneAccountCreator *creator) {
	LinphoneXmlRpcRequest *request = NULL;
	if ((!creator->username && !creator->phone_number) || !creator->activation_code) {
		if (creator->cbs->login_linphone_account_response_cb != NULL) {
			creator->cbs->login_linphone_account_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters");
		}
		NOTIFY_IF_EXIST_ACCOUNT_CREATOR(login_linphone_account, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters")
		return LinphoneAccountCreatorStatusMissingArguments;
	}

	LinphoneXmlRpcSession* xmlrpc_session = (LinphoneXmlRpcSession*)belle_sip_object_data_get(
		BELLE_SIP_OBJECT(creator),
		"xmlrpc_session"
	);

	fill_domain_and_algorithm_if_needed(creator);
	if (xmlrpc_session) {
		ms_debug("Account creator: recover_account_from_confirmation_key (username=%s, activation code=%s, domain=%s, algo=%s)",
			creator->username ? creator->username : creator->phone_number,
			creator->activation_code,
			_get_domain(creator),
			creator->algorithm);

		request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgStringStruct, "recover_account_from_confirmation_key");
		linphone_xml_rpc_request_add_string_arg(request, creator->username ? creator->username : creator->phone_number);
		linphone_xml_rpc_request_add_string_arg(request, creator->activation_code);
		linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator));
		linphone_xml_rpc_request_add_string_arg(request, creator->algorithm);
		linphone_xml_rpc_request_set_user_data(request, creator);
		linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _login_account_confirmation_key_cb_custom);
		linphone_xml_rpc_session_send_request(xmlrpc_session, request);
		linphone_xml_rpc_request_unref(request);
		return LinphoneAccountCreatorStatusRequestOk;
	}

	return LinphoneAccountCreatorStatusRequestFailed;
}

static void _activate_account_cb_custom(LinphoneXmlRpcRequest *request) {
	LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
	LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed;

	const char* resp = linphone_xml_rpc_request_get_string_response(request);
	if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) {
		if (strcmp(resp, "ERROR_ACCOUNT_ALREADY_ACTIVATED") == 0) {
			status = LinphoneAccountCreatorStatusAccountAlreadyActivated;
		} else if (strcmp(resp, "ERROR_KEY_DOESNT_MATCH") == 0) {
			status = LinphoneAccountCreatorStatusWrongActivationCode;
		} else if (strcmp(resp, "ERROR_ALGO_NOT_SUPPORTED") == 0) {
			status = LinphoneAccountCreatorStatusAlgoNotSupported;
		} else if (strcmp(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == 0) {
			status = LinphoneAccountCreatorStatusAccountNotExist;
		} else if (strstr(resp, "ERROR_") == resp) {
			status = LinphoneAccountCreatorStatusAccountNotActivated;
		} else {
			status = LinphoneAccountCreatorStatusAccountActivated;
			set_string(&creator->ha1, resp, FALSE);
		}
	}

	if (creator->cbs->activate_account_response_cb != NULL) {
		creator->cbs->activate_account_response_cb(creator, status, resp);
	}
	NOTIFY_IF_EXIST_ACCOUNT_CREATOR(activate_account, creator, status, resp)
}

LinphoneAccountCreatorStatus linphone_account_creator_activate_phone_account_linphone_xmlrpc(LinphoneAccountCreator *creator) {
	LinphoneXmlRpcRequest *request = NULL;

	if (!creator->phone_number || !creator->activation_code) {
		if (creator->cbs->is_account_activated_response_cb != NULL) {
			creator->cbs->is_account_activated_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters");
		}
		NOTIFY_IF_EXIST_ACCOUNT_CREATOR(activate_account, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters")
		return LinphoneAccountCreatorStatusMissingArguments;
	}

	LinphoneXmlRpcSession* xmlrpc_session = (LinphoneXmlRpcSession*)belle_sip_object_data_get(
		BELLE_SIP_OBJECT(creator),
		"xmlrpc_session"
	);

	fill_domain_and_algorithm_if_needed(creator);
	if (xmlrpc_session) {
		ms_debug("Account creator: activate_account_phone (phone number=%s, username=%s, activation code=%s, domain=%s, algo=%s)",
			creator->phone_number,
			creator->username ? creator->username : creator->phone_number,
			creator->activation_code,
			_get_domain(creator),
			creator->algorithm);

		request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "activate_phone_account");
		linphone_xml_rpc_request_add_string_arg(request, creator->phone_number);
		linphone_xml_rpc_request_add_string_arg(request, creator->username ? creator->username : creator->phone_number);
		linphone_xml_rpc_request_add_string_arg(request, creator->activation_code);
		linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator));
		linphone_xml_rpc_request_add_string_arg(request, creator->algorithm);
		linphone_xml_rpc_request_set_user_data(request, creator);
		linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _activate_account_cb_custom);
		linphone_xml_rpc_session_send_request(xmlrpc_session, request);
		linphone_xml_rpc_request_unref(request);
		return LinphoneAccountCreatorStatusRequestOk;
	}

	return LinphoneAccountCreatorStatusRequestFailed;
}

LinphoneAccountCreatorStatus linphone_account_creator_activate_email_account_linphone_xmlrpc(LinphoneAccountCreator *creator) {
	LinphoneXmlRpcRequest *request = NULL;
	if (!creator->activation_code || !creator->username) {
		if (creator->cbs->is_account_activated_response_cb != NULL) {
			creator->cbs->is_account_activated_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters");
		}
		NOTIFY_IF_EXIST_ACCOUNT_CREATOR(activate_account, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters")
		return LinphoneAccountCreatorStatusMissingArguments;
	}

	LinphoneXmlRpcSession* xmlrpc_session = (LinphoneXmlRpcSession*)belle_sip_object_data_get(
		BELLE_SIP_OBJECT(creator),
		"xmlrpc_session"
	);

	fill_domain_and_algorithm_if_needed(creator);
	if (xmlrpc_session) {
		ms_debug("Account creator: activate_account_email (username=%s, activation code=%s, domain=%s, algo=%s)",
			creator->username,
			creator->activation_code,
			_get_domain(creator),
			creator->algorithm);

		request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "activate_email_account");
		linphone_xml_rpc_request_add_string_arg(request, creator->username);
		linphone_xml_rpc_request_add_string_arg(request, creator->activation_code);
		linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator));
		linphone_xml_rpc_request_add_string_arg(request, creator->algorithm);
		linphone_xml_rpc_request_set_user_data(request, creator);
		linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _activate_account_cb_custom);
		linphone_xml_rpc_session_send_request(xmlrpc_session, request);
		linphone_xml_rpc_request_unref(request);
		return LinphoneAccountCreatorStatusRequestOk;
	}

	return LinphoneAccountCreatorStatusRequestFailed;
}

static void get_linphone_confirmation_key_response_cb(LinphoneXmlRpcRequest *request) {
	LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
	LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed;

	const char* resp = linphone_xml_rpc_request_get_string_response(request);
	if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) {
		if (strcmp(resp, "ERROR_USERNAME_PARAMETER_NOT_FOUND") == 0) {
			status = LinphoneAccountCreatorStatusMissingArguments;
		} else if (strcmp(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == 0) {
			status = LinphoneAccountCreatorStatusAccountNotExist;
		} else if (strcmp(resp, "ERROR_ALGO_NOT_SUPPORTED") == 0) {
			status = LinphoneAccountCreatorStatusAlgoNotSupported;
		} else if (strstr(resp, "ERROR_") == resp) {
			status = LinphoneAccountCreatorStatusRequestFailed;
		} else {
			status = LinphoneAccountCreatorStatusRequestOk;
			set_string(&creator->activation_code, resp, FALSE);
		}
	}

	if (creator->cbs->confirmation_key_response_cb != NULL) {
		creator->cbs->confirmation_key_response_cb(creator, status, resp);
	}
	NOTIFY_IF_EXIST_ACCOUNT_CREATOR(confirmation_key, creator, status, resp)
}

LinphoneAccountCreatorStatus linphone_account_creator_get_confirmation_key_linphone_xmlrpc(LinphoneAccountCreator *creator) {
	if (!creator->username || !creator->password || !creator->proxy_cfg) {
		if (creator->cbs->confirmation_key_response_cb != NULL) {
			creator->cbs->confirmation_key_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters");
		}
		NOTIFY_IF_EXIST_ACCOUNT_CREATOR(confirmation_key, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters")
		return LinphoneAccountCreatorStatusMissingArguments;
	}

	fill_domain_and_algorithm_if_needed(creator);
	ms_debug("Account creator: confirmation_key (username=%s, password=%s, domain=%s, algo=%s)",
		creator->username,
		creator->password,
		linphone_proxy_config_get_domain(creator->proxy_cfg),
		creator->algorithm);

	LinphoneXmlRpcSession* xmlrpc_session = (LinphoneXmlRpcSession*)belle_sip_object_data_get(
		BELLE_SIP_OBJECT(creator),
		"xmlrpc_session"
	);

	LinphoneXmlRpcRequest *request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "get_confirmation_key");
	linphone_xml_rpc_request_add_string_arg(request, creator->username);
	linphone_xml_rpc_request_add_string_arg(request, ha1_for_passwd(creator->username, linphone_proxy_config_get_domain(creator->proxy_cfg), creator->password, creator->algorithm));
	linphone_xml_rpc_request_add_string_arg(request, linphone_proxy_config_get_domain(creator->proxy_cfg));
	linphone_xml_rpc_request_add_string_arg(request, creator->algorithm);
	linphone_xml_rpc_request_set_user_data(request, creator);
	linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), get_linphone_confirmation_key_response_cb);
	linphone_xml_rpc_session_send_request(xmlrpc_session, request);
	linphone_xml_rpc_request_unref(request);
	return LinphoneAccountCreatorStatusRequestOk;
}
/****************** END OF CREATE VALIDATE ACCOUNT SECTION ********************/

/****************** START OF ACCOUNT VALIDATED SECTION ************************/
static void _is_account_activated_cb_custom(LinphoneXmlRpcRequest *request) {
	LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
	LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed;

	const char* resp = linphone_xml_rpc_request_get_string_response(request);
	if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) {
		status = (strcmp(resp, "OK") == 0) ? LinphoneAccountCreatorStatusAccountActivated : LinphoneAccountCreatorStatusAccountNotActivated;
	}

	if (creator->cbs->is_account_activated_response_cb != NULL) {
		creator->cbs->is_account_activated_response_cb(creator, status, resp);
	}
	NOTIFY_IF_EXIST_ACCOUNT_CREATOR(is_account_activated, creator, status, resp)
}

LinphoneAccountCreatorStatus linphone_account_creator_is_account_activated_linphone_xmlrpc(LinphoneAccountCreator *creator) {
	LinphoneXmlRpcRequest *request = NULL;
	char *identity = _get_identity(creator);
	if (!identity) {
		if (creator->cbs->is_account_activated_response_cb != NULL) {
			creator->cbs->is_account_activated_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters");
		}
		NOTIFY_IF_EXIST_ACCOUNT_CREATOR(is_account_activated, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters")
		return LinphoneAccountCreatorStatusMissingArguments;
	}

	LinphoneXmlRpcSession* xmlrpc_session = (LinphoneXmlRpcSession*)belle_sip_object_data_get(
		BELLE_SIP_OBJECT(creator),
		"xmlrpc_session"
	);

	fill_domain_and_algorithm_if_needed(creator);
	if (xmlrpc_session) {
		ms_debug("Account creator: is_account_activated (username=%s, domain=%s, algo=%s)",
			creator->username ? creator->username : creator->phone_number,
			_get_domain(creator),
			creator->algorithm);

		request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "is_account_activated");
		linphone_xml_rpc_request_add_string_arg(request, creator->username ? creator->username : creator->phone_number);
		linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator));
        linphone_xml_rpc_request_add_string_arg(request, creator->algorithm);
		linphone_xml_rpc_request_set_user_data(request, creator);
		linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _is_account_activated_cb_custom);
		linphone_xml_rpc_session_send_request(xmlrpc_session, request);
		linphone_xml_rpc_request_unref(request);
		ms_free(identity);
		return LinphoneAccountCreatorStatusRequestOk;
	}

	ms_free(identity);
	return LinphoneAccountCreatorStatusRequestFailed;
}
/****************** END OF CREATE ACCOUNT VALIDATED SECTION********************/

/****************** START OF PHONE NUMBER VALIDATED SECTION *******************/

static void _is_phone_number_used_cb_custom(LinphoneXmlRpcRequest *request) {
	LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
	LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed;

	const char* resp = linphone_xml_rpc_request_get_string_response(request);
	if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) {
		status = (strcmp(resp, "OK_ACCOUNT") == 0) ? LinphoneAccountCreatorStatusAliasIsAccount
		: (strcmp(resp, "OK_ALIAS") == 0) ? LinphoneAccountCreatorStatusAliasExist
		: LinphoneAccountCreatorStatusAliasNotExist;
	}

	if (creator->cbs->is_alias_used_response_cb != NULL) {
		creator->cbs->is_alias_used_response_cb(creator, status, resp);
	}
	NOTIFY_IF_EXIST_ACCOUNT_CREATOR(is_alias_used, creator, status, resp)
}

LinphoneAccountCreatorStatus linphone_account_creator_is_phone_number_used_linphone_xmlrpc(LinphoneAccountCreator *creator) {
	LinphoneXmlRpcRequest *request = NULL;
	char *identity = _get_identity(creator);
	if (!identity) {
		if (creator->cbs->is_alias_used_response_cb != NULL) {
			creator->cbs->is_alias_used_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters");
		}
		NOTIFY_IF_EXIST_ACCOUNT_CREATOR(is_alias_used, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters")
		return LinphoneAccountCreatorStatusMissingArguments;
	}

	LinphoneXmlRpcSession* xmlrpc_session = (LinphoneXmlRpcSession*)belle_sip_object_data_get(
		BELLE_SIP_OBJECT(creator),
		"xmlrpc_session"
	);

	fill_domain_and_algorithm_if_needed(creator);
	if (xmlrpc_session) {
		ms_debug("Account creator: is_phone_number_used (phone number=%s, domain=%s)",
			creator->phone_number,
			_get_domain(creator));

		request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "is_phone_number_used");
		linphone_xml_rpc_request_add_string_arg(request, creator->phone_number);
		linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator));
		linphone_xml_rpc_request_set_user_data(request, creator);
		linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _is_phone_number_used_cb_custom);
		linphone_xml_rpc_session_send_request(xmlrpc_session, request);
		linphone_xml_rpc_request_unref(request);
		ms_free(identity);
		return LinphoneAccountCreatorStatusRequestOk;
	}

	ms_free(identity);
	return LinphoneAccountCreatorStatusRequestFailed;
}

/****************** END OF PHONE NUMBER VALIDATED SECTION *********************/

/****************** START OF LINK PHONE NUMBER WITH ACCOUNT SECTION ***********/
static void _link_phone_number_with_account_cb_custom(LinphoneXmlRpcRequest *request) {
	LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
	LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed;

	const char* resp = linphone_xml_rpc_request_get_string_response(request);
	if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) {
		status = (strcmp(resp, "OK") == 0) ? LinphoneAccountCreatorStatusRequestOk
		: (strcmp(resp, "ERROR_CANNOT_SEND_SMS") == 0) ? LinphoneAccountCreatorStatusPhoneNumberInvalid
		: (strcmp(resp, "ERROR_MAX_SMS_EXCEEDED") == 0) ? LinphoneAccountCreatorStatusPhoneNumberOverused
		: LinphoneAccountCreatorStatusAccountNotLinked;
	}

	if (creator->cbs->link_account_response_cb != NULL) {
		creator->cbs->link_account_response_cb(creator, status, resp);
	}
	NOTIFY_IF_EXIST_ACCOUNT_CREATOR(link_account, creator, status, resp)
}

LinphoneAccountCreatorStatus linphone_account_creator_link_phone_number_with_account_linphone_xmlrpc(LinphoneAccountCreator *creator) {
	LinphoneXmlRpcRequest *request = NULL;
	if (!creator->phone_number || !creator->username) {
		if (creator->cbs->link_account_response_cb != NULL) {
			creator->cbs->link_account_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters");
		}
		NOTIFY_IF_EXIST_ACCOUNT_CREATOR(link_account, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters")
		return LinphoneAccountCreatorStatusMissingArguments;
	}

	LinphoneXmlRpcSession* xmlrpc_session = (LinphoneXmlRpcSession*)belle_sip_object_data_get(
		BELLE_SIP_OBJECT(creator),
		"xmlrpc_session"
	);

	fill_domain_and_algorithm_if_needed(creator);
	if (xmlrpc_session) {
		ms_debug("Account creator: link_phone_number_with_account (phone number=%s, username=%s, domain=%s, language=%s)",
			creator->phone_number,
			creator->username,
			_get_domain(creator),
			creator->language);

		request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "link_phone_number_with_account");
		linphone_xml_rpc_request_add_string_arg(request, creator->phone_number);
		linphone_xml_rpc_request_add_string_arg(request, creator->username);
		linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator));
		linphone_xml_rpc_request_add_string_arg(request, creator->language);
		linphone_xml_rpc_request_set_user_data(request, creator);
		linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _link_phone_number_with_account_cb_custom);
		linphone_xml_rpc_session_send_request(xmlrpc_session, request);
		linphone_xml_rpc_request_unref(request);
		return LinphoneAccountCreatorStatusRequestOk;
	}

	return LinphoneAccountCreatorStatusRequestFailed;
}

static void _get_phone_number_for_account_cb_custom(LinphoneXmlRpcRequest *request) {
	LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
	LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed;

	const char* resp = linphone_xml_rpc_request_get_string_response(request);
	if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) {
		status = (strcmp(resp, "ERROR_USERNAME_PARAMETER_NOT_FOUND") == 0
			|| strcmp(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == 0
			|| strcmp(resp, "ERROR_ALIAS_DOESNT_EXIST") == 0) ? LinphoneAccountCreatorStatusAccountNotLinked : LinphoneAccountCreatorStatusAccountLinked;
	}

	if (creator->cbs->is_account_linked_response_cb != NULL) {
		creator->cbs->is_account_linked_response_cb(creator, status, resp);
	}
	NOTIFY_IF_EXIST_ACCOUNT_CREATOR(is_account_linked, creator, status, resp)
}

LinphoneAccountCreatorStatus linphone_account_creator_is_account_linked_linphone_xmlrpc(LinphoneAccountCreator *creator) {
	LinphoneXmlRpcRequest *request = NULL;
	if (!creator->username || !_get_domain(creator)) {
		if (creator->cbs->is_account_linked_response_cb != NULL) {
			creator->cbs->is_account_linked_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters");
		}
		NOTIFY_IF_EXIST_ACCOUNT_CREATOR(is_account_linked, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters")
		return LinphoneAccountCreatorStatusMissingArguments;
	}

	LinphoneXmlRpcSession* xmlrpc_session = (LinphoneXmlRpcSession*)belle_sip_object_data_get(
		BELLE_SIP_OBJECT(creator),
		"xmlrpc_session"
	);

	fill_domain_and_algorithm_if_needed(creator);
	if (xmlrpc_session) {
		ms_debug("Account creator: is_account_linked (username=%s, domain=%s)",
			creator->username,
			_get_domain(creator));

		request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "get_phone_number_for_account");
		linphone_xml_rpc_request_add_string_arg(request, creator->username);
		linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator));
		linphone_xml_rpc_request_set_user_data(request, creator);
		linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _get_phone_number_for_account_cb_custom);
		linphone_xml_rpc_session_send_request(xmlrpc_session, request);
		linphone_xml_rpc_request_unref(request);
		return LinphoneAccountCreatorStatusRequestOk;
	}

	return LinphoneAccountCreatorStatusRequestFailed;
}
/****************** END OF LINK PHONE NUMBER WITH ACCOUNT SECTION *************/

/****************** START OF ACTIVE PHONE NUMBER LINK **************************/
static void _activate_phone_number_link_cb_custom(LinphoneXmlRpcRequest *request) {
	LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
	LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed;

	const char* resp = linphone_xml_rpc_request_get_string_response(request);
	if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) {
		status = (strstr(resp, "ERROR_") == resp) ? LinphoneAccountCreatorStatusAccountNotActivated : LinphoneAccountCreatorStatusAccountActivated;
	}

	if (creator->cbs->activate_alias_response_cb != NULL) {
		creator->cbs->activate_alias_response_cb(creator, status, resp);
	}
	NOTIFY_IF_EXIST_ACCOUNT_CREATOR(activate_alias, creator, status, resp)
}

LinphoneAccountCreatorStatus linphone_account_creator_activate_phone_number_link_linphone_xmlrpc(LinphoneAccountCreator *creator) {
	LinphoneXmlRpcRequest *request = NULL;
	if (!creator->phone_number || !creator->username || !creator->activation_code || (!creator->password && !creator->ha1) || !_get_domain(creator)) {
		if (creator->cbs->activate_alias_response_cb != NULL) {
			creator->cbs->activate_alias_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters");
		}
		NOTIFY_IF_EXIST_ACCOUNT_CREATOR(activate_alias, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters")
		return LinphoneAccountCreatorStatusMissingArguments;
	}

	LinphoneXmlRpcSession* xmlrpc_session = (LinphoneXmlRpcSession*)belle_sip_object_data_get(
		BELLE_SIP_OBJECT(creator),
		"xmlrpc_session"
	);

	fill_domain_and_algorithm_if_needed(creator);
	if (xmlrpc_session) {
		ms_debug("Account creator: activate_phone_number_link (phone number=%s, username=%s, activation code=%s, domain=%s, algo=%s)",
			creator->phone_number,
			creator->username,
			creator->activation_code,
			_get_domain(creator),
			creator->algorithm);

		request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "activate_phone_number_link");
		linphone_xml_rpc_request_add_string_arg(request, creator->phone_number);
		linphone_xml_rpc_request_add_string_arg(request, creator->username);
		linphone_xml_rpc_request_add_string_arg(request, creator->activation_code);
		linphone_xml_rpc_request_add_string_arg(request, creator->ha1 ? creator->ha1 : ha1_for_passwd(creator->username, _get_domain(creator), creator->password, creator->algorithm));
		linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator));
		linphone_xml_rpc_request_add_string_arg(request, creator->algorithm);
		linphone_xml_rpc_request_set_user_data(request, creator);
		linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _activate_phone_number_link_cb_custom);
		linphone_xml_rpc_session_send_request(xmlrpc_session, request);
		linphone_xml_rpc_request_unref(request);
		return LinphoneAccountCreatorStatusRequestOk;
	}

	return LinphoneAccountCreatorStatusRequestFailed;
}
/****************** END OF ACTIVE PHONE NUMBER LINK **************************/

/****************** START OF ACTIVE PHONE NUMBER LINK **************************/
static void _recover_phone_account_cb_custom(LinphoneXmlRpcRequest *request) {
	LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
	LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed;

	const char* resp = linphone_xml_rpc_request_get_string_response(request);
	if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) {
		if (strstr(resp, "ERROR_") == resp) {
			status = (strcmp(resp, "ERROR_CANNOT_SEND_SMS") == 0) ? LinphoneAccountCreatorStatusPhoneNumberInvalid
				: (strcmp(resp, "ERROR_MAX_SMS_EXCEEDED") == 0) ? LinphoneAccountCreatorStatusPhoneNumberOverused
				: (strcmp(resp, "ERROR_ACCOUNT_DOESNT_EXIST") == 0) ? LinphoneAccountCreatorStatusAccountNotExist
				: LinphoneAccountCreatorStatusRequestFailed;
		} else {
			status = LinphoneAccountCreatorStatusRequestOk;
			set_string(&creator->username, resp, FALSE);
		}
	}

	if (creator->cbs->recover_account_response_cb != NULL) {
		creator->cbs->recover_account_response_cb(creator, status, resp);
	}
	NOTIFY_IF_EXIST_ACCOUNT_CREATOR(recover_account, creator, status, resp)
}

LinphoneAccountCreatorStatus linphone_account_creator_recover_phone_account_linphone_xmlrpc(LinphoneAccountCreator *creator) {
	LinphoneXmlRpcRequest *request = NULL;
	if (!creator->phone_number) {
		if (creator->cbs->recover_account_response_cb != NULL) {
			creator->cbs->recover_account_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters");
		}
		NOTIFY_IF_EXIST_ACCOUNT_CREATOR(recover_account, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters")
		return LinphoneAccountCreatorStatusMissingArguments;
	}

	LinphoneXmlRpcSession* xmlrpc_session = (LinphoneXmlRpcSession*)belle_sip_object_data_get(
		BELLE_SIP_OBJECT(creator),
		"xmlrpc_session"
	);

	fill_domain_and_algorithm_if_needed(creator);
	if (xmlrpc_session) {
		ms_debug("Account creator: recover_phone_account (phone number=%s, domain=%s, language=%s)",
			creator->phone_number,
			_get_domain(creator),
			creator->language);

		request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "recover_phone_account");
		linphone_xml_rpc_request_add_string_arg(request, creator->phone_number);
		linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator));
		linphone_xml_rpc_request_add_string_arg(request, creator->language);
		linphone_xml_rpc_request_set_user_data(request, creator);
		linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _recover_phone_account_cb_custom);
		linphone_xml_rpc_session_send_request(xmlrpc_session, request);
		linphone_xml_rpc_request_unref(request);
		return LinphoneAccountCreatorStatusRequestOk;
	}

	return LinphoneAccountCreatorStatusRequestFailed;
}
/****************** END OF ACTIVE PHONE NUMBER LINK **************************/

/****************** START OF UPDATE ACCOUNT **************************/
static void _password_updated_cb_custom(LinphoneXmlRpcRequest *request) {
	LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);
	LinphoneAccountCreatorStatus status = LinphoneAccountCreatorStatusRequestFailed;

	const char* resp = linphone_xml_rpc_request_get_string_response(request);
	if (linphone_xml_rpc_request_get_status(request) == LinphoneXmlRpcStatusOk) {
		if (strcmp(resp, "OK") == 0) {
			status = LinphoneAccountCreatorStatusRequestOk;
		} else if (strcmp(resp, "ERROR_ALGO_NOT_SUPPORTED") == 0) {
			status = LinphoneAccountCreatorStatusAlgoNotSupported;
		} else if (strcmp(resp, "ERROR_PASSWORD_DOESNT_MATCH") == 0) {
			status = LinphoneAccountCreatorStatusAccountNotExist;
		} else {
			status = LinphoneAccountCreatorStatusServerError;
		}
	}

	if (creator->cbs->update_account_response_cb != NULL) {
		creator->cbs->update_account_response_cb(creator, status, resp);
	}
	NOTIFY_IF_EXIST_ACCOUNT_CREATOR(update_account, creator, status, resp)
}

LinphoneAccountCreatorStatus linphone_account_creator_update_password_linphone_xmlrpc(LinphoneAccountCreator *creator){
	LinphoneXmlRpcRequest *request = NULL;
	char *identity = _get_identity(creator);
	const char* new_pwd = (const char*)linphone_account_creator_get_user_data(creator);
	if (!identity ||
			((!creator->username && !creator->phone_number)
				|| !_get_domain(creator)
				|| (!creator->password && !creator->ha1) || !new_pwd
			)
		) {
		if (creator->cbs->update_account_response_cb != NULL) {
			creator->cbs->update_account_response_cb(creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters");
		}
		NOTIFY_IF_EXIST_ACCOUNT_CREATOR(update_account, creator, LinphoneAccountCreatorStatusMissingArguments, "Missing required parameters")
		return LinphoneAccountCreatorStatusMissingArguments;
	}
	bctbx_free(identity);

	LinphoneXmlRpcSession* xmlrpc_session = (LinphoneXmlRpcSession*)belle_sip_object_data_get(
		BELLE_SIP_OBJECT(creator),
		"xmlrpc_session"
	);

	fill_domain_and_algorithm_if_needed(creator);
	if (xmlrpc_session) {
		const char *username = creator->username ? creator->username : creator->phone_number;
		char *ha1 = bctbx_strdup(creator->ha1 ? creator->ha1 : ha1_for_passwd(username, _get_domain(creator), creator->password, creator->algorithm));
		char *new_ha1 = bctbx_strdup(ha1_for_passwd(username, _get_domain(creator), new_pwd, creator->algorithm));

		ms_debug("Account creator: update_password (username=%s, domain=%s, algo=%s)",
			creator->username,
			_get_domain(creator),
			creator->algorithm);

		request = linphone_xml_rpc_request_new(LinphoneXmlRpcArgString, "update_hash");
		linphone_xml_rpc_request_add_string_arg(request, username);
		linphone_xml_rpc_request_add_string_arg(request, ha1);
		linphone_xml_rpc_request_add_string_arg(request, new_ha1);
		linphone_xml_rpc_request_add_string_arg(request, _get_domain(creator));
		linphone_xml_rpc_request_add_string_arg(request, creator->algorithm);
		linphone_xml_rpc_request_set_user_data(request, creator);
		linphone_xml_rpc_request_cbs_set_response(linphone_xml_rpc_request_get_callbacks(request), _password_updated_cb_custom);
		linphone_xml_rpc_session_send_request(xmlrpc_session, request);
		linphone_xml_rpc_request_unref(request);

		bctbx_free(ha1);
		bctbx_free(new_ha1);

		return LinphoneAccountCreatorStatusRequestOk;
	}

	return LinphoneAccountCreatorStatusRequestFailed;
}
/****************** END OF UPDATE ACCOUNT **************************/

/************************** End Account Creator Linphone **************************/
