remote_provisioning.c 9.78 KiB
/*
 * 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 <bctoolbox/defs.h>
#include "linphone/lpconfig.h"
#include "private.h"
#include "xml2lpc.h"
#define XML2LPC_CALLBACK_BUFFER_SIZE 1024
static void belle_request_process_io_error(void *ctx, BCTBX_UNUSED(const belle_sip_io_error_event_t *event)) {
	LinphoneCore *lc = (LinphoneCore *)ctx;
	linphone_configuring_terminated(lc, LinphoneConfiguringFailed, "http io error");
static void belle_request_process_timeout(void *ctx, BCTBX_UNUSED(const belle_sip_timeout_event_t *event)) {
	LinphoneCore *lc = (LinphoneCore *)ctx;
	linphone_configuring_terminated(lc, LinphoneConfiguringFailed, "http timeout");
static void belle_request_process_auth_requested(void *ctx, belle_sip_auth_event_t *event) {
	LinphoneCore *lc = (LinphoneCore *)ctx;
	const char *realm = belle_sip_auth_event_get_realm(event);
	const char *username = belle_sip_auth_event_get_username(event);
	const char *domain = belle_sip_auth_event_get_domain(event);
	const char *algorithm = belle_sip_auth_event_get_algorithm(event);
	const LinphoneAuthInfo *auth_info = _linphone_core_find_auth_info(lc, realm, username, domain, algorithm, TRUE);
	linphone_auth_info_fill_belle_sip_event(auth_info, event);
static void linphone_remote_provisioning_apply(LinphoneCore *lc, const char *xml) {
	LinphoneConfig *config = linphone_core_get_config(lc);
	const char *error_msg = _linphone_config_load_from_xml_string(config, xml);
	_linphone_config_apply_factory_config(config);
	linphone_configuring_terminated(lc, error_msg ? LinphoneConfiguringFailed : LinphoneConfiguringSuccessful,
	                                error_msg);
int linphone_remote_provisioning_load_file(LinphoneCore *lc, const char *file_path) {
	int status = -1;
	char *provisioning = ms_load_path_content(file_path, NULL);
	if (provisioning) {
		linphone_remote_provisioning_apply(lc, provisioning);
		status = 0;
		ms_free(provisioning);
	return status;
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
static void belle_request_process_response_event(void *ctx, const belle_http_response_event_t *event) { LinphoneCore *lc = (LinphoneCore *)ctx; belle_sip_message_t *message = BELLE_SIP_MESSAGE(event->response); const char *body = belle_sip_message_get_body(message); int statusCode = belle_http_response_get_status_code(event->response); if (statusCode == 200) { linphone_remote_provisioning_apply(lc, body); } else if (statusCode == 401) { linphone_configuring_terminated(lc, LinphoneConfiguringFailed, "http auth requested"); } else { linphone_configuring_terminated(lc, LinphoneConfiguringFailed, "http error"); } } int linphone_remote_provisioning_download_and_apply(LinphoneCore *lc, const char *remote_provisioning_uri, const bctbx_list_t *remote_provisioning_headers) { belle_generic_uri_t *uri = belle_generic_uri_parse(remote_provisioning_uri); const char *scheme = uri ? belle_generic_uri_get_scheme(uri) : NULL; const char *host = uri ? belle_generic_uri_get_host(uri) : NULL; if (scheme && (strcmp(scheme, "file") == 0)) { // We allow for 'local remote-provisioning' in case the file is to be opened from the hard drive. const char *file_path = remote_provisioning_uri + strlen("file://"); // skip scheme if (uri) { belle_sip_object_unref(uri); } return linphone_remote_provisioning_load_file(lc, file_path); } else if (scheme && strncmp(scheme, "http", 4) == 0 && host && strlen(host) > 0) { belle_http_request_listener_callbacks_t belle_request_listener = {0}; belle_http_request_t *request; belle_request_listener.process_response = belle_request_process_response_event; belle_request_listener.process_auth_requested = belle_request_process_auth_requested; belle_request_listener.process_io_error = belle_request_process_io_error; belle_request_listener.process_timeout = belle_request_process_timeout; lc->provisioning_http_listener = belle_http_request_listener_create_from_callbacks(&belle_request_listener, lc); request = belle_http_request_create( "GET", uri, belle_sip_header_create("User-Agent", linphone_core_get_user_agent(lc)), NULL); const bctbx_list_t *header_it = remote_provisioning_headers; while (header_it) { const bctbx_list_t *pair_value = (const bctbx_list_t *)bctbx_list_get_data(header_it); const char *field = (const char *)bctbx_list_get_data(pair_value); pair_value = bctbx_list_next(pair_value); belle_sip_header_t *header = belle_http_header_create(field, (const char *)bctbx_list_get_data(pair_value)); if (header) belle_sip_message_add_header(BELLE_SIP_MESSAGE(request), header); header_it = bctbx_list_next(header_it); } LinphoneProxyConfig *cfg = linphone_core_get_default_proxy_config(lc); if (cfg != nullptr) { char *addr = linphone_address_as_string_uri_only(linphone_proxy_config_get_identity_address(cfg)); belle_sip_message_add_header(BELLE_SIP_MESSAGE(request), belle_http_header_create("From", addr)); ms_free(addr); } return belle_http_provider_send_request(lc->http_provider, request, lc->provisioning_http_listener); } else { ms_error("Invalid provisioning URI [%s] (missing scheme or host ?)", remote_provisioning_uri); if (uri) { belle_sip_object_unref(uri); } return -1;
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
} } // Remove all "[misc] config-uri-header_X" void linphone_core_clear_provisioning_headers(LinphoneCore *lc) { char config_field[40]; int count = 0; snprintf(config_field, 40, "config-uri-header_%d", count); while (linphone_config_has_entry(lc->config, "misc", config_field)) { linphone_config_clean_entry(lc->config, "misc", config_field); snprintf(config_field, 40, "config-uri-header_%d", ++count); } } // Add a pair "config-uri-header_<N+1> ; header" where header will be added to the remote provisioning download. N is // the current header index (which is '-1' if no headers have been defined) void linphone_core_add_provisioning_header(LinphoneCore *lc, const char *header_name, const char *value) { char config_field[40]; // 1. get last index int count = 0; snprintf(config_field, 40, "config-uri-header_%d", count); while (linphone_config_has_entry(lc->config, "misc", config_field)) { snprintf(config_field, 40, "config-uri-header_%d", ++count); } // 2. Build value char *header = bctbx_strdup_printf("%s:%s", header_name, value); // 3. Write new header linphone_config_set_string(lc->config, "misc", config_field, header); bctbx_free(header); } // Split "<header name>:<value>". Note : we are not using stringlist because it will split other ',' in value. And by // keeping ':', we match the header syntax for config readability. bctbx_list_t *linphone_remote_provisioning_split_header(const char *serialized_header) { bctbx_list_t *header = NULL; int index = 0; while (serialized_header[index] != ':' && serialized_header[index] != '\0') ++index; if (serialized_header[index] != '\0') { // Check null in case of malformed configuration char *header_name = bctbx_strndup(serialized_header, index); char *value = bctbx_strdup(serialized_header + index + 1); header = bctbx_list_append(header, header_name); header = bctbx_list_append(header, value); } return header; } bctbx_list_t *linphone_remote_provisioning_split_headers(bctbx_list_t *headers) { bctbx_list_t *splitted_headers = NULL; while (headers) { // Allow headers to be NULL bctbx_list_t *header = linphone_remote_provisioning_split_header((const char *)bctbx_list_get_data(headers)); if (header) splitted_headers = bctbx_list_append(splitted_headers, header); headers = bctbx_list_next(headers); } return splitted_headers; } // Get an array of serialized pair (header name/value). Note : deserialized is not supported by wrappers (aka // bctbx_list<bctbx_list<char*>>) bctbx_list_t *linphone_core_get_provisioning_headers(const LinphoneCore *lc) { bctbx_list_t *headers = NULL; char config_field[40]; int count = 0; const char *header_name = NULL; snprintf(config_field, 40, "config-uri-header_%d", count); while ((header_name = linphone_config_get_string(lc->config, "misc", config_field, NULL))) { // Should be never NULL headers = bctbx_list_append(headers, bctbx_strdup(header_name)); snprintf(config_field, 40, "config-uri-header_%d", ++count);
211212213214215216217218219220221222223224225226227228229230231232233234235236
} return headers; } LinphoneStatus linphone_core_set_provisioning_uri(LinphoneCore *lc, const char *remote_provisioning_uri) { belle_generic_uri_t *uri = remote_provisioning_uri ? belle_generic_uri_parse(remote_provisioning_uri) : NULL; if (!remote_provisioning_uri || uri) { linphone_config_set_string(lc->config, "misc", "config-uri", remote_provisioning_uri); if (!remote_provisioning_uri) linphone_core_clear_provisioning_headers(lc); if (uri) { belle_sip_object_unref(uri); } return 0; } ms_error("Invalid provisioning URI [%s] (could not be parsed)", remote_provisioning_uri); return -1; } const char *linphone_core_get_provisioning_uri(const LinphoneCore *lc) { return linphone_config_get_string(lc->config, "misc", "config-uri", NULL); } bool_t linphone_core_is_provisioning_transient(LinphoneCore *lc) { return linphone_config_get_int(lc->config, "misc", "transient_provisioning", 0) == 1; }