xmlrpc.c 14.6 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 185
	LinphoneXmlRpcRequest *request = (LinphoneXmlRpcRequest *)data;
	ms_error("Authentication error during XML-RPC request sending");
186 187 188 189 190
	if (!linphone_xml_rpc_request_aborted(request)){
		request->status = LinphoneXmlRpcStatusFailed;
		if (request->callbacks->response != NULL) {
			request->callbacks->response(request);
		}
191 192
	}
	linphone_xml_rpc_request_unref(request);
193 194
}

195
static void parse_valid_xml_rpc_response(LinphoneXmlRpcRequest *request, const char *response_body) {
196 197
	xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new();
	xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error);
198
	request->status = LinphoneXmlRpcStatusFailed;
199 200
	xml_ctx->doc = xmlReadDoc((const unsigned char*)response_body, 0, NULL, 0);
	if (xml_ctx->doc != NULL) {
Simon Morlat's avatar
Simon Morlat committed
201
		char *response_str = NULL;
202
		if (linphone_create_xml_xpath_context(xml_ctx) < 0) goto end;
203
		switch (request->response.type) {
Ghislain MARY's avatar
Ghislain MARY committed
204 205 206
			case LinphoneXmlRpcArgInt:
				response_str = linphone_get_xml_text_content(xml_ctx, "/methodResponse/params/param/value/int");
				if (response_str != NULL) {
207 208
					request->response.data.i = atoi(response_str);
					request->status = LinphoneXmlRpcStatusOk;
Ghislain MARY's avatar
Ghislain MARY committed
209 210 211 212 213
				}
				break;
			case LinphoneXmlRpcArgString:
				response_str = linphone_get_xml_text_content(xml_ctx, "/methodResponse/params/param/value/string");
				if (response_str != NULL) {
214 215
					request->response.data.s = belle_sip_strdup(response_str);
					request->status = LinphoneXmlRpcStatusOk;
Ghislain MARY's avatar
Ghislain MARY committed
216 217 218 219
				}
				break;
			default:
				break;
220
		}
Simon Morlat's avatar
Simon Morlat committed
221
		if (response_str) linphone_free_xml_text_content(response_str);
222 223 224 225 226
	} else {
		ms_warning("Wrongly formatted XML-RPC response: %s", xml_ctx->errorBuffer);
	}
end:
	linphone_xmlparsing_context_destroy(xml_ctx);
227 228
	if (request->callbacks->response != NULL) {
		request->callbacks->response(request);
229
	}
230 231
}

232
static void notify_xml_rpc_error(LinphoneXmlRpcRequest *request) {
233
	request->status = LinphoneXmlRpcStatusFailed;
234 235
	if (request->callbacks->response != NULL) {
		request->callbacks->response(request);
236
	}
237 238 239
}

static void process_response_from_post_xml_rpc_request(void *data, const belle_http_response_event_t *event) {
240
	LinphoneXmlRpcRequest *request = (LinphoneXmlRpcRequest *)data;
241 242

	/* Check the answer code */
243
	if (!linphone_xml_rpc_request_aborted(request) && event->response) {
244 245
		int code = belle_http_response_get_status_code(event->response);
		if (code == 200) { /* Valid response from the server. */
246
			parse_valid_xml_rpc_response(request, belle_sip_message_get_body((belle_sip_message_t *)event->response));
247
		} else {
Simon Morlat's avatar
Simon Morlat committed
248
			ms_error("process_response_from_post_xml_rpc_request(): error code = %i", code);
249
			notify_xml_rpc_error(request);
250 251
		}
	}
252
	linphone_xml_rpc_request_unref(request);
253 254 255
}


256
static LinphoneXmlRpcRequest * _linphone_xml_rpc_request_new(LinphoneXmlRpcArgType return_type, const char *method) {
Ghislain MARY's avatar
Ghislain MARY committed
257
	LinphoneXmlRpcRequest *request = belle_sip_object_new(LinphoneXmlRpcRequest);
258
	request->callbacks = linphone_xml_rpc_request_cbs_new();
Ghislain MARY's avatar
Ghislain MARY committed
259 260 261 262 263 264 265
	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) {
266
	LinphoneXmlRpcArg *arg = reinterpret_cast<LinphoneXmlRpcArg *>(belle_sip_malloc0(sizeof(LinphoneXmlRpcArg)));
Ghislain MARY's avatar
Ghislain MARY committed
267 268 269 270 271 272
	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) {
273
	LinphoneXmlRpcArg *arg = reinterpret_cast<LinphoneXmlRpcArg *>(belle_sip_malloc0(sizeof(LinphoneXmlRpcArg)));
Ghislain MARY's avatar
Ghislain MARY committed
274 275 276 277 278 279
	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) {
280
	belle_sip_list_free_with_data(request->arg_list, (void (*)(void*))free_arg);
Ghislain MARY's avatar
Ghislain MARY committed
281 282 283
	if ((request->response.type == LinphoneXmlRpcArgString) && (request->response.data.s != NULL)) {
		belle_sip_free(request->response.data.s);
	}
284 285
	if (request->content) belle_sip_free(request->content);
	belle_sip_free(request->method);
286
	linphone_xml_rpc_request_cbs_unref(request->callbacks);
287 288
}

Ghislain MARY's avatar
Ghislain MARY committed
289
static void _linphone_xml_rpc_session_destroy(LinphoneXmlRpcSession *session) {
290 291 292 293 294 295 296
	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
297
	(belle_sip_object_destroy_t)_linphone_xml_rpc_request_destroy,
298 299
	NULL, // clone
	NULL, // marshal
300
	FALSE
301 302 303
);

BELLE_SIP_INSTANCIATE_VPTR(LinphoneXmlRpcSession, belle_sip_object_t,
Ghislain MARY's avatar
Ghislain MARY committed
304
	(belle_sip_object_destroy_t)_linphone_xml_rpc_session_destroy,
305 306
	NULL, // clone
	NULL, // marshal
307
	FALSE
308 309 310
);


311 312
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
313 314
	format_request(request);
	return request;
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
}

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
334 335 336 337 338 339 340 341 342 343
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);
}

344 345 346 347
LinphoneXmlRpcRequestCbs * linphone_xml_rpc_request_get_callbacks(const LinphoneXmlRpcRequest *request) {
	return request->callbacks;
}

348 349 350 351 352 353 354 355
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
356 357 358 359 360 361
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;
362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
}


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

389 390 391 392
LinphoneXmlRpcRequest * linphone_xml_rpc_session_create_request(LinphoneXmlRpcSession *session, LinphoneXmlRpcArgType return_type, const char *method) {
	return linphone_xml_rpc_request_new(return_type, method);
}

393
void linphone_xml_rpc_session_send_request(LinphoneXmlRpcSession *session, LinphoneXmlRpcRequest *request) {
394 395 396 397 398 399
	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;
400
	linphone_xml_rpc_request_ref(request);
401 402 403 404 405 406 407 408 409

	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;
	}
	req = belle_http_request_create("POST", uri, belle_sip_header_content_type_create("text", "xml"), NULL);
	if (!req) {
410
		belle_sip_object_unref(uri);
411
		process_io_error_from_post_xml_rpc_request(request, NULL);
412
		return;
413
	}
414
	data = linphone_xml_rpc_request_get_content(request);
415
	bh = belle_sip_memory_body_handler_new_copy_from_buffer(data, strlen(data), NULL, NULL);
416 417 418 419
	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;
420
	l = belle_http_request_listener_create_from_callbacks(&cbs, request);
421
	belle_http_provider_send_request(session->core->http_provider, req, l);
422 423 424 425
	/*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);
426
}
427 428 429 430 431

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