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

Implement RFC3994: Indication of Message Composition for Instant Messaging.

parent fe1ca6f0
......@@ -13,7 +13,8 @@ COMMON_CFLAGS=\
$(MEDIASTREAMER_CFLAGS) \
$(VIDEO_CFLAGS) \
$(READLINE_CFLAGS) \
$(SQLITE3_CFLAGS)
$(SQLITE3_CFLAGS) \
$(LIBXML2_CFLAGS)
if BUILD_CONSOLE
......@@ -29,7 +30,8 @@ linphonec_LDADD=$(top_builddir)/coreapi/liblinphone.la \
$(READLINE_LIBS) \
$(SQLITE3_LIBS) \
$(X11_LIBS) \
$(BELLESIP_LIBS)
$(BELLESIP_LIBS) \
$(LIBXML2_LIBS)
if BUILD_WIN32
#special build of linphonec to detach from the windows console
......
......@@ -51,6 +51,7 @@ liblinphone_la_SOURCES=\
contactprovider.c contactprovider.h contact_providers_priv.h \
ldap/ldapprovider.c ldap/ldapprovider.h \
dict.c \
xml.c \
$(GITVERSION_FILE)
if BUILD_UPNP
......
......@@ -464,6 +464,8 @@ void sal_set_callbacks(Sal *ctx, const SalCallbacks *cbs){
ctx->callbacks.subscribe_presence_received=(SalOnSubscribePresenceReceived)unimplemented_stub;
if (ctx->callbacks.text_received==NULL)
ctx->callbacks.text_received=(SalOnTextReceived)unimplemented_stub;
if (ctx->callbacks.is_composing_received==NULL)
ctx->callbacks.is_composing_received=(SalOnIsComposingReceived)unimplemented_stub;
if (ctx->callbacks.ping_reply==NULL)
ctx->callbacks.ping_reply=(SalOnPingReply)unimplemented_stub;
if (ctx->callbacks.auth_requested==NULL)
......@@ -915,5 +917,12 @@ unsigned char * sal_get_random_bytes(unsigned char *ret, size_t size){
return belle_sip_random_bytes(ret,size);
}
belle_sip_source_t * sal_create_timer(Sal *sal, belle_sip_source_func_t func, void *data, unsigned int timeout_value_ms, const char* timer_name) {
belle_sip_main_loop_t *ml = belle_sip_stack_get_main_loop(sal->stack);
return belle_sip_main_loop_create_timeout(ml, func, data, timeout_value_ms, timer_name);
}
void sal_cancel_timer(Sal *sal, belle_sip_source_t *timer) {
belle_sip_main_loop_t *ml = belle_sip_stack_get_main_loop(sal->stack);
belle_sip_main_loop_remove_source(ml, timer);
}
......@@ -76,6 +76,10 @@ static bool_t is_external_body(belle_sip_header_content_type_t* content_type) {
return strcmp("message",belle_sip_header_content_type_get_type(content_type))==0
&& strcmp("external-body",belle_sip_header_content_type_get_subtype(content_type))==0;
}
static bool_t is_im_iscomposing(belle_sip_header_content_type_t* content_type) {
return strcmp("application",belle_sip_header_content_type_get_type(content_type))==0
&& strcmp("im-iscomposing+xml",belle_sip_header_content_type_get_subtype(content_type))==0;
}
static void process_request_event(void *op_base, const belle_sip_request_event_t *event) {
SalOp* op = (SalOp*)op_base;
......@@ -88,8 +92,6 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
belle_sip_header_call_id_t* call_id = belle_sip_message_get_header_by_type(req,belle_sip_header_call_id_t);
belle_sip_header_cseq_t* cseq = belle_sip_message_get_header_by_type(req,belle_sip_header_cseq_t);
belle_sip_header_date_t *date=belle_sip_message_get_header_by_type(req,belle_sip_header_date_t);
SalMessage salmsg;
char message_id[256]={0};
int response_code=501;
char* from;
bool_t plain_text=FALSE;
......@@ -99,7 +101,8 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
content_type=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_content_type_t);
if (content_type && ((plain_text=is_plain_text(content_type))
|| (external_body=is_external_body(content_type)))) {
SalMessage salmsg;
char message_id[256]={0};
address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header))
,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header)));
from=belle_sip_object_to_string(BELLE_SIP_OBJECT(address));
......@@ -121,6 +124,17 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
belle_sip_free(from);
if (salmsg.url) ms_free((char*)salmsg.url);
response_code=200;
} else if (content_type && is_im_iscomposing(content_type)) {
SalIsComposing saliscomposing;
address=belle_sip_header_address_create(belle_sip_header_address_get_displayname(BELLE_SIP_HEADER_ADDRESS(from_header))
,belle_sip_header_address_get_uri(BELLE_SIP_HEADER_ADDRESS(from_header)));
from=belle_sip_object_to_string(BELLE_SIP_OBJECT(address));
saliscomposing.from=from;
saliscomposing.text=belle_sip_message_get_body(BELLE_SIP_MESSAGE(req));
op->base.root->callbacks.is_composing_received(op,&saliscomposing);
belle_sip_object_unref(address);
belle_sip_free(from);
response_code=200;
} else {
ms_error("Unsupported MESSAGE with content type [%s/%s]",belle_sip_header_content_type_get_type(content_type)
,belle_sip_header_content_type_get_subtype(content_type));
......
......@@ -843,6 +843,11 @@ static void text_received(SalOp *op, const SalMessage *msg){
}
}
static void is_composing_received(SalOp *op, const SalIsComposing *is_composing) {
LinphoneCore *lc = (LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
linphone_core_is_composing_received(lc, op, is_composing);
}
static void parse_presence_requested(SalOp *op, const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result) {
linphone_notify_parse_presence(op, content_type, content_subtype, body, result);
}
......@@ -1001,8 +1006,14 @@ static int op_equals(LinphoneCall *a, SalOp *b) {
static void text_delivery_update(SalOp *op, SalTextDeliveryStatus status){
LinphoneChatMessage *chat_msg=(LinphoneChatMessage* )sal_op_get_user_pointer(op);
const MSList* calls = linphone_core_get_calls(chat_msg->chat_room->lc);
const MSList* calls;
if (chat_msg == NULL) {
// Do not handle delivery status for isComposing messages.
return;
}
calls = linphone_core_get_calls(chat_msg->chat_room->lc);
chat_msg->state=chatStatusSal2Linphone(status);
linphone_chat_message_store_state(chat_msg);
if (chat_msg && chat_msg->cb) {
......@@ -1123,6 +1134,7 @@ SalCallbacks linphone_sal_callbacks={
refer_received,
text_received,
text_delivery_update,
is_composing_received,
notify_refer,
subscribe_received,
subscribe_closed,
......
......@@ -26,6 +26,12 @@
#include "private.h"
#include "lpconfig.h"
#include <libxml/xmlwriter.h>
#define COMPOSING_DEFAULT_IDLE_TIMEOUT 15
#define COMPOSING_DEFAULT_REFRESH_TIMEOUT 60
#define COMPOSING_DEFAULT_REMOTE_REFRESH_TIMEOUT 120
/**
* @addtogroup chatroom
* @{
......@@ -85,13 +91,40 @@ LinphoneChatRoom* linphone_core_get_or_create_chat_room(LinphoneCore* lc, const
}
return ret;
}
static void linphone_chat_room_delete_composing_idle_timer(LinphoneChatRoom *cr) {
if (cr->composing_idle_timer) {
sal_cancel_timer(cr->lc->sal, cr->composing_idle_timer);
belle_sip_object_unref(cr->composing_idle_timer);
cr->composing_idle_timer = NULL;
}
}
static void linphone_chat_room_delete_composing_refresh_timer(LinphoneChatRoom *cr) {
if (cr->composing_refresh_timer) {
sal_cancel_timer(cr->lc->sal, cr->composing_refresh_timer);
belle_sip_object_unref(cr->composing_refresh_timer);
cr->composing_refresh_timer = NULL;
}
}
static void linphone_chat_room_delete_remote_composing_refresh_timer(LinphoneChatRoom *cr) {
if (cr->remote_composing_refresh_timer) {
sal_cancel_timer(cr->lc->sal, cr->remote_composing_refresh_timer);
belle_sip_object_unref(cr->remote_composing_refresh_timer);
cr->remote_composing_refresh_timer = NULL;
}
}
/**
* Destroy a LinphoneChatRoom.
* @param cr #LinphoneChatRoom object
*/
void linphone_chat_room_destroy(LinphoneChatRoom *cr){
LinphoneCore *lc=cr->lc;
linphone_chat_room_delete_composing_idle_timer(cr);
linphone_chat_room_delete_composing_refresh_timer(cr);
linphone_chat_room_delete_remote_composing_refresh_timer(cr);
lc->chatrooms=ms_list_remove(lc->chatrooms,(void *) cr);
linphone_address_destroy(cr->peer_url);
ms_free(cr->peer);
......@@ -142,6 +175,12 @@ static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatM
msg->dir=LinphoneChatMessageOutgoing;
msg->from=linphone_address_new(identity);
msg->storage_id=linphone_chat_message_store(msg);
if (cr->is_composing == LinphoneIsComposingActive) {
cr->is_composing = LinphoneIsComposingIdle;
}
linphone_chat_room_delete_composing_idle_timer(cr);
linphone_chat_room_delete_composing_refresh_timer(cr);
}
/**
......@@ -225,6 +264,89 @@ void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessag
ms_free(from);
}
static int linphone_chat_room_remote_refresh_composing_expired(void *data, unsigned int revents) {
LinphoneChatRoom *cr = (LinphoneChatRoom *)data;
belle_sip_object_unref(cr->remote_composing_refresh_timer);
cr->remote_composing_refresh_timer = NULL;
cr->remote_is_composing = LinphoneIsComposingIdle;
if (cr->lc->vtable.is_composing_received != NULL)
cr->lc->vtable.is_composing_received(cr->lc, cr);
return BELLE_SIP_STOP;
}
static const char *iscomposing_prefix = "/xsi:isComposing";
static void process_im_is_composing_notification(LinphoneChatRoom *cr, xmlparsing_context_t *xml_ctx) {
char xpath_str[MAX_XPATH_LENGTH];
xmlXPathObjectPtr iscomposing_object;
const char *state_str = NULL;
const char *refresh_str = NULL;
int refresh_duration = COMPOSING_DEFAULT_REMOTE_REFRESH_TIMEOUT;
int i;
LinphoneIsComposingState state = LinphoneIsComposingIdle;
if (linphone_create_xml_xpath_context(xml_ctx) < 0) return;
xmlXPathRegisterNs(xml_ctx->xpath_ctx, (const xmlChar *)"xsi", (const xmlChar *)"urn:ietf:params:xml:ns:im-iscomposing");
iscomposing_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, iscomposing_prefix);
if ((iscomposing_object != NULL) && (iscomposing_object->nodesetval != NULL)) {
for (i = 1; i <= iscomposing_object->nodesetval->nodeNr; i++) {
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/xsi:state", iscomposing_prefix, i);
state_str = linphone_get_xml_text_content(xml_ctx, xpath_str);
if (state_str == NULL) continue;
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/xsi:refresh", iscomposing_prefix, i);
refresh_str = linphone_get_xml_text_content(xml_ctx, xpath_str);
}
}
if (state_str != NULL) {
if (strcmp(state_str, "active") == 0) {
state = LinphoneIsComposingActive;
if (refresh_str != NULL) {
refresh_duration = atoi(refresh_str);
}
if (!cr->remote_composing_refresh_timer) {
cr->remote_composing_refresh_timer = sal_create_timer(cr->lc->sal, linphone_chat_room_remote_refresh_composing_expired, cr, refresh_duration * 1000, "composing remote refresh timeout");
} else {
belle_sip_source_set_timeout(cr->remote_composing_refresh_timer, refresh_duration * 1000);
}
} else {
linphone_chat_room_delete_remote_composing_refresh_timer(cr);
}
cr->remote_is_composing = state;
if (cr->lc->vtable.is_composing_received != NULL)
cr->lc->vtable.is_composing_received(cr->lc, cr);
}
}
static void linphone_chat_room_notify_is_composing(LinphoneChatRoom *cr, const char *text) {
xmlparsing_context_t *xml_ctx = linphone_xmlparsing_context_new();
xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error);
xml_ctx->doc = xmlReadDoc((const unsigned char*)text, 0, NULL, 0);
if (xml_ctx->doc != NULL) {
process_im_is_composing_notification(cr, xml_ctx);
} else {
ms_warning("Wrongly formatted presence XML: %s", xml_ctx->errorBuffer);
}
linphone_xmlparsing_context_destroy(xml_ctx);
}
void linphone_core_is_composing_received(LinphoneCore *lc, SalOp *op, const SalIsComposing *is_composing) {
LinphoneChatRoom *cr = NULL;
LinphoneAddress *addr = linphone_address_new(is_composing->from);
linphone_address_clean(addr);
cr = linphone_core_get_chat_room(lc, addr);
if (cr != NULL) {
linphone_chat_room_notify_is_composing(cr, is_composing->text);
}
linphone_address_destroy(addr);
}
bool_t linphone_chat_room_is_remote_composing(const LinphoneChatRoom *cr) {
return (cr->remote_is_composing == LinphoneIsComposingActive) ? TRUE : FALSE;
}
/**
* Returns back pointer to LinphoneCore object.
**/
......@@ -319,6 +441,128 @@ void linphone_chat_room_send_message2(LinphoneChatRoom *cr, LinphoneChatMessage*
_linphone_chat_room_send_message(cr, msg);
}
static char * linphone_chat_room_create_is_composing_xml(LinphoneChatRoom *cr) {
xmlBufferPtr buf;
xmlTextWriterPtr writer;
int err;
char *content = NULL;
buf = xmlBufferCreate();
if (buf == NULL) {
ms_error("Error creating the XML buffer");
return content;
}
writer = xmlNewTextWriterMemory(buf, 0);
if (writer == NULL) {
ms_error("Error creating the XML writer");
return content;
}
err = xmlTextWriterStartDocument(writer, "1.0", "UTF-8", NULL);
if (err >= 0) {
err = xmlTextWriterStartElementNS(writer, NULL, (const xmlChar *)"isComposing", (const xmlChar *)"urn:ietf:params:xml:ns:im-iscomposing");
}
if (err >= 0) {
err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xmlns", (const xmlChar *)"xsi",
NULL, (const xmlChar *)"http://www.w3.org/2001/XMLSchema-instance");
}
if (err >= 0) {
err = xmlTextWriterWriteAttributeNS(writer, (const xmlChar *)"xsi", (const xmlChar *)"schemaLocation",
NULL, (const xmlChar *)"urn:ietf:params:xml:ns:im-composing iscomposing.xsd");
}
if (err >= 0) {
err = xmlTextWriterWriteElement(writer, (const xmlChar *)"state",
(cr->is_composing == LinphoneIsComposingActive) ? (const xmlChar *)"active" : (const xmlChar *)"idle");
}
if ((err >= 0) && (cr->is_composing == LinphoneIsComposingActive)) {
char refresh_str[4] = { 0 };
snprintf(refresh_str, sizeof(refresh_str), "%u", COMPOSING_DEFAULT_REFRESH_TIMEOUT);
err = xmlTextWriterWriteElement(writer, (const xmlChar *)"refresh", (const xmlChar *)refresh_str);
}
if (err >= 0) {
/* Close the "isComposing" element. */
err = xmlTextWriterEndElement(writer);
}
if (err >= 0) {
err = xmlTextWriterEndDocument(writer);
}
if (err > 0) {
/* xmlTextWriterEndDocument returns the size of the content. */
content = ms_strdup((char *)buf->content);
}
xmlFreeTextWriter(writer);
xmlBufferFree(buf);
return content;
}
static void linphone_chat_room_send_is_composing_notification(LinphoneChatRoom *cr) {
SalOp *op = NULL;
LinphoneCall *call;
const char *identity = NULL;
char *content = NULL;
if (lp_config_get_int(cr->lc->config, "sip", "chat_use_call_dialogs", 0)) {
if ((call = linphone_core_get_call_by_remote_address(cr->lc, cr->peer)) != NULL) {
if (call->state == LinphoneCallConnected ||
call->state == LinphoneCallStreamsRunning ||
call->state == LinphoneCallPaused ||
call->state == LinphoneCallPausing ||
call->state == LinphoneCallPausedByRemote) {
ms_message("send SIP message through the existing call.");
op = call->op;
identity = linphone_core_find_best_identity(cr->lc, linphone_call_get_remote_address(call));
}
}
}
if (op == NULL) {
LinphoneProxyConfig *proxy = linphone_core_lookup_known_proxy(cr->lc, cr->peer_url);
if (proxy)
identity = linphone_proxy_config_get_identity(proxy);
else
identity = linphone_core_get_primary_contact(cr->lc);
/*sending out of calls*/
op = sal_op_new(cr->lc->sal);
linphone_configure_op(cr->lc, op, cr->peer_url, NULL, lp_config_get_int(cr->lc->config, "sip", "chat_msg_with_contact", 0));
}
content = linphone_chat_room_create_is_composing_xml(cr);
if (content != NULL) {
sal_message_send(op, identity, cr->peer, "application/im-iscomposing+xml", content);
ms_free(content);
}
}
static int linphone_chat_room_stop_composing(void *data, unsigned int revents) {
LinphoneChatRoom *cr = (LinphoneChatRoom *)data;
cr->is_composing = LinphoneIsComposingIdle;
linphone_chat_room_send_is_composing_notification(cr);
linphone_chat_room_delete_composing_refresh_timer(cr);
belle_sip_object_unref(cr->composing_idle_timer);
cr->composing_idle_timer = NULL;
return BELLE_SIP_STOP;
}
static int linphone_chat_room_refresh_composing(void *data, unsigned int revents) {
LinphoneChatRoom *cr = (LinphoneChatRoom *)data;
linphone_chat_room_send_is_composing_notification(cr);
return BELLE_SIP_CONTINUE;
}
void linphone_chat_room_compose(LinphoneChatRoom *cr) {
if (cr->is_composing == LinphoneIsComposingIdle) {
cr->is_composing = LinphoneIsComposingActive;
linphone_chat_room_send_is_composing_notification(cr);
if (!cr->composing_refresh_timer) {
cr->composing_refresh_timer = sal_create_timer(cr->lc->sal, linphone_chat_room_refresh_composing, cr, COMPOSING_DEFAULT_REFRESH_TIMEOUT * 1000, "composing refresh timeout");
} else {
belle_sip_source_set_timeout(cr->composing_refresh_timer, COMPOSING_DEFAULT_REFRESH_TIMEOUT * 1000);
}
if (!cr->composing_idle_timer) {
cr->composing_idle_timer = sal_create_timer(cr->lc->sal, linphone_chat_room_stop_composing, cr, COMPOSING_DEFAULT_IDLE_TIMEOUT * 1000, "composing idle timeout");
}
}
belle_sip_source_set_timeout(cr->composing_idle_timer, COMPOSING_DEFAULT_IDLE_TIMEOUT * 1000);
}
/**
* Returns a #LinphoneChatMessageState as a string.
*/
......
......@@ -977,6 +977,20 @@ LINPHONE_PUBLIC MSList *linphone_chat_room_get_history(LinphoneChatRoom *cr,int
LINPHONE_PUBLIC void linphone_chat_room_mark_as_read(LinphoneChatRoom *cr);
LINPHONE_PUBLIC void linphone_chat_room_delete_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg);
LINPHONE_PUBLIC void linphone_chat_room_delete_history(LinphoneChatRoom *cr);
/**
* Notify the destination of the chat message being composed that the user is typing a new message.
* @param[in] cr The #LinphoneChatRoom object corresponding to the conversation for which a new message is being typed.
*/
LINPHONE_PUBLIC void linphone_chat_room_compose(LinphoneChatRoom *cr);
/**
* Tells whether the remote is currently composing a message.
* @param[in] cr The "LinphoneChatRoom object corresponding to the conversation.
* @return TRUE if the remote is currently composing a message, FALSE otherwise.
*/
LINPHONE_PUBLIC bool_t linphone_chat_room_is_remote_composing(const LinphoneChatRoom *cr);
LINPHONE_PUBLIC int linphone_chat_room_get_unread_messages_count(LinphoneChatRoom *cr);
LINPHONE_PUBLIC LinphoneCore* linphone_chat_room_get_lc(LinphoneChatRoom *cr);
LINPHONE_PUBLIC void linphone_chat_room_set_user_data(LinphoneChatRoom *cr, void * ud);
......@@ -1130,6 +1144,14 @@ typedef void (*LinphoneCoreTextMessageReceivedCb)(LinphoneCore *lc, LinphoneChat
*/
typedef void (*LinphoneCoreMessageReceivedCb)(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *message);
/**
* Is composing notification callback prototype.
*
* @param[in] lc #LinphoneCore object
* @param[in] room #LinphoneChatRoom involved in the conversation.
*/
typedef void (*LinphoneCoreIsComposingReceivedCb)(LinphoneCore *lc, LinphoneChatRoom *room);
/**
* Callback for being notified of DTMFs received.
* @param lc the linphone core
......@@ -1179,6 +1201,7 @@ typedef struct _LinphoneCoreVTable{
LinphoneCoreAuthInfoRequestedCb auth_info_requested; /**< Ask the application some authentication information */
LinphoneCoreCallLogUpdatedCb call_log_updated; /**< Notifies that call log list has been updated */
LinphoneCoreMessageReceivedCb message_received; /** a message is received, can be text or external body*/
LinphoneCoreIsComposingReceivedCb is_composing_received; /**< An is-composing notification has been received */
LinphoneCoreDtmfReceivedCb dtmf_received; /**< A dtmf has been received received */
LinphoneCoreReferReceivedCb refer_received; /**< An out of call refer was received */
LinphoneCoreCallEncryptionChangedCb call_encryption_changed; /**<Notifies on change in the encryption of call streams */
......
......@@ -22,15 +22,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "lpconfig.h"
#include "linphonepresence.h"
#include <libxml/xmlreader.h>
#include <libxml/xmlwriter.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#define XMLPARSING_BUFFER_LEN 2048
#define MAX_XPATH_LENGTH 256
extern const char *__policy_enum_to_str(LinphoneSubscribePolicy pol);
......@@ -83,13 +74,6 @@ struct _LinphonePresenceModel {
MSList *notes; /**< A list of _LinphonePresenceNote structures. */
};
typedef struct _xmlparsing_context {
xmlDoc *doc;
xmlXPathContextPtr xpath_ctx;
char errorBuffer[XMLPARSING_BUFFER_LEN];
char warningBuffer[XMLPARSING_BUFFER_LEN];
} xmlparsing_context_t;
static const char *person_prefix = "/pidf:presence/dm:person";
......@@ -98,38 +82,6 @@ static const char *person_prefix = "/pidf:presence/dm:person";
* PRIVATE FUNCTIONS *
****************************************************************************/
static xmlparsing_context_t * xmlparsing_context_new() {
xmlparsing_context_t *xmlCtx = (xmlparsing_context_t *)malloc(sizeof(xmlparsing_context_t));
if (xmlCtx != NULL) {
xmlCtx->doc = NULL;
xmlCtx->xpath_ctx = NULL;
xmlCtx->errorBuffer[0] = '\0';
xmlCtx->warningBuffer[0] = '\0';
}
return xmlCtx;
}
static void xmlparsing_context_destroy(xmlparsing_context_t *ctx) {
if (ctx->doc != NULL) {
xmlFreeDoc(ctx->doc);
ctx->doc = NULL;
}
if (ctx->xpath_ctx != NULL) {
xmlXPathFreeContext(ctx->xpath_ctx);
ctx->xpath_ctx = NULL;
}
free(ctx);
}
static void xmlparsing_genericxml_error(void *ctx, const char *fmt, ...) {
xmlparsing_context_t *xmlCtx = (xmlparsing_context_t *)ctx;
int sl = strlen(xmlCtx->errorBuffer);
va_list args;
va_start(args, fmt);
vsnprintf(xmlCtx->errorBuffer + sl, XMLPARSING_BUFFER_LEN - sl, fmt, args);
va_end(args);
}
static char presence_id_valid_characters[] = "0123456789abcdefghijklmnopqrstuvwxyz";
static char * generate_presence_id(void) {
......@@ -1183,45 +1135,6 @@ void * linphone_presence_note_get_user_data(LinphonePresenceNote *note) {
* XML PRESENCE INTERNAL HANDLING *
****************************************************************************/
static int create_xml_xpath_context(xmlparsing_context_t *xml_ctx) {
if (xml_ctx->xpath_ctx != NULL) {
xmlXPathFreeContext(xml_ctx->xpath_ctx);
}
xml_ctx->xpath_ctx = xmlXPathNewContext(xml_ctx->doc);
if (xml_ctx->xpath_ctx == NULL) return -1;
return 0;
}
static char * get_xml_text_content(xmlparsing_context_t *xml_ctx, const char *xpath_expression) {
xmlXPathObjectPtr xpath_obj;
xmlChar *text = NULL;
int i;
xpath_obj = xmlXPathEvalExpression((const xmlChar *)xpath_expression, xml_ctx->xpath_ctx);
if (xpath_obj != NULL) {
if (xpath_obj->nodesetval != NULL) {
xmlNodeSetPtr nodes = xpath_obj->nodesetval;
for (i = 0; i < nodes->nodeNr; i++) {
xmlNodePtr node = nodes->nodeTab[i];
if (node->children != NULL) {
text = xmlNodeListGetString(xml_ctx->doc, node->children, 1);
}
}
}
xmlXPathFreeObject(xpath_obj);
}
return (char *)text;
}
static void free_xml_text_content(const char *text) {
xmlFree((xmlChar *)text);
}
static xmlXPathObjectPtr get_xml_xpath_object_for_node_list(xmlparsing_context_t *xml_ctx, const char *xpath_expression) {
return xmlXPathEvalExpression((const xmlChar *)xpath_expression, xml_ctx->xpath_ctx);
}
static const char *service_prefix = "/pidf:presence/pidf:tuple";
static int process_pidf_xml_presence_service_notes(xmlparsing_context_t *xml_ctx, LinphonePresenceService *service, unsigned int service_idx) {
......@@ -1233,19 +1146,19 @@ static int process_pidf_xml_presence_service_notes(xmlparsing_context_t *xml_ctx
int i;
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:note", service_prefix, service_idx);
note_object = get_xml_xpath_object_for_node_list(xml_ctx, xpath_str);
note_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, xpath_str);
if ((note_object != NULL) && (note_object->nodesetval != NULL)) {
for (i = 1; i <= note_object->nodesetval->nodeNr; i++) {
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:note[%i]", service_prefix, service_idx, i);
note_str = get_xml_text_content(xml_ctx, xpath_str);
note_str = linphone_get_xml_text_content(xml_ctx, xpath_str);
if (note_str == NULL) continue;
snprintf(xpath_str, sizeof(xpath_str), "%s[%i]/pidf:note[%i]/@xml:lang", service_prefix, service_idx, i);
lang = get_xml_text_content(xml_ctx, xpath_str);
lang = linphone_get_xml_text_content(xml_ctx, xpath_str);
note = linphone_presence_note_new(note_str, lang);
presence_service_add_note(service, note);
if (lang != NULL) free_xml_text_content(lang);
free_xml_text_content(note_str);
if (lang != NULL) linphone_free_xml_text_content(lang);
linphone_free_xml_text_content(note_str);
}
}
if (note_object != NULL) xmlXPathFreeObject(note_object);
......@@ -1264,11 +1177,11 @@ static int process_pidf_xml_presence_services(xmlparsing_context_t *xml_ctx, Lin
LinphonePresenceBasicStatus basic_status;
int i;