Commit 0babed04 authored by Ghislain MARY's avatar Ghislain MARY Committed by Ronan

Use ChatRoom C++ class.

parent 95c781ec
......@@ -29,22 +29,24 @@
#include "ortp/b64.h"
#include "linphone/wrapper_utils.h"
#include "chat/chat-room.h"
#include "chat/chat-room-p.h"
#include "utils/content-type.h"
#include "utils/utils.h"
#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xmlwriter.h>
#define COMPOSING_DEFAULT_IDLE_TIMEOUT 15
#define COMPOSING_DEFAULT_REFRESH_TIMEOUT 60
#define COMPOSING_DEFAULT_REMOTE_REFRESH_TIMEOUT 120
struct _LinphoneChatRoom{
belle_sip_object_t base;
void *user_data;
LinphonePrivate::ChatRoom *cr;
};
BELLE_SIP_DECLARE_VPTR_NO_EXPORT(LinphoneChatRoom);
static void linphone_chat_message_release(LinphoneChatMessage *msg);
static void linphone_chat_room_delete_composing_idle_timer(LinphoneChatRoom *cr);
static void linphone_chat_room_delete_composing_refresh_timer(LinphoneChatRoom *cr);
static void linphone_chat_room_delete_remote_composing_refresh_timer(LinphoneChatRoom *cr);
static void _linphone_chat_message_destroy(LinphoneChatMessage *msg);
static void linphone_chat_room_notify_is_composing(LinphoneChatRoom *cr, const char *text);
static void linphone_chat_room_notify_imdn(LinphoneChatRoom *cr, const char *text);
static void linphone_chat_message_deactivate(LinphoneChatMessage *msg);
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneChatMessageCbs);
......@@ -116,31 +118,6 @@ void linphone_chat_message_cbs_set_file_transfer_progress_indication(
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneChatMessage);
static void _linphone_chat_room_destroy(LinphoneChatRoom *cr) {
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);
bctbx_list_free_with_data(cr->transient_messages, (bctbx_list_free_func)linphone_chat_message_release);
if (cr->weak_messages != NULL) bctbx_list_free(cr->weak_messages);
if (cr->received_rtt_characters) {
cr->received_rtt_characters = bctbx_list_free_with_data(cr->received_rtt_characters, (bctbx_list_free_func)ms_free);
}
if (cr->lc != NULL) {
if (bctbx_list_find(cr->lc->chatrooms, cr)) {
ms_error("LinphoneChatRoom[%p] is destroyed while still being used by the LinphoneCore. This is abnormal."
" linphone_core_get_chat_room() doesn't give a reference, there is no need to call "
"linphone_chat_room_unref(). "
"In order to remove a chat room from the core, use linphone_core_delete_chat_room().",
cr);
cr->lc->chatrooms = bctbx_list_remove(cr->lc->chatrooms, cr);
}
}
linphone_address_unref(cr->peer_url);
if (cr->pending_message)
linphone_chat_message_destroy(cr->pending_message);
ms_free(cr->peer);
}
void linphone_chat_message_set_state(LinphoneChatMessage *msg, LinphoneChatMessageState state) {
/* do not invoke callbacks on orphan messages */
if (state != msg->state && msg->chat_room != NULL) {
......@@ -184,24 +161,25 @@ const bctbx_list_t *linphone_core_get_chat_rooms(LinphoneCore *lc) {
}
static bool_t linphone_chat_room_matches(LinphoneChatRoom *cr, const LinphoneAddress *from) {
return linphone_address_weak_equal(cr->peer_url, from);
return linphone_address_weak_equal(cr->cr->getPeerAddress(), from);
}
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneChatRoom);
static void _linphone_chat_room_destroy(LinphoneChatRoom *cr) {
delete cr->cr;
}
BELLE_SIP_INSTANCIATE_VPTR(LinphoneChatRoom, belle_sip_object_t,
(belle_sip_object_destroy_t)_linphone_chat_room_destroy,
NULL, // clone
NULL, // marshal
FALSE);
static LinphoneChatRoom *_linphone_core_create_chat_room_base(LinphoneCore *lc, LinphoneAddress *addr){
LinphoneChatRoom *_linphone_core_create_chat_room_base(LinphoneCore *lc, LinphoneAddress *addr){
LinphoneChatRoom *cr = belle_sip_object_new(LinphoneChatRoom);
cr->lc = lc;
cr->peer = linphone_address_as_string(addr);
cr->peer_url = addr;
cr->unread_count = -1;
cr->received_rtt_characters = NULL;
cr->cr = new LinphonePrivate::ChatRoom(lc, addr);
cr->cr->getPrivate()->setCBackPointer(cr);
return cr;
}
......@@ -214,7 +192,7 @@ static LinphoneChatRoom *_linphone_core_create_chat_room(LinphoneCore *lc, Linph
LinphoneChatRoom *_linphone_core_create_chat_room_from_call(LinphoneCall *call){
LinphoneChatRoom *cr = _linphone_core_create_chat_room_base(call->core,
linphone_address_clone(linphone_call_get_remote_address(call)));
cr->call = call;
linphone_chat_room_set_call(cr, call);
return cr;
}
......@@ -265,7 +243,7 @@ LinphoneChatRoom *linphone_core_get_chat_room(LinphoneCore *lc, const LinphoneAd
void linphone_core_delete_chat_room(LinphoneCore *lc, LinphoneChatRoom *cr) {
if (bctbx_list_find(lc->chatrooms, cr)) {
lc->chatrooms = bctbx_list_remove(cr->lc->chatrooms, cr);
lc->chatrooms = bctbx_list_remove(lc->chatrooms, cr);
linphone_chat_room_delete_history(cr);
linphone_chat_room_unref(cr);
} else {
......@@ -277,57 +255,12 @@ LinphoneChatRoom *linphone_core_get_chat_room_from_uri(LinphoneCore *lc, const c
return _linphone_core_get_or_create_chat_room(lc, to);
}
static void linphone_chat_room_delete_composing_idle_timer(LinphoneChatRoom *cr) {
if (cr->composing_idle_timer) {
if (cr->lc && cr->lc->sal)
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) {
if (cr->lc && cr->lc->sal)
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) {
if (cr->lc && cr->lc->sal)
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;
}
}
void linphone_chat_room_destroy(LinphoneChatRoom *cr) {
linphone_chat_room_unref(cr);
}
void linphone_chat_room_release(LinphoneChatRoom *cr) {
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);
bctbx_list_for_each(cr->weak_messages, (bctbx_list_iterate_func)linphone_chat_message_deactivate);
bctbx_list_for_each(cr->transient_messages, (bctbx_list_iterate_func)linphone_chat_message_deactivate);
cr->lc = NULL;
linphone_chat_room_unref(cr);
}
static void on_weak_message_destroy(void *obj, belle_sip_object_t *message_being_destroyed) {
LinphoneChatRoom *cr = (LinphoneChatRoom *)obj;
cr->weak_messages = bctbx_list_remove(cr->weak_messages, message_being_destroyed);
}
void linphone_chat_room_add_weak_message(LinphoneChatRoom *cr, LinphoneChatMessage *cm) {
bctbx_list_t *item = bctbx_list_find(cr->weak_messages, cm);
if (item == NULL) {
cr->weak_messages = bctbx_list_append(cr->weak_messages, belle_sip_object_weak_ref(cm, on_weak_message_destroy, cr));
}
cr->cr->getPrivate()->release();
}
LinphoneChatRoom *linphone_chat_room_ref(LinphoneChatRoom *cr) {
......@@ -347,183 +280,8 @@ void linphone_chat_room_set_user_data(LinphoneChatRoom *cr, void *ud) {
cr->user_data = ud;
}
void linphone_chat_room_add_transient_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
if (bctbx_list_find(msg->chat_room->transient_messages, msg) == NULL) {
cr->transient_messages = bctbx_list_append(cr->transient_messages, linphone_chat_message_ref(msg));
}
}
void linphone_chat_room_remove_transient_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
if (bctbx_list_find(msg->chat_room->transient_messages, msg) != NULL) {
cr->transient_messages = bctbx_list_remove(cr->transient_messages, msg);
linphone_chat_message_unref(msg);
}
}
static void store_or_update_chat_message(LinphoneChatMessage *msg) {
if (msg->storage_id != 0) {
/* The message has already been stored (probably because of file transfer), update it */
linphone_chat_message_store_update(msg);
} else {
/* Store the new message */
msg->storage_id = linphone_chat_message_store(msg);
}
}
void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
int retval = -1;
LinphoneCore *lc = cr->lc;
LinphoneImEncryptionEngine *imee = lc->im_encryption_engine;
/*stubed rtt text*/
if (cr->call && linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(cr->call))) {
uint32_t new_line = 0x2028;
linphone_chat_message_put_char(msg, new_line); // New Line
linphone_chat_message_unref(msg);
return;
}
msg->dir = LinphoneChatMessageOutgoing;
/* Check if we shall upload a file to a server */
if (msg->file_transfer_information != NULL && msg->content_type == NULL) {
/* open a transaction with the server and send an empty request(RCS5.1 section 3.5.4.8.3.1) */
if (linphone_chat_room_upload_file(msg) == 0) {
/* Add to transient list only if message is going out */
linphone_chat_room_add_transient_message(cr, msg);
/* Store the message so that even if the upload is stopped, it can be done again */
msg->storage_id = linphone_chat_message_store(msg);
} else {
linphone_chat_message_unref(msg);
return;
}
} else {
SalOp *op = msg->op;
LinphoneCall *call=NULL;
char *content_type;
const char *identity = NULL;
char *clear_text_message = NULL;
char *clear_text_content_type = NULL;
if (msg->message) {
clear_text_message = ms_strdup(msg->message);
}
if (msg->content_type) {
clear_text_content_type = ms_strdup(msg->content_type);
}
/* Add to transient list */
linphone_chat_room_add_transient_message(cr, msg);
msg->time = ms_time(0);
if (lp_config_get_int(cr->lc->config, "sip", "chat_use_call_dialogs", 0) != 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 msg through the existing call.");
op = call->op;
identity = linphone_core_find_best_identity(cr->lc, linphone_call_get_remote_address(call));
}
}
}
if (!identity) {
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);
}
}
if (msg->from){
/*
* BUG
* the file transfer message constructor sets the from, but doesn't do it as well as here.
*/
linphone_address_unref(msg->from);
}
msg->from = linphone_address_new(identity);
if (imee) {
LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee);
LinphoneImEncryptionEngineCbsOutgoingMessageCb cb_process_outgoing_message = linphone_im_encryption_engine_cbs_get_process_outgoing_message(imee_cbs);
if (cb_process_outgoing_message) {
retval = cb_process_outgoing_message(imee, cr, msg);
if(retval == 0) {
msg->is_secured = TRUE;
}
}
}
if (op == NULL) {
/*sending out of calls*/
msg->op = op = sal_op_new(cr->lc->sal);
linphone_configure_op(cr->lc, op, cr->peer_url, msg->custom_headers,
lp_config_get_int(cr->lc->config, "sip", "chat_msg_with_contact", 0));
sal_op_set_user_pointer(op, msg); /*if out of call, directly store msg*/
}
if (retval > 0) {
sal_error_info_set((SalErrorInfo *)sal_op_get_error_info(op), SalReasonNotAcceptable, "SIP", retval, "Unable to encrypt IM", NULL);
store_or_update_chat_message(msg);
linphone_chat_message_update_state(msg, LinphoneChatMessageStateNotDelivered);
linphone_chat_message_unref(msg);
return;
}
if (msg->external_body_url) {
content_type = ms_strdup_printf("message/external-body; access-type=URL; URL=\"%s\"", msg->external_body_url);
sal_message_send(op, identity, cr->peer, content_type, NULL, NULL);
ms_free(content_type);
} else {
char *peer_uri = linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
const char *content_type = msg->content_type;
if (content_type == NULL) {
sal_text_send(op, identity, cr->peer, msg->message);
} else {
sal_message_send(op, identity, cr->peer, content_type, msg->message, peer_uri);
}
ms_free(peer_uri);
}
if (msg->message && clear_text_message && strcmp(msg->message, clear_text_message) != 0) {
// We replace the encrypted message by the original one so it can be correctly stored and displayed by the application
ms_free(msg->message);
msg->message = ms_strdup(clear_text_message);
}
if (msg->content_type && clear_text_content_type && (strcmp(msg->content_type, clear_text_content_type) != 0)) {
/* We replace the encrypted content type by the original one */
ms_free(msg->content_type);
msg->content_type = ms_strdup(clear_text_content_type);
}
msg->message_id = ms_strdup(sal_op_get_call_id(op)); /* must be known at that time */
store_or_update_chat_message(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);
if (clear_text_message) {
ms_free(clear_text_message);
}
if (clear_text_content_type) {
ms_free(clear_text_content_type);
}
if (call && call->op == op) {
/*In this case, chat delivery status is not notified, so unrefing chat message right now*/
/*Might be better fixed by delivering status, but too costly for now*/
linphone_chat_room_remove_transient_message(msg->chat_room, msg);
linphone_chat_message_unref(msg);
return;
}
}
// if operation failed, we should not change message state
if (msg->dir == LinphoneChatMessageOutgoing) {
linphone_chat_message_set_state(msg, LinphoneChatMessageStateInProgress);
}
cr->cr->getPrivate()->removeTransientMessage(msg);
}
void linphone_chat_message_update_state(LinphoneChatMessage *msg, LinphoneChatMessageState new_state) {
......@@ -531,50 +289,15 @@ void linphone_chat_message_update_state(LinphoneChatMessage *msg, LinphoneChatMe
linphone_chat_message_store_state(msg);
if (msg->state == LinphoneChatMessageStateDelivered || msg->state == LinphoneChatMessageStateNotDelivered) {
if (bctbx_list_find(msg->chat_room->transient_messages, msg) != NULL) {
// msg is not transient anymore, we can remove it from our transient list and unref it
linphone_chat_room_add_weak_message(msg->chat_room, msg);
linphone_chat_room_remove_transient_message(msg->chat_room, msg);
} else {
// msg has already been removed from the transient messages, do nothing. */
}
msg->chat_room->cr->getPrivate()->moveTransientMessageToWeakMessages(msg);
}
}
void linphone_chat_room_send_message(LinphoneChatRoom *cr, const char *msg) {
_linphone_chat_room_send_message(cr, linphone_chat_room_create_message(cr, msg));
cr->cr->sendMessage(cr->cr->createMessage(msg));
}
static bool_t is_file_transfer(const char *content_type) {
return (strcmp("application/vnd.gsma.rcs-ft-http+xml", content_type) == 0);
}
static bool_t is_im_iscomposing(const char* content_type) {
return (strcmp("application/im-iscomposing+xml", content_type) == 0);
}
static bool_t is_imdn(const char *content_type) {
return (strcmp("message/imdn+xml", content_type) == 0);
}
static bool_t is_text(const char *content_type) {
return (strcmp("text/plain", content_type) == 0);
}
void linphone_chat_room_message_received(LinphoneChatRoom *cr, LinphoneCore *lc, LinphoneChatMessage *msg) {
if (msg->message) {
/*legacy API*/
linphone_core_notify_text_message_received(lc, cr, msg->from, msg->message);
}
linphone_core_notify_message_received(lc, cr, msg);
if(!is_imdn(msg->content_type) && !is_im_iscomposing(msg->content_type)) {
cr->remote_is_composing = LinphoneIsComposingIdle;
linphone_core_notify_is_composing_received(cr->lc, cr);
linphone_chat_message_send_delivery_notification(msg, LinphoneReasonNone);
}
}
static void create_file_transfer_information_from_vnd_gsma_rcs_ft_http_xml(LinphoneChatMessage *msg) {
void create_file_transfer_information_from_vnd_gsma_rcs_ft_http_xml(LinphoneChatMessage *msg) {
xmlChar *file_url = NULL;
xmlDocPtr xmlMessageBody;
xmlNodePtr cur;
......@@ -653,285 +376,17 @@ static void create_file_transfer_information_from_vnd_gsma_rcs_ft_http_xml(Linph
xmlFree(file_url);
}
LinphoneReason linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessage *sal_msg) {
LinphoneChatRoom *cr = NULL;
LinphoneAddress *addr;
LinphoneAddress *to;
LinphoneChatMessage *msg = NULL;
LinphoneImEncryptionEngine *imee = lc->im_encryption_engine;
const SalCustomHeader *ch;
LinphoneReason reason = LinphoneReasonNone;
int retval = -1;
bool_t increase_msg_count = TRUE;
addr = linphone_address_new(sal_msg->from);
int linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessage *sal_msg) {
LinphoneAddress *addr = linphone_address_new(sal_msg->from);
linphone_address_clean(addr);
cr = linphone_core_get_chat_room(lc, addr);
/* Check if this is a duplicate message */
if ((msg = linphone_chat_room_find_message_with_dir(cr, sal_op_get_call_id(op), LinphoneChatMessageIncoming))) {
reason = lc->chat_deny_code;
goto end;
}
msg = linphone_chat_room_create_message(cr, sal_msg->text);
linphone_chat_message_set_content_type(msg, sal_msg->content_type);
linphone_chat_message_set_from(msg, cr->peer_url);
to = sal_op_get_to(op) ? linphone_address_new(sal_op_get_to(op)) : linphone_address_new(linphone_core_get_identity(lc));
msg->to = to;
msg->time = sal_msg->time;
msg->state = LinphoneChatMessageStateDelivered;
msg->dir = LinphoneChatMessageIncoming;
msg->message_id = ms_strdup(sal_op_get_call_id(op));
ch = sal_op_get_recv_custom_header(op);
if (ch) {
msg->custom_headers = sal_custom_header_clone(ch);
}
if (sal_msg->url) {
linphone_chat_message_set_external_body_url(msg, sal_msg->url);
}
if (imee) {
LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee);
LinphoneImEncryptionEngineCbsIncomingMessageCb cb_process_incoming_message = linphone_im_encryption_engine_cbs_get_process_incoming_message(imee_cbs);
if (cb_process_incoming_message) {
retval = cb_process_incoming_message(imee, cr, msg);
if(retval == 0) {
msg->is_secured = TRUE;
} else if(retval > 0) {
// Unable to decrypt message
linphone_core_notify_message_received_unable_decrypt(cr->lc, cr, msg);
reason = linphone_error_code_to_reason(retval);
linphone_chat_message_send_delivery_notification(msg, reason);
// return LinphoneReasonNone to avoid flexisip resending us a message we can't decrypt
reason = LinphoneReasonNone;
goto end;
}
}
}
if ((retval <= 0) && (linphone_core_is_content_type_supported(lc, msg->content_type) == FALSE)) {
retval = 415;
ms_error("Unsupported MESSAGE (content-type %s not recognized)", msg->content_type);
}
if (retval > 0) {
reason = linphone_error_code_to_reason(retval);
linphone_chat_message_send_delivery_notification(msg, reason);
goto end;
}
if (is_file_transfer(msg->content_type)) {
create_file_transfer_information_from_vnd_gsma_rcs_ft_http_xml(msg);
linphone_chat_message_set_to_be_stored(msg, TRUE);
} else if (is_im_iscomposing(msg->content_type)) {
linphone_chat_room_notify_is_composing(cr, msg->message);
linphone_chat_message_set_to_be_stored(msg, FALSE);
increase_msg_count = FALSE;
if(lp_config_get_int(cr->lc->config, "sip", "deliver_imdn", 0) != 1) {
goto end;
}
} else if (is_imdn(msg->content_type)) {
linphone_chat_room_notify_imdn(cr, msg->message);
linphone_chat_message_set_to_be_stored(msg, FALSE);
increase_msg_count = FALSE;
if(lp_config_get_int(cr->lc->config, "sip", "deliver_imdn", 0) != 1) {
goto end;
}
} else if (is_text(msg->content_type)) {
linphone_chat_message_set_to_be_stored(msg, TRUE);
}
if (increase_msg_count == TRUE) {
if (cr->unread_count < 0)
cr->unread_count = 1;
else
cr->unread_count++;
/* Mark the message as pending so that if linphone_core_chat_room_mark_as_read() is called
in the linphone_chat_room_message_received() callback, it will effectively be marked as
being read before being stored. */
cr->pending_message = msg;
}
linphone_chat_room_message_received(cr, lc, msg);
if(linphone_chat_message_get_to_be_stored(msg)) {
msg->storage_id = linphone_chat_message_store(msg);
}
cr->pending_message = NULL;
end:
LinphoneChatRoom *cr = linphone_core_get_chat_room(lc, addr);
LinphoneReason reason = cr->cr->getPrivate()->messageReceived(op, sal_msg);
linphone_address_unref(addr);
if (msg != NULL) linphone_chat_message_unref(msg);
return reason;
}
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;
linphone_core_notify_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;
char *state_str = NULL;
char *refresh_str = NULL;
int refresh_duration = lp_config_get_int(cr->lc->config, "sip", "composing_remote_refresh_timeout",
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) {
if (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);
}
}
xmlXPathFreeObject(iscomposing_object);
}
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;
linphone_core_notify_is_composing_received(cr->lc, cr);
linphone_free_xml_text_content(state_str);
}
if (refresh_str != NULL) {
linphone_free_xml_text_content(refresh_str);
}
}
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