xmlrpc.c 15.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
linphone
Copyright (C) 2010-2015 Belledonne Communications SARL

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 2
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, write to the Free Software
17
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 19 20 21 22 23
*/

#include <string.h>
#include <libxml/tree.h>
#include <libxml/xmlwriter.h>

24
#include "linphone/core.h"
25

26
#include "c-wrapper/c-wrapper.h"
27

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

31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneXmlRpcRequestCbs);

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

static LinphoneXmlRpcRequestCbs * linphone_xml_rpc_request_cbs_new(void) {
	return belle_sip_object_new(LinphoneXmlRpcRequestCbs);
}

LinphoneXmlRpcRequestCbs * linphone_xml_rpc_request_cbs_ref(LinphoneXmlRpcRequestCbs *cbs) {
	belle_sip_object_ref(cbs);
	return cbs;
}

void linphone_xml_rpc_request_cbs_unref(LinphoneXmlRpcRequestCbs *cbs) {
	belle_sip_object_unref(cbs);
}

void *linphone_xml_rpc_request_cbs_get_user_data(const LinphoneXmlRpcRequestCbs *cbs) {
	return cbs->user_data;
}

void linphone_xml_rpc_request_cbs_set_user_data(LinphoneXmlRpcRequestCbs *cbs, void *ud) {
	cbs->user_data = ud;
}

LinphoneXmlRpcRequestCbsResponseCb linphone_xml_rpc_request_cbs_get_response(const LinphoneXmlRpcRequestCbs *cbs) {
	return cbs->response;
}

void linphone_xml_rpc_request_cbs_set_response(LinphoneXmlRpcRequestCbs *cbs, LinphoneXmlRpcRequestCbsResponseCb cb) {
	cbs->response = cb;
}


70 71 72 73 74 75 76
static void format_request(LinphoneXmlRpcRequest *request) {
	char si[64];
	belle_sip_list_t *arg_ptr = request->arg_list;
	xmlBufferPtr buf;
	xmlTextWriterPtr writer;
	int err;

Ghislain MARY's avatar
Ghislain MARY committed
77 78 79 80 81
	if (request->content != NULL) {
		belle_sip_free(request->content);
		request->content = NULL;
	}

82 83 84 85 86 87 88 89 90 91 92
	buf = xmlBufferCreate();
	if (buf == NULL) {
		ms_error("Error creating the XML buffer");
		return;
	}
	writer = xmlNewTextWriterMemory(buf, 0);
	if (writer == NULL) {
		ms_error("Error creating the XML writer");
		return;
	}

93 94 95
	/* autoindent so that logs are human-readable, as SIP sip on-purpose */
	xmlTextWriterSetIndent(writer, 1);

96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
	err = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL);
	if (err >= 0) {
		err = xmlTextWriterStartElement(writer, (const xmlChar *)"methodCall");
	}
	if (err >= 0) {
		err = xmlTextWriterWriteElement(writer, (const xmlChar *)"methodName", (const xmlChar *)request->method);
	}
	if (err >= 0) {
		err = xmlTextWriterStartElement(writer, (const xmlChar *)"params");
	}
	while (arg_ptr != NULL) {
		LinphoneXmlRpcArg *arg = (LinphoneXmlRpcArg *)arg_ptr->data;
		if (err >= 0) {
			err = xmlTextWriterStartElement(writer, (const xmlChar *)"param");
		}
		if (err >= 0) {
			err = xmlTextWriterStartElement(writer, (const xmlChar *)"value");
		}
		switch (arg->type) {
			case LinphoneXmlRpcArgNone:
				break;
			case LinphoneXmlRpcArgInt:
				memset(si, 0, sizeof(si));
				snprintf(si, sizeof(si), "%i", arg->data.i);
				err = xmlTextWriterWriteElement(writer, (const xmlChar *)"int", (const xmlChar *)si);
				break;
			case LinphoneXmlRpcArgString:
123
				err = xmlTextWriterWriteElement(writer, (const xmlChar *)"string", arg->data.s ? (const xmlChar *)arg->data.s : (const xmlChar *)"");
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
				break;
		}
		if (err >= 0) {
			/* Close the "value" element. */
			err = xmlTextWriterEndElement(writer);
		}
		if (err >= 0) {
			/* Close the "param" element. */
			err = xmlTextWriterEndElement(writer);
		}
		arg_ptr = arg_ptr->next;
	}
	if (err >= 0) {
		/* Close the "params" element. */
		err = xmlTextWriterEndElement(writer);
	}
	if (err >= 0) {
		/* Close the "methodCall" element. */
		err = xmlTextWriterEndElement(writer);
	}
	if (err >= 0) {
		err = xmlTextWriterEndDocument(writer);
	}
	if (err > 0) {
		/* xmlTextWriterEndDocument returns the size of the content. */
		request->content = belle_sip_strdup((const char *)buf->content);
	}
	xmlFreeTextWriter(writer);
	xmlBufferFree(buf);
}

static void free_arg(LinphoneXmlRpcArg *arg) {
	if ((arg->type == LinphoneXmlRpcArgString) && (arg->data.s != NULL)) {
		belle_sip_free(arg->data.s);
	}
	belle_sip_free(arg);
}

162 163 164 165 166 167 168 169 170
static bool_t linphone_xml_rpc_request_aborted(LinphoneXmlRpcRequest *req){
	LinphoneXmlRpcSession *session = (LinphoneXmlRpcSession*) belle_sip_object_data_get(BELLE_SIP_OBJECT(req), "session");
	if (!session){
		ms_error("linphone_xml_rpc_request_aborted(): no session, this should not happen.");
		return FALSE;
	}
	return session->released;
}

171
static void process_io_error_from_post_xml_rpc_request(void *data, const belle_sip_io_error_event_t *event) {
172 173
	LinphoneXmlRpcRequest *request = (LinphoneXmlRpcRequest *)data;
	ms_error("I/O Error during XML-RPC request sending");
174 175 176 177 178
	if (!linphone_xml_rpc_request_aborted(request)){
		request->status = LinphoneXmlRpcStatusFailed;
			if (request->callbacks->response != NULL) {
			request->callbacks->response(request);
		}
179 180
	}
	linphone_xml_rpc_request_unref(request);
181 182 183
}

static void process_auth_requested_from_post_xml_rpc_request(void *data, belle_sip_auth_event_t *event) {
184
	LinphoneXmlRpcRequest *request = (LinphoneXmlRpcRequest *)data;
185 186 187 188 189 190
	LinphoneAccountCreator *creator = (LinphoneAccountCreator *)linphone_xml_rpc_request_get_user_data(request);

	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);

191
	const LinphoneAuthInfo *auth_info = linphone_core_find_auth_info(creator->core, realm, username, domain);
192 193 194 195 196 197 198 199 200 201 202 203 204

	if (auth_info) {
		const char *auth_username = linphone_auth_info_get_username(auth_info);
		const char *auth_password = linphone_auth_info_get_password(auth_info);
		belle_sip_auth_event_set_username(event, auth_username);
		belle_sip_auth_event_set_passwd(event, auth_password);
	} else {
		ms_error("Authentication error during XML-RPC request sending");
		if (!linphone_xml_rpc_request_aborted(request)){
			request->status = LinphoneXmlRpcStatusFailed;
			if (request->callbacks->response != NULL) {
				request->callbacks->response(request);
			}
205
		}
206
		linphone_xml_rpc_request_unref(request);
207
	}
208 209
}

210
static void parse_valid_xml_rpc_response(LinphoneXmlRpcRequest *request, const char *response_body) {
211 212
	xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new();
	xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error);
213
	request->status = LinphoneXmlRpcStatusFailed;
214
	xml_ctx->doc = xmlReadDoc((const unsigned char*)response_body, 0, NULL, 0);
215
	if (xml_ctx->doc != NULL) {
Simon Morlat's avatar
Simon Morlat committed
216
		char *response_str = NULL;
217
		if (linphone_create_xml_xpath_context(xml_ctx) < 0) goto end;
218
		switch (request->response.type) {
Ghislain MARY's avatar
Ghislain MARY committed
219 220 221
			case LinphoneXmlRpcArgInt:
				response_str = linphone_get_xml_text_content(xml_ctx, "/methodResponse/params/param/value/int");
				if (response_str != NULL) {
222 223
					request->response.data.i = atoi(response_str);
					request->status = LinphoneXmlRpcStatusOk;
Ghislain MARY's avatar
Ghislain MARY committed
224 225 226 227 228
				}
				break;
			case LinphoneXmlRpcArgString:
				response_str = linphone_get_xml_text_content(xml_ctx, "/methodResponse/params/param/value/string");
				if (response_str != NULL) {
229 230
					request->response.data.s = belle_sip_strdup(response_str);
					request->status = LinphoneXmlRpcStatusOk;
Ghislain MARY's avatar
Ghislain MARY committed
231 232 233 234
				}
				break;
			default:
				break;
235
		}
Simon Morlat's avatar
Simon Morlat committed
236
		if (response_str) linphone_free_xml_text_content(response_str);
237 238 239 240 241
	} else {
		ms_warning("Wrongly formatted XML-RPC response: %s", xml_ctx->errorBuffer);
	}
end:
	linphone_xmlparsing_context_destroy(xml_ctx);
242 243
	if (request->callbacks->response != NULL) {
		request->callbacks->response(request);
244
	}
245 246
}

247
static void notify_xml_rpc_error(LinphoneXmlRpcRequest *request) {
248
	request->status = LinphoneXmlRpcStatusFailed;
249 250
	if (request->callbacks->response != NULL) {
		request->callbacks->response(request);
251
	}
252 253 254
}

static void process_response_from_post_xml_rpc_request(void *data, const belle_http_response_event_t *event) {
255
	LinphoneXmlRpcRequest *request = (LinphoneXmlRpcRequest *)data;
256 257

	/* Check the answer code */
258
	if (!linphone_xml_rpc_request_aborted(request) && event->response) {
259 260
		int code = belle_http_response_get_status_code(event->response);
		if (code == 200) { /* Valid response from the server. */
261
			parse_valid_xml_rpc_response(request, belle_sip_message_get_body((belle_sip_message_t *)event->response));
262
		} else {
Simon Morlat's avatar
Simon Morlat committed
263
			ms_error("process_response_from_post_xml_rpc_request(): error code = %i", code);
264
			notify_xml_rpc_error(request);
265 266
		}
	}
267
	linphone_xml_rpc_request_unref(request);
268 269 270
}


271
static LinphoneXmlRpcRequest * _linphone_xml_rpc_request_new(LinphoneXmlRpcArgType return_type, const char *method) {
Ghislain MARY's avatar
Ghislain MARY committed
272
	LinphoneXmlRpcRequest *request = belle_sip_object_new(LinphoneXmlRpcRequest);
273
	request->callbacks = linphone_xml_rpc_request_cbs_new();
Ghislain MARY's avatar
Ghislain MARY committed
274 275 276 277 278 279 280
	request->status = LinphoneXmlRpcStatusPending;
	request->response.type = return_type;
	request->method = belle_sip_strdup(method);
	return request;
}

static void _linphone_xml_rpc_request_add_int_arg(LinphoneXmlRpcRequest *request, int value) {
281
	LinphoneXmlRpcArg *arg = reinterpret_cast<LinphoneXmlRpcArg *>(belle_sip_malloc0(sizeof(LinphoneXmlRpcArg)));
Ghislain MARY's avatar
Ghislain MARY committed
282 283 284 285 286 287
	arg->type = LinphoneXmlRpcArgInt;
	arg->data.i = value;
	request->arg_list = belle_sip_list_append(request->arg_list, arg);
}

static void _linphone_xml_rpc_request_add_string_arg(LinphoneXmlRpcRequest *request, const char *value) {
288
	LinphoneXmlRpcArg *arg = reinterpret_cast<LinphoneXmlRpcArg *>(belle_sip_malloc0(sizeof(LinphoneXmlRpcArg)));
Ghislain MARY's avatar
Ghislain MARY committed
289 290 291 292 293 294
	arg->type = LinphoneXmlRpcArgString;
	arg->data.s = belle_sip_strdup(value);
	request->arg_list = belle_sip_list_append(request->arg_list, arg);
}

static void _linphone_xml_rpc_request_destroy(LinphoneXmlRpcRequest *request) {
295
	belle_sip_list_free_with_data(request->arg_list, (void (*)(void*))free_arg);
Ghislain MARY's avatar
Ghislain MARY committed
296 297 298
	if ((request->response.type == LinphoneXmlRpcArgString) && (request->response.data.s != NULL)) {
		belle_sip_free(request->response.data.s);
	}
299 300
	if (request->content) belle_sip_free(request->content);
	belle_sip_free(request->method);
301
	linphone_xml_rpc_request_cbs_unref(request->callbacks);
302 303
}

Ghislain MARY's avatar
Ghislain MARY committed
304
static void _linphone_xml_rpc_session_destroy(LinphoneXmlRpcSession *session) {
305 306 307 308 309 310 311
	belle_sip_free(session->url);
}

BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneXmlRpcRequest);
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneXmlRpcSession);

BELLE_SIP_INSTANCIATE_VPTR(LinphoneXmlRpcRequest, belle_sip_object_t,
Ghislain MARY's avatar
Ghislain MARY committed
312
	(belle_sip_object_destroy_t)_linphone_xml_rpc_request_destroy,
313 314
	NULL, // clone
	NULL, // marshal
315
	FALSE
316 317 318
);

BELLE_SIP_INSTANCIATE_VPTR(LinphoneXmlRpcSession, belle_sip_object_t,
Ghislain MARY's avatar
Ghislain MARY committed
319
	(belle_sip_object_destroy_t)_linphone_xml_rpc_session_destroy,
320 321
	NULL, // clone
	NULL, // marshal
322
	FALSE
323 324 325
);


326 327
LinphoneXmlRpcRequest * linphone_xml_rpc_request_new(LinphoneXmlRpcArgType return_type, const char *method) {
	LinphoneXmlRpcRequest *request = _linphone_xml_rpc_request_new(return_type, method);
Ghislain MARY's avatar
Ghislain MARY committed
328 329
	format_request(request);
	return request;
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
}

LinphoneXmlRpcRequest * linphone_xml_rpc_request_ref(LinphoneXmlRpcRequest *request) {
	belle_sip_object_ref(request);
	return request;
}

void linphone_xml_rpc_request_unref(LinphoneXmlRpcRequest *request) {
	belle_sip_object_unref(request);
}

void *linphone_xml_rpc_request_get_user_data(const LinphoneXmlRpcRequest *request) {
	return request->user_data;
}

void linphone_xml_rpc_request_set_user_data(LinphoneXmlRpcRequest *request, void *ud) {
	request->user_data = ud;
}

Ghislain MARY's avatar
Ghislain MARY committed
349 350 351 352 353 354 355 356 357 358
void linphone_xml_rpc_request_add_int_arg(LinphoneXmlRpcRequest *request, int value) {
	_linphone_xml_rpc_request_add_int_arg(request, value);
	format_request(request);
}

void linphone_xml_rpc_request_add_string_arg(LinphoneXmlRpcRequest *request, const char *value) {
	_linphone_xml_rpc_request_add_string_arg(request, value);
	format_request(request);
}

359 360 361 362
LinphoneXmlRpcRequestCbs * linphone_xml_rpc_request_get_callbacks(const LinphoneXmlRpcRequest *request) {
	return request->callbacks;
}

363 364 365 366 367 368 369 370
const char * linphone_xml_rpc_request_get_content(const LinphoneXmlRpcRequest *request) {
	return request->content;
}

LinphoneXmlRpcStatus linphone_xml_rpc_request_get_status(const LinphoneXmlRpcRequest *request) {
	return request->status;
}

Ghislain MARY's avatar
Ghislain MARY committed
371 372 373 374 375 376
int linphone_xml_rpc_request_get_int_response(const LinphoneXmlRpcRequest *request) {
	return request->response.data.i;
}

const char * linphone_xml_rpc_request_get_string_response(const LinphoneXmlRpcRequest *request) {
	return request->response.data.s;
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
}


LinphoneXmlRpcSession * linphone_xml_rpc_session_new(LinphoneCore *core, const char *url) {
	LinphoneXmlRpcSession *session = belle_sip_object_new(LinphoneXmlRpcSession);
	session->core = core;
	session->url = belle_sip_strdup(url);
	return session;
}

LinphoneXmlRpcSession * linphone_xml_rpc_session_ref(LinphoneXmlRpcSession *session) {
	belle_sip_object_ref(session);
	return session;
}

void linphone_xml_rpc_session_unref(LinphoneXmlRpcSession *session) {
	belle_sip_object_unref(session);
}

void *linphone_xml_rpc_session_get_user_data(const LinphoneXmlRpcSession *session) {
	return session->user_data;
}

void linphone_xml_rpc_session_set_user_data(LinphoneXmlRpcSession *session, void *ud) {
	session->user_data = ud;
}

404 405 406 407
LinphoneXmlRpcRequest * linphone_xml_rpc_session_create_request(LinphoneXmlRpcSession *session, LinphoneXmlRpcArgType return_type, const char *method) {
	return linphone_xml_rpc_request_new(return_type, method);
}

408
void linphone_xml_rpc_session_send_request(LinphoneXmlRpcSession *session, LinphoneXmlRpcRequest *request) {
409 410 411 412 413 414
	belle_http_request_listener_callbacks_t cbs = { 0 };
	belle_http_request_listener_t *l;
	belle_generic_uri_t *uri;
	belle_http_request_t *req;
	belle_sip_memory_body_handler_t *bh;
	const char *data;
415
	linphone_xml_rpc_request_ref(request);
416 417 418 419 420 421 422

	uri = belle_generic_uri_parse(session->url);
	if (!uri) {
		ms_error("Could not send request, URL %s is invalid", session->url);
		process_io_error_from_post_xml_rpc_request(request, NULL);
		return;
	}
423 424 425

	LinphoneProxyConfig *cfg = linphone_core_get_default_proxy_config(session->core);
	if (cfg) {
426
		char *addr = linphone_address_as_string_uri_only(linphone_proxy_config_get_identity_address(cfg));
427 428 429 430
		req = belle_http_request_create("POST", uri,
			belle_sip_header_content_type_create("text", "xml"),
			belle_http_header_create("From", addr),
			NULL);
431
		bctbx_free(addr);
432 433 434 435 436
	} else {
		req = belle_http_request_create("POST", uri,
			belle_sip_header_content_type_create("text", "xml"),
			NULL);
	}
437
	if (!req) {
438
		belle_sip_object_unref(uri);
439
		process_io_error_from_post_xml_rpc_request(request, NULL);
440
		return;
441
	}
442
	data = linphone_xml_rpc_request_get_content(request);
443
	bh = belle_sip_memory_body_handler_new_copy_from_buffer(data, strlen(data), NULL, NULL);
444 445 446 447
	belle_sip_message_set_body_handler(BELLE_SIP_MESSAGE(req), BELLE_SIP_BODY_HANDLER(bh));
	cbs.process_response = process_response_from_post_xml_rpc_request;
	cbs.process_io_error = process_io_error_from_post_xml_rpc_request;
	cbs.process_auth_requested = process_auth_requested_from_post_xml_rpc_request;
448
	l = belle_http_request_listener_create_from_callbacks(&cbs, request);
449

450
	belle_http_provider_send_request(session->core->http_provider, req, l);
451 452 453 454
	/*ensure that the listener object will be destroyed with the request*/
	belle_sip_object_data_set(BELLE_SIP_OBJECT(request), "listener", l, belle_sip_object_unref);
	/*prevent destruction of the session while there are still pending http requests*/
	belle_sip_object_data_set(BELLE_SIP_OBJECT(request), "session", belle_sip_object_ref(session), belle_sip_object_unref);
455
}
456 457 458 459 460

void linphone_xml_rpc_session_release(LinphoneXmlRpcSession *session){
	session->released = TRUE;
	belle_sip_object_unref(session);
}