Commit cbae403a authored by Ghislain MARY's avatar Ghislain MARY
Browse files

Handle XML-RPC request in liblinphone directly instead of using libsoup.

parent a9e04dda
......@@ -92,6 +92,8 @@ set(SOURCE_FILES
xml2lpc.c
xml2lpc.h
xml.c
xmlrpc.c
xmlrpc.h
vtables.c
)
if(ENABLE_TUNNEL)
......
......@@ -373,6 +373,7 @@ LINPHONE_PUBLIC const char* linphone_privacy_to_string(LinphonePrivacy privacy);
#include "content.h"
#include "event.h"
#include "linphonefriend.h"
#include "xmlrpc.h"
#else
#include "linphone/buffer.h"
#include "linphone/call_log.h"
......@@ -380,6 +381,7 @@ LINPHONE_PUBLIC const char* linphone_privacy_to_string(LinphonePrivacy privacy);
#include "linphone/content.h"
#include "linphone/event.h"
#include "linphone/linphonefriend.h"
#include "linphone/xmlrpc.h"
#endif
LINPHONE_PUBLIC LinphoneAddress * linphone_address_new(const char *addr);
......
......@@ -979,6 +979,38 @@ struct _LinphoneBuffer {
BELLE_SIP_DECLARE_VPTR(LinphoneBuffer);
/*****************************************************************************
* XML-RPC interface *
****************************************************************************/
struct _LinphoneXmlRpcRequest {
belle_sip_object_t base;
void *user_data;
belle_sip_list_t *arg_list;
char *content; /**< The string representation of the XML-RPC request */
char *method;
LinphoneXmlRpcStatus status;
int response;
};
BELLE_SIP_DECLARE_VPTR(LinphoneXmlRpcRequest);
struct _LinphoneXmlRpcSession {
belle_sip_object_t base;
void *user_data;
belle_sip_list_t *arg_list;
LinphoneCore *core;
LinphoneXmlRpcRequest *request;
LinphoneContent *content;
char *url;
ms_cond_t cond;
ms_mutex_t cond_mutex;
ms_mutex_t mutex;
};
BELLE_SIP_DECLARE_VPTR(LinphoneXmlRpcSession);
/*****************************************************************************
* REMOTE PROVISIONING FUNCTIONS *
****************************************************************************/
......@@ -1083,7 +1115,9 @@ BELLE_SIP_TYPE_ID(LinphoneContent),
BELLE_SIP_TYPE_ID(LinphoneLDAPContactProvider),
BELLE_SIP_TYPE_ID(LinphoneLDAPContactSearch),
BELLE_SIP_TYPE_ID(LinphoneProxyConfig),
BELLE_SIP_TYPE_ID(LinphoneFriend)
BELLE_SIP_TYPE_ID(LinphoneFriend),
BELLE_SIP_TYPE_ID(LinphoneXmlRpcRequest),
BELLE_SIP_TYPE_ID(LinphoneXmlRpcSession)
BELLE_SIP_DECLARE_TYPES_END
......
......@@ -1507,7 +1507,7 @@ LinphoneAccountCreator *linphone_account_creator_new(struct _LinphoneCore *core,
return NULL;
}
obj=ms_new0(LinphoneAccountCreator,1);
cfg=linphone_proxy_config_new();
cfg=linphone_core_create_proxy_config(core);
ssctx=sip_setup_context_new(ss,cfg);
obj->lc=core;
obj->ssctx=ssctx;
......
......@@ -31,10 +31,13 @@ struct _SipSetup;
struct _BuddyInfo;
struct _LinphoneXmlRpcSession;
struct _SipSetupContext{
struct _SipSetup *funcs;
struct _LinphoneProxyConfig *cfg;
struct _LinphoneXmlRpcSession *xmlrpc_session;
char domain[128];
char username[128];
void *data;
......
......@@ -20,18 +20,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "linphonecore.h"
#include "private.h"
#include <ctype.h>
#include <libsoup/soup.h>
typedef struct _BLReq{
int status;
int result;
SoupMessage *msg;
SoupSession *session;
ortp_thread_t th;
}BLReq;
static const int XMLRPC_FAILED = -1;
static const int XMLRPC_OK = 0;
static const char *XMLRPC_URL = "https://www.linphone.org/wizard.php";
static void sip_wizard_init_instance(SipSetupContext *ctx){
......@@ -40,6 +30,13 @@ static void sip_wizard_init_instance(SipSetupContext *ctx){
linphone_proxy_config_enable_register(cfg,FALSE);
}
static void sip_wizard_uninit_instance(SipSetupContext *ctx) {
if (ctx->xmlrpc_session != NULL) {
linphone_xml_rpc_session_unref(ctx->xmlrpc_session);
ctx->xmlrpc_session = NULL;
}
}
static const char ** sip_wizard_get_domains(SipSetupContext *ctx) {
LinphoneProxyConfig *cfg=sip_setup_context_get_proxy_config(ctx);
const char **domains = (const char**) &cfg->reg_proxy;
......@@ -47,94 +44,23 @@ static const char ** sip_wizard_get_domains(SipSetupContext *ctx) {
}
static int xml_rpc_parse_response(BLReq *blreq, SoupMessage *sm){
SoupBuffer *sb;
GValue retval;
GError *error=NULL;
sb=soup_message_body_flatten(sm->response_body);
ms_message("This the xml-rpc response:\n%s\n",sb->data);
if (soup_xmlrpc_parse_method_response(sb->data,sb->length,&retval,&error)==FALSE){
if (error!=NULL){
ms_error("xmlrpc fault: %s",error->message);
g_error_free(error);
}else{
ms_error("Could not parse xml-rpc response !");
}
blreq->status=XMLRPC_FAILED;
}else{
ms_message("Extracting values from return type...");
blreq->result = g_value_get_int(&retval);
g_value_unset(&retval);
blreq->status=XMLRPC_OK;
}
soup_buffer_free(sb);
return blreq->status;
}
static void got_headers(BLReq *blreq, SoupMessage*msg){
ms_message("Got headers !");
blreq->status=XMLRPC_OK;
}
#if SERIALIZE_HTTPS
/*on windows libsoup support for threads with gnutls is not yet functionnal (only in git)
This will come in next release of libsoup, probably.
In the meantime, we are forced to serialize all soup https processing with a big
ugly global mutex...*/
static GStaticMutex big_mutex = G_STATIC_MUTEX_INIT;
#endif
static void * process_xml_rpc_request(void *up){
BLReq *blreq=(BLReq*)up;
SoupMessage *sm=blreq->msg;
int code;
g_signal_connect_swapped(G_OBJECT(sm),"got-headers",(GCallback)got_headers,blreq);
blreq->status=XMLRPC_OK;
#if SERIALIZE_HTTPS
g_static_mutex_lock(&big_mutex);
#endif
code=soup_session_send_message(blreq->session,sm);
if (code==200){
xml_rpc_parse_response(blreq,sm);
}else{
ms_error("request failed, error-code=%i (%s)",code,soup_status_get_phrase(code));
blreq->status=XMLRPC_FAILED;
}
#if SERIALIZE_HTTPS
g_static_mutex_unlock(&big_mutex);
#endif
return NULL;
}
static int do_simple_xmlrpc_request(SoupMessage *msg) {
int ret=-1;
BLReq *req;
static int do_simple_xmlrpc_request(SipSetupContext *ctx, LinphoneXmlRpcRequest *request) {
int ret = -1;
if (!msg){
ms_error("Fail to create SoupMessage !");
if (!request) {
ms_error("Fail to create XML-RPC request!");
return -1;
}else{
SoupBuffer *sb=soup_message_body_flatten(msg->request_body);
ms_message("This is the XML-RPC request we are going to send:\n%s\n",sb->data);
soup_buffer_free(sb);
} else {
ms_message("This is the XML-RPC request we are going to send:\n%s\n", linphone_xml_rpc_request_get_content(request));
}
req=ms_new0(BLReq, 1);
req->session=soup_session_sync_new();
req->msg=msg;
process_xml_rpc_request(req);
if (req->status == XMLRPC_OK) {
ret=req->result;
}
// Freeing allocated structures lead to a crash (why?)
//g_free(req->session);
//g_free(msg);
ms_free(req);
if (ctx->xmlrpc_session == NULL) {
ctx->xmlrpc_session = linphone_xml_rpc_session_new(ctx->cfg->lc, XMLRPC_URL);
}
if (linphone_xml_rpc_session_send_request(ctx->xmlrpc_session, request) == LinphoneXmlRpcStatusOk) {
ret = linphone_xml_rpc_request_get_response(request);
}
linphone_xml_rpc_request_unref(request);
return ret;
}
......@@ -145,30 +71,27 @@ static int do_simple_xmlrpc_request(SoupMessage *msg) {
* -1 if information isn't available
*/
static int sip_wizard_account_exists(SipSetupContext *ctx, const char *identity) {
SoupMessage *msg=soup_xmlrpc_request_new(XMLRPC_URL,
"check_account",
G_TYPE_STRING, identity,
G_TYPE_INVALID);
return do_simple_xmlrpc_request(msg);
LinphoneXmlRpcRequest *request = linphone_xml_rpc_request_new("check_account",
LinphoneXmlRpcArgString, identity,
LinphoneXmlRpcArgNone);
return do_simple_xmlrpc_request(ctx, request);
}
static int sip_wizard_account_validated(SipSetupContext *ctx, const char *identity) {
SoupMessage *msg=soup_xmlrpc_request_new(XMLRPC_URL,
"check_account_validated",
G_TYPE_STRING, identity,
G_TYPE_INVALID);
return do_simple_xmlrpc_request(msg);
LinphoneXmlRpcRequest *request = linphone_xml_rpc_request_new("check_account_validated",
LinphoneXmlRpcArgString, identity,
LinphoneXmlRpcArgNone);
return do_simple_xmlrpc_request(ctx, request);
}
static int sip_wizard_create_account(SipSetupContext *ctx, const char *identity, const char *passwd, const char *email, int suscribe) {
SoupMessage *msg=soup_xmlrpc_request_new(XMLRPC_URL,
"create_account",
G_TYPE_STRING, identity,
G_TYPE_STRING, passwd,
G_TYPE_STRING, email,
G_TYPE_INT, suscribe,
G_TYPE_INVALID);
return do_simple_xmlrpc_request(msg);
static int sip_wizard_create_account(SipSetupContext *ctx, const char *identity, const char *passwd, const char *email, int subscribe) {
LinphoneXmlRpcRequest *request = linphone_xml_rpc_request_new("create_account",
LinphoneXmlRpcArgString, identity,
LinphoneXmlRpcArgString, passwd,
LinphoneXmlRpcArgString, email,
LinphoneXmlRpcArgInt, subscribe,
LinphoneXmlRpcArgNone);
return do_simple_xmlrpc_request(ctx, request);
}
static void guess_display_name(LinphoneAddress *from){
......@@ -232,6 +155,7 @@ SipSetup linphone_sip_wizard={
.name="SipWizard",
.capabilities=SIP_SETUP_CAP_ACCOUNT_MANAGER,
.init_instance=sip_wizard_init_instance,
.uninit_instance=sip_wizard_uninit_instance,
.account_exists=sip_wizard_account_exists,
.create_account=sip_wizard_create_account,
.login_account=sip_wizard_do_login,
......@@ -247,7 +171,7 @@ SipSetup linphone_sip_wizard={
NULL,
NULL,
sip_wizard_init_instance,
NULL,
sip_wizard_uninit_instance,
sip_wizard_account_exists,
sip_wizard_create_account,
sip_wizard_do_login,
......@@ -261,6 +185,4 @@ SipSetup linphone_sip_wizard={
sip_wizard_account_validated
};
#endif
/*
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
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "linphonecore.h"
#include "private.h"
#include <string.h>
#include <libxml/tree.h>
#include <libxml/xmlwriter.h>
typedef struct _LinphoneXmlRpcArg {
LinphoneXmlRpcArgType type;
union {
int i;
char *s;
} data;
} LinphoneXmlRpcArg;
static void format_request(LinphoneXmlRpcRequest *request) {
char si[64];
belle_sip_list_t *arg_ptr = request->arg_list;
xmlBufferPtr buf;
xmlTextWriterPtr writer;
int err;
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;
}
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:
err = xmlTextWriterWriteElement(writer, (const xmlChar *)"string", (const xmlChar *)arg->data.s);
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);
}
static void process_io_error_from_post_xml_rpc_request(void *data, const belle_sip_io_error_event_t *event) {
LinphoneXmlRpcSession *session = (LinphoneXmlRpcSession *)data;
ms_error("I/O Error during XML-RPC request sending to %s", session->url);
session->request->status = LinphoneXmlRpcStatusFailed;
ms_cond_signal(&session->cond);
}
static void process_auth_requested_from_post_xml_rpc_request(void *data, belle_sip_auth_event_t *event) {
LinphoneXmlRpcSession *session = (LinphoneXmlRpcSession *)data;
ms_error("Error during XML-RPC request sending to connect %s", session->url);
session->request->status = LinphoneXmlRpcStatusFailed;
ms_cond_signal(&session->cond);
}
static void parse_valid_xml_rpc_response(LinphoneXmlRpcSession *session, const char *response_body) {
xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new();
xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error);
session->request->status = LinphoneXmlRpcStatusFailed;
xml_ctx->doc = xmlReadDoc((const unsigned char*)response_body, 0, NULL, 0);
if (xml_ctx->doc != NULL) {
const char *response_str;
if (linphone_create_xml_xpath_context(xml_ctx) < 0) goto end;
response_str = linphone_get_xml_text_content(xml_ctx, "/methodResponse/params/param/value/int");
if (response_str != NULL) {
session->request->response = atoi(response_str);
session->request->status = LinphoneXmlRpcStatusOk;
}
linphone_free_xml_text_content(response_str);
} else {
ms_warning("Wrongly formatted XML-RPC response: %s", xml_ctx->errorBuffer);
}
end:
linphone_xmlparsing_context_destroy(xml_ctx);
ms_cond_signal(&session->cond);
}
static void notify_xml_rpc_error(LinphoneXmlRpcSession *session) {
session->request->status = LinphoneXmlRpcStatusOk;
ms_cond_signal(&session->cond);
}
/**
* Callback function called when we have a response from server during the upload of the log collection to the server (rcs5.1 recommandation)
* Note: The first post is empty and the server shall reply a 204 (No content) message, this will trigger a new post request to the server
* to upload the file. The server response to this second post is processed by this same function
*
* @param[in] data The user-defined pointer associated with the request, it contains the LinphoneCore object
* @param[in] event The response from server
*/
static void process_response_from_post_xml_rpc_request(void *data, const belle_http_response_event_t *event) {
LinphoneXmlRpcSession *session = (LinphoneXmlRpcSession *)data;
/* Check the answer code */
if (event->response) {
int code = belle_http_response_get_status_code(event->response);
if (code == 200) { /* Valid response from the server. */
parse_valid_xml_rpc_response(session, belle_sip_message_get_body((belle_sip_message_t *)event->response));
} else {
notify_xml_rpc_error(session);
}
}
}
static void linphone_xml_rpc_request_destroy(LinphoneXmlRpcRequest *request) {
belle_sip_list_free_with_data(request->arg_list, (void (*)(void*))free_arg);
if (request->content) belle_sip_free(request->content);
belle_sip_free(request->method);
}
static void linphone_xml_rpc_session_destroy(LinphoneXmlRpcSession *session) {
belle_sip_free(session->url);
ms_cond_destroy(&session->cond);
ms_mutex_destroy(&session->cond_mutex);
ms_mutex_destroy(&session->mutex);
}
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneXmlRpcRequest);
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneXmlRpcSession);
BELLE_SIP_INSTANCIATE_VPTR(LinphoneXmlRpcRequest, belle_sip_object_t,
(belle_sip_object_destroy_t)linphone_xml_rpc_request_destroy,
NULL, // clone
NULL, // marshal
TRUE
);
BELLE_SIP_INSTANCIATE_VPTR(LinphoneXmlRpcSession, belle_sip_object_t,
(belle_sip_object_destroy_t)linphone_xml_rpc_session_destroy,
NULL, // clone
NULL, // marshal
TRUE
);
LinphoneXmlRpcRequest * linphone_xml_rpc_request_new(const char *method, ...) {
bool_t cont = TRUE;
va_list args;
LinphoneXmlRpcArgType arg_type;
LinphoneXmlRpcArg *arg = NULL;
LinphoneXmlRpcRequest *request = belle_sip_object_new(LinphoneXmlRpcRequest);
belle_sip_object_ref(request);
request->status = LinphoneXmlRpcStatusPending;
request->response = -1;
request->method = belle_sip_strdup(method);
va_start(args, method);
while (cont) {
arg_type = va_arg(args, LinphoneXmlRpcArgType);
switch (arg_type) {
case LinphoneXmlRpcArgNone:
cont = FALSE;
break;
case LinphoneXmlRpcArgInt:
arg = belle_sip_malloc0(sizeof(LinphoneXmlRpcArg));
arg->type = LinphoneXmlRpcArgInt;
arg->data.i = va_arg(args, int);
request->arg_list = belle_sip_list_append(request->arg_list, arg);
break;
case LinphoneXmlRpcArgString:
arg = belle_sip_malloc0(sizeof(LinphoneXmlRpcArg));
arg->type = LinphoneXmlRpcArgString;
arg->data.s = belle_sip_strdup(va_arg(args, char *));
request->arg_list = belle_sip_list_append(request->arg_list, arg);
break;
}
}
va_end(args);
format_request(request);
return request;
}
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;
}
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;
}
int linphone_xml_rpc_request_get_response(const LinphoneXmlRpcRequest *request) {
return request->response;
}
LinphoneXmlRpcSession * linphone_xml_rpc_session_new(LinphoneCore *core, const char *url) {
LinphoneXmlRpcSession *session = belle_sip_object_new(LinphoneXmlRpcSession);
belle_sip_object_ref(session);
session->core = core;
session->url = belle_sip_strdup(url);
ms_cond_init(&session->cond, NULL);
ms_mutex_init(&session->cond_mutex, NULL);
ms_mutex_init(&session->mutex, NULL);
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;
}