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
This commit is contained in:
Ghislain MARY 2017-01-26 15:17:45 +01:00
parent 1c7fc21b3d
commit b297a4cb1d
18 changed files with 631 additions and 393 deletions

View file

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

View file

@ -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_ */

View file

@ -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");
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"));
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);
}
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) {

View file

@ -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,

View file

@ -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 (cr != NULL) {
linphone_chat_room_notify_imdn(cr, imdn->content);
}
linphone_address_unref(addr);
return LinphoneReasonNone;
}
LinphoneCore *linphone_chat_room_get_lc(LinphoneChatRoom *cr) {
return linphone_chat_room_get_core(cr);
}
@ -886,7 +864,7 @@ LinphoneChatMessage *linphone_chat_room_create_message(LinphoneChatRoom *cr, con
msg->callbacks = linphone_chat_message_cbs_new();
msg->chat_room = (LinphoneChatRoom *)cr;
msg->message = message ? ms_strdup(message) : NULL;
msg->content_type = NULL; /* this property is used only when transfering file */
msg->content_type = ms_strdup("text/plain");
msg->file_transfer_information = NULL; /* this property is used only when transfering file */
msg->http_request = NULL;
msg->time = ms_time(0);
@ -1432,6 +1410,17 @@ void linphone_chat_message_set_external_body_url(LinphoneChatMessage *msg, const
msg->external_body_url = url ? ms_strdup(url) : NULL;
}
const char * linphone_chat_message_get_content_type(const LinphoneChatMessage *msg) {
return msg->content_type;
}
void linphone_chat_message_set_content_type(LinphoneChatMessage *msg, const char *content_type) {
if (msg->content_type) {
ms_free(msg->content_type);
}
msg->content_type = content_type ? ms_strdup(content_type) : NULL;
}
const char *linphone_chat_message_get_appdata(const LinphoneChatMessage *msg) {
return msg->appdata;
}

View file

@ -89,7 +89,7 @@ uint8_t lime_byteToChar(uint8_t inputByte) {
* @param[in] inputString The input string buffer, must be hexadecimal(it is not checked by function, any non hexa char is converted to 0)
* @param[in] inputStringLength The lenght in chars of the string buffer, output is half this length
*/
void lime_strToUint8(uint8_t *outputBytes, uint8_t *inputString, uint16_t inputStringLength) {
void lime_strToUint8(uint8_t *outputBytes, const uint8_t *inputString, uint16_t inputStringLength) {
int i;
for (i=0; i<inputStringLength/2; i++) {
outputBytes[i] = (lime_charToByte(inputString[2*i]))<<4 | lime_charToByte(inputString[2*i+1]);
@ -481,7 +481,7 @@ void lime_freeKeys(limeURIKeys_t *associatedKeys) {
associatedKeys->peerURI = NULL;
}
int lime_encryptMessage(limeKey_t *key, uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage) {
int lime_encryptMessage(limeKey_t *key, const uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage) {
uint8_t authenticatedData[28];
/* Authenticated data is senderZID(12 bytes)||receiverZID(12 bytes)||sessionIndex(4 bytes) */
memcpy(authenticatedData, selfZID, 12);
@ -573,10 +573,11 @@ int lime_decryptMessage(limeKey_t *key, uint8_t *encryptedMessage, uint32_t mess
return retval;
}
int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t *peerURI, uint8_t **output) {
int lime_createMultipartMessage(xmlDocPtr cacheBuffer, const char *contentType, uint8_t *message, uint8_t *peerURI, uint8_t **output) {
uint8_t selfZidHex[25];
uint8_t selfZid[12]; /* same data but in byte buffer */
uint32_t encryptedMessageLength;
uint32_t encryptedContentTypeLength;
limeURIKeys_t associatedKeys;
xmlDocPtr xmlOutputMessage;
xmlNodePtr rootNode;
@ -592,6 +593,7 @@ int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t
/* encrypted message length is plaintext + 16 for tag */
encryptedMessageLength = (uint32_t)strlen((char *)message) + 16;
encryptedContentTypeLength = (uint32_t)strlen((char *)contentType) + 16;
/* retrieve keys associated to the peer URI */
associatedKeys.peerURI = (uint8_t *)malloc(strlen((char *)peerURI)+1);
@ -624,17 +626,21 @@ int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t
xmlNodePtr msgNode;
size_t b64Size = 0;
unsigned char *encryptedMessageb64;
unsigned char *encryptedContentTypeb64;
/* encrypt message with current key */
limeKey_t *currentKey = associatedKeys.peerKeys[i];
/* encrypted message include a 16 bytes tag */
uint8_t *encryptedMessage = (uint8_t *)ms_malloc(encryptedMessageLength);
uint8_t *encryptedContentType = (uint8_t *)ms_malloc(encryptedContentTypeLength);
lime_encryptMessage(currentKey, message, (uint32_t)strlen((char *)message), selfZid, encryptedMessage);
lime_encryptMessage(currentKey, (const uint8_t *)contentType, (uint32_t)strlen((char *)contentType), selfZid, encryptedContentType);
/* add a "msg" node the the output message, doc node is :
* <msg>
* <pzid>peerZID</pzid>
* <index>session index</index>
* <text>ciphertext</text>
* <content-type>ciphertext</content-type>
* </msg> */
msgNode = xmlNewDocNode(xmlOutputMessage, NULL, (const xmlChar *)"msg", NULL);
lime_int8ToStr(peerZidHex, currentKey->peerZID, 12);
@ -661,6 +667,16 @@ int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t
ms_free(encryptedMessage);
ms_free(encryptedMessageb64);
/* convert the encrypted content-type to base 64 */
b64Size = 0;
bctbx_base64_encode(NULL, &b64Size, encryptedContentType, encryptedContentTypeLength); /* b64Size is 0, so it is set to the requested output buffer size */
encryptedContentTypeb64 = ms_malloc(b64Size+1); /* allocate a buffer of requested size +1 for NULL termination */
bctbx_base64_encode(encryptedContentTypeb64, &b64Size, encryptedContentType, encryptedContentTypeLength); /* b64Size is 0, so it is set to the requested output buffer size */
encryptedContentTypeb64[b64Size] = '\0'; /* libxml need a null terminated string */
xmlNewTextChild(msgNode, NULL, (const xmlChar *)"content-type", (const xmlChar *)encryptedContentTypeb64);
ms_free(encryptedContentType);
ms_free(encryptedContentTypeb64);
/* add the message Node into the doc */
xmlAddChild(rootNode, msgNode);
@ -683,17 +699,21 @@ int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t
return 0;
}
int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output) {
int retval;
int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output, char **content_type) {
int retval = 0;
uint8_t selfZidHex[25];
uint8_t selfZid[12]; /* same data but in byte buffer */
char xpath_str[MAX_XPATH_LENGTH];
limeKey_t associatedKey;
xmlChar *peerZidHex = NULL;
xmlNodePtr cur;
const char *peerZidHex = NULL;
xmlparsing_context_t *xml_ctx;
xmlXPathObjectPtr msg_object;
uint8_t *encryptedMessage = NULL;
size_t encryptedMessageLength = 0;
uint8_t *encryptedContentType = NULL;
size_t encryptedContentTypeLength = 0;
uint32_t usedSessionIndex = 0;
xmlDocPtr xmlEncryptedMessage;
int i;
if (cacheBuffer == NULL) {
return LIME_INVALID_CACHE;
@ -704,81 +724,80 @@ int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_
}
lime_strToUint8(selfZid, selfZidHex, 24);
/* parse the message into an xml doc */
/* make sure we have a valid xml message before trying to parse it */
if (memcmp(message, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", 38) != 0) {
return LIME_INVALID_ENCRYPTED_MESSAGE;
}
xmlEncryptedMessage = xmlParseDoc((const xmlChar *)message);
if (xmlEncryptedMessage == NULL) {
return LIME_INVALID_ENCRYPTED_MESSAGE;
xml_ctx = linphone_xmlparsing_context_new();
xmlSetGenericErrorFunc(xml_ctx, linphone_xmlparsing_genericxml_error);
xml_ctx->doc = xmlReadDoc((const unsigned char*)message, 0, NULL, 0);
if (xml_ctx->doc == NULL) {
retval = LIME_INVALID_ENCRYPTED_MESSAGE;
goto error;
}
/* retrieve the sender ZID which is the first child of root */
cur = xmlDocGetRootElement(xmlEncryptedMessage);
if (cur != NULL) {
cur = cur->xmlChildrenNode;
if ((!xmlStrcmp(cur->name, (const xmlChar *)"ZID"))){ /* sender ZID found, extract it */
peerZidHex = xmlNodeListGetString(xmlEncryptedMessage, cur->xmlChildrenNode, 1);
/* convert it from hexa string to bytes string and set the result in the associatedKey structure */
lime_strToUint8(associatedKey.peerZID, peerZidHex, (uint16_t)strlen((char *)peerZidHex));
cur = cur->next;
}
if (linphone_create_xml_xpath_context(xml_ctx) < 0) {
retval = LIME_INVALID_ENCRYPTED_MESSAGE;
goto error;
}
/* Retrieve the sender ZID */
peerZidHex = linphone_get_xml_text_content(xml_ctx, "/doc/ZID");
if (peerZidHex != NULL) {
/* get from cache the matching key */
retval = lime_getCachedRcvKeyByZid(cacheBuffer, &associatedKey);
/* Convert it from hexa string to bytes string and set the result in the associatedKey structure */
lime_strToUint8(associatedKey.peerZID, (const uint8_t *)peerZidHex, (uint16_t)strlen(peerZidHex));
linphone_free_xml_text_content(peerZidHex);
/* Get the matching key from cache */
retval = lime_getCachedRcvKeyByZid(cacheBuffer, &associatedKey);
if (retval != 0) {
xmlFree(peerZidHex);
xmlFreeDoc(xmlEncryptedMessage);
return retval;
goto error;
}
/* retrieve the portion of message which is encrypted with our key */
while (cur != NULL) { /* loop on all "msg" node in the message */
xmlNodePtr msgChildrenNode = cur->xmlChildrenNode;
xmlChar *currentZidHex = xmlNodeListGetString(cacheBuffer, msgChildrenNode->xmlChildrenNode, 1); /* pZID is the first element of msg */
if (!xmlStrcmp(currentZidHex, (const xmlChar *)selfZidHex)) { /* we found the msg node we are looking for */
/* get the index (second node in the msg one) */
xmlChar *sessionIndexHex;
xmlChar *encryptedMessageb64;
msgChildrenNode = msgChildrenNode->next;
sessionIndexHex = xmlNodeListGetString(cacheBuffer, msgChildrenNode->xmlChildrenNode, 1);
usedSessionIndex = (((uint32_t)lime_charToByte(sessionIndexHex[0]))<<28)
| (((uint32_t)lime_charToByte(sessionIndexHex[1]))<<24)
| (((uint32_t)lime_charToByte(sessionIndexHex[2]))<<20)
| (((uint32_t)lime_charToByte(sessionIndexHex[3]))<<16)
| (((uint32_t)lime_charToByte(sessionIndexHex[4]))<<12)
| (((uint32_t)lime_charToByte(sessionIndexHex[5]))<<8)
| (((uint32_t)lime_charToByte(sessionIndexHex[6]))<<4)
| (((uint32_t)lime_charToByte(sessionIndexHex[7])));
xmlFree(sessionIndexHex);
/* get the encrypted message */
msgChildrenNode = msgChildrenNode->next;
/* convert the cipherText from base 64 */
encryptedMessageb64 = xmlNodeListGetString(cacheBuffer, msgChildrenNode->xmlChildrenNode, 1);
bctbx_base64_decode(NULL, &encryptedMessageLength, encryptedMessageb64, strlen((char *)encryptedMessageb64)); /* encryptedMessageLength is 0, so it will be set to the requested buffer length */
encryptedMessage = (uint8_t *)ms_malloc(encryptedMessageLength);
bctbx_base64_decode(encryptedMessage, &encryptedMessageLength, encryptedMessageb64, strlen((char *)encryptedMessageb64));
xmlFree(encryptedMessageb64);
xmlFree(currentZidHex);
break;
}
cur = cur->next;
xmlFree(currentZidHex);
/* Retrieve the portion of message which is encrypted with our key */
msg_object = linphone_get_xml_xpath_object_for_node_list(xml_ctx, "/doc/msg");
if ((msg_object != NULL) && (msg_object->nodesetval != NULL)) {
for (i = 1; i <= msg_object->nodesetval->nodeNr; i++) {
const char *currentZidHex;
const char *sessionIndexHex;
const char *encryptedMessageb64;
const char *encryptedContentTypeb64;
snprintf(xpath_str, sizeof(xpath_str), "/doc/msg[%i]/pzid", i);
currentZidHex = linphone_get_xml_text_content(xml_ctx, xpath_str);
if ((currentZidHex != NULL) && (strcmp(currentZidHex, (char *)selfZidHex) == 0)) {
/* We found the msg node we are looking for */
snprintf(xpath_str, sizeof(xpath_str), "/doc/msg[%i]/index", i);
sessionIndexHex = linphone_get_xml_text_content(xml_ctx, xpath_str);
if (sessionIndexHex != NULL) {
usedSessionIndex = (((uint32_t)lime_charToByte(sessionIndexHex[0]))<<28)
| (((uint32_t)lime_charToByte(sessionIndexHex[1]))<<24)
| (((uint32_t)lime_charToByte(sessionIndexHex[2]))<<20)
| (((uint32_t)lime_charToByte(sessionIndexHex[3]))<<16)
| (((uint32_t)lime_charToByte(sessionIndexHex[4]))<<12)
| (((uint32_t)lime_charToByte(sessionIndexHex[5]))<<8)
| (((uint32_t)lime_charToByte(sessionIndexHex[6]))<<4)
| (((uint32_t)lime_charToByte(sessionIndexHex[7])));
linphone_free_xml_text_content(sessionIndexHex);
}
snprintf(xpath_str, sizeof(xpath_str), "/doc/msg[%i]/text", i);
encryptedMessageb64 = linphone_get_xml_text_content(xml_ctx, xpath_str);
if (encryptedMessageb64 != NULL) {
bctbx_base64_decode(NULL, &encryptedMessageLength, (const unsigned char *)encryptedMessageb64, strlen(encryptedMessageb64)); /* encryptedMessageLength is 0, so it will be set to the requested buffer length */
encryptedMessage = (uint8_t *)ms_malloc(encryptedMessageLength);
bctbx_base64_decode(encryptedMessage, &encryptedMessageLength, (const unsigned char *)encryptedMessageb64, strlen(encryptedMessageb64));
linphone_free_xml_text_content(encryptedMessageb64);
}
snprintf(xpath_str, sizeof(xpath_str), "/doc/msg[%i]/content-type", i);
encryptedContentTypeb64 = linphone_get_xml_text_content(xml_ctx, xpath_str);
if (encryptedContentTypeb64 != NULL) {
bctbx_base64_decode(NULL, &encryptedContentTypeLength, (const unsigned char *)encryptedContentTypeb64, strlen(encryptedContentTypeb64)); /* encryptedContentTypeLength is 0, so it will be set to the requested buffer length */
encryptedContentType = (uint8_t *)ms_malloc(encryptedContentTypeLength);
bctbx_base64_decode(encryptedContentType, &encryptedContentTypeLength, (const unsigned char *)encryptedContentTypeb64, strlen(encryptedContentTypeb64));
linphone_free_xml_text_content(encryptedContentTypeb64);
}
break;
}
if (currentZidHex != NULL) linphone_free_xml_text_content(currentZidHex);
}
}
}
xmlFree(peerZidHex);
xmlFreeDoc(xmlEncryptedMessage);
/* do we have retrieved correctly all the needed data */
if (encryptedMessage == NULL) {
return LIME_UNABLE_TO_DECRYPT_MESSAGE;
@ -801,23 +820,35 @@ int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_
lime_deriveKey(&associatedKey);
}
/* decrypt the message */
*output = (uint8_t *)ms_malloc(encryptedMessageLength - 16 +1); /* plain message is same length than encrypted one with 16 bytes less for the tag + 1 to add the null termination char */
/* Decrypt the message */
*output = (uint8_t *)ms_malloc(encryptedMessageLength - 16 + 1); /* plain message is same length than encrypted one with 16 bytes less for the tag + 1 to add the null termination char */
retval = lime_decryptMessage(&associatedKey, encryptedMessage, (uint32_t)encryptedMessageLength, selfZid, *output);
ms_free(encryptedMessage);
if (retval != 0) {
ms_free(*output);
*output = NULL;
return LIME_UNABLE_TO_DECRYPT_MESSAGE;
}
/* Decrypt the content-type */
if (encryptedContentType != NULL) {
*content_type = (char *)ms_malloc(encryptedContentTypeLength - 16 + 1); /* content-type is same length than encrypted one with 16 bytes less for the tag + 1 to add the null termination char */
retval = lime_decryptMessage(&associatedKey, encryptedContentType, (uint32_t)encryptedContentTypeLength, selfZid, *((uint8_t **)content_type));
ms_free(encryptedContentType);
if (retval != 0) {
ms_free(*content_type);
*content_type = NULL;
return LIME_UNABLE_TO_DECRYPT_MESSAGE;
}
}
/* update used key */
lime_deriveKey(&associatedKey);
lime_setCachedKey(cacheBuffer, &associatedKey, LIME_RECEIVER);
return 0;
error:
linphone_xmlparsing_context_destroy(xml_ctx);
return retval;
}
bool_t linphone_chat_room_lime_available(LinphoneChatRoom *cr) {
@ -887,6 +918,7 @@ int lime_im_encryption_engine_process_incoming_message_cb(LinphoneImEncryptionEn
int retval;
xmlDocPtr cacheXml;
uint8_t *decrypted_body = NULL;
char *decrypted_content_type = NULL;
cacheString=ms_load_file_content(CACHEFD, &cacheSize);
if (!cacheString){
@ -899,7 +931,7 @@ int lime_im_encryption_engine_process_incoming_message_cb(LinphoneImEncryptionEn
fclose(CACHEFD);
cacheXml = xmlParseDoc((xmlChar*)cacheString);
ms_free(cacheString);
retval = lime_decryptMultipartMessage(cacheXml, (uint8_t *)msg->message, &decrypted_body);
retval = lime_decryptMultipartMessage(cacheXml, (uint8_t *)msg->message, &decrypted_body, &decrypted_content_type);
if (retval != 0) {
ms_warning("Unable to decrypt message, reason : %s", lime_error_code_to_string(retval));
if (decrypted_body) ms_free(decrypted_body);
@ -922,13 +954,15 @@ int lime_im_encryption_engine_process_incoming_message_cb(LinphoneImEncryptionEn
ms_free(msg->message);
}
msg->message = (char *)decrypted_body;
if (strcmp("application/cipher.vnd.gsma.rcs-ft-http+xml", msg->content_type) == 0) {
ms_free(msg->content_type);
msg->content_type = ms_strdup("application/vnd.gsma.rcs-ft-http+xml");
if (decrypted_content_type != NULL) {
linphone_chat_message_set_content_type(msg, decrypted_content_type);
} else {
ms_free(msg->content_type);
msg->content_type = ms_strdup("text/plain");
if (strcmp("application/cipher.vnd.gsma.rcs-ft-http+xml", msg->content_type) == 0) {
linphone_chat_message_set_content_type(msg, "application/vnd.gsma.rcs-ft-http+xml");
} else {
linphone_chat_message_set_content_type(msg, "text/plain");
}
}
}
@ -941,25 +975,22 @@ int lime_im_encryption_engine_process_incoming_message_cb(LinphoneImEncryptionEn
int lime_im_encryption_engine_process_outgoing_message_cb(LinphoneImEncryptionEngine *engine, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
LinphoneCore *lc = linphone_im_encryption_engine_get_core(engine);
int errcode = -1;
char *content_type = "xml/cipher";
char *new_content_type = "xml/cipher";
if(linphone_core_lime_enabled(room->lc)) {
if (linphone_chat_room_lime_available(room)) {
if (msg->content_type) {
if (strcmp(msg->content_type, "application/vnd.gsma.rcs-ft-http+xml") == 0) {
/* it's a file transfer, content type shall be set to
application/cipher.vnd.gsma.rcs-ft-http+xml*/
content_type = "application/cipher.vnd.gsma.rcs-ft-http+xml";
/* It's a file transfer, content type shall be set to application/cipher.vnd.gsma.rcs-ft-http+xml
TODO: As of january 2017, the content type is now included in the encrypted body, this
application/cipher.vnd.gsma.rcs-ft-http+xml is kept for compatibility with previous versions,
but may be dropped in the future to use xml/cipher instead. */
new_content_type = "application/cipher.vnd.gsma.rcs-ft-http+xml";
} else if (strcmp(msg->content_type, "application/im-iscomposing+xml") == 0) {
/* We don't encrypt composing messages */
return errcode;
} else if (strcmp(msg->content_type, "message/imdn+xml") == 0) {
/* We don't encrypt imdn messages */
return errcode;
}
}
msg->content_type = ms_strdup(content_type);
/* access the zrtp cache to get keys needed to cipher the message */
const char *zrtp_secrets_cache = linphone_core_get_zrtp_secrets_file(lc);
FILE *CACHEFD = fopen(zrtp_secrets_cache, "rb+");
@ -986,7 +1017,7 @@ int lime_im_encryption_engine_process_outgoing_message_cb(LinphoneImEncryptionEn
fclose(CACHEFD);
cacheXml = xmlParseDoc((xmlChar*)cacheString);
ms_free(cacheString);
retval = lime_createMultipartMessage(cacheXml, (uint8_t *)msg->message, (uint8_t *)peer, &crypted_body);
retval = lime_createMultipartMessage(cacheXml, msg->content_type, (uint8_t *)msg->message, (uint8_t *)peer, &crypted_body);
if (retval != 0) {
ms_warning("Unable to encrypt message for %s : %s", peer, lime_error_code_to_string(retval));
if (crypted_body) ms_free(crypted_body);
@ -1007,6 +1038,7 @@ int lime_im_encryption_engine_process_outgoing_message_cb(LinphoneImEncryptionEn
ms_free(msg->message);
}
msg->message = (char *)crypted_body;
msg->content_type = ms_strdup(new_content_type);
}
ms_free(peer);
xmlFreeDoc(cacheXml);
@ -1070,14 +1102,14 @@ void lime_im_encryption_engine_generate_file_transfer_key_cb(LinphoneImEncryptio
bool_t lime_is_available() { return FALSE; }
int lime_decryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) { return LIME_NOT_ENABLED;}
int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output) { return LIME_NOT_ENABLED;}
int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t *peerURI, uint8_t **output) { return LIME_NOT_ENABLED;}
int lime_createMultipartMessage(xmlDocPtr cacheBuffer, const char *content_type, uint8_t *message, uint8_t *peerURI, uint8_t **output) { return LIME_NOT_ENABLED;}
int lime_encryptFile(void **cryptoContext, unsigned char *key, size_t length, char *plain, char *cipher) {return LIME_NOT_ENABLED;}
void lime_freeKeys(limeURIKeys_t *associatedKeys){
}
int lime_getCachedSndKeysByURI(xmlDocPtr cacheBuffer, limeURIKeys_t *associatedKeys){
return LIME_NOT_ENABLED;
}
int lime_encryptMessage(limeKey_t *key, uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage) {
int lime_encryptMessage(limeKey_t *key, const uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage) {
return LIME_NOT_ENABLED;
}
int lime_setCachedKey(xmlDocPtr cacheBuffer, limeKey_t *associatedKey, uint8_t role) {

View file

@ -119,7 +119,7 @@ LINPHONE_PUBLIC void lime_freeKeys(limeURIKeys_t *associatedKeys);
* @return 0 on success, error code otherwise
*
*/
LINPHONE_PUBLIC int lime_encryptMessage(limeKey_t *key, uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage);
LINPHONE_PUBLIC int lime_encryptMessage(limeKey_t *key, const uint8_t *plainMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *encryptedMessage);
/**
* @brief Encrypt a file before transfering it to the server, encryption is done in several call, first one will be done with cryptoContext null, last one with length = 0
@ -170,13 +170,14 @@ LINPHONE_PUBLIC int lime_decryptMessage(limeKey_t *key, uint8_t *encryptedMessag
* Retrieve in cache the needed keys which are then updated. Output buffer is allocated and must be freed by caller
*
* @param[in,out] cacheBuffer The xmlDoc containing current cache, get the keys and selfZID from it, updated by this function with derivated keys
* @param[in] message The plain text message to be encrypted
* @param[in] content_type The content type of the message to encrypt
* @param[in] message The message content to be encrypted
* @param[in] peerURI The destination URI, associated keys will be found in cache
* @param[out] output The output buffer, allocated and set with the encrypted message xml body(null terminated string). Must be freed by caller
*
* @return 0 on success, error code otherwise
*/
LINPHONE_PUBLIC int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t *peerURI, uint8_t **output);
LINPHONE_PUBLIC int lime_createMultipartMessage(xmlDocPtr cacheBuffer, const char *content_type, uint8_t *message, uint8_t *peerURI, uint8_t **output);
/**
* @brief decrypt a multipart xml message
@ -185,11 +186,11 @@ LINPHONE_PUBLIC int lime_createMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *
* @param[in,out] cacheBuffer The xmlDoc containing current cache, get the key and selfZID from it, updated by this function with derivated keys
* @param[in] message The multipart message, contain one or several part identified by destination ZID, one shall match the self ZID retrieved from cache
* @param[out] output The output buffer, allocated and set with the decrypted message(null terminated string). Must be freed by caller
*
* @param[out] content_type The content type of the decrypted message
* @return 0 on success, error code otherwise
*/
LINPHONE_PUBLIC int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output);
LINPHONE_PUBLIC int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_t **output, char **content_type);
/**
* @brief given a readable version of error code generated by Lime functions

View file

@ -1997,9 +1997,10 @@ static void linphone_core_init(LinphoneCore * lc, LinphoneCoreCbs *cbs, LpConfig
#ifdef SQLITE_STORAGE_ENABLED
sqlite3_bctbx_vfs_register(0);
#endif
lc->vcard_context = linphone_vcard_context_new();
linphone_core_initialize_supported_content_types(lc);
remote_provisioning_uri = linphone_core_get_provisioning_uri(lc);
if (remote_provisioning_uri == NULL) {
linphone_configuring_terminated(lc, LinphoneConfiguringSkipped, NULL);
@ -7199,3 +7200,19 @@ void linphone_core_set_im_encryption_engine(LinphoneCore *lc, LinphoneImEncrypti
LinphoneImEncryptionEngine *linphone_core_get_im_encryption_engine(const LinphoneCore *lc) {
return lc->im_encryption_engine;
}
void linphone_core_initialize_supported_content_types(LinphoneCore *lc) {
sal_add_content_type_support(lc->sal, "text/plain");
sal_add_content_type_support(lc->sal, "message/external-body");
sal_add_content_type_support(lc->sal, "application/vnd.gsma.rcs-ft-http+xml");
sal_add_content_type_support(lc->sal, "application/im-iscomposing+xml");
sal_add_content_type_support(lc->sal, "message/imdn+xml");
}
bool_t linphone_core_is_content_type_supported(const LinphoneCore *lc, const char *content_type) {
return sal_is_content_type_supported(lc->sal, content_type);
}
void linphone_core_add_content_type_support(LinphoneCore *lc, const char *content_type) {
sal_add_content_type_support(lc->sal, content_type);
}

View file

@ -203,16 +203,17 @@ static int callback_all(void *data, int argc, char **argv, char **colName){
* | 0 | storage_id
* | 1 | localContact
* | 2 | remoteContact
* | 3 | direction flag
* | 4 | message
* | 5 | time (unused now, used to be string-based timestamp)
* | 6 | read flag
* | 7 | status
* | 8 | external body url
* | 3 | direction flag (LinphoneChatMessageDir)
* | 4 | message (text content of the message)
* | 5 | time (unused now, used to be string-based timestamp, replaced by the utc timestamp)
* | 6 | read flag (no longer used, replaced by the LinphoneChatMessageStateDisplayed state)
* | 7 | status (LinphoneChatMessageState)
* | 8 | external body url (deprecated file transfer system)
* | 9 | utc timestamp
* | 10 | app data text
* | 11 | linphone content id
* | 12 | message id
* | 11 | linphone content id (LinphoneContent describing a file transfer)
* | 12 | message id (used for IMDN)
* | 13 | content type (of the message field [must be text representable])
*/
static int create_chat_message(void *data, int argc, char **argv, char **colName){
LinphoneChatRoom *cr = (LinphoneChatRoom *)data;
@ -245,6 +246,7 @@ static int create_chat_message(void *data, int argc, char **argv, char **colName
new_message->external_body_url= ms_strdup(argv[8]);
new_message->appdata = ms_strdup(argv[10]);
new_message->message_id = ms_strdup(argv[12]);
new_message->content_type = ms_strdup(argv[13]);
if (argv[11] != NULL) {
int id = atoi(argv[11]);
@ -253,6 +255,17 @@ static int create_chat_message(void *data, int argc, char **argv, char **colName
}
}
/* Fix content type for old messages that were stored without it */
if (new_message->content_type == NULL) {
if (new_message->file_transfer_information != NULL) {
new_message->content_type = ms_strdup("application/vnd.gsma.rcs-ft-http+xml");
} else if (new_message->external_body_url != NULL) {
new_message->content_type = ms_strdup("message/external-body");
} else {
new_message->content_type = ms_strdup("text/plain");
}
}
/* Add the new message to the weak messages list. */
linphone_chat_room_add_weak_message(cr, new_message);
}
@ -330,7 +343,7 @@ unsigned int linphone_chat_message_store(LinphoneChatMessage *msg){
peer=linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(msg->chat_room));
local_contact=linphone_address_as_string_uri_only(linphone_chat_message_get_local_address(msg));
buf = sqlite3_mprintf("INSERT INTO history VALUES(NULL,%Q,%Q,%i,%Q,%Q,%i,%i,%Q,%lld,%Q,%i,%Q);",
buf = sqlite3_mprintf("INSERT INTO history VALUES(NULL,%Q,%Q,%i,%Q,%Q,%i,%i,%Q,%lld,%Q,%i,%Q,%Q);",
local_contact,
peer,
msg->dir,
@ -342,7 +355,8 @@ unsigned int linphone_chat_message_store(LinphoneChatMessage *msg){
(int64_t)msg->time,
msg->appdata,
content_id,
msg->message_id
msg->message_id,
msg->content_type
);
linphone_sql_request(lc->db,buf);
sqlite3_free(buf);
@ -748,13 +762,21 @@ void linphone_update_table(sqlite3* db) {
if (ret != SQLITE_OK) {
ms_message("Table already up to date: %s", errmsg);
} else {
ms_message("Table history updated successfully for message_id data.");
ms_message("Table history updated successfully for messageId data.");
}
// Convert is_read to LinphoneChatMessageStateDisplayed
buf = sqlite3_mprintf("UPDATE history SET status=%i WHERE read=1 AND direction=%i;", LinphoneChatMessageStateDisplayed, LinphoneChatMessageIncoming);
linphone_sql_request(db, buf);
sqlite3_free(buf);
/* New field for content type */
ret = sqlite3_exec(db, "ALTER TABLE history ADD COLUMN content_type TEXT;", NULL, NULL, &errmsg);
if (ret != SQLITE_OK) {
ms_message("Table already up to date: %s", errmsg);
} else {
ms_message("Table history updated successfully for content_type data.");
}
}
void linphone_message_storage_init_chat_rooms(LinphoneCore *lc) {

View file

@ -554,8 +554,6 @@ LinphoneProxyConfig *linphone_proxy_config_new_from_config_file(LinphoneCore *lc
void linphone_proxy_config_write_to_config_file(struct _LpConfig* config,LinphoneProxyConfig *obj, int index);
LinphoneReason linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessage *msg);
LinphoneReason linphone_core_is_composing_received(LinphoneCore *lc, SalOp *op, const SalIsComposing *is_composing);
LinphoneReason linphone_core_imdn_received(LinphoneCore *lc, SalOp *op, const SalImdn *imdn);
void linphone_core_real_time_text_received(LinphoneCore *lc, LinphoneChatRoom *cr, uint32_t character, LinphoneCall *call);
void linphone_call_init_stats(LinphoneCallStats *stats, int type);
@ -1093,9 +1091,7 @@ struct _LinphoneCore
char *tls_cert;
char *tls_key;
/*default resource list server*/
LinphoneAddress *default_rls_addr;
LinphoneAddress *default_rls_addr; /*default resource list server*/
LinphoneImEncryptionEngine *im_encryption_engine;
};
@ -1148,6 +1144,8 @@ bool_t linphone_core_is_payload_type_usable_for_bandwidth(LinphoneCore *lc, cons
#define linphone_core_ready(lc) ((lc)->state==LinphoneGlobalOn || (lc)->state==LinphoneGlobalShutdown)
void _linphone_core_configure_resolver(void);
void linphone_core_initialize_supported_content_types(LinphoneCore *lc);
struct _EcCalibrator{
MSFactory *factory;
ms_thread_t thread;

View file

@ -310,11 +310,13 @@ LINPHONE_PUBLIC bool_t linphone_chat_room_is_remote_composing(const LinphoneChat
* @return the number of unread messages.
*/
LINPHONE_PUBLIC int linphone_chat_room_get_unread_messages_count(LinphoneChatRoom *cr);
/**
* Returns back pointer to #LinphoneCore object.
* @deprecated use linphone_chat_room_get_core()
**/
LINPHONE_PUBLIC LINPHONE_DEPRECATED LinphoneCore* linphone_chat_room_get_lc(LinphoneChatRoom *cr);
/**
* Returns back pointer to #LinphoneCore object.
**/
@ -349,68 +351,83 @@ LINPHONE_PUBLIC unsigned int linphone_chat_message_store(LinphoneChatMessage *ms
* Returns a #LinphoneChatMessageState as a string.
*/
LINPHONE_PUBLIC const char* linphone_chat_message_state_to_string(const LinphoneChatMessageState state);
/**
* Get the state of the message
*@param message #LinphoneChatMessage obj
*@return #LinphoneChatMessageState
*/
LINPHONE_PUBLIC LinphoneChatMessageState linphone_chat_message_get_state(const LinphoneChatMessage* message);
/**
* Duplicate a LinphoneChatMessage
**/
LINPHONE_PUBLIC LinphoneChatMessage* linphone_chat_message_clone(const LinphoneChatMessage* message);
/**
* Acquire a reference to the chat message.
* @param msg the chat message
* @return the same chat message
**/
LINPHONE_PUBLIC LinphoneChatMessage * linphone_chat_message_ref(LinphoneChatMessage *msg);
/**
* Release reference to the chat message.
* @param msg the chat message.
**/
LINPHONE_PUBLIC void linphone_chat_message_unref(LinphoneChatMessage *msg);
/**
* Destroys a LinphoneChatMessage.
**/
LINPHONE_PUBLIC void linphone_chat_message_destroy(LinphoneChatMessage* msg);
/** @deprecated Use linphone_chat_message_set_from_address() instead. */
#define linphone_chat_message_set_from(msg, addr) linphone_chat_message_set_from_address(msg, addr)
/**
* Set origin of the message
* @param[in] message #LinphoneChatMessage obj
* @param[in] from #LinphoneAddress origin of this message (copied)
*/
LINPHONE_PUBLIC void linphone_chat_message_set_from_address(LinphoneChatMessage* message, const LinphoneAddress* from);
/** @deprecated Use linphone_chat_message_get_from_address() instead. */
#define linphone_chat_message_get_from(msg) linphone_chat_message_get_from_address(msg)
/**
* Get origin of the message
* @param[in] message #LinphoneChatMessage obj
* @return #LinphoneAddress
*/
LINPHONE_PUBLIC const LinphoneAddress* linphone_chat_message_get_from_address(const LinphoneChatMessage* message);
#define linphone_chat_message_set_to(msg, addr) linphone_chat_message_set_to_address(msg, addr)
/**
* Set destination of the message
* @param[in] message #LinphoneChatMessage obj
* @param[in] addr #LinphoneAddress destination of this message (copied)
*/
LINPHONE_PUBLIC void linphone_chat_message_set_to_address(LinphoneChatMessage* message, const LinphoneAddress* addr);
/** @deprecated Use linphone_chat_message_get_to_address() instead. */
#define linphone_chat_message_get_to(msg) linphone_chat_message_get_to_address(msg)
/**
* Get destination of the message
* @param[in] message #LinphoneChatMessage obj
* @return #LinphoneAddress
*/
LINPHONE_PUBLIC const LinphoneAddress* linphone_chat_message_get_to_address(const LinphoneChatMessage* message);
/**
* Linphone message can carry external body as defined by rfc2017
* @param message #LinphoneChatMessage
* @return external body url or NULL if not present.
*/
LINPHONE_PUBLIC const char* linphone_chat_message_get_external_body_url(const LinphoneChatMessage* message);
/**
* Linphone message can carry external body as defined by rfc2017
*
@ -418,6 +435,7 @@ LINPHONE_PUBLIC const char* linphone_chat_message_get_external_body_url(const Li
* @param url ex: access-type=URL; URL="http://www.foo.com/file"
*/
LINPHONE_PUBLIC void linphone_chat_message_set_external_body_url(LinphoneChatMessage* message,const char* url);
/**
* Get the file_transfer_information (used by call backs to recover informations during a rcs file transfer)
*
@ -425,6 +443,22 @@ LINPHONE_PUBLIC void linphone_chat_message_set_external_body_url(LinphoneChatMes
* @return a pointer to the LinphoneContent structure or NULL if not present.
*/
LINPHONE_PUBLIC const LinphoneContent* linphone_chat_message_get_file_transfer_information(const LinphoneChatMessage* message);
/**
* Get the content type of a chat message.
* @param[in] message LinphoneChatMessage object
* @return The content type of the chat message
*/
LINPHONE_PUBLIC const char * linphone_chat_message_get_content_type(const LinphoneChatMessage *message);
/**
* Set the content type of a chat message.
* This content type must match a content that is text representable, such as text/plain, text/html or image/svg+xml.
* @param[in] message LinphoneChatMessage object
* @param[in] content_type The new content type of the chat message
*/
LINPHONE_PUBLIC void linphone_chat_message_set_content_type(LinphoneChatMessage *message, const char *content_type);
/**
* Start the download of the file from remote server
*
@ -434,16 +468,19 @@ LINPHONE_PUBLIC const LinphoneContent* linphone_chat_message_get_file_transfer_i
* @deprecated Use linphone_chat_message_download_file() instead.
*/
LINPHONE_PUBLIC LINPHONE_DEPRECATED void linphone_chat_message_start_file_download(LinphoneChatMessage* message, LinphoneChatMessageStateChangedCb status_cb, void* ud);
/**
* Start the download of the file referenced in a LinphoneChatMessage from remote server.
* @param[in] message LinphoneChatMessage object.
*/
LINPHONE_PUBLIC int linphone_chat_message_download_file(LinphoneChatMessage *message);
/**
* Cancel an ongoing file transfer attached to this message.(upload or download)
* @param msg #LinphoneChatMessage
*/
LINPHONE_PUBLIC void linphone_chat_message_cancel_file_transfer(LinphoneChatMessage* msg);
/**
* Linphone message has an app-specific field that can store a text. The application might want
* to use it for keeping data over restarts, like thumbnail image path.
@ -451,6 +488,7 @@ LINPHONE_PUBLIC void linphone_chat_message_cancel_file_transfer(LinphoneChatMess
* @return the application-specific data or NULL if none has been stored.
*/
LINPHONE_PUBLIC const char* linphone_chat_message_get_appdata(const LinphoneChatMessage* message);
/**
* Linphone message has an app-specific field that can store a text. The application might want
* to use it for keeping data over restarts, like thumbnail image path.
@ -462,35 +500,42 @@ LINPHONE_PUBLIC const char* linphone_chat_message_get_appdata(const LinphoneChat
* @param data the data to store into the message
*/
LINPHONE_PUBLIC void linphone_chat_message_set_appdata(LinphoneChatMessage* message, const char* data);
/**
* Get text part of this message
* @return text or NULL if no text.
*/
LINPHONE_PUBLIC const char* linphone_chat_message_get_text(const LinphoneChatMessage* message);
/**
* Get the time the message was sent.
*/
LINPHONE_PUBLIC time_t linphone_chat_message_get_time(const LinphoneChatMessage* message);
/**
* User pointer get function
*/
LINPHONE_PUBLIC void* linphone_chat_message_get_user_data(const LinphoneChatMessage* message);
/**
*User pointer set function
*/
LINPHONE_PUBLIC void linphone_chat_message_set_user_data(LinphoneChatMessage* message,void*);
/**
* Returns the chatroom this message belongs to.
**/
LINPHONE_PUBLIC LinphoneChatRoom* linphone_chat_message_get_chat_room(LinphoneChatMessage *msg);
LINPHONE_PUBLIC const LinphoneAddress* linphone_chat_message_get_peer_address(LinphoneChatMessage *msg);
/**
* Returns the origin address of a message if it was a outgoing message, or the destination address if it was an incoming message.
*@param message #LinphoneChatMessage obj
*@return #LinphoneAddress
*/
LINPHONE_PUBLIC LinphoneAddress *linphone_chat_message_get_local_address(const LinphoneChatMessage* message);
/**
* Add custom headers to the message.
* @param message the message
@ -498,47 +543,56 @@ LINPHONE_PUBLIC LinphoneAddress *linphone_chat_message_get_local_address(const L
* @param header_value header value
**/
LINPHONE_PUBLIC void linphone_chat_message_add_custom_header(LinphoneChatMessage* message, const char *header_name, const char *header_value);
/**
* Retrieve a custom header value given its name.
* @param message the message
* @param header_name header name searched
**/
LINPHONE_PUBLIC const char * linphone_chat_message_get_custom_header(LinphoneChatMessage* message, const char *header_name);
/**
* Removes a custom header from the message.
* @param msg the message
* @param header_name name of the header to remove
**/
LINPHONE_PUBLIC void linphone_chat_message_remove_custom_header(LinphoneChatMessage *msg, const char *header_name);
/**
* Returns TRUE if the message has been read, otherwise returns FALSE.
* @param message the message
**/
LINPHONE_PUBLIC bool_t linphone_chat_message_is_read(LinphoneChatMessage* message);
/**
* Returns TRUE if the message has been sent, returns FALSE if the message has been received.
* @param message the message
**/
LINPHONE_PUBLIC bool_t linphone_chat_message_is_outgoing(LinphoneChatMessage* message);
/**
* Returns the id used to identify this message in the storage database
* @param message the message
* @return the id
*/
LINPHONE_PUBLIC unsigned int linphone_chat_message_get_storage_id(LinphoneChatMessage* message);
LINPHONE_PUBLIC LinphoneReason linphone_chat_message_get_reason(LinphoneChatMessage* msg);
/**
* Get full details about delivery error of a chat message.
* @param msg a LinphoneChatMessage
* @return a LinphoneErrorInfo describing the details.
**/
LINPHONE_PUBLIC const LinphoneErrorInfo *linphone_chat_message_get_error_info(const LinphoneChatMessage *msg);
/**
* Set the path to the file to read from or write to during the file transfer.
* @param[in] msg LinphoneChatMessage object
* @param[in] filepath The path to the file to use for the file transfer.
*/
LINPHONE_PUBLIC void linphone_chat_message_set_file_transfer_filepath(LinphoneChatMessage *msg, const char *filepath);
/**
* Get the path to the file to read from or write to during the file transfer.
* @param[in] msg LinphoneChatMessage object

View file

@ -3206,7 +3206,7 @@ LINPHONE_PUBLIC void linphone_core_set_root_ca_data(LinphoneCore *lc, const char
* Set the pointer to an externally provided ssl configuration for the crypto library
* @param lc #LinphoneCore object
* @param[in] ssl_config A pointer to an opaque structure which will be provided directly to the crypto library used in bctoolbox. Use with extra care.
* This ssl_config structure is responsability of the caller and will not be freed at the connection's end.
* This ssl_config structure is responsibility of the caller and will not be freed at the connection's end.
* @ingroup initializing
* @endinternal
*/
@ -3374,7 +3374,7 @@ LINPHONE_PUBLIC const bctbx_list_t * linphone_core_get_call_logs(LinphoneCore *l
/**
* Get the list of call logs (past calls) that matches the given #LinphoneAddress.
* At the contrary of linphone_core_get_call_logs, it is your responsability to unref the logs and free this list once you are done using it.
* At the contrary of linphone_core_get_call_logs, it is your responsibility to unref the logs and free this list once you are done using it.
* @param[in] lc LinphoneCore object
* @param[in] addr LinphoneAddress object
* @return \bctbx_list{LinphoneCallLog}
@ -4920,6 +4920,22 @@ LINPHONE_PUBLIC void linphone_core_set_im_encryption_engine(LinphoneCore *lc, Li
*/
LINPHONE_PUBLIC LinphoneImEncryptionEngine * linphone_core_get_im_encryption_engine(const LinphoneCore *lc);
/**
* Tells whether a content type is supported.
* @param[in] lc LinphoneCore object
* @param[in] content_type The content type to check
* @return A boolean value telling whether the specified content type is supported or not.
*/
LINPHONE_PUBLIC bool_t linphone_core_is_content_type_supported(const LinphoneCore *lc, const char *content_type);
/**
* Add support for the specified content type.
* It is the application responsibility to handle it correctly afterwards.
* @param[in] lc LinphoneCore object
* @param[in] content_type The content type to add support for
*/
LINPHONE_PUBLIC void linphone_core_add_content_type_support(LinphoneCore *lc, const char *content_type);
#include "linphone/ringtoneplayer.h"
#include "linphone/factory.h"

View file

@ -307,16 +307,6 @@ typedef struct SalMessage{
time_t time;
}SalMessage;
typedef struct SalIsComposing {
const char *from;
const char *text;
} SalIsComposing;
typedef struct SalImdn {
const char *from;
const char *content;
} SalImdn;
#define SAL_MEDIA_DESCRIPTION_MAX_MESSAGE_ATTRIBUTES 5
SalMediaDescription *sal_media_description_new(void);
@ -447,11 +437,11 @@ typedef enum SalSubscribeStatus{
SalSubscribeTerminated
}SalSubscribeStatus;
typedef enum SalTextDeliveryStatus{
SalTextDeliveryInProgress,
SalTextDeliveryDone,
SalTextDeliveryFailed
}SalTextDeliveryStatus;
typedef enum SalMessageDeliveryStatus{
SalMessageDeliveryInProgress,
SalMessageDeliveryDone,
SalMessageDeliveryFailed
}SalMessageDeliveryStatus;
/**
* auth event mode
@ -505,10 +495,8 @@ typedef void (*SalOnRegisterFailure)(SalOp *op);
typedef void (*SalOnVfuRequest)(SalOp *op);
typedef void (*SalOnDtmfReceived)(SalOp *op, char dtmf);
typedef void (*SalOnRefer)(Sal *sal, SalOp *op, const char *referto);
typedef void (*SalOnTextReceived)(SalOp *op, const SalMessage *msg);
typedef void (*SalOnTextDeliveryUpdate)(SalOp *op, SalTextDeliveryStatus);
typedef void (*SalOnIsComposingReceived)(SalOp *op, const SalIsComposing *is_composing);
typedef void (*SalOnImdnReceived)(SalOp *op, const SalImdn *imdn);
typedef void (*SalOnMessageReceived)(SalOp *op, const SalMessage *msg);
typedef void (*SalOnMessageDeliveryUpdate)(SalOp *op, SalMessageDeliveryStatus);
typedef void (*SalOnNotifyRefer)(SalOp *op, SalReferStatus state);
typedef void (*SalOnSubscribeResponse)(SalOp *op, SalSubscribeStatus status, int will_retry);
typedef void (*SalOnNotify)(SalOp *op, SalSubscribeStatus status, const char *event, SalBodyHandler *body);
@ -544,10 +532,8 @@ typedef struct SalCallbacks{
SalOnVfuRequest vfu_request;
SalOnDtmfReceived dtmf_received;
SalOnRefer refer_received;
SalOnTextReceived text_received;
SalOnTextDeliveryUpdate text_delivery_update;
SalOnIsComposingReceived is_composing_received;
SalOnImdnReceived imdn_received;
SalOnMessageReceived message_received;
SalOnMessageDeliveryUpdate message_delivery_update;
SalOnNotifyRefer notify_refer;
SalOnSubscribeReceived subscribe_received;
SalOnIncomingSubscribeClosed incoming_subscribe_closed;
@ -644,6 +630,8 @@ void sal_use_tcp_tls_keepalive(Sal *ctx, bool_t enabled);
int sal_set_tunnel(Sal *ctx, void *tunnelclient);
/*Default value is true*/
void sal_enable_sip_update_method(Sal *ctx,bool_t value);
bool_t sal_is_content_type_supported(const Sal *sal, const char *content_type);
void sal_add_content_type_support(Sal *sal, const char *content_type);
/**
* returns keepalive period in ms

View file

@ -1,4 +0,0 @@
[Dolphin]
PreviewsShown=true
Timestamp=2015,8,3,13,7,36
Version=3

View file

@ -130,9 +130,11 @@ set(RC_FILES
rcfiles/zero_length_params_rc
)
set(IMAGE_FILES
set(IMAGE_FILES
images/linphone.svg
images/nowebcamCIF.jpg
images/nowebcamVGA.jpg)
images/nowebcamVGA.jpg
)
set(VCARD_FILES
vcards/thousand_vcards.vcf

View file

@ -78,7 +78,7 @@ RCFILES = \
rcfiles/friends_rc\
rcfiles/carddav_rc
IMAGE_FILES = images/nowebcamCIF.jpg images/nowebcamVGA.jpg
IMAGE_FILES = images/linphone.svg images/nowebcamCIF.jpg images/nowebcamVGA.jpg
VCARDS_FILE = vcards/vcards.vcf vcards/thousand_vcards.vcf

View file

@ -0,0 +1,83 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 14.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 43363) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Calque_2" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<g>
<g>
<path fill="#C95328" d="M512,433.229C512,476.562,476.557,512,433.228,512H78.768C35.452,512,0,476.562,0,433.229V78.763
C0,35.43,35.452,0,78.768,0h354.46C476.557,0,512,35.43,512,78.763V433.229z"/>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="256.0044" y1="498.877" x2="256.0044" y2="13.1323">
<stop offset="0" style="stop-color:#E6B043"/>
<stop offset="0.5" style="stop-color:#C95328"/>
</linearGradient>
<path fill="url(#SVGID_1_)" d="M78.768,498.877c-36.188,0-65.632-29.438-65.632-65.648V78.763
c0-36.21,29.443-65.631,65.632-65.631h354.46c36.202,0,65.645,29.421,65.645,65.631v354.465c0,36.21-29.443,65.648-65.645,65.648
H78.768z"/>
</g>
<g>
<g>
<g>
<path fill="#C95328" d="M473.412,129.19c4.022,16.057,11.006,21.111,14.433,6.539c2.626-12.988-0.907-31.173-15.294-50.564
c-28.484-41.915-103.78-66.186-162.935-44.793c-31.137,11.984-24.661,13.922,7.499,7.175
c58.118-10.362,116.214,15.009,141.418,51.351C466.034,109.169,470.832,119.557,473.412,129.19z"/>
<path fill="#C95328" d="M419.015,143.098c7.494,14.941,12.717,26.531,19.203,25.729c6.259-0.803,15.077-12.937,11.313-29.547
c-2.509-10.809-8.09-22.524-17.543-33.749c-28.203-35.815-93.65-54.292-150.259-31.286
c-29.453,11.077-49.386,32.979-50.211,36.716c-1.109,4.036,18.227-9.882,45.146-18.015
c35.711-11.303,76.217-6.516,105.15,11.224C400.18,115.171,412.271,129.452,419.015,143.098z"/>
<path fill="#C95328" d="M272.768,116.451c-23.938,6.501-41.564,24.912-39.126,28.578c2.162,3.725,15.671-0.811,29.184-4.844
c8.038-2.487,16.081-3.953,23.938-4.465c25.749-1.574,45.468,6.943,57.738,17.625c11.416,9.433,22.448,19.878,31.148,19.694
c8.566-0.117,15.136-11.611,7.022-24.39c-8.806-13.192-24.526-26.198-47.588-32.772
C316.077,110.415,294.023,110.359,272.768,116.451z"/>
</g>
</g>
<g>
<g>
<path fill="#FFFFFF" d="M471.9,135.938c4.029,16.063,11.007,21.124,14.451,6.533c2.614-12.977-0.917-31.161-15.322-50.563
c-28.482-41.924-103.761-66.172-162.917-44.792c-31.146,11.995-24.653,13.946,7.502,7.184
c58.08-10.377,116.211,14.988,141.407,51.347C464.529,115.924,469.323,126.286,471.9,135.938z"/>
<path fill="#FFFFFF" d="M417.504,149.867c7.505,14.928,12.724,26.518,19.188,25.708c6.278-0.797,15.089-12.928,11.332-29.564
c-2.513-10.809-8.095-22.504-17.548-33.728C402.261,76.469,336.838,58,280.209,81.002
c-29.433,11.073-49.345,32.962-50.192,36.706c-1.089,4.03,18.224-9.864,45.125-17.983c35.73-11.315,76.235-6.557,105.167,11.199
C398.668,121.933,410.762,136.223,417.504,149.867z"/>
<path fill="#FFFFFF" d="M271.267,123.201c-23.948,6.485-41.572,24.917-39.134,28.582c2.168,3.729,15.69-0.8,29.175-4.826
c8.052-2.511,16.09-3.969,23.948-4.463c25.726-1.586,45.473,6.911,57.743,17.604c11.416,9.432,22.457,19.879,31.142,19.694
c8.571-0.115,15.134-11.604,7.032-24.405c-8.817-13.18-24.553-26.163-47.588-32.739
C314.582,117.183,292.515,117.094,271.267,123.201z"/>
</g>
</g>
</g>
<path fill="none" d="M78.768,499.246c-36.188,0-65.632-29.439-65.632-65.649V79.132c0-36.211,29.443-65.632,65.632-65.632h354.46
c36.202,0,65.645,29.421,65.645,65.632v354.465c0,36.21-29.443,65.649-65.645,65.649H78.768z"/>
<g>
<path fill="#C95328" d="M137.141,473.474c29.627,7.798,61.013,10.667,86.881,4.377c48.737-11.859,64.443-28.113,61.93-68.991
c0,0,0.908-19.719-40.68-18.982c-9.21,0.167-32.732,5.088-36.07,15.193c-3.342,10.114,1.719,20.368,1.991,25.324
s-13.443,11.676-19.206,12.246c-7.496,0.736-26.381-0.456-45.438-5.158l-0.82-0.211c-18.89-5.324-35.903-13.613-42.053-17.955
c-4.732-3.343-13.342-15.948-10.658-20.132c2.684-4.176,12.149-10.597,14.237-21.035c2.088-10.438-15.934-26.333-23.864-31.026
c-35.798-21.185-44.75-3.579-44.75-3.579c-22.373,34.308-16.737,56.193,19.789,90.57c19.386,18.245,48.101,31.245,77.711,39.097
L137.141,473.474z"/>
<path fill="#FFFFFF" d="M135.768,478.956c29.632,7.799,61.018,10.667,86.882,4.368c48.741-11.851,64.447-28.104,61.93-68.982
c0,0,0.912-19.728-40.676-18.982c-9.215,0.167-32.732,5.079-36.074,15.193c-3.338,10.114,1.719,20.368,1.996,25.324
c0.272,4.956-13.443,11.676-19.21,12.246c-7.491,0.736-26.382-0.456-45.435-5.158l-0.82-0.21
c-18.891-5.325-35.904-13.614-42.057-17.956c-4.728-3.343-13.342-15.957-10.658-20.132c2.689-4.176,12.149-10.597,14.241-21.035
c2.088-10.447-15.938-26.333-23.868-31.026c-35.794-21.185-44.746-3.588-44.746-3.588c-22.373,34.307-16.737,56.193,19.79,90.579
c19.386,18.236,48.097,31.245,77.71,39.097L135.768,478.956z"/>
</g>
<path fill="#C95328" d="M301.623,271.947l29.996-80.491l-103.663,42.298c-2.732-0.666-5.474-1.368-7.509-2.052
c-26.605-8.176-51.837-11.605-76.395-12.264c-32.062,0.658-61.403,5.438-87.325,15c-16.438,6.843-30.881,15.378-43.658,25.491
v45.518c2.197-2.176,4.412-4.324,6.815-6.201c12.97-10.264,26.605-17.729,40.943-23.211c14.329-4.771,27.969-8.167,40.921-10.229
c12.29-1.351,21.838-2.719,27.978-2.719c22.513,0,45.026,3.412,66.833,9.544c22.513,6.149,43.675,15.685,62.754,27.947
c18.443,12.982,34.127,28.667,45.724,47.745c6.833,11.614,11.596,21.869,15,32.79c3.408,10.22,5.447,20.456,5.447,30
c-0.667,19.097-6.811,37.526-19.096,53.877c-9.505,14.026-23.207,25.22-40.601,34.447h51.587
c7.053-5.859,14.189-12.649,21.759-20.78c8.189-9.535,15.698-21.132,21.163-34.816c6.123-13.631,9.54-28.64,9.54-44.99
c-0.697-28.658-7.509-54.605-21.847-77.088C335.719,302.632,320.04,285.605,301.623,271.947z"/>
<path fill="#FFFFFF" d="M299.688,280.614l30.004-80.518l-103.662,42.289c-2.732-0.675-5.474-1.351-7.509-2.044
c-26.605-8.184-51.833-11.604-76.399-12.254c-32.066,0.649-61.399,5.438-87.32,14.982c-15.759,6.579-29.645,14.729-42.035,24.281
v45.245c1.71-1.605,3.36-3.298,5.188-4.72c12.974-10.254,26.605-17.71,40.938-23.184c14.338-4.781,27.974-8.185,40.926-10.255
c12.29-1.333,21.838-2.719,27.978-2.719c22.518,0,45.026,3.43,66.829,9.544c22.513,6.157,43.684,15.701,62.763,27.982
c18.438,12.947,34.127,28.648,45.715,47.728c6.824,11.614,11.605,21.833,15.009,32.78c3.408,10.211,5.447,20.457,5.447,29.992
c-0.667,19.113-6.816,37.535-19.097,53.912c-6.623,9.754-15.324,18.131-25.842,25.456h46.868
c3.825-3.632,7.702-7.509,11.719-11.825c8.189-9.518,15.693-21.131,21.158-34.78c6.118-13.649,9.543-28.658,9.543-45.009
c-0.697-28.693-7.509-54.605-21.851-77.088C333.794,311.281,318.114,294.237,299.688,280.614z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.7 KiB

View file

@ -279,6 +279,10 @@ void text_message_base(LinphoneCoreManager* marie, LinphoneCoreManager* pauline)
BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneMessageDelivered,1));
BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceived,1));
BC_ASSERT_PTR_NOT_NULL(marie->stat.last_received_chat_message);
if (marie->stat.last_received_chat_message != NULL) {
BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_content_type(marie->stat.last_received_chat_message), "text/plain");
}
BC_ASSERT_PTR_NOT_NULL(linphone_core_get_chat_room(marie->lc,pauline->identity));
}
@ -1410,6 +1414,7 @@ static void lime_unit(void) {
xmlDocPtr cacheBufferBob;
uint8_t *multipartMessage = NULL;
uint8_t *decryptedMessage = NULL;
char *decryptedContentType = NULL;
xmlChar *xmlStringOutput;
int xmlStringLength;
limeURIKeys_t associatedKeys;
@ -1529,7 +1534,7 @@ static void lime_unit(void) {
/* encrypt a msg */
retval = lime_createMultipartMessage(cacheBufferAlice, (uint8_t *)PLAIN_TEXT_TEST_MESSAGE, (uint8_t *)"sip:pauline@sip.example.org", &multipartMessage);
retval = lime_createMultipartMessage(cacheBufferAlice, "text/plain", (uint8_t *)PLAIN_TEXT_TEST_MESSAGE, (uint8_t *)"sip:pauline@sip.example.org", &multipartMessage);
BC_ASSERT_EQUAL(retval, 0, int, "%d");
if (retval == 0) {
@ -1537,15 +1542,17 @@ static void lime_unit(void) {
}
/* decrypt the multipart msg */
retval = lime_decryptMultipartMessage(cacheBufferBob, multipartMessage, &decryptedMessage);
retval = lime_decryptMultipartMessage(cacheBufferBob, multipartMessage, &decryptedMessage, &decryptedContentType);
BC_ASSERT_EQUAL(retval, 0, int, "%d");
if (retval == 0) {
BC_ASSERT_STRING_EQUAL((char *)decryptedMessage, (char *)PLAIN_TEXT_TEST_MESSAGE);
BC_ASSERT_STRING_EQUAL((char *)decryptedContentType, "text/plain");
ms_message("Succesfully decrypted msg is %s", decryptedMessage);
}
free(multipartMessage);
free(decryptedMessage);
ms_free(multipartMessage);
ms_free(decryptedMessage);
ms_free(decryptedContentType);
/* update ZID files */
/* dump the xml document into a string */
@ -2304,6 +2311,64 @@ void chat_message_custom_headers(void) {
linphone_core_manager_destroy(pauline);
}
void _text_message_with_custom_content_type(bool_t with_lime) {
LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc");
LinphoneCoreManager *pauline = linphone_core_manager_new("pauline_tcp_rc");
LinphoneChatRoom *chat_room = linphone_core_get_chat_room(pauline->lc, marie->identity);
LinphoneChatMessage *msg;
LinphoneChatMessageCbs *cbs;
bctbx_vfs_t *vfs = bctbx_vfs_get_default();
char *send_filepath;
bctbx_vfs_file_t *file_to_send;
size_t file_size;
char *buf;
if (with_lime) {
if (enable_lime_for_message_test(marie, pauline) < 0) goto end;
}
send_filepath = bc_tester_res("images/linphone.svg");
file_to_send = bctbx_file_open(vfs, send_filepath, "r");
file_size = (size_t)bctbx_file_size(file_to_send);
buf = bctbx_malloc(file_size + 1);
bctbx_file_read(file_to_send, buf, file_size, 0);
buf[file_size] = '\0';
bctbx_file_close(file_to_send);
bc_free(send_filepath);
msg = linphone_chat_room_create_message(chat_room, buf);
linphone_chat_message_set_content_type(msg, "image/svg+xml");
linphone_core_add_content_type_support(marie->lc, "image/svg+xml");
linphone_core_add_content_type_support(pauline->lc, "image/svg+xml");
cbs = linphone_chat_message_get_callbacks(msg);
linphone_chat_message_cbs_set_msg_state_changed(cbs, liblinphone_tester_chat_message_msg_state_changed);
linphone_chat_room_send_chat_message(chat_room, msg);
BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneMessageReceived, 1));
BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &pauline->stat.number_of_LinphoneMessageDelivered, 1));
if (marie->stat.last_received_chat_message) {
BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_content_type(marie->stat.last_received_chat_message), "image/svg+xml");
BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(marie->stat.last_received_chat_message), buf);
}
bctbx_free(buf);
end:
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
remove("tmpZIDCacheMarie.xml");
remove("tmpZIDCachePauline.xml");
}
void text_message_with_custom_content_type(void) {
_text_message_with_custom_content_type(FALSE);
}
void text_message_with_custom_content_type_and_lime(void) {
_text_message_with_custom_content_type(TRUE);
}
test_t message_tests[] = {
TEST_NO_TAG("Text message", text_message),
TEST_NO_TAG("Text message within call dialog", text_message_within_call_dialog),
@ -2371,7 +2436,9 @@ test_t message_tests[] = {
TEST_ONE_TAG("Real Time Text offer answer with different payload numbers (sender side)", real_time_text_message_different_text_codecs_payload_numbers_sender_side, "RTT"),
TEST_ONE_TAG("Real Time Text offer answer with different payload numbers (receiver side)", real_time_text_message_different_text_codecs_payload_numbers_receiver_side, "RTT"),
TEST_ONE_TAG("Real Time Text copy paste", real_time_text_copy_paste, "RTT"),
TEST_NO_TAG("IM Encryption Engine custom headers", chat_message_custom_headers)
TEST_NO_TAG("IM Encryption Engine custom headers", chat_message_custom_headers),
TEST_NO_TAG("Text message with custom content-type", text_message_with_custom_content_type),
TEST_ONE_TAG("Text message with custom content-type and lime", text_message_with_custom_content_type_and_lime, "LIME")
};
test_suite_t message_test_suite = {