/*
 * Copyright (c) 2010-2022 Belledonne Communications SARL.
 *
 * This file is part of Liblinphone
 * (see https://gitlab.linphone.org/BC/public/liblinphone).
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include "sal_impl.h"
#include "tester_utils.h"

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

SalAuthInfo *sal_auth_info_create(belle_sip_auth_event_t *event) {
	SalAuthInfo *auth_info = sal_auth_info_new();
	auth_info->realm = ms_strdup(belle_sip_auth_event_get_realm(event));
	auth_info->username = ms_strdup(belle_sip_auth_event_get_username(event));
	auth_info->userid = ms_strdup(belle_sip_auth_event_get_userid(event));
	auth_info->domain = ms_strdup(belle_sip_auth_event_get_domain(event));
	auth_info->mode = (SalAuthMode)belle_sip_auth_event_get_mode(event);
	auth_info->algorithm = ms_strdup(belle_sip_auth_event_get_algorithm(event));
	return auth_info;
}

SalAuthMode sal_auth_info_get_mode(const SalAuthInfo *auth_info) {
	return auth_info->mode;
}
belle_sip_signing_key_t *sal_auth_info_get_signing_key(const SalAuthInfo *auth_info) {
	return auth_info->key;
}
belle_sip_certificates_chain_t *sal_auth_info_get_certificates_chain(const SalAuthInfo *auth_info) {
	return auth_info->certificates;
}
void sal_auth_info_set_mode(SalAuthInfo *auth_info, SalAuthMode mode) {
	auth_info->mode = mode;
}
void sal_certificates_chain_delete(belle_sip_certificates_chain_t *chain) {
	belle_sip_object_unref((belle_sip_object_t *)chain);
}
void sal_signing_key_delete(belle_sip_signing_key_t *key) {
	belle_sip_object_unref((belle_sip_object_t *)key);
}

int sal_auth_compute_ha1(const char *userid, const char *realm, const char *password, char ha1[33]) {
	return belle_sip_auth_helper_compute_ha1(userid, realm, password, ha1);
}

int sal_auth_compute_ha1_for_algorithm(
    const char *userid, const char *realm, const char *password, char *ha1, size_t size, const char *algo) {
	return belle_sip_auth_helper_compute_ha1_for_algorithm(userid, realm, password, ha1, size, algo);
}

SalCustomHeader *sal_custom_header_ref(SalCustomHeader *ch) {
	if (ch == NULL) return NULL;
	belle_sip_object_ref(ch);
	return ch;
}

void sal_custom_header_unref(SalCustomHeader *ch) {
	if (ch == NULL) return;
	belle_sip_object_unref(ch);
}

SalCustomHeader *sal_custom_header_append(SalCustomHeader *ch, const char *name, const char *value) {
	belle_sip_message_t *msg = (belle_sip_message_t *)ch;
	belle_sip_header_t *h;

	if (msg == NULL) {
		msg = (belle_sip_message_t *)belle_sip_request_new();
		belle_sip_object_ref(msg);
	}
	h = belle_sip_header_create(name, value);
	if (h == NULL) {
		belle_sip_error("Fail to parse custom header.");
		return (SalCustomHeader *)msg;
	}
	belle_sip_message_add_header(msg, h);
	return (SalCustomHeader *)msg;
}

const char *sal_custom_header_find(const SalCustomHeader *ch, const char *name) {
	if (ch) {
		belle_sip_header_t *h = belle_sip_message_get_header((belle_sip_message_t *)ch, name);

		if (h) {
			return belle_sip_header_get_unparsed_value(h);
		}
	}
	return NULL;
}

SalCustomHeader *sal_custom_header_remove(SalCustomHeader *ch, const char *name) {
	belle_sip_message_t *msg = (belle_sip_message_t *)ch;
	if (msg == NULL) return NULL;

	belle_sip_message_remove_header(msg, name);
	return (SalCustomHeader *)msg;
}

void sal_custom_header_free(SalCustomHeader *ch) {
	if (ch == NULL) return;
	belle_sip_object_unref((belle_sip_message_t *)ch);
}

SalCustomHeader *sal_custom_header_clone(const SalCustomHeader *ch) {
	if (ch == NULL) return NULL;
	return (SalCustomHeader *)belle_sip_object_ref((belle_sip_message_t *)ch);
}

SalCustomSdpAttribute *
sal_custom_sdp_attribute_append(SalCustomSdpAttribute *csa, const char *name, const char *value) {
	belle_sdp_session_description_t *desc = (belle_sdp_session_description_t *)csa;
	belle_sdp_attribute_t *attr;

	if (desc == NULL) {
		desc = (belle_sdp_session_description_t *)belle_sdp_session_description_new();
		belle_sip_object_ref(desc);
	}
	attr = BELLE_SDP_ATTRIBUTE(belle_sdp_attribute_create(name, value));
	if (attr == NULL) {
		belle_sip_error("Fail to create custom SDP attribute.");
		return (SalCustomSdpAttribute *)desc;
	}
	belle_sdp_session_description_add_attribute(desc, attr);
	return (SalCustomSdpAttribute *)desc;
}

const char *sal_custom_sdp_attribute_find(const SalCustomSdpAttribute *csa, const char *name) {
	if (csa) {
		return belle_sdp_session_description_get_attribute_value((belle_sdp_session_description_t *)csa, name);
	}
	return NULL;
}

bool_t sal_custom_sdp_attribute_is_present(const SalCustomSdpAttribute *csa, const char *name) {
	if (csa) {
		return belle_sdp_session_description_get_attribute((belle_sdp_session_description_t *)csa, name) != NULL;
	}
	return FALSE;
}

void sal_custom_sdp_attribute_free(SalCustomSdpAttribute *csa) {
	if (csa == NULL) return;
	belle_sip_object_unref((belle_sdp_session_description_t *)csa);
}

SalCustomSdpAttribute *sal_custom_sdp_attribute_clone(const SalCustomSdpAttribute *csa) {
	if (csa == NULL) return NULL;
	return (SalCustomSdpAttribute *)belle_sip_object_ref((belle_sdp_session_description_t *)csa);
}

/** Parse a file containing either a certificate chain order in PEM format or a single DER cert
 * @param auth_info structure where to store the result of parsing
 * @param path path to certificate chain file
 * @param format either PEM or DER
 */
void sal_certificates_chain_parse_file(SalAuthInfo *auth_info, const char *path, SalCertificateRawFormat format) {
	auth_info->certificates = (belle_sip_certificates_chain_t *)belle_sip_certificates_chain_parse_file(
	    path, (belle_sip_certificate_raw_format_t)format);
	if (auth_info->certificates) belle_sip_object_ref((belle_sip_object_t *)auth_info->certificates);
}

/**
 * Parse a file containing either a private or public rsa key
 * @param auth_info structure where to store the result of parsing
 * @param passwd password (optionnal)
 */
void sal_signing_key_parse_file(SalAuthInfo *auth_info, const char *path, const char *passwd) {
	auth_info->key = (belle_sip_signing_key_t *)belle_sip_signing_key_parse_file(path, passwd);
	if (auth_info->key) belle_sip_object_ref((belle_sip_object_t *)auth_info->key);
}

/** Parse a buffer containing either a certificate chain order in PEM format or a single DER cert
 * @param auth_info structure where to store the result of parsing
 * @param buffer the buffer to parse
 * @param format either PEM or DER
 */
void sal_certificates_chain_parse(SalAuthInfo *auth_info, const char *buffer, SalCertificateRawFormat format) {
	size_t len = buffer != NULL ? strlen(buffer) : 0;
	auth_info->certificates = (belle_sip_certificates_chain_t *)belle_sip_certificates_chain_parse(
	    buffer, len, (belle_sip_certificate_raw_format_t)format);
	if (auth_info->certificates) belle_sip_object_ref((belle_sip_object_t *)auth_info->certificates);
}

/**
 * Parse a buffer containing either a private or public rsa key
 * @param auth_info structure where to store the result of parsing
 * @param passwd password (optionnal)
 */
void sal_signing_key_parse(SalAuthInfo *auth_info, const char *buffer, const char *passwd) {
	size_t len = buffer != NULL ? strlen(buffer) : 0;
	auth_info->key = (belle_sip_signing_key_t *)belle_sip_signing_key_parse(buffer, len, passwd);
	if (auth_info->key) belle_sip_object_ref((belle_sip_object_t *)auth_info->key);
}

/**
 * Parse a directory to get a certificate with the given subject common name
 *
 */
void sal_certificates_chain_parse_directory(char **certificate_pem,
                                            char **key_pem,
                                            char **fingerprint,
                                            const char *path,
                                            const char *subject,
                                            SalCertificateRawFormat format,
                                            bool_t generate_certificate,
                                            bool_t generate_dtls_fingerprint) {
	belle_sip_certificates_chain_t *certificate = NULL;
	belle_sip_signing_key_t *key = NULL;
	*certificate_pem = NULL;
	*key_pem = NULL;
	if (belle_sip_get_certificate_and_pkey_in_dir(path, subject, &certificate, &key,
	                                              (belle_sip_certificate_raw_format_t)format) == 0) {
		*certificate_pem = belle_sip_certificates_chain_get_pem(certificate);
		*key_pem = belle_sip_signing_key_get_pem(key);
		ms_message("Retrieve certificate with CN=%s successful\n", subject);
	} else {
		if (generate_certificate == TRUE) {
			if (belle_sip_generate_self_signed_certificate(path, subject, &certificate, &key) == 0) {
				*certificate_pem = belle_sip_certificates_chain_get_pem(certificate);
				*key_pem = belle_sip_signing_key_get_pem(key);
				ms_message("Generate self-signed certificate with CN=%s successful\n", subject);
			} else {
				ms_error("Self-signed certificate generation failed.");
				return;
			}
		}
	}
	/* generate the fingerprint as described in RFC4572 if needed */
	if ((generate_dtls_fingerprint == TRUE) && (fingerprint != NULL)) {
		if (*fingerprint != NULL) {
			ms_free(*fingerprint);
		}
		*fingerprint = belle_sip_certificates_chain_get_fingerprint(certificate);
	}

	/* free key and certificate */
	if (certificate != NULL) {
		belle_sip_object_unref(certificate);
	}
	if (key != NULL) {
		belle_sip_object_unref(key);
	}
}

unsigned char *sal_get_random_bytes(unsigned char *ret, size_t size) {
	return belle_sip_random_bytes(ret, size);
}

char *sal_get_random_token(int size) {
	return belle_sip_random_token(reinterpret_cast<char *>(ms_malloc((size_t)size)), (size_t)size);
}

char *sal_get_random_token_lowercase(int size) {
	char *random = sal_get_random_token(size);

	for (int i = 0; i < size; i++) {
		random[i] = (char)tolower(random[i]);
	}

	return random;
}

unsigned int sal_get_random(void) {
	unsigned int ret = 0;
	belle_sip_random_bytes((unsigned char *)&ret, 4);
	return ret;
}

unsigned long sal_begin_background_task(const char *name, void (*max_time_reached)(void *), void *data) {
	return belle_sip_begin_background_task(name, max_time_reached, data);
}

void sal_end_background_task(unsigned long id) {
	belle_sip_end_background_task(id);
}

static belle_sip_header_t *sal_body_handler_find_header(const SalBodyHandler *body_handler, const char *header_name) {
	belle_sip_body_handler_t *bsbh = BELLE_SIP_BODY_HANDLER(body_handler);
	if (bsbh) {
		const belle_sip_list_t *l = belle_sip_body_handler_get_headers(bsbh);
		for (; l != NULL; l = l->next) {
			belle_sip_header_t *header = BELLE_SIP_HEADER(l->data);
			if (strcmp(belle_sip_header_get_name(header), header_name) == 0) {
				return header;
			}
		}
	}
	return NULL;
}

SalBodyHandler *sal_body_handler_new(void) {
	belle_sip_memory_body_handler_t *body_handler = belle_sip_memory_body_handler_new(NULL, NULL);
	return (SalBodyHandler *)BELLE_SIP_BODY_HANDLER(body_handler);
}

SalBodyHandler *sal_body_handler_ref(SalBodyHandler *body_handler) {
	return (SalBodyHandler *)belle_sip_object_ref(BELLE_SIP_OBJECT(body_handler));
}

void sal_body_handler_unref(SalBodyHandler *body_handler) {
	belle_sip_object_unref(BELLE_SIP_OBJECT(body_handler));
}

const char *sal_body_handler_get_type(const SalBodyHandler *body_handler) {
	belle_sip_header_content_type_t *content_type =
	    BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type"));
	if (content_type != NULL) {
		return belle_sip_header_content_type_get_type(content_type);
	}
	return NULL;
}

void sal_body_handler_set_type(SalBodyHandler *body_handler, const char *type) {
	belle_sip_header_content_type_t *content_type =
	    BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type"));
	if (content_type == NULL) {
		content_type = belle_sip_header_content_type_new();
		belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(body_handler), BELLE_SIP_HEADER(content_type));
	}
	belle_sip_header_content_type_set_type(content_type, type);
}

const char *sal_body_handler_get_subtype(const SalBodyHandler *body_handler) {
	belle_sip_header_content_type_t *content_type =
	    BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type"));
	if (content_type != NULL) {
		return belle_sip_header_content_type_get_subtype(content_type);
	}
	return NULL;
}

void sal_body_handler_set_subtype(SalBodyHandler *body_handler, const char *subtype) {
	belle_sip_header_content_type_t *content_type =
	    BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type"));
	if (content_type == NULL) {
		content_type = belle_sip_header_content_type_new();
		belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(body_handler), BELLE_SIP_HEADER(content_type));
	}
	belle_sip_header_content_type_set_subtype(content_type, subtype);
}

const belle_sip_list_t *sal_body_handler_get_content_type_parameters_names(const SalBodyHandler *body_handler) {
	belle_sip_header_content_type_t *content_type =
	    BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type"));
	if (content_type != NULL) {
		return belle_sip_parameters_get_parameter_names(BELLE_SIP_PARAMETERS(content_type));
	}
	return NULL;
}

const char *sal_body_handler_get_content_type_parameter(const SalBodyHandler *body_handler, const char *name) {
	belle_sip_header_content_type_t *content_type =
	    BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type"));
	if (content_type != NULL) {
		return belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type), name);
	}
	return NULL;
}

void sal_body_handler_set_content_type_parameter(SalBodyHandler *body_handler,
                                                 const char *paramName,
                                                 const char *paramValue) {
	belle_sip_header_content_type_t *content_type =
	    BELLE_SIP_HEADER_CONTENT_TYPE(sal_body_handler_find_header(body_handler, "Content-Type"));
	if (content_type != NULL) {
		belle_sip_parameters_set_parameter(BELLE_SIP_PARAMETERS(content_type), paramName, paramValue);
	}
}

const char *sal_body_handler_get_encoding(const SalBodyHandler *body_handler) {
	belle_sip_header_t *content_encoding = sal_body_handler_find_header(body_handler, "Content-Encoding");
	if (content_encoding != NULL) {
		return belle_sip_header_get_unparsed_value(content_encoding);
	}
	return NULL;
}

void sal_body_handler_set_encoding(SalBodyHandler *body_handler, const char *encoding) {
	belle_sip_header_t *content_encoding = sal_body_handler_find_header(body_handler, "Content-Encoding");
	if (content_encoding != NULL) {
		belle_sip_body_handler_remove_header_from_ptr(BELLE_SIP_BODY_HANDLER(body_handler), content_encoding);
	}
	belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(body_handler),
	                                  belle_sip_header_create("Content-Encoding", encoding));
}

const char *sal_body_handler_get_content_disposition(const SalBodyHandler *body_handler) {
	belle_sip_header_t *content_disposition = sal_body_handler_find_header(body_handler, "Content-Disposition");
	if (content_disposition != NULL) {
		return belle_sip_header_get_unparsed_value(content_disposition);
	}
	return NULL;
}

void sal_body_handler_set_content_disposition(SalBodyHandler *body_handler, const char *disposition) {
	belle_sip_header_t *content_disposition = sal_body_handler_find_header(body_handler, "Content-Disposition");
	if (content_disposition != NULL) {
		belle_sip_body_handler_remove_header_from_ptr(BELLE_SIP_BODY_HANDLER(body_handler), content_disposition);
	}
	belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(body_handler),
	                                  belle_sip_header_create("Content-Disposition", disposition));
}

void sal_body_handler_add_header(SalBodyHandler *body_handler, const char *header_name, const char *header_value) {
	belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(body_handler),
	                                  belle_sip_header_create(header_name, header_value));
}

void *sal_body_handler_get_data(const SalBodyHandler *body_handler) {
	return belle_sip_memory_body_handler_get_buffer(BELLE_SIP_MEMORY_BODY_HANDLER(body_handler));
}

void sal_body_handler_set_data(SalBodyHandler *body_handler, void *data) {
	belle_sip_memory_body_handler_set_buffer(BELLE_SIP_MEMORY_BODY_HANDLER(body_handler), data);
}

size_t sal_body_handler_get_size(const SalBodyHandler *body_handler) {
	return belle_sip_body_handler_get_size(BELLE_SIP_BODY_HANDLER(body_handler));
}

void sal_body_handler_set_size(SalBodyHandler *body_handler, size_t size) {
	belle_sip_header_content_length_t *content_length =
	    BELLE_SIP_HEADER_CONTENT_LENGTH(sal_body_handler_find_header(body_handler, "Content-Length"));
	if (content_length == NULL) {
		content_length = belle_sip_header_content_length_new();
		belle_sip_body_handler_add_header(BELLE_SIP_BODY_HANDLER(body_handler), BELLE_SIP_HEADER(content_length));
	}
	belle_sip_header_content_length_set_content_length(content_length, size);
	belle_sip_body_handler_set_size(BELLE_SIP_BODY_HANDLER(body_handler), size);
}

bool_t sal_body_handler_is_multipart(const SalBodyHandler *body_handler) {
	if (BELLE_SIP_IS_INSTANCE_OF(body_handler, belle_sip_multipart_body_handler_t)) return TRUE;
	return FALSE;
}

SalBodyHandler *sal_body_handler_get_part(const SalBodyHandler *body_handler, int idx) {
	const belle_sip_list_t *l =
	    belle_sip_multipart_body_handler_get_parts(BELLE_SIP_MULTIPART_BODY_HANDLER(body_handler));
	return (SalBodyHandler *)belle_sip_list_nth_data(l, idx);
}

const belle_sip_list_t *sal_body_handler_get_parts(const SalBodyHandler *body_handler) {
	if (!sal_body_handler_is_multipart(body_handler)) return NULL;
	return belle_sip_multipart_body_handler_get_parts(BELLE_SIP_MULTIPART_BODY_HANDLER(body_handler));
}

SalBodyHandler *sal_body_handler_find_part_by_header(const SalBodyHandler *body_handler,
                                                     const char *header_name,
                                                     const char *header_value) {
	const belle_sip_list_t *l =
	    belle_sip_multipart_body_handler_get_parts(BELLE_SIP_MULTIPART_BODY_HANDLER(body_handler));
	for (; l != NULL; l = l->next) {
		belle_sip_body_handler_t *bsbh = BELLE_SIP_BODY_HANDLER(l->data);
		const belle_sip_list_t *headers = belle_sip_body_handler_get_headers(bsbh);
		for (; headers != NULL; headers = headers->next) {
			belle_sip_header_t *header = BELLE_SIP_HEADER(headers->data);
			if ((strcmp(belle_sip_header_get_name(header), header_name) == 0) &&
			    (strcmp(belle_sip_header_get_unparsed_value(header), header_value) == 0)) {
				return (SalBodyHandler *)bsbh;
			}
		}
	}
	return NULL;
}

const char *sal_body_handler_get_header(const SalBodyHandler *body_handler, const char *header_name) {
	belle_sip_header_t *header = sal_body_handler_find_header(body_handler, header_name);
	if (header != NULL) {
		return belle_sip_header_get_unparsed_value(header);
	}
	return NULL;
}

const belle_sip_list_t *sal_body_handler_get_headers(const SalBodyHandler *body_handler) {
	return belle_sip_body_handler_get_headers(BELLE_SIP_BODY_HANDLER(body_handler));
}
