Commit b297a4cb authored by Ghislain MARY's avatar Ghislain MARY

Rework chat handling.

 - Add content type information in LIME encrypted message
 - Single entry point for all types of chat messages that are first decrypted if necessary and then handled according to their content type
 - Add possibility to send chat messages with a content type that is not 'text/plain'
 - Encrypt IMDN
parent 1c7fc21b
......@@ -591,12 +591,8 @@ void sal_set_callbacks(Sal *ctx, const SalCallbacks *cbs){
ctx->callbacks.notify_presence=(SalOnNotifyPresence)unimplemented_stub;
if (ctx->callbacks.subscribe_presence_received==NULL)
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.imdn_received == NULL)
ctx->callbacks.imdn_received = (SalOnImdnReceived)unimplemented_stub;
if (ctx->callbacks.message_received==NULL)
ctx->callbacks.message_received=(SalOnMessageReceived)unimplemented_stub;
if (ctx->callbacks.ping_reply==NULL)
ctx->callbacks.ping_reply=(SalOnPingReply)unimplemented_stub;
if (ctx->callbacks.auth_requested==NULL)
......@@ -618,6 +614,7 @@ void sal_uninit(Sal* sal){
belle_sip_object_unref(sal->listener);
if (sal->supported) belle_sip_object_unref(sal->supported);
bctbx_list_free_with_data(sal->supported_tags,ms_free);
bctbx_list_free_with_data(sal->supported_content_types, ms_free);
if (sal->uuid) ms_free(sal->uuid);
if (sal->root_ca) ms_free(sal->root_ca);
if (sal->root_ca_data) ms_free(sal->root_ca_data);
......@@ -1500,4 +1497,17 @@ void *sal_get_stack_impl(Sal *sal) {
return sal->stack;
}
bool_t sal_is_content_type_supported(const Sal *sal, const char *content_type) {
bctbx_list_t *item;
for (item = sal->supported_content_types; item != NULL; item = bctbx_list_next(item)) {
const char *item_content_type = (const char *)bctbx_list_get_data(item);
if (strcmp(item_content_type, content_type) == 0) return TRUE;
}
return FALSE;
}
void sal_add_content_type_support(Sal *sal, const char *content_type) {
if ((content_type != NULL) && (sal_is_content_type_supported(sal, content_type) == FALSE)) {
sal->supported_content_types = bctbx_list_append(sal->supported_content_types, ms_strdup(content_type));
}
}
......@@ -55,6 +55,7 @@ struct Sal{
SalOpSDPHandling default_sdp_handling;
bool_t pending_trans_checking; /*testing purpose*/
void *ssl_config;
bctbx_list_t *supported_content_types; /* list of char* */
};
typedef enum SalOpState {
......@@ -178,4 +179,5 @@ void _sal_op_add_custom_headers(SalOp *op, belle_sip_message_t *msg);
SalSubscribeStatus belle_sip_message_get_subscription_state(const belle_sip_message_t *msg);
#endif /* SAL_IMPL_H_ */
......@@ -24,7 +24,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
static void process_error( SalOp* op) {
if (op->dir == SalOpDirOutgoing) {
op->base.root->callbacks.text_delivery_update(op, SalTextDeliveryFailed);
op->base.root->callbacks.message_delivery_update(op, SalMessageDeliveryFailed);
} else {
ms_warning("unexpected io error for incoming message on op [%p]",op);
}
......@@ -46,35 +46,39 @@ static void process_timeout(void *user_ctx, const belle_sip_timeout_event_t *eve
static void process_response_event(void *op_base, const belle_sip_response_event_t *event){
SalOp* op = (SalOp*)op_base;
int code = belle_sip_response_get_status_code(belle_sip_response_event_get_response(event));
SalTextDeliveryStatus status;
SalMessageDeliveryStatus status;
sal_op_set_error_info_from_response(op,belle_sip_response_event_get_response(event));
if (code>=100 && code <200)
status=SalTextDeliveryInProgress;
status=SalMessageDeliveryInProgress;
else if (code>=200 && code <300)
status=SalTextDeliveryDone;
status=SalMessageDeliveryDone;
else
status=SalTextDeliveryFailed;
status=SalMessageDeliveryFailed;
op->base.root->callbacks.text_delivery_update(op,status);
op->base.root->callbacks.message_delivery_update(op,status);
}
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 bool_t is_imdn_xml(belle_sip_header_content_type_t *content_type) {
return (strcmp("message", belle_sip_header_content_type_get_type(content_type)) == 0)
&& (strcmp("imdn+xml", belle_sip_header_content_type_get_subtype(content_type)) == 0);
}
static void add_message_accept(SalOp *op, belle_sip_message_t *msg) {
bctbx_list_t *item;
const char *str;
char *old;
char *header = ms_strdup("xml/cipher, application/cipher.vnd.gsma.rcs-ft-http+xml");
for (item = op->base.root->supported_content_types; item != NULL; item = bctbx_list_next(item)) {
str = (const char *)bctbx_list_get_data(item);
old = header;
header = ms_strdup_printf("%s, %s", old, str);
ms_free(old);
}
static void add_message_accept(belle_sip_message_t *msg){
belle_sip_message_add_header(msg,belle_sip_header_create("Accept","text/plain, message/external-body, application/im-iscomposing+xml, xml/cipher, application/vnd.gsma.rcs-ft-http+xml, application/cipher.vnd.gsma.rcs-ft-http+xml, message/imdn+xml"));
belle_sip_message_add_header(msg, belle_sip_header_create("Accept", header));
ms_free(header);
}
void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *event){
......@@ -95,72 +99,45 @@ void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *eve
content_type=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_content_type_t);
if (content_type) {
SalMessage salmsg;
char message_id[256]={0};
if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans);
op->pending_server_trans=server_transaction;
belle_sip_object_ref(op->pending_server_trans);
if (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);
} else if (is_imdn_xml(content_type)) {
SalImdn salimdn;
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));
salimdn.from = from;
salimdn.content = belle_sip_message_get_body(BELLE_SIP_MESSAGE(req));
op->base.root->callbacks.imdn_received(op, &salimdn);
belle_sip_object_unref(address);
belle_sip_free(from);
} else {
SalMessage salmsg;
char message_id[256]={0};
external_body=is_external_body(content_type);
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));
snprintf(message_id,sizeof(message_id)-1,"%s%i"
,belle_sip_header_call_id_get_call_id(call_id)
,belle_sip_header_cseq_get_seq_number(cseq));
salmsg.from=from;
/* if we just deciphered a message, use the deciphered part(which can be a rcs xml body pointing to the file to retreive from server)*/
salmsg.text=(!external_body)?belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)):NULL;
salmsg.url=NULL;
salmsg.content_type = ms_strdup_printf("%s/%s", belle_sip_header_content_type_get_type(content_type), belle_sip_header_content_type_get_subtype(content_type));
if (external_body && belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL")) {
size_t url_length=strlen(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL"));
salmsg.url = ms_strdup(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL")+1); /* skip first "*/
((char*)salmsg.url)[url_length-2]='\0'; /*remove trailing "*/
}
salmsg.message_id=message_id;
salmsg.time=date ? belle_sip_header_date_get_time(date) : time(NULL);
op->base.root->callbacks.text_received(op,&salmsg);
belle_sip_object_unref(address);
belle_sip_free(from);
if (salmsg.url) ms_free((char*)salmsg.url);
ms_free((char *)salmsg.content_type);
external_body=is_external_body(content_type);
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));
snprintf(message_id,sizeof(message_id)-1,"%s%i"
,belle_sip_header_call_id_get_call_id(call_id)
,belle_sip_header_cseq_get_seq_number(cseq));
salmsg.from=from;
/* if we just deciphered a message, use the deciphered part(which can be a rcs xml body pointing to the file to retreive from server)*/
salmsg.text=(!external_body)?belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)):NULL;
salmsg.url=NULL;
salmsg.content_type = ms_strdup_printf("%s/%s", belle_sip_header_content_type_get_type(content_type), belle_sip_header_content_type_get_subtype(content_type));
if (external_body && belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL")) {
size_t url_length=strlen(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL"));
salmsg.url = ms_strdup(belle_sip_parameters_get_parameter(BELLE_SIP_PARAMETERS(content_type),"URL")+1); /* skip first "*/
((char*)salmsg.url)[url_length-2]='\0'; /*remove trailing "*/
}
salmsg.message_id=message_id;
salmsg.time=date ? belle_sip_header_date_get_time(date) : time(NULL);
op->base.root->callbacks.message_received(op,&salmsg);
belle_sip_object_unref(address);
belle_sip_free(from);
if (salmsg.url) ms_free((char*)salmsg.url);
ms_free((char *)salmsg.content_type);
} else {
ms_error("Unsupported MESSAGE (no Content-Type)");
goto error;
resp = belle_sip_response_create_from_request(req, errcode);
add_message_accept(op, (belle_sip_message_t*)resp);
belle_sip_server_transaction_send_response(server_transaction,resp);
sal_op_release(op);
}
return;
error:
resp = belle_sip_response_create_from_request(req, errcode);
add_message_accept((belle_sip_message_t*)resp);
belle_sip_server_transaction_send_response(server_transaction,resp);
sal_op_release(op);
}
static void process_request_event(void *op_base, const belle_sip_request_event_t *event) {
......
......@@ -1169,7 +1169,7 @@ static bool_t is_duplicate_msg(LinphoneCore *lc, const char *msg_id){
}
static void text_received(SalOp *op, const SalMessage *msg){
static void message_received(SalOp *op, const SalMessage *msg){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
LinphoneReason reason = lc->chat_deny_code;
......@@ -1180,20 +1180,6 @@ static void text_received(SalOp *op, const SalMessage *msg){
if (!call) sal_op_release(op);
}
static void is_composing_received(SalOp *op, const SalIsComposing *is_composing) {
LinphoneCore *lc = (LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
LinphoneReason reason = linphone_core_is_composing_received(lc, op, is_composing);
sal_message_reply(op, linphone_reason_to_sal(reason));
sal_op_release(op);
}
static void imdn_received(SalOp *op, const SalImdn *imdn) {
LinphoneCore *lc = (LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
LinphoneReason reason = linphone_core_imdn_received(lc, op, imdn);
sal_message_reply(op, linphone_reason_to_sal(reason));
sal_op_release(op);
}
static void parse_presence_requested(SalOp *op, const char *content_type, const char *content_subtype, const char *body, SalPresenceModel **result) {
linphone_notify_parse_presence(content_type, content_subtype, body, result);
}
......@@ -1348,19 +1334,19 @@ static void notify_refer(SalOp *op, SalReferStatus status){
}
}
static LinphoneChatMessageState chatStatusSal2Linphone(SalTextDeliveryStatus status){
static LinphoneChatMessageState chatStatusSal2Linphone(SalMessageDeliveryStatus status){
switch(status){
case SalTextDeliveryInProgress:
case SalMessageDeliveryInProgress:
return LinphoneChatMessageStateInProgress;
case SalTextDeliveryDone:
case SalMessageDeliveryDone:
return LinphoneChatMessageStateDelivered;
case SalTextDeliveryFailed:
case SalMessageDeliveryFailed:
return LinphoneChatMessageStateNotDelivered;
}
return LinphoneChatMessageStateIdle;
}
static void text_delivery_update(SalOp *op, SalTextDeliveryStatus status){
static void message_delivery_update(SalOp *op, SalMessageDeliveryStatus status){
LinphoneChatMessage *chat_msg=(LinphoneChatMessage* )sal_op_get_user_pointer(op);
if (chat_msg == NULL) {
......@@ -1371,7 +1357,7 @@ static void text_delivery_update(SalOp *op, SalTextDeliveryStatus status){
if (chat_msg->chat_room != NULL) {
linphone_chat_message_update_state(chat_msg, chatStatusSal2Linphone(status));
}
if (status != SalTextDeliveryInProgress) { /*only release op if not in progress*/
if (status != SalMessageDeliveryInProgress) { /*only release op if not in progress*/
linphone_chat_message_destroy(chat_msg);
}
}
......@@ -1507,10 +1493,8 @@ SalCallbacks linphone_sal_callbacks={
vfu_request,
dtmf_received,
refer_received,
text_received,
text_delivery_update,
is_composing_received,
imdn_received,
message_received,
message_delivery_update,
notify_refer,
subscribe_received,
incoming_subscribe_closed,
......
......@@ -41,6 +41,8 @@ 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);
BELLE_SIP_DECLARE_NO_IMPLEMENTED_INTERFACES(LinphoneChatMessageCbs);
......@@ -511,6 +513,97 @@ void linphone_chat_room_message_received(LinphoneChatRoom *cr, LinphoneCore *lc,
linphone_chat_message_send_delivery_notification(msg, LinphoneReasonNone);
}
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 void create_file_transfer_information_from_vnd_gsma_rcs_ft_http_xml(LinphoneChatMessage *msg) {
xmlChar *file_url = NULL;
xmlDocPtr xmlMessageBody;
xmlNodePtr cur;
/* parse the msg body to get all informations from it */
xmlMessageBody = xmlParseDoc((const xmlChar *)msg->message);
msg->file_transfer_information = linphone_content_new();
cur = xmlDocGetRootElement(xmlMessageBody);
if (cur != NULL) {
cur = cur->xmlChildrenNode;
while (cur != NULL) {
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-info")) {
/* we found a file info node, check if it has a type="file" attribute */
xmlChar *typeAttribute = xmlGetProp(cur, (const xmlChar *)"type");
if (!xmlStrcmp(typeAttribute, (const xmlChar *)"file")) { /* this is the node we are looking for */
cur = cur->xmlChildrenNode; /* now loop on the content of the file-info node */
while (cur != NULL) {
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-size")) {
xmlChar *fileSizeString = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
linphone_content_set_size(msg->file_transfer_information, strtol((const char *)fileSizeString, NULL, 10));
xmlFree(fileSizeString);
}
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-name")) {
xmlChar *filename = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
linphone_content_set_name(msg->file_transfer_information, (char *)filename);
xmlFree(filename);
}
if (!xmlStrcmp(cur->name, (const xmlChar *)"content-type")) {
xmlChar *contentType = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
int contentTypeIndex = 0;
char *type;
char *subtype;
while (contentType[contentTypeIndex] != '/' && contentType[contentTypeIndex] != '\0') {
contentTypeIndex++;
}
type = ms_strndup((char *)contentType, contentTypeIndex);
subtype = ms_strdup(((char *)contentType + contentTypeIndex + 1));
linphone_content_set_type(msg->file_transfer_information, type);
linphone_content_set_subtype(msg->file_transfer_information, subtype);
ms_free(subtype);
ms_free(type);
xmlFree(contentType);
}
if (!xmlStrcmp(cur->name, (const xmlChar *)"data")) {
file_url = xmlGetProp(cur, (const xmlChar *)"url");
}
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-key")) {
/* there is a key in the msg: file has been encrypted */
/* convert the key from base 64 */
xmlChar *keyb64 = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
size_t keyLength = b64_decode((char *)keyb64, strlen((char *)keyb64), NULL, 0);
uint8_t *keyBuffer = (uint8_t *)malloc(keyLength);
/* decode the key into local key buffer */
b64_decode((char *)keyb64, strlen((char *)keyb64), keyBuffer, keyLength);
linphone_content_set_key(msg->file_transfer_information, (char *)keyBuffer, keyLength);
/* duplicate key value into the linphone content private structure */
xmlFree(keyb64);
free(keyBuffer);
}
cur = cur->next;
}
xmlFree(typeAttribute);
break;
}
xmlFree(typeAttribute);
}
cur = cur->next;
}
}
xmlFreeDoc(xmlMessageBody);
linphone_chat_message_set_external_body_url(msg, (const char *)file_url);
xmlFree(file_url);
}
LinphoneReason linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessage *sal_msg) {
LinphoneChatRoom *cr = NULL;
LinphoneAddress *addr;
......@@ -525,18 +618,18 @@ LinphoneReason linphone_core_message_received(LinphoneCore *lc, SalOp *op, const
linphone_address_clean(addr);
cr = linphone_core_get_chat_room(lc, addr);
msg = linphone_chat_room_create_message(cr, sal_msg->text); /* create a msg with empty body */
msg->content_type = ms_strdup(sal_msg->content_type); /* add the content_type "application/vnd.gsma.rcs-ft-http+xml" */
msg = linphone_chat_room_create_message(cr, sal_msg->text);
msg->content_type = ms_strdup(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);
......@@ -545,7 +638,7 @@ LinphoneReason linphone_core_message_received(LinphoneCore *lc, SalOp *op, const
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);
......@@ -553,101 +646,26 @@ LinphoneReason linphone_core_message_received(LinphoneCore *lc, SalOp *op, const
retval = cb_process_incoming_message(imee, cr, msg);
}
}
if (retval <= 0 && strcmp("text/plain", msg->content_type) != 0 && strcmp("message/external-body", msg->content_type) != 0
&& strcmp("application/vnd.gsma.rcs-ft-http+xml", msg->content_type) != 0) {
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 (strcmp("application/vnd.gsma.rcs-ft-http+xml", msg->content_type) == 0) {
xmlChar *file_url = NULL;
xmlDocPtr xmlMessageBody;
xmlNodePtr cur;
/* content_type field is, for now, used only for rcs file transfer but we shall strcmp it with "application/vnd.gsma.rcs-ft-http+xml" */
/* parse the msg body to get all informations from it */
xmlMessageBody = xmlParseDoc((const xmlChar *)msg->message);
msg->file_transfer_information = linphone_content_new();
cur = xmlDocGetRootElement(xmlMessageBody);
if (cur != NULL) {
cur = cur->xmlChildrenNode;
while (cur != NULL) {
if (!xmlStrcmp(
cur->name, (const xmlChar *)"file-info")) { /* we found a file info node, check it has a
type="file" attribute */
xmlChar *typeAttribute = xmlGetProp(cur, (const xmlChar *)"type");
if (!xmlStrcmp(typeAttribute, (const xmlChar *)"file")) { /* this is the node we are looking for */
cur = cur->xmlChildrenNode; /* now loop on the content of the file-info node */
while (cur != NULL) {
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-size")) {
xmlChar *fileSizeString = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
linphone_content_set_size(msg->file_transfer_information,
strtol((const char *)fileSizeString, NULL, 10));
xmlFree(fileSizeString);
}
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-name")) {
xmlChar *filename = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
linphone_content_set_name(
msg->file_transfer_information,
(char *)filename);
xmlFree(filename);
}
if (!xmlStrcmp(cur->name, (const xmlChar *)"content-type")) {
xmlChar *contentType = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
int contentTypeIndex = 0;
char *type;
char *subtype;
while (contentType[contentTypeIndex] != '/' && contentType[contentTypeIndex] != '\0') {
contentTypeIndex++;
}
type = ms_strndup((char *)contentType, contentTypeIndex);
subtype = ms_strdup(((char *)contentType + contentTypeIndex + 1));
linphone_content_set_type(msg->file_transfer_information, type);
linphone_content_set_subtype(msg->file_transfer_information, subtype);
ms_free(subtype);
ms_free(type);
xmlFree(contentType);
}
if (!xmlStrcmp(cur->name, (const xmlChar *)"data")) {
file_url = xmlGetProp(cur, (const xmlChar *)"url");
}
if (!xmlStrcmp(cur->name, (const xmlChar *)"file-key")) {
/* there is a key in the msg: file has been encrypted */
/* convert the key from base 64 */
xmlChar *keyb64 = xmlNodeListGetString(xmlMessageBody, cur->xmlChildrenNode, 1);
size_t keyLength = b64_decode((char *)keyb64, strlen((char *)keyb64), NULL, 0);
uint8_t *keyBuffer = (uint8_t *)malloc(keyLength);
/* decode the key into local key buffer */
b64_decode((char *)keyb64, strlen((char *)keyb64), keyBuffer, keyLength);
linphone_content_set_key(msg->file_transfer_information, (char *)keyBuffer, keyLength);
/* duplicate key value into the linphone content private structure */
xmlFree(keyb64);
free(keyBuffer);
}
cur = cur->next;
}
xmlFree(typeAttribute);
break;
}
xmlFree(typeAttribute);
}
cur = cur->next;
}
}
xmlFreeDoc(xmlMessageBody);
linphone_chat_message_set_external_body_url(msg, (const char *)file_url);
xmlFree(file_url);
if (is_file_transfer(msg->content_type)) {
create_file_transfer_information_from_vnd_gsma_rcs_ft_http_xml(msg);
} else if (is_im_iscomposing(msg->content_type)) {
linphone_chat_room_notify_is_composing(cr, msg->message);
goto end;
} else if (is_imdn(msg->content_type)) {
linphone_chat_room_notify_imdn(cr, msg->message);
goto end;
}
msg->storage_id = linphone_chat_message_store(msg);
......@@ -744,36 +762,6 @@ static void linphone_chat_room_notify_is_composing(LinphoneChatRoom *cr, const c
linphone_xmlparsing_context_destroy(xml_ctx);
}
LinphoneReason linphone_core_is_composing_received(LinphoneCore *lc, SalOp *op, const SalIsComposing *is_composing) {
LinphoneAddress *addr = linphone_address_new(is_composing->from);
LinphoneChatRoom *cr = _linphone_core_get_chat_room(lc, addr);
LinphoneImEncryptionEngine *imee = linphone_core_get_im_encryption_engine(lc);
LinphoneReason reason = LinphoneReasonNone;
if (cr != NULL) {
int retval = -1;
LinphoneChatMessage *msg = linphone_chat_room_create_message(cr, is_composing->text);
linphone_chat_message_set_from_address(msg, addr);
msg->content_type = ms_strdup("application/im-iscomposing+xml");
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) {
linphone_chat_room_notify_is_composing(cr, msg->message);
} else {
reason = linphone_error_code_to_reason(retval);
}
linphone_chat_message_unref(msg);
}
linphone_address_unref(addr);
return reason;
}
bool_t linphone_chat_room_is_remote_composing(const LinphoneChatRoom *cr) {
return (cr->remote_is_composing == LinphoneIsComposingActive) ? TRUE : FALSE;
}
......@@ -858,16 +846,6 @@ static void linphone_chat_room_notify_imdn(LinphoneChatRoom *cr, const char *tex
linphone_xmlparsing_context_destroy(xml_ctx);
}
LinphoneReason linphone_core_imdn_received(LinphoneCore *lc, SalOp *op, const SalImdn *imdn) {
LinphoneAddress *addr = linphone_address_new(imdn->from);
LinphoneChatRoom *cr = _linphone_core_get_chat_room(lc, addr);
if