Merge remote-tracking branch 'private/dev_im_encryption_engine'

This commit is contained in:
Sylvain Berfini 2016-12-01 11:12:10 +01:00
commit 9a26b72164
16 changed files with 1152 additions and 433 deletions

View file

@ -37,6 +37,7 @@ set(LINPHONE_PRIVATE_HEADER_FILES
contact_providers_priv.h
enum.h
lime.h
im_encryption_engine.h
lpc2xml.h
offeranswer.h
private.h
@ -82,6 +83,7 @@ set(LINPHONE_SOURCE_FILES_C
info.c
ldapprovider.c
lime.c
im_encryption_engine.c
linphonecall.c
linphonecore.c
linphone_tunnel_config.c

View file

@ -53,6 +53,7 @@ liblinphone_la_SOURCES=\
localplayer.c \
lpc2xml.c lpc2xml.h \
lime.c lime.h\
im_encryption_engine.c \
lpconfig.c \
lsd.c \
message_storage.c \

View file

@ -993,6 +993,14 @@ const char *sal_custom_header_find(const SalCustomHeader *ch, const char *name){
return NULL;
}
SalCustomHeader *sal_custom_header_remove(SalCustomHeader *ch, const char *name) {
belle_sip_message_t *msg=(belle_sip_message_t*)ch;
if (msg==NULL) return NULL;
belle_sip_message_remove_header(msg, name);
return (SalCustomHeader*)msg;
}
void sal_custom_header_free(SalCustomHeader *ch){
if (ch==NULL) return;
belle_sip_object_unref((belle_sip_message_t*)ch);

View file

@ -20,7 +20,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include "linphone/core.h"
#include "private.h"
#include "lime.h"
#include <libxml/xmlwriter.h>
static void process_error( SalOp* op) {
@ -60,23 +59,6 @@ static void process_response_event(void *op_base, const belle_sip_response_event
op->base.root->callbacks.text_delivery_update(op,status);
}
static bool_t is_rcs_filetransfer(belle_sip_header_content_type_t* content_type) {
return (strcmp("application",belle_sip_header_content_type_get_type(content_type))==0)
&& ((strcmp("vnd.gsma.rcs-ft-http+xml",belle_sip_header_content_type_get_subtype(content_type))==0) || (strcmp("cipher.vnd.gsma.rcs-ft-http+xml",belle_sip_header_content_type_get_subtype(content_type))==0));
}
static bool_t is_plain_text(belle_sip_header_content_type_t* content_type) {
return strcmp("text",belle_sip_header_content_type_get_type(content_type))==0
&& strcmp("plain",belle_sip_header_content_type_get_subtype(content_type))==0;
}
static bool_t is_cipher_xml(belle_sip_header_content_type_t* content_type) {
return (strcmp("xml",belle_sip_header_content_type_get_type(content_type))==0
&& strcmp("cipher",belle_sip_header_content_type_get_subtype(content_type))==0)
|| (strcmp("application",belle_sip_header_content_type_get_type(content_type))==0
&& strcmp("cipher.vnd.gsma.rcs-ft-http+xml",belle_sip_header_content_type_get_subtype(content_type))==0);
}
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;
@ -97,79 +79,34 @@ void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *eve
belle_sip_header_from_t* from_header;
belle_sip_header_content_type_t* content_type;
belle_sip_response_t* resp;
int errcode=500;
int errcode = 500;
belle_sip_header_call_id_t* call_id = belle_sip_message_get_header_by_type(req,belle_sip_header_call_id_t);
belle_sip_header_cseq_t* cseq = belle_sip_message_get_header_by_type(req,belle_sip_header_cseq_t);
belle_sip_header_date_t *date=belle_sip_message_get_header_by_type(req,belle_sip_header_date_t);
char* from;
bool_t plain_text=FALSE;
bool_t external_body=FALSE;
bool_t cipher_xml=FALSE;
bool_t rcs_filetransfer=FALSE;
uint8_t *decryptedMessage = NULL;
from_header=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_from_t);
content_type=belle_sip_message_get_header_by_type(BELLE_SIP_MESSAGE(req),belle_sip_header_content_type_t);
if (content_type){
/* check if we have a xml/cipher message to be decrypted */
if ((cipher_xml=is_cipher_xml(content_type))) {
/* access the zrtp cache to get keys needed to decipher the message */
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
FILE *CACHEFD = NULL;
if (lc->zrtp_secrets_cache != NULL) CACHEFD = fopen(lc->zrtp_secrets_cache, "rb+");
if (CACHEFD == NULL) {
ms_warning("Unable to access ZRTP ZID cache to decrypt message");
goto error;
} else {
size_t cacheSize;
char *cacheString;
int retval;
xmlDocPtr cacheXml;
cacheString=ms_load_file_content(CACHEFD, &cacheSize);
if (!cacheString){
ms_warning("Unable to load content of ZRTP ZID cache to decrypt message");
goto error;
}
cacheString[cacheSize] = '\0';
cacheSize += 1;
fclose(CACHEFD);
cacheXml = xmlParseDoc((xmlChar*)cacheString);
ms_free(cacheString);
retval = lime_decryptMultipartMessage(cacheXml, (uint8_t *)belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)), &decryptedMessage);
if (retval != 0) {
ms_warning("Unable to decrypt message, reason : %s - op [%p]", lime_error_code_to_string(retval), op);
free(decryptedMessage);
xmlFreeDoc(cacheXml);
errcode = 488;
goto error;
} else {
/* dump updated cache to a string */
xmlChar *xmlStringOutput;
int xmlStringLength;
xmlDocDumpFormatMemoryEnc(cacheXml, &xmlStringOutput, &xmlStringLength, "UTF-8", 0);
/* write it to the cache file */
CACHEFD = fopen(lc->zrtp_secrets_cache, "wb+");
if (fwrite(xmlStringOutput, 1, xmlStringLength, CACHEFD)<=0){
ms_warning("Fail to write cache");
}
xmlFree(xmlStringOutput);
fclose(CACHEFD);
}
xmlFreeDoc(cacheXml);
}
}
external_body=is_external_body(content_type);
plain_text=is_plain_text(content_type);
rcs_filetransfer = is_rcs_filetransfer(content_type);
if (external_body || plain_text || rcs_filetransfer || decryptedMessage!=NULL) {
if (content_type) {
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);
resp = belle_sip_response_create_from_request(req,200);
belle_sip_server_transaction_send_response(server_transaction,resp);
belle_sip_object_unref(address);
belle_sip_free(from);
} else {
SalMessage salmsg;
char message_id[256]={0};
external_body=is_external_body(content_type);
if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans);
op->pending_server_trans=server_transaction;
@ -183,16 +120,9 @@ void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *eve
,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)*/
if (cipher_xml) {
salmsg.text = (char *)decryptedMessage;
} else { /* message body wasn't ciphered */
salmsg.text=(plain_text||rcs_filetransfer)?belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)):NULL;
}
salmsg.text=(!external_body)?belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)):NULL;
salmsg.url=NULL;
salmsg.content_type = NULL;
if (rcs_filetransfer) { /* if we have a rcs file transfer, set the type, message body (stored in salmsg.text) contains all needed information to retrieve the file */
salmsg.content_type = "application/vnd.gsma.rcs-ft-http+xml";
}
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 "*/
@ -202,28 +132,11 @@ void sal_process_incoming_message(SalOp *op,const belle_sip_request_event_t *eve
salmsg.time=date ? belle_sip_header_date_get_time(date) : time(NULL);
op->base.root->callbacks.text_received(op,&salmsg);
free(decryptedMessage);
belle_sip_object_unref(address);
belle_sip_free(from);
if (salmsg.url) ms_free((char*)salmsg.url);
} else 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);
resp = belle_sip_response_create_from_request(req,200);
belle_sip_server_transaction_send_response(server_transaction,resp);
belle_sip_object_unref(address);
belle_sip_free(from);
}else{
ms_error("Unsupported MESSAGE (content-type not recognized)");
errcode = 415;
goto error;
}
}else {
} else {
ms_error("Unsupported MESSAGE (no Content-Type)");
goto error;
}
@ -245,7 +158,6 @@ int sal_message_send(SalOp *op, const char *from, const char *to, const char* co
char content_type_raw[256];
size_t content_length = msg?strlen(msg):0;
time_t curtime = ms_time(NULL);
uint8_t *multipartEncryptedMessage = NULL;
const char *body;
int retval;
@ -269,71 +181,16 @@ int sal_message_send(SalOp *op, const char *from, const char *to, const char* co
}
}
/* shall we try to encrypt the message?*/
if ((strcmp(content_type, "xml/cipher") == 0) || ((strcmp(content_type, "application/cipher.vnd.gsma.rcs-ft-http+xml") == 0))) {
/* access the zrtp cache to get keys needed to cipher the message */
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
FILE *CACHEFD = fopen(lc->zrtp_secrets_cache, "rb+");
if (CACHEFD == NULL) {
ms_warning("Unable to access ZRTP ZID cache to encrypt message");
/*probably not a good idea to do this:*/
sal_error_info_set(&op->error_info, SalReasonNotAcceptable, 488, "Unable to encrypt IM", NULL);
op->base.root->callbacks.text_delivery_update(op,SalTextDeliveryFailed);
return -1;
} else {
size_t cacheSize;
char *cacheString;
xmlDocPtr cacheXml;
int retval;
cacheString=ms_load_file_content(CACHEFD, &cacheSize);
if (!cacheString){
ms_warning("Unable to load content of ZRTP ZID cache to encrypt message");
return -1;
}
cacheString[cacheSize] = '\0';
cacheSize += 1;
fclose(CACHEFD);
cacheXml = xmlParseDoc((xmlChar*)cacheString);
ms_free(cacheString);
retval = lime_createMultipartMessage(cacheXml, (uint8_t *)msg, (uint8_t *)peer_uri, &multipartEncryptedMessage);
if (retval != 0) {
ms_warning("Unable to encrypt message for %s : %s - op [%p]", peer_uri, lime_error_code_to_string(retval), op);
xmlFreeDoc(cacheXml);
free(multipartEncryptedMessage);
/*probably not a good idea to do this:*/
sal_error_info_set(&op->error_info, SalReasonNotAcceptable, 488, "Unable to encrypt IM", NULL);
op->base.root->callbacks.text_delivery_update(op,SalTextDeliveryFailed);
return -1;
} else {
/* dump updated cache to a string */
xmlChar *xmlStringOutput;
int xmlStringLength;
xmlDocDumpFormatMemoryEnc(cacheXml, &xmlStringOutput, &xmlStringLength, "UTF-8", 0);
/* write it to the cache file */
CACHEFD = fopen(lc->zrtp_secrets_cache, "wb+");
if (fwrite(xmlStringOutput, 1, xmlStringLength, CACHEFD)<=0){
ms_warning("Unable to write zid cache");
}
xmlFree(xmlStringOutput);
fclose(CACHEFD);
content_length = strlen((const char *)multipartEncryptedMessage);
}
xmlFreeDoc(cacheXml);
}
}
snprintf(content_type_raw,sizeof(content_type_raw),BELLE_SIP_CONTENT_TYPE ": %s",content_type);
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_content_type_parse(content_type_raw)));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_length)));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_date_create_from_time(&curtime)));
body = (multipartEncryptedMessage==NULL) ? msg : (char*) multipartEncryptedMessage;
body = msg;
if (body){
/*don't call set_body() with null argument because it resets content type and content length*/
belle_sip_message_set_body(BELLE_SIP_MESSAGE(req), body, content_length);
}
retval = sal_op_send_request(op,req);
free(multipartEncryptedMessage);
return retval;
}

View file

@ -1159,10 +1159,11 @@ static bool_t is_duplicate_msg(LinphoneCore *lc, const char *msg_id){
static void text_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);
if (lc->chat_deny_code==LinphoneReasonNone && is_duplicate_msg(lc,msg->message_id)==FALSE){
linphone_core_message_received(lc,op,msg);
LinphoneReason reason = lc->chat_deny_code;
if (reason == LinphoneReasonNone && is_duplicate_msg(lc, msg->message_id) == FALSE) {
reason = linphone_core_message_received(lc, op, msg);
}
sal_message_reply(op,linphone_reason_to_sal(lc->chat_deny_code));
sal_message_reply(op,linphone_reason_to_sal(reason));
if (!call) sal_op_release(op);
}

View file

@ -27,7 +27,6 @@
#include "linphone/lpconfig.h"
#include "belle-sip/belle-sip.h"
#include "ortp/b64.h"
#include "lime.h"
#include <libxml/parser.h>
#include <libxml/tree.h>
@ -265,50 +264,6 @@ LinphoneChatRoom *linphone_core_get_chat_room_from_uri(LinphoneCore *lc, const c
return _linphone_core_get_or_create_chat_room(lc, to);
}
bool_t linphone_chat_room_lime_available(LinphoneChatRoom *cr) {
if (cr) {
switch (linphone_core_lime_enabled(cr->lc)) {
case LinphoneLimeDisabled: return FALSE;
case LinphoneLimeMandatory: return TRUE;
case LinphoneLimePreferred: {
FILE *CACHEFD = NULL;
if (cr->lc->zrtp_secrets_cache != NULL) {
CACHEFD = fopen(cr->lc->zrtp_secrets_cache, "rb+");
if (CACHEFD) {
size_t cacheSize;
xmlDocPtr cacheXml;
char *cacheString=ms_load_file_content(CACHEFD, &cacheSize);
if (!cacheString){
ms_warning("Unable to load content of ZRTP ZID cache to decrypt message");
return FALSE;
}
cacheString[cacheSize] = '\0';
cacheSize += 1;
fclose(CACHEFD);
cacheXml = xmlParseDoc((xmlChar*)cacheString);
ms_free(cacheString);
if (cacheXml) {
bool_t res;
limeURIKeys_t associatedKeys;
/* retrieve keys associated to the peer URI */
associatedKeys.peerURI = (uint8_t *)malloc(strlen(cr->peer)+1);
strcpy((char *)(associatedKeys.peerURI), cr->peer);
associatedKeys.associatedZIDNumber = 0;
associatedKeys.peerKeys = NULL;
res = (lime_getCachedSndKeysByURI(cacheXml, &associatedKeys) == 0 && associatedKeys.associatedZIDNumber != 0);
lime_freeKeys(&associatedKeys);
xmlFreeDoc(cacheXml);
return res;
}
}
}
}
}
}
return FALSE;
}
static void linphone_chat_room_delete_composing_idle_timer(LinphoneChatRoom *cr) {
if (cr->composing_idle_timer) {
if (cr->lc && cr->lc->sal)
@ -366,6 +321,10 @@ void linphone_chat_room_set_user_data(LinphoneChatRoom *cr, void *ud) {
}
void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage *msg) {
int retval = -1;
LinphoneCore *lc = cr->lc;
LinphoneImEncryptionEngine *imee = lc->im_encryption_engine;
/*stubed rtt text*/
if (cr->call && linphone_call_params_realtime_text_enabled(linphone_call_get_current_params(cr->call))) {
uint32_t new_line = 0x2028;
@ -406,6 +365,15 @@ void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage
}
}
}
if (imee) {
LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee);
LinphoneImEncryptionEngineOutgoingMessageCb cb_process_outgoing_message = linphone_im_encryption_engine_cbs_get_process_outgoing_message(imee_cbs);
if (cb_process_outgoing_message) {
retval = cb_process_outgoing_message(lc, cr, msg);
}
}
if (op == NULL) {
LinphoneProxyConfig *proxy = linphone_core_lookup_known_proxy(cr->lc, cr->peer_url);
if (proxy) {
@ -418,6 +386,13 @@ void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage
lp_config_get_int(cr->lc->config, "sip", "chat_msg_with_contact", 0));
sal_op_set_user_pointer(op, msg); /*if out of call, directly store msg*/
}
if (retval > 0) {
sal_error_info_set((SalErrorInfo *)sal_op_get_error_info(op), SalReasonNotAcceptable, retval, "Unable to encrypt IM", NULL);
linphone_chat_message_update_state(msg, LinphoneChatMessageStateNotDelivered);
linphone_chat_message_unref(msg);
return;
}
if (msg->external_body_url) {
content_type = ms_strdup_printf("message/external-body; access-type=URL; URL=\"%s\"", msg->external_body_url);
@ -425,21 +400,7 @@ void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage
ms_free(content_type);
} else {
char *peer_uri = linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(cr));
const char *content_type;
if (linphone_chat_room_lime_available(cr)) {
/* ref the msg or it may be destroyed by callback if the encryption failed */
if (msg->content_type && 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";
} else {
content_type = "xml/cipher";
}
} else {
content_type = msg->content_type;
}
const char *content_type = msg->content_type;
if (content_type == NULL) {
sal_text_send(op, identity, cr->peer, msg->message);
} else {
@ -509,30 +470,68 @@ void linphone_chat_room_message_received(LinphoneChatRoom *cr, LinphoneCore *lc,
linphone_core_notify_is_composing_received(cr->lc, cr);
}
void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessage *sal_msg) {
LinphoneReason linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessage *sal_msg) {
LinphoneChatRoom *cr = NULL;
LinphoneAddress *addr;
LinphoneAddress *to;
LinphoneChatMessage *msg;
LinphoneImEncryptionEngine *imee = lc->im_encryption_engine;
const SalCustomHeader *ch;
LinphoneReason reason = LinphoneReasonNone;
int retval = -1;
addr = linphone_address_new(sal_msg->from);
linphone_address_clean(addr);
cr = linphone_core_get_chat_room(lc, addr);
if (sal_msg->content_type !=
NULL) { /* 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" */
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" */
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->is_read = FALSE;
msg->dir = LinphoneChatMessageIncoming;
ch = sal_op_get_recv_custom_header(op);
if (ch) {
msg->custom_headers = sal_custom_header_clone(ch);
}
if (sal_msg->url) {
linphone_chat_message_set_external_body_url(msg, sal_msg->url);
}
if (imee) {
LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee);
LinphoneImEncryptionEngineIncomingMessageCb 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(lc, 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) {
retval = 415;
ms_error("Unsupported MESSAGE (content-type %s not recognized)", msg->content_type);
}
if (retval > 0) {
reason = linphone_error_code_to_reason(retval);
goto end;
}
if (strcmp("application/vnd.gsma.rcs-ft-http+xml", msg->content_type) == 0) {
xmlChar *file_url = NULL;
xmlDocPtr xmlMessageBody;
xmlNodePtr cur;
msg = linphone_chat_room_create_message(cr, NULL); /* 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->file_transfer_information = linphone_content_new();
/* 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 *)sal_msg->text);
xmlMessageBody = xmlParseDoc((const xmlChar *)msg->message);
msg->file_transfer_information = linphone_content_new();
cur = xmlDocGetRootElement(xmlMessageBody);
if (cur != NULL) {
@ -579,18 +578,16 @@ void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessag
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 */
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,
strlen((char *)keyBuffer)); /* duplicate key value into the linphone content private structure */
linphone_content_set_key(msg->file_transfer_information, (char *)keyBuffer, keyLength);
/* duplicate key value into the linphone content private structure */
xmlFree(keyb64);
free(keyBuffer);
}
@ -609,31 +606,8 @@ void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessag
linphone_chat_message_set_external_body_url(msg, (const char *)file_url);
xmlFree(file_url);
} else { /* msg is not rcs file transfer, create it with provided sal_msg->text as ->msg */
msg = linphone_chat_room_create_message(cr, sal_msg->text);
}
linphone_chat_message_set_from(msg, cr->peer_url);
{
LinphoneAddress *to;
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->is_read = FALSE;
msg->dir = LinphoneChatMessageIncoming;
ch = sal_op_get_recv_custom_header(op);
if (ch)
msg->custom_headers = sal_custom_header_clone(ch);
if (sal_msg->url) {
linphone_chat_message_set_external_body_url(msg, sal_msg->url);
}
linphone_address_destroy(addr);
msg->storage_id = linphone_chat_message_store(msg);
if (cr->unread_count < 0)
@ -642,7 +616,11 @@ void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessag
cr->unread_count++;
linphone_chat_room_message_received(cr, lc, msg);
end:
linphone_address_destroy(addr);
linphone_chat_message_unref(msg);
return reason;
}
static int linphone_chat_room_remote_refresh_composing_expired(void *data, unsigned int revents) {
@ -1151,6 +1129,10 @@ const char *linphone_chat_message_get_custom_header(LinphoneChatMessage *msg, co
return sal_custom_header_find(msg->custom_headers, header_name);
}
void linphone_chat_message_remove_custom_header(LinphoneChatMessage *msg, const char *header_name) {
msg->custom_headers = sal_custom_header_remove(msg->custom_headers, header_name);
}
bool_t linphone_chat_message_is_read(LinphoneChatMessage *msg) {
return msg->is_read;
}

View file

@ -24,11 +24,8 @@
#include "linphone/core.h"
#include "private.h"
#include "lime.h"
#include "ortp/b64.h"
#define FILE_TRANSFER_KEY_SIZE 32
static bool_t file_transfer_in_progress_and_valid(LinphoneChatMessage* msg) {
return (msg->chat_room && msg->chat_room->lc && msg->http_request && !belle_http_request_is_cancelled(msg->http_request));
}
@ -94,11 +91,12 @@ static void linphone_chat_message_file_transfer_on_progress(belle_sip_body_handl
}
}
static int linphone_chat_message_file_transfer_on_send_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t *m,
static int on_send_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t *m,
void *data, size_t offset, uint8_t *buffer, size_t *size) {
LinphoneChatMessage *msg = (LinphoneChatMessage *)data;
LinphoneCore *lc = NULL;
char *buf = (char *)buffer;
LinphoneImEncryptionEngine *imee = NULL;
int retval = -1;
if (!file_transfer_in_progress_and_valid(msg)) {
if (msg->http_request) {
@ -109,52 +107,55 @@ static int linphone_chat_message_file_transfer_on_send_body(belle_sip_user_body_
}
lc = msg->chat_room->lc;
/* if we've not reach the end of file yet, ask for more data*/
/* if we've not reach the end of file yet, ask for more data */
/* in case of file body handler, won't be called */
if (offset < linphone_content_get_size(msg->file_transfer_information)) {
char *plainBuffer = NULL;
if (linphone_content_get_key(msg->file_transfer_information) !=
NULL) { /* if we have a key to cipher the msg, use it! */
/* if this chunk is not the last one, the lenght must be a multiple of block cipher size(16 bytes)*/
if (offset + *size < linphone_content_get_size(msg->file_transfer_information)) {
*size -= (*size % 16);
}
plainBuffer = (char *)ms_malloc0(*size);
}
/* get data from call back */
if (linphone_chat_message_cbs_get_file_transfer_send(msg->callbacks)) {
LinphoneBuffer *lb = linphone_chat_message_cbs_get_file_transfer_send(msg->callbacks)(
msg, msg->file_transfer_information, offset, *size);
LinphoneChatMessageCbsFileTransferSendCb file_transfer_send_cb = linphone_chat_message_cbs_get_file_transfer_send(msg->callbacks);
if (file_transfer_send_cb) {
LinphoneBuffer *lb = file_transfer_send_cb(msg, msg->file_transfer_information, offset, *size);
if (lb == NULL) {
*size = 0;
} else {
*size = linphone_buffer_get_size(lb);
memcpy(plainBuffer ? plainBuffer : buf, linphone_buffer_get_content(lb), *size);
memcpy(buffer, linphone_buffer_get_content(lb), *size);
linphone_buffer_unref(lb);
}
} else {
/* Legacy */
linphone_core_notify_file_transfer_send(lc, msg, msg->file_transfer_information,
plainBuffer ? plainBuffer : buf, size);
linphone_core_notify_file_transfer_send(lc, msg, msg->file_transfer_information, (char *)buffer, size);
}
if (linphone_content_get_key(msg->file_transfer_information) !=
NULL) { /* if we have a key to cipher the msg, use it! */
lime_encryptFile(linphone_content_get_cryptoContext_address(msg->file_transfer_information),
(unsigned char *)linphone_content_get_key(msg->file_transfer_information), *size,
plainBuffer, (char *)buffer);
ms_free(plainBuffer);
/* check if we reach the end of file */
if (offset + *size >= linphone_content_get_size(msg->file_transfer_information)) {
/* conclude file ciphering by calling it context with a zero size */
lime_encryptFile(linphone_content_get_cryptoContext_address(msg->file_transfer_information), NULL, 0,
NULL, NULL);
}
imee = linphone_core_get_im_encryption_engine(lc);
if (imee) {
LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee);
LinphoneImEncryptionEngineUploadingFileCb cb_process_uploading_file = linphone_im_encryption_engine_cbs_get_process_uploading_file(imee_cbs);
if (cb_process_uploading_file) {
char *encrypted_buffer = (char *)ms_malloc0(*size);
retval = cb_process_uploading_file(lc, msg, offset, (char *)buffer, size, encrypted_buffer);
if (retval == 0) {
memcpy(buffer, encrypted_buffer, *size);
}
ms_free(encrypted_buffer);
}
}
return BELLE_SIP_CONTINUE;
return retval <= 0 ? BELLE_SIP_CONTINUE : BELLE_SIP_STOP;
}
static void on_send_end(belle_sip_user_body_handler_t *bh, void *data) {
LinphoneChatMessage *msg = (LinphoneChatMessage *)data;
LinphoneCore *lc = msg->chat_room->lc;
LinphoneImEncryptionEngine *imee = linphone_core_get_im_encryption_engine(lc);
if (imee) {
LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee);
LinphoneImEncryptionEngineUploadingFileCb cb_process_uploading_file = linphone_im_encryption_engine_cbs_get_process_uploading_file(imee_cbs);
if (cb_process_uploading_file) {
cb_process_uploading_file(lc, msg, 0, NULL, NULL, NULL);
}
}
}
static void linphone_chat_message_process_response_from_post_file(void *data,
@ -176,21 +177,27 @@ static void linphone_chat_message_process_response_from_post_file(void *data,
char *first_part_header;
belle_sip_body_handler_t *first_part_bh;
bool_t is_file_encryption_enabled = FALSE;
LinphoneImEncryptionEngine *imee = linphone_core_get_im_encryption_engine(msg->chat_room->lc);
if (imee) {
LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee);
LinphoneImEncryptionEngineIsEncryptionEnabledForFileTransferCb is_encryption_enabled_for_file_transfer_cb =
linphone_im_encryption_engine_cbs_get_is_encryption_enabled_for_file_transfer(imee_cbs);
if (is_encryption_enabled_for_file_transfer_cb) {
is_file_encryption_enabled = is_encryption_enabled_for_file_transfer_cb(msg->chat_room->lc, msg->chat_room);
}
}
/* shall we encrypt the file */
if (linphone_chat_room_lime_available(msg->chat_room) &&
linphone_core_lime_for_file_sharing_enabled(msg->chat_room->lc)) {
char keyBuffer
[FILE_TRANSFER_KEY_SIZE]; /* temporary storage of generated key: 192 bits of key + 64 bits of
initial vector */
/* generate a random 192 bits key + 64 bits of initial vector and store it into the
* file_transfer_information->key field of the msg */
sal_get_random_bytes((unsigned char *)keyBuffer, FILE_TRANSFER_KEY_SIZE);
linphone_content_set_key(
msg->file_transfer_information, keyBuffer,
FILE_TRANSFER_KEY_SIZE); /* key is duplicated in the content private structure */
if (is_file_encryption_enabled) {
LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee);
LinphoneImEncryptionEngineGenerateFileTransferKeyCb generate_file_transfer_key_cb =
linphone_im_encryption_engine_cbs_get_generate_file_transfer_key(imee_cbs);
if (generate_file_transfer_key_cb) {
generate_file_transfer_key_cb(msg->chat_room->lc, msg->chat_room, msg);
}
/* temporary storage for the Content-disposition header value : use a generic filename to not leak it
* Actual filename stored in msg->file_transfer_information->name will be set in encrypted msg
* sended to the */
* Actual filename stored in msg->file_transfer_information->name will be set in encrypted msg
* sended to the */
first_part_header = belle_sip_strdup_printf("form-data; name=\"File\"; filename=\"filename.txt\"");
} else {
/* temporary storage for the Content-disposition header value */
@ -200,18 +207,21 @@ static void linphone_chat_message_process_response_from_post_file(void *data,
/* create a user body handler to take care of the file and add the content disposition and content-type
* headers */
first_part_bh = (belle_sip_body_handler_t *)belle_sip_user_body_handler_new(
linphone_content_get_size(msg->file_transfer_information),
linphone_chat_message_file_transfer_on_progress, NULL, NULL,
on_send_body, on_send_end, msg);
if (msg->file_transfer_filepath != NULL) {
first_part_bh =
(belle_sip_body_handler_t *)belle_sip_file_body_handler_new(msg->file_transfer_filepath, NULL, msg);
belle_sip_user_body_handler_t *body_handler = (belle_sip_user_body_handler_t *)first_part_bh;
first_part_bh = (belle_sip_body_handler_t *)belle_sip_file_body_handler_new(msg->file_transfer_filepath,
linphone_chat_message_file_transfer_on_progress, msg);
belle_sip_file_body_handler_set_user_body_handler((belle_sip_file_body_handler_t *)first_part_bh, body_handler);
} else if (linphone_content_get_buffer(msg->file_transfer_information) != NULL) {
first_part_bh = (belle_sip_body_handler_t *)belle_sip_memory_body_handler_new_from_buffer(
linphone_content_get_buffer(msg->file_transfer_information),
linphone_content_get_size(msg->file_transfer_information), NULL, msg);
} else {
first_part_bh = (belle_sip_body_handler_t *)belle_sip_user_body_handler_new(
linphone_content_get_size(msg->file_transfer_information), NULL, NULL,
linphone_chat_message_file_transfer_on_send_body, msg);
linphone_content_get_size(msg->file_transfer_information), linphone_chat_message_file_transfer_on_progress, msg);
}
belle_sip_body_handler_add_header(first_part_bh,
belle_sip_header_create("Content-disposition", first_part_header));
belle_sip_free(first_part_header);
@ -233,7 +243,9 @@ static void linphone_chat_message_process_response_from_post_file(void *data,
if (body && strlen(body) > 0) {
/* if we have an encryption key for the file, we must insert it into the msg and restore the correct
* filename */
if (linphone_content_get_key(msg->file_transfer_information) != NULL) {
const char *content_key = linphone_content_get_key(msg->file_transfer_information);
size_t content_key_size = linphone_content_get_key_size(msg->file_transfer_information);
if (content_key != NULL) {
/* parse the msg body */
xmlDocPtr xmlMessageBody = xmlParseDoc((const xmlChar *)body);
@ -252,12 +264,11 @@ static void linphone_chat_message_process_response_from_post_file(void *data,
->xmlChildrenNode; /* need to parse the children node to update the file-name
one */
/* convert key to base64 */
size_t b64Size = b64_encode(NULL, FILE_TRANSFER_KEY_SIZE, NULL, 0);
size_t b64Size = b64_encode(NULL, content_key_size, NULL, 0);
char *keyb64 = (char *)ms_malloc0(b64Size + 1);
int xmlStringLength;
b64Size = b64_encode(linphone_content_get_key(msg->file_transfer_information),
FILE_TRANSFER_KEY_SIZE, keyb64, b64Size);
b64Size = b64_encode(content_key, content_key_size, keyb64, b64Size);
keyb64[b64Size] = '\0'; /* libxml need a null terminated string */
/* add the node containing the key to the file-info node */
@ -320,10 +331,12 @@ const LinphoneContent *linphone_chat_message_get_file_transfer_information(const
return msg->file_transfer_information;
}
static void on_recv_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t *m, void *data, size_t offset,
const uint8_t *buffer, size_t size) {
static void on_recv_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t *m, void *data, size_t offset, uint8_t *buffer, size_t size) {
LinphoneChatMessage *msg = (LinphoneChatMessage *)data;
LinphoneCore *lc;
LinphoneCore *lc = NULL;
LinphoneImEncryptionEngine *imee = NULL;
int retval = -1;
char *decrypted_buffer = NULL;
if (!msg->chat_room) {
linphone_chat_message_cancel_file_transfer(msg);
@ -344,37 +357,68 @@ static void on_recv_body(belle_sip_user_body_handler_t *bh, belle_sip_message_t
if (size == 0) {
return;
}
if (linphone_content_get_key(msg->file_transfer_information) !=
NULL) { /* we have a key, we must decrypt the file */
/* get data from callback to a plainBuffer */
char *plainBuffer = (char *)ms_malloc0(size);
lime_decryptFile(linphone_content_get_cryptoContext_address(msg->file_transfer_information),
(unsigned char *)linphone_content_get_key(msg->file_transfer_information), size, plainBuffer,
(char *)buffer);
if (linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)) {
LinphoneBuffer *lb = linphone_buffer_new_from_data((unsigned char *)plainBuffer, size);
linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)(msg, msg->file_transfer_information, lb);
linphone_buffer_unref(lb);
} else {
/* legacy: call back given by application level */
linphone_core_notify_file_transfer_recv(lc, msg, msg->file_transfer_information, plainBuffer, size);
decrypted_buffer = (char *)ms_malloc0(size);
imee = linphone_core_get_im_encryption_engine(lc);
if (imee) {
LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee);
LinphoneImEncryptionEngineDownloadingFileCb cb_process_downloading_file = linphone_im_encryption_engine_cbs_get_process_downloading_file(imee_cbs);
if (cb_process_downloading_file) {
retval = cb_process_downloading_file(lc, msg, (const char *)buffer, size, decrypted_buffer);
if (retval == 0) {
memcpy(buffer, decrypted_buffer, size);
}
}
ms_free(plainBuffer);
} else { /* regular file, no deciphering */
}
ms_free(decrypted_buffer);
if (retval <= 0) {
if (linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)) {
LinphoneBuffer *lb = linphone_buffer_new_from_data(buffer, size);
linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)(msg, msg->file_transfer_information, lb);
linphone_buffer_unref(lb);
} else {
/* Legacy: call back given by application level */
linphone_core_notify_file_transfer_recv(lc, msg, msg->file_transfer_information, (char *)buffer, size);
linphone_core_notify_file_transfer_recv(lc, msg, msg->file_transfer_information, (const char *)buffer, size);
}
} else {
ms_warning("File transfer decrypt failed with code %d", (int)retval);
linphone_chat_message_set_state(msg, LinphoneChatMessageStateFileTransferError);
}
return;
}
static void on_recv_end(belle_sip_user_body_handler_t *bh, void *data) {
LinphoneChatMessage *msg = (LinphoneChatMessage *)data;
LinphoneCore *lc = msg->chat_room->lc;
LinphoneImEncryptionEngine *imee = linphone_core_get_im_encryption_engine(lc);
int retval = -1;
if (imee) {
LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee);
LinphoneImEncryptionEngineDownloadingFileCb cb_process_downloading_file = linphone_im_encryption_engine_cbs_get_process_downloading_file(imee_cbs);
if (cb_process_downloading_file) {
retval = cb_process_downloading_file(lc, msg, NULL, 0, NULL);
}
}
if (retval <= 0) {
if (linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)) {
LinphoneBuffer *lb = linphone_buffer_new();
linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)(msg, msg->file_transfer_information, lb);
linphone_buffer_unref(lb);
} else {
/* Legacy: call back given by application level */
linphone_core_notify_file_transfer_recv(lc, msg, msg->file_transfer_information, NULL, 0);
}
}
if (retval <= 0 && linphone_chat_message_get_state(msg) != LinphoneChatMessageStateFileTransferError) {
linphone_chat_message_set_state(msg, LinphoneChatMessageStateFileTransferDone);
}
}
static LinphoneContent *linphone_chat_create_file_transfer_information_from_headers(const belle_sip_message_t *m) {
LinphoneContent *content = linphone_content_new();
@ -410,6 +454,7 @@ static void linphone_chat_process_response_headers_from_get_file(void *data, con
/*we are receiving a response, set a specific body handler to acquire the response.
* if not done, belle-sip will create a memory body handler, the default*/
belle_sip_message_t *response = BELLE_SIP_MESSAGE(event->response);
belle_sip_body_handler_t *body_handler = NULL;
size_t body_size = 0;
if (msg->file_transfer_information == NULL) {
@ -421,21 +466,20 @@ static void linphone_chat_process_response_headers_from_get_file(void *data, con
body_size = linphone_content_get_size(msg->file_transfer_information);
}
if (msg->file_transfer_filepath == NULL) {
belle_sip_message_set_body_handler(
(belle_sip_message_t *)event->response,
(belle_sip_body_handler_t *)belle_sip_user_body_handler_new(
body_size, linphone_chat_message_file_transfer_on_progress, on_recv_body, NULL, msg));
} else {
belle_sip_body_handler_t *bh = (belle_sip_body_handler_t *)belle_sip_file_body_handler_new(
body_handler = (belle_sip_body_handler_t *)belle_sip_user_body_handler_new(body_size, linphone_chat_message_file_transfer_on_progress, NULL, on_recv_body, NULL, on_recv_end, msg);
if (msg->file_transfer_filepath != NULL) {
belle_sip_user_body_handler_t *bh = (belle_sip_user_body_handler_t *)body_handler;
body_handler = (belle_sip_body_handler_t *)belle_sip_file_body_handler_new(
msg->file_transfer_filepath, linphone_chat_message_file_transfer_on_progress, msg);
if (belle_sip_body_handler_get_size(bh) == 0) {
if (belle_sip_body_handler_get_size((belle_sip_body_handler_t *)body_handler) == 0) {
/* If the size of the body has not been initialized from the file stat, use the one from the
* file_transfer_information. */
belle_sip_body_handler_set_size(bh, body_size);
belle_sip_body_handler_set_size((belle_sip_body_handler_t *)body_handler, body_size);
}
belle_sip_message_set_body_handler((belle_sip_message_t *)event->response, bh);
belle_sip_file_body_handler_set_user_body_handler((belle_sip_file_body_handler_t *)body_handler, bh);
}
belle_sip_message_set_body_handler((belle_sip_message_t *)event->response, body_handler);
}
}
@ -444,47 +488,10 @@ static void linphone_chat_process_response_from_get_file(void *data, const belle
/* check the answer code */
if (event->response) {
int code = belle_http_response_get_status_code(event->response);
if (code == 200) {
LinphoneCore *lc = msg->chat_room->lc;
if (msg->file_transfer_filepath == NULL) {
/* if the file was encrypted, finish the decryption and free context */
if (linphone_content_get_key(msg->file_transfer_information) != NULL) {
lime_decryptFile(linphone_content_get_cryptoContext_address(msg->file_transfer_information), NULL, 0, NULL, NULL);
}
if (linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)) {
LinphoneBuffer *lb = linphone_buffer_new();
linphone_chat_message_cbs_get_file_transfer_recv(msg->callbacks)(msg, msg->file_transfer_information, lb);
linphone_buffer_unref(lb);
} else {
linphone_core_notify_file_transfer_recv(lc, msg, msg->file_transfer_information, NULL, 0);
}
} else {
if (linphone_content_get_key(msg->file_transfer_information) != NULL) {
bctbx_vfs_t *vfs = bctbx_vfs_get_default();
bctbx_vfs_file_t *decrypted_file;
bctbx_vfs_file_t *encrypted_file = bctbx_file_open(vfs, msg->file_transfer_filepath, "r");
size_t encrypted_file_size = (size_t)bctbx_file_size(encrypted_file);
char *encrypted_content = bctbx_malloc(encrypted_file_size);
char *decrypted_content = bctbx_malloc(encrypted_file_size);
bctbx_file_read(encrypted_file, encrypted_content, encrypted_file_size, 0);
bctbx_file_close(encrypted_file);
lime_decryptFile(linphone_content_get_cryptoContext_address(msg->file_transfer_information),
(unsigned char *)linphone_content_get_key(msg->file_transfer_information),
encrypted_file_size, decrypted_content, encrypted_content);
lime_decryptFile(linphone_content_get_cryptoContext_address(msg->file_transfer_information), NULL, 0, NULL, NULL);
decrypted_file = bctbx_file_open(vfs, msg->file_transfer_filepath, "w");
bctbx_file_write(decrypted_file, decrypted_content, encrypted_file_size, 0);
bctbx_file_close(decrypted_file);
bctbx_free(encrypted_content);
bctbx_free(decrypted_content);
}
linphone_core_notify_file_transfer_recv(lc, msg, msg->file_transfer_information, NULL, 0);
}
linphone_chat_message_set_state(msg, LinphoneChatMessageStateFileTransferDone);
} else if (code >= 400 && code < 500) {
if (code >= 400 && code < 500) {
ms_warning("File transfer failed with code %d", code);
linphone_chat_message_set_state(msg, LinphoneChatMessageStateFileTransferError);
} else {
} else if (code != 200) {
ms_warning("Unhandled HTTP code response %d for file transfer", code);
}
_release_http_request(msg);

View file

@ -0,0 +1,124 @@
/*
ImEncryptionEgine.c
Copyright (C) 2016 Belledonne Communications SARL
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "linphone/core.h"
#include "im_encryption_engine.h"
struct _LinphoneImEncryptionEngineCbs {
void *user_data;
LinphoneImEncryptionEngineIncomingMessageCb process_incoming_message;
LinphoneImEncryptionEngineOutgoingMessageCb process_outgoing_message;
LinphoneImEncryptionEngineIsEncryptionEnabledForFileTransferCb is_encryption_enabled_for_file_transfer;
LinphoneImEncryptionEngineGenerateFileTransferKeyCb generate_file_transfer_key;
LinphoneImEncryptionEngineDownloadingFileCb process_downlading_file;
LinphoneImEncryptionEngineUploadingFileCb process_uploading_file;
};
struct _LinphoneImEncryptionEngine {
void *user_data;
LinphoneImEncryptionEngineCbs *callbacks;
};
LinphoneImEncryptionEngineCbs *linphone_im_encryption_engine_cbs_new(void) {
LinphoneImEncryptionEngineCbs *cbs = ms_new0(LinphoneImEncryptionEngineCbs, 1);
return cbs;
}
void linphone_im_encryption_engine_cbs_destory(LinphoneImEncryptionEngineCbs *cbs) {
ms_free(cbs);
}
void *linphone_im_encryption_engine_cbs_get_user_data(const LinphoneImEncryptionEngineCbs *cbs) {
return cbs->user_data;
}
void linphone_im_encryption_engine_cbs_set_user_data(LinphoneImEncryptionEngineCbs *cbs, void *data) {
cbs->user_data = data;
}
LinphoneImEncryptionEngine *linphone_im_encryption_engine_new(void) {
LinphoneImEncryptionEngine *imee = ms_new0(LinphoneImEncryptionEngine, 1);
imee->callbacks = linphone_im_encryption_engine_cbs_new();
return imee;
}
void linphone_im_encryption_engine_destory(LinphoneImEncryptionEngine *imee) {
if (imee->callbacks) linphone_im_encryption_engine_cbs_destory(imee->callbacks);
ms_free(imee);
}
void *linphone_im_encryption_engine_get_user_data(const LinphoneImEncryptionEngine *imee) {
return imee->user_data;
}
void linphone_im_encryption_engine_set_user_data(LinphoneImEncryptionEngine *imee, void *data) {
imee->user_data = data;
}
LinphoneImEncryptionEngineCbs* linphone_im_encryption_engine_get_callbacks(const LinphoneImEncryptionEngine *imee) {
return imee->callbacks;
}
LinphoneImEncryptionEngineIncomingMessageCb linphone_im_encryption_engine_cbs_get_process_incoming_message(LinphoneImEncryptionEngineCbs *cbs) {
return cbs->process_incoming_message;
}
void linphone_im_encryption_engine_cbs_set_process_incoming_message(LinphoneImEncryptionEngineCbs *cbs, LinphoneImEncryptionEngineIncomingMessageCb cb) {
cbs->process_incoming_message = cb;
}
LinphoneImEncryptionEngineOutgoingMessageCb linphone_im_encryption_engine_cbs_get_process_outgoing_message(LinphoneImEncryptionEngineCbs *cbs) {
return cbs->process_outgoing_message;
}
void linphone_im_encryption_engine_cbs_set_process_outgoing_message(LinphoneImEncryptionEngineCbs *cbs, LinphoneImEncryptionEngineOutgoingMessageCb cb) {
cbs->process_outgoing_message = cb;
}
LinphoneImEncryptionEngineDownloadingFileCb linphone_im_encryption_engine_cbs_get_process_downloading_file(LinphoneImEncryptionEngineCbs *cbs) {
return cbs->process_downlading_file;
}
void linphone_im_encryption_engine_cbs_set_process_downloading_file(LinphoneImEncryptionEngineCbs *cbs, LinphoneImEncryptionEngineDownloadingFileCb cb) {
cbs->process_downlading_file = cb;
}
LinphoneImEncryptionEngineUploadingFileCb linphone_im_encryption_engine_cbs_get_process_uploading_file(LinphoneImEncryptionEngineCbs *cbs) {
return cbs->process_uploading_file;
}
void linphone_im_encryption_engine_cbs_set_process_uploading_file(LinphoneImEncryptionEngineCbs *cbs, LinphoneImEncryptionEngineUploadingFileCb cb) {
cbs->process_uploading_file = cb;
}
LinphoneImEncryptionEngineIsEncryptionEnabledForFileTransferCb linphone_im_encryption_engine_cbs_get_is_encryption_enabled_for_file_transfer(LinphoneImEncryptionEngineCbs *cbs) {
return cbs->is_encryption_enabled_for_file_transfer;
}
void linphone_im_encryption_engine_cbs_set_is_encryption_enabled_for_file_transfer(LinphoneImEncryptionEngineCbs *cbs, LinphoneImEncryptionEngineIsEncryptionEnabledForFileTransferCb cb) {
cbs->is_encryption_enabled_for_file_transfer = cb;
}
LinphoneImEncryptionEngineGenerateFileTransferKeyCb linphone_im_encryption_engine_cbs_get_generate_file_transfer_key(LinphoneImEncryptionEngineCbs *cbs) {
return cbs->generate_file_transfer_key;
}
void linphone_im_encryption_engine_cbs_set_generate_file_transfer_key(LinphoneImEncryptionEngineCbs *cbs, LinphoneImEncryptionEngineGenerateFileTransferKeyCb cb) {
cbs->generate_file_transfer_key = cb;
}

View file

@ -0,0 +1,242 @@
/*
ImEncryptionEgine.h
Copyright (C) 2016 Belledonne Communications SARL
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef IM_ENCRYPTION_ENGINE_H
#define IM_ENCRYPTION_ENGINE_H
#include <mediastreamer2/mscommon.h>
#ifndef LINPHONE_PUBLIC
#define LINPHONE_PUBLIC MS2_PUBLIC
#endif
/**
* Callback to decrypt incoming LinphoneChatMessage
* @param lc the LinphoneCore object
* @param room the LinphoneChatRoom object
* @param msg the LinphoneChatMessage object
* @return -1 if nothing to be done, 0 on success or an integer > 0 for error
*/
typedef int (*LinphoneImEncryptionEngineIncomingMessageCb)(LinphoneCore* lc, LinphoneChatRoom *room, LinphoneChatMessage *msg);
/**
* Callback to encrypt outging LinphoneChatMessage
* @param lc the LinphoneCore object
* @param room the LinphoneChatRoom object
* @param msg the LinphoneChatMessage object
* @return -1 if nothing to be done, 0 on success or an integer > 0 for error
*/
typedef int (*LinphoneImEncryptionEngineOutgoingMessageCb)(LinphoneCore* lc, LinphoneChatRoom *room, LinphoneChatMessage *msg);
/**
* Callback to know whether or not the engine will encrypt files before uploading them
* @param lc the LinphoneCore object
* @param room the LinphoneChatRoom object
* @return TRUE if files will be encrypted, FALSE otherwise
*/
typedef bool_t (*LinphoneImEncryptionEngineIsEncryptionEnabledForFileTransferCb)(LinphoneCore *lc, LinphoneChatRoom *room);
/**
* Callback to generate the key used to encrypt the files before uploading them
* Key can be stored in the LinphoneContent object inside the LinphoneChatMessage using linphone_content_set_key
* @param lc the LinphoneCore object
* @param room the LinphoneChatRoom object
* @param msg the LinphoneChatMessage object
*/
typedef void (*LinphoneImEncryptionEngineGenerateFileTransferKeyCb)(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *msg);
/**
* Callback to decrypt downloading file
* @param lc the LinphoneCore object
* @param msg the LinphoneChatMessage object
* @param buffer the encrypted data buffer
* @param size the size of the encrypted data buffer
* @param decrypted_buffer the buffer in which to write the decrypted data
* @return -1 if nothing to be done, 0 on success or an integer > 0 for error
*/
typedef int (*LinphoneImEncryptionEngineDownloadingFileCb)(LinphoneCore *lc, LinphoneChatMessage *msg, const char *buffer, size_t size, char *decrypted_buffer);
/**
* Callback to encrypt uploading file
* @param lc the LinphoneCore object
* @param msg the LinphoneChatMessage object
* @param buffer the encrypted data buffer
* @param size the size of the plain data buffer and the size of the encrypted data buffer once encryption is done
* @param encrypted_buffer the buffer in which to write the encrypted data
* @return -1 if nothing to be done, 0 on success or an integer > 0 for error
*/
typedef int (*LinphoneImEncryptionEngineUploadingFileCb)(LinphoneCore *lc, LinphoneChatMessage *msg, size_t offset, const char *buffer, size_t *size, char *encrypted_buffer);
typedef struct _LinphoneImEncryptionEngineCbs LinphoneImEncryptionEngineCbs;
typedef struct _LinphoneImEncryptionEngine LinphoneImEncryptionEngine;
LinphoneImEncryptionEngineCbs *linphone_im_encryption_engine_cbs_new(void);
void linphone_im_encryption_engine_cbs_destory(LinphoneImEncryptionEngineCbs *cbs);
/**
* Gets the user data in the LinphoneImEncryptionEngineCbs object
* @param cbs the LinphoneImEncryptionEngineCbs
* @return the user data
* @ingroup misc
*/
LINPHONE_PUBLIC void *linphone_im_encryption_engine_cbs_get_user_data(const LinphoneImEncryptionEngineCbs *cbs);
/**
* Sets the user data in the LinphoneImEncryptionEngineCbs object
* @param cbs the LinphoneImEncryptionEngineCbs object
* @param data the user data
* @ingroup misc
*/
LINPHONE_PUBLIC void linphone_im_encryption_engine_cbs_set_user_data(LinphoneImEncryptionEngineCbs *cbs, void *data);
/**
* Creates a LinphoneImEncryptionEngine object
*/
LINPHONE_PUBLIC LinphoneImEncryptionEngine *linphone_im_encryption_engine_new(void);
/**
* Destroys the LinphoneImEncryptionEngine
* @param imee the LinphoneImEncryptionEngine object
* @ingroup misc
*/
LINPHONE_PUBLIC void linphone_im_encryption_engine_destory(LinphoneImEncryptionEngine *imee);
/**
* Gets the user data in the LinphoneImEncryptionEngine object
* @param imee the LinphoneImEncryptionEngine
* @return the user data
* @ingroup misc
*/
LINPHONE_PUBLIC void *linphone_im_encryption_engine_get_user_data(const LinphoneImEncryptionEngine *imee);
/**
* Sets the user data in the LinphoneImEncryptionEngine object
* @param imee the LinphoneImEncryptionEngine object
* @param data the user data
* @ingroup misc
*/
LINPHONE_PUBLIC void linphone_im_encryption_engine_set_user_data(LinphoneImEncryptionEngine *imee, void *data);
/**
* Gets the LinphoneImEncryptionEngineCbs object that holds the callbacks
* @param imee the LinphoneImEncryptionEngine object
* @return the LinphoneImEncryptionEngineCbs object
* @ingroup misc
*/
LINPHONE_PUBLIC LinphoneImEncryptionEngineCbs* linphone_im_encryption_engine_get_callbacks(const LinphoneImEncryptionEngine *imee);
/**
* Gets the callback that will decrypt the chat messages upon reception
* @param cbs the LinphoneImEncryptionEngineCbs object
* @return the callback
* @ingroup misc
*/
LINPHONE_PUBLIC LinphoneImEncryptionEngineIncomingMessageCb linphone_im_encryption_engine_cbs_get_process_incoming_message(LinphoneImEncryptionEngineCbs *cbs);
/**
* Sets the callback that will decrypt the chat messages upon reception
* @param cbs the LinphoneImEncryptionEngineCbs object
* @param cb the callback to call
* @ingroup misc
*/
LINPHONE_PUBLIC void linphone_im_encryption_engine_cbs_set_process_incoming_message(LinphoneImEncryptionEngineCbs *cbs, LinphoneImEncryptionEngineIncomingMessageCb cb);
/**
* Gets the callback that will encrypt the chat messages before sending them
* @param cbs the LinphoneImEncryptionEngineCbs object
* @return the callback
* @ingroup misc
*/
LINPHONE_PUBLIC LinphoneImEncryptionEngineOutgoingMessageCb linphone_im_encryption_engine_cbs_get_process_outgoing_message(LinphoneImEncryptionEngineCbs *cbs);
/**
* Sets the callback that will encrypt the chat messages before sending them
* @param cbs the LinphoneImEncryptionEngineCbs object
* @param cb the callback to call
* @ingroup misc
*/
LINPHONE_PUBLIC void linphone_im_encryption_engine_cbs_set_process_outgoing_message(LinphoneImEncryptionEngineCbs *cbs, LinphoneImEncryptionEngineOutgoingMessageCb cb);
/**
* Gets the callback that will decrypt the files while downloading them
* @param cbs the LinphoneImEncryptionEngineCbs object
* @return the callback
* @ingroup misc
*/
LINPHONE_PUBLIC LinphoneImEncryptionEngineDownloadingFileCb linphone_im_encryption_engine_cbs_get_process_downloading_file(LinphoneImEncryptionEngineCbs *cbs);
/**
* Sets the callback that will decrypt the files while downloading them
* @param cbs the LinphoneImEncryptionEngineCbs object
* @param cb the callback to call
* @ingroup misc
*/
LINPHONE_PUBLIC void linphone_im_encryption_engine_cbs_set_process_downloading_file(LinphoneImEncryptionEngineCbs *cbs, LinphoneImEncryptionEngineDownloadingFileCb cb);
/**
* Gets the callback that will will encrypt the files while uploading them
* @param cbs the LinphoneImEncryptionEngineCbs object
* @return the callback
* @ingroup misc
*/
LINPHONE_PUBLIC LinphoneImEncryptionEngineUploadingFileCb linphone_im_encryption_engine_cbs_get_process_uploading_file(LinphoneImEncryptionEngineCbs *cbs);
/**
* Sets the callback that will encrypt the files while uploading them
* @param cbs the LinphoneImEncryptionEngineCbs object
* @param cb the callback to call
* @ingroup misc
*/
LINPHONE_PUBLIC void linphone_im_encryption_engine_cbs_set_process_uploading_file(LinphoneImEncryptionEngineCbs *cbs, LinphoneImEncryptionEngineUploadingFileCb cb);
/**
* Gets the callback telling wheter or not to encrypt the files
* @param cbs the LinphoneImEncryptionEngineCbs object
* @return the callback
* @ingroup misc
*/
LINPHONE_PUBLIC LinphoneImEncryptionEngineIsEncryptionEnabledForFileTransferCb linphone_im_encryption_engine_cbs_get_is_encryption_enabled_for_file_transfer(LinphoneImEncryptionEngineCbs *cbs);
/**
* Sets the callback telling wheter or not to encrypt the files
* @param cbs the LinphoneImEncryptionEngineCbs object
* @param cb the callback to call
* @ingroup misc
*/
LINPHONE_PUBLIC void linphone_im_encryption_engine_cbs_set_is_encryption_enabled_for_file_transfer(LinphoneImEncryptionEngineCbs *cbs, LinphoneImEncryptionEngineIsEncryptionEnabledForFileTransferCb cb);
/**
* Gets the callback that will generate the key to encrypt the file before uploading it
* @param cbs the LinphoneImEncryptionEngineCbs object
* @return the callback
* @ingroup misc
*/
LINPHONE_PUBLIC LinphoneImEncryptionEngineGenerateFileTransferKeyCb linphone_im_encryption_engine_cbs_get_generate_file_transfer_key(LinphoneImEncryptionEngineCbs *cbs);
/**
* Sets the callback that will generate the key to encrypt the file before uploading it
* @param cbs the LinphoneImEncryptionEngineCbs object
* @param cb the callback to call
* @ingroup misc
*/
LINPHONE_PUBLIC void linphone_im_encryption_engine_cbs_set_generate_file_transfer_key(LinphoneImEncryptionEngineCbs *cbs, LinphoneImEncryptionEngineGenerateFileTransferKeyCb cb);
#endif /* IM_ENCRYPTION_ENGINE_H */

View file

@ -23,9 +23,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#endif
#ifdef HAVE_LIME
#include "linphone/core.h"
#include "private.h"
#include "bctoolbox/crypto.h"
#define FILE_TRANSFER_KEY_SIZE 32
/**
* @brief check at runtime if LIME is available
*
@ -812,6 +814,229 @@ int lime_decryptMultipartMessage(xmlDocPtr cacheBuffer, uint8_t *message, uint8_
return 0;
}
bool_t linphone_chat_room_lime_available(LinphoneChatRoom *cr) {
if (cr) {
switch (linphone_core_lime_enabled(cr->lc)) {
case LinphoneLimeDisabled: return FALSE;
case LinphoneLimeMandatory: return TRUE;
case LinphoneLimePreferred: {
FILE *CACHEFD = NULL;
if (cr->lc->zrtp_secrets_cache != NULL) {
CACHEFD = fopen(cr->lc->zrtp_secrets_cache, "rb+");
if (CACHEFD) {
size_t cacheSize;
xmlDocPtr cacheXml;
char *cacheString=ms_load_file_content(CACHEFD, &cacheSize);
if (!cacheString){
ms_warning("Unable to load content of ZRTP ZID cache to decrypt message");
return FALSE;
}
cacheString[cacheSize] = '\0';
cacheSize += 1;
fclose(CACHEFD);
cacheXml = xmlParseDoc((xmlChar*)cacheString);
ms_free(cacheString);
if (cacheXml) {
bool_t res;
limeURIKeys_t associatedKeys;
/* retrieve keys associated to the peer URI */
associatedKeys.peerURI = (uint8_t *)malloc(strlen(cr->peer)+1);
strcpy((char *)(associatedKeys.peerURI), cr->peer);
associatedKeys.associatedZIDNumber = 0;
associatedKeys.peerKeys = NULL;
res = (lime_getCachedSndKeysByURI(cacheXml, &associatedKeys) == 0 && associatedKeys.associatedZIDNumber != 0);
lime_freeKeys(&associatedKeys);
xmlFreeDoc(cacheXml);
return res;
}
}
}
}
}
}
return FALSE;
}
int lime_im_encryption_engine_process_incoming_message_cb(LinphoneCore* lc, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
int errcode = -1;
/* check if we have a xml/cipher message to be decrypted */
if (msg->content_type && (strcmp("xml/cipher", msg->content_type) == 0 || strcmp("application/cipher.vnd.gsma.rcs-ft-http+xml", msg->content_type) == 0)) {
/* access the zrtp cache to get keys needed to decipher the message */
FILE *CACHEFD = NULL;
const char *zrtp_secrets_cache = linphone_core_get_zrtp_secrets_file(lc);
errcode = 0;
if (zrtp_secrets_cache != NULL) CACHEFD = fopen(zrtp_secrets_cache, "rb+");
if (CACHEFD == NULL) {
ms_warning("Unable to access ZRTP ZID cache to decrypt message");
errcode = 500;
return errcode;
} else {
size_t cacheSize;
char *cacheString;
int retval;
xmlDocPtr cacheXml;
uint8_t *decrypted_body = NULL;
cacheString=ms_load_file_content(CACHEFD, &cacheSize);
if (!cacheString){
ms_warning("Unable to load content of ZRTP ZID cache to decrypt message");
errcode = 500;
return errcode;
}
cacheString[cacheSize] = '\0';
cacheSize += 1;
fclose(CACHEFD);
cacheXml = xmlParseDoc((xmlChar*)cacheString);
ms_free(cacheString);
retval = lime_decryptMultipartMessage(cacheXml, (uint8_t *)msg->message, &decrypted_body);
if (retval != 0) {
ms_warning("Unable to decrypt message, reason : %s", lime_error_code_to_string(retval));
if (decrypted_body) free(decrypted_body);
xmlFreeDoc(cacheXml);
errcode = 488;
return errcode;
} else {
/* dump updated cache to a string */
xmlChar *xmlStringOutput;
int xmlStringLength;
xmlDocDumpFormatMemoryEnc(cacheXml, &xmlStringOutput, &xmlStringLength, "UTF-8", 0);
/* write it to the cache file */
CACHEFD = fopen(zrtp_secrets_cache, "wb+");
if (fwrite(xmlStringOutput, 1, xmlStringLength, CACHEFD)<=0){
ms_warning("Fail to write cache");
}
xmlFree(xmlStringOutput);
fclose(CACHEFD);
if (msg->message) {
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");
} else {
ms_free(msg->content_type);
msg->content_type = ms_strdup("text/plain");
}
}
xmlFreeDoc(cacheXml);
}
}
return errcode;
}
int lime_im_encryption_engine_process_outgoing_message_cb(LinphoneCore* lc, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
int errcode = -1;
char *content_type = NULL;
if (linphone_chat_room_lime_available(room)) {
if (msg->content_type && 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";
} else {
content_type = "xml/cipher";
}
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+");
errcode = 0;
if (CACHEFD == NULL) {
ms_warning("Unable to access ZRTP ZID cache to encrypt message");
errcode = 488;
} else {
size_t cacheSize;
char *cacheString;
xmlDocPtr cacheXml;
int retval;
uint8_t *crypted_body = NULL;
char *peer = linphone_address_as_string_uri_only(linphone_chat_room_get_peer_address(room));
cacheString=ms_load_file_content(CACHEFD, &cacheSize);
if (!cacheString){
ms_warning("Unable to load content of ZRTP ZID cache to encrypt message");
errcode = 500;
return errcode;
}
cacheString[cacheSize] = '\0';
cacheSize += 1;
fclose(CACHEFD);
cacheXml = xmlParseDoc((xmlChar*)cacheString);
ms_free(cacheString);
retval = lime_createMultipartMessage(cacheXml, (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) free(crypted_body);
errcode = 488;
} else {
/* dump updated cache to a string */
xmlChar *xmlStringOutput;
int xmlStringLength;
xmlDocDumpFormatMemoryEnc(cacheXml, &xmlStringOutput, &xmlStringLength, "UTF-8", 0);
/* write it to the cache file */
CACHEFD = fopen(zrtp_secrets_cache, "wb+");
if (fwrite(xmlStringOutput, 1, xmlStringLength, CACHEFD)<=0){
ms_warning("Unable to write zid cache");
}
xmlFree(xmlStringOutput);
fclose(CACHEFD);
if (msg->message) {
ms_free(msg->message);
}
msg->message = (char *)crypted_body;
}
ms_free(peer);
xmlFreeDoc(cacheXml);
}
}
return errcode;
}
int lime_im_encryption_engine_process_downloading_file_cb(LinphoneCore *lc, LinphoneChatMessage *msg, const char *buffer, size_t size, char *decrypted_buffer) {
if (linphone_content_get_key(msg->file_transfer_information) == NULL) return -1;
if (buffer == NULL || size == 0) {
return lime_decryptFile(linphone_content_get_cryptoContext_address(msg->file_transfer_information), NULL, 0, NULL, NULL);
}
return lime_decryptFile(linphone_content_get_cryptoContext_address(msg->file_transfer_information),
(unsigned char *)linphone_content_get_key(msg->file_transfer_information), size, decrypted_buffer,
(char *)buffer);
}
int lime_im_encryption_engine_process_uploading_file_cb(LinphoneCore *lc, LinphoneChatMessage *msg, size_t offset, const char *buffer, size_t *size, char *encrypted_buffer) {
if (linphone_content_get_key(msg->file_transfer_information) == NULL) return -1;
if (buffer == NULL || *size == 0) {
return lime_encryptFile(linphone_content_get_cryptoContext_address(msg->file_transfer_information), NULL, 0, NULL, NULL);
}
if (offset + *size < linphone_content_get_size(msg->file_transfer_information)) {
*size -= (*size % 16);
}
return lime_encryptFile(linphone_content_get_cryptoContext_address(msg->file_transfer_information),
(unsigned char *)linphone_content_get_key(msg->file_transfer_information), *size,
(char *)buffer, encrypted_buffer);
}
bool_t lime_im_encryption_engine_is_file_encryption_enabled_cb(LinphoneCore *lc, LinphoneChatRoom *room) {
return linphone_chat_room_lime_available(room) && linphone_core_lime_for_file_sharing_enabled(lc);
}
void lime_im_encryption_engine_generate_file_transfer_key_cb(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
char keyBuffer [FILE_TRANSFER_KEY_SIZE]; /* temporary storage of generated key: 192 bits of key + 64 bits of initial vector */
/* generate a random 192 bits key + 64 bits of initial vector and store it into the
* file_transfer_information->key field of the msg */
sal_get_random_bytes((unsigned char *)keyBuffer, FILE_TRANSFER_KEY_SIZE);
linphone_content_set_key(msg->file_transfer_information, keyBuffer, FILE_TRANSFER_KEY_SIZE); /* key is duplicated in the content private structure */
}
#else /* HAVE_LIME */
@ -836,6 +1061,27 @@ int lime_getCachedRcvKeyByZid(xmlDocPtr cacheBuffer, limeKey_t *associatedKey) {
}
int lime_decryptMessage(limeKey_t *key, uint8_t *encryptedMessage, uint32_t messageLength, uint8_t selfZID[12], uint8_t *plainMessage) {
return LIME_NOT_ENABLED;
}
bool_t linphone_chat_room_lime_available(LinphoneChatRoom *cr) {
return FALSE;
}
int lime_im_encryption_engine_process_incoming_message_cb(LinphoneCore* lc, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
return 500;
}
int lime_im_encryption_engine_process_outgoing_message_cb(LinphoneCore* lc, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
return 500;
}
int lime_im_encryption_engine_process_downloading_file_cb(LinphoneCore *lc, LinphoneChatMessage *msg, const char *buffer, size_t size, char *decrypted_buffer) {
return 500;
}
int lime_im_encryption_engine_process_uploading_file_cb(LinphoneCore *lc, LinphoneChatMessage *msg, size_t offset, const char *buffer, size_t *size, char *encrypted_buffer) {
return 500;
}
bool_t lime_im_encryption_engine_is_file_encryption_enabled_cb(LinphoneCore *lc, LinphoneChatRoom *room) {
return FALSE;
}
void lime_im_encryption_engine_generate_file_transfer_key_cb(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *msg) {
}
#endif /* HAVE_LIME */

View file

@ -38,6 +38,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <libxml/parser.h>
#include <libxml/xmlwriter.h>
#include "linphone/core.h"
#include <mediastreamer2/mscommon.h>
#ifndef LINPHONE_PUBLIC
@ -203,4 +204,17 @@ LINPHONE_PUBLIC char *lime_error_code_to_string(int errorCode);
* @return TRUE if Lime is available, FALSE if not
*/
LINPHONE_PUBLIC bool_t lime_is_available(void);
int lime_im_encryption_engine_process_incoming_message_cb(LinphoneCore* lc, LinphoneChatRoom *room, LinphoneChatMessage *msg);
int lime_im_encryption_engine_process_outgoing_message_cb(LinphoneCore* lc, LinphoneChatRoom *room, LinphoneChatMessage *msg);
int lime_im_encryption_engine_process_downloading_file_cb(LinphoneCore *lc, LinphoneChatMessage *msg, const char *buffer, size_t size, char *decrypted_buffer);
int lime_im_encryption_engine_process_uploading_file_cb(LinphoneCore *lc, LinphoneChatMessage *msg, size_t offset, const char *buffer, size_t *size, char *encrypted_buffer);
bool_t lime_im_encryption_engine_is_file_encryption_enabled_cb(LinphoneCore *lc, LinphoneChatRoom *room);
void lime_im_encryption_engine_generate_file_transfer_key_cb(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMessage *msg);
#endif /* LIME_H */

View file

@ -515,7 +515,7 @@ static void process_response_from_post_file_log_collection(void *data, const bel
first_part_header = belle_sip_strdup_printf("form-data; name=\"File\"; filename=\"%s\"", linphone_content_get_name(core->log_collection_upload_information));
/* Create a user body handler to take care of the file and add the content disposition and content-type headers */
first_part_bh = belle_sip_user_body_handler_new(linphone_content_get_size(core->log_collection_upload_information), NULL, NULL, log_collection_upload_on_send_body, core);
first_part_bh = belle_sip_user_body_handler_new(linphone_content_get_size(core->log_collection_upload_information), NULL, NULL, NULL, log_collection_upload_on_send_body, NULL, core);
belle_sip_body_handler_add_header((belle_sip_body_handler_t *)first_part_bh, belle_sip_header_create("Content-disposition", first_part_header));
belle_sip_free(first_part_header);
belle_sip_body_handler_add_header((belle_sip_body_handler_t *)first_part_bh,
@ -1963,6 +1963,23 @@ void linphone_core_enable_lime(LinphoneCore *lc, LinphoneLimeState val){
if (linphone_core_ready(lc)){
lp_config_set_int(lc->config,"sip","lime",val);
}
if (val != LinphoneLimeDisabled) {
LinphoneImEncryptionEngine *imee = linphone_im_encryption_engine_new();
LinphoneImEncryptionEngineCbs *cbs = linphone_im_encryption_engine_get_callbacks(imee);
linphone_im_encryption_engine_cbs_set_process_incoming_message(cbs, lime_im_encryption_engine_process_incoming_message_cb);
linphone_im_encryption_engine_cbs_set_process_outgoing_message(cbs, lime_im_encryption_engine_process_outgoing_message_cb);
linphone_im_encryption_engine_cbs_set_process_downloading_file(cbs, lime_im_encryption_engine_process_downloading_file_cb);
linphone_im_encryption_engine_cbs_set_process_uploading_file(cbs, lime_im_encryption_engine_process_uploading_file_cb);
linphone_im_encryption_engine_cbs_set_is_encryption_enabled_for_file_transfer(cbs, lime_im_encryption_engine_is_file_encryption_enabled_cb);
linphone_im_encryption_engine_cbs_set_generate_file_transfer_key(cbs, lime_im_encryption_engine_generate_file_transfer_key_cb);
lc->im_encryption_engine = imee;
} else {
if (lc->im_encryption_engine) {
linphone_im_encryption_engine_destory(lc->im_encryption_engine);
lc->im_encryption_engine = NULL;
}
}
}
bool_t linphone_core_lime_available(const LinphoneCore *lc){
@ -7199,6 +7216,49 @@ const char *linphone_reason_to_string(LinphoneReason err){
return "unknown error";
}
LinphoneReason linphone_error_code_to_reason(int err) {
if (err == 200) {
return LinphoneReasonNone;
} else if (err == 503) {
return LinphoneReasonIOError;
} else if (err == 400) {
return LinphoneReasonUnknown;
} else if (err == 486) {
return LinphoneReasonBusy;
} else if (err == 603) {
return LinphoneReasonDeclined;
} else if (err == 600) {
return LinphoneReasonDoNotDisturb;
} else if (err == 403) {
return LinphoneReasonForbidden;
} else if (err == 415) {
return LinphoneReasonUnsupportedContent;
} else if (err == 404) {
return LinphoneReasonNotFound;
} else if (err == 480) {
return LinphoneReasonTemporarilyUnavailable;
} else if (err == 401) {
return LinphoneReasonUnauthorized;
} else if (err == 488) {
return LinphoneReasonNotAcceptable;
} else if (err == 481) {
return LinphoneReasonNoMatch;
} else if (err == 301) {
return LinphoneReasonMovedPermanently;
} else if (err == 410) {
return LinphoneReasonGone;
} else if (err == 484) {
return LinphoneReasonAddressIncomplete;
} else if (err == 501) {
return LinphoneReasonNotImplemented;
} else if (err == 504) {
return LinphoneReasonServerTimeout;
} else if (err == 502) {
return LinphoneReasonBadGateway;
}
return LinphoneReasonUnknown;
}
const char *linphone_error_to_string(LinphoneReason err){
return linphone_reason_to_string(err);
}
@ -8025,3 +8085,11 @@ const char *linphone_core_get_tls_key_path(const LinphoneCore *lc) {
const char *tls_key_path = lp_config_get_string(lc->config, "sip", "client_cert_key", NULL);
return tls_key_path;
}
void linphone_core_set_im_encryption_engine(LinphoneCore *lc, LinphoneImEncryptionEngine *imee) {
lc->im_encryption_engine = imee;
}
LinphoneImEncryptionEngine *linphone_core_get_im_encryption_engine(const LinphoneCore *lc) {
return lc->im_encryption_engine;
}

View file

@ -550,7 +550,7 @@ LINPHONE_PUBLIC void linphone_core_get_local_ip(LinphoneCore *lc, int af, const
LinphoneProxyConfig *linphone_proxy_config_new_from_config_file(LinphoneCore *lc, int index);
void linphone_proxy_config_write_to_config_file(struct _LpConfig* config,LinphoneProxyConfig *obj, int index);
void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessage *msg);
LinphoneReason linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessage *msg);
void linphone_core_is_composing_received(LinphoneCore *lc, SalOp *op, const SalIsComposing *is_composing);
void linphone_core_real_time_text_received(LinphoneCore *lc, LinphoneChatRoom *cr, uint32_t character, LinphoneCall *call);
@ -1072,6 +1072,8 @@ struct _LinphoneCore
/*default resource list server*/
LinphoneAddress *default_rls_addr;
LinphoneImEncryptionEngine *im_encryption_engine;
};

View file

@ -19,6 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef LINPHONECORE_H
#define LINPHONECORE_H
#include "belle-sip/belle-sip.h"
#include "ortp/ortp.h"
#include "ortp/payloadtype.h"
#include "mediastreamer2/mscommon.h"
@ -206,6 +207,12 @@ typedef enum _LinphoneReason LinphoneReason;
**/
LINPHONE_PUBLIC const char *linphone_reason_to_string(LinphoneReason err);
/**
* Converts a error code to a LinphoneReason.
* @ingroup misc
**/
LINPHONE_PUBLIC LinphoneReason linphone_error_code_to_reason(int err);
/**
* Object representing full details about a signaling error or status.
* All LinphoneErrorInfo object returned by the liblinphone API are readonly and transcients. For safety they must be used immediately
@ -1509,7 +1516,7 @@ LINPHONE_PUBLIC uint32_t linphone_chat_room_get_char(const LinphoneChatRoom *cr)
*
* @return true if zrtp secrets have already been shared and ready to use
*/
LINPHONE_PUBLIC bool_t linphone_chat_room_lime_available(LinphoneChatRoom *cr);
LINPHONE_PUBLIC bool_t linphone_chat_room_lime_available(LinphoneChatRoom *cr);
/**
* Returns an list of chat rooms
@ -1668,7 +1675,7 @@ LINPHONE_PUBLIC LinphoneAddress *linphone_chat_message_get_local_address(const L
/**
* Add custom headers to the message.
* @param message the message
* @param header_name name of the header_name
* @param header_name name of the header
* @param header_value header value
**/
LINPHONE_PUBLIC void linphone_chat_message_add_custom_header(LinphoneChatMessage* message, const char *header_name, const char *header_value);
@ -1678,6 +1685,12 @@ LINPHONE_PUBLIC void linphone_chat_message_add_custom_header(LinphoneChatMessage
* @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 message 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
@ -4599,6 +4612,24 @@ LINPHONE_PUBLIC const char *linphone_core_get_tls_cert_path(const LinphoneCore *
*/
LINPHONE_PUBLIC const char *linphone_core_get_tls_key_path(const LinphoneCore *lc);
#include "linphone/im_encryption_engine.h"
/**
* @ingroup chatroom
* Sets an IM Encryption Engine in the core
* @param lc LinphoneCore object
* @param imee LinphoneImEncryptionEngine object
*/
LINPHONE_PUBLIC void linphone_core_set_im_encryption_engine(LinphoneCore *lc, LinphoneImEncryptionEngine *imee);
/**
* @ingroup chatroom
* Gets the IM Encryption Engine in the core if possible
* @param lc LinphoneCore object
* @return the IM Encryption Engine in the core or NULL
*/
LINPHONE_PUBLIC LinphoneImEncryptionEngine * linphone_core_get_im_encryption_engine(const LinphoneCore *lc);
#include "linphone/ringtoneplayer.h"
#ifdef __cplusplus

View file

@ -851,6 +851,7 @@ void sal_resolve_cancel(SalResolverContext *ctx);
SalCustomHeader *sal_custom_header_append(SalCustomHeader *ch, const char *name, const char *value);
const char *sal_custom_header_find(const SalCustomHeader *ch, const char *name);
SalCustomHeader *sal_custom_header_remove(SalCustomHeader *ch, const char *name);
void sal_custom_header_free(SalCustomHeader *ch);
SalCustomHeader *sal_custom_header_clone(const SalCustomHeader *ch);

View file

@ -74,25 +74,35 @@ void message_received(LinphoneCore *lc, LinphoneChatRoom *room, LinphoneChatMess
* */
void file_transfer_received(LinphoneChatMessage *msg, const LinphoneContent* content, const LinphoneBuffer *buffer){
FILE* file=NULL;
char *receive_file = bc_tester_file("receive_file.dump");
char *receive_file = NULL;
LinphoneChatRoom *cr = linphone_chat_message_get_chat_room(msg);
LinphoneCore *lc = linphone_chat_room_get_core(cr);
if (!linphone_chat_message_get_user_data(msg)) {
/*first chunk, creating file*/
file = fopen(receive_file,"wb");
linphone_chat_message_set_user_data(msg,(void*)file); /*store fd for next chunks*/
}
bc_free(receive_file);
file = (FILE*)linphone_chat_message_get_user_data(msg);
BC_ASSERT_PTR_NOT_NULL(file);
if (linphone_buffer_is_empty(buffer)) { /* tranfer complete */
stats* counters = get_stats(lc);
counters->number_of_LinphoneFileTransferDownloadSuccessful++;
linphone_chat_message_set_user_data(msg, NULL);
fclose(file);
} else { /* store content on a file*/
if (fwrite(linphone_buffer_get_content(buffer),linphone_buffer_get_size(buffer),1,file)==0){
ms_error("file_transfer_received(): write() failed: %s",strerror(errno));
if (linphone_chat_message_get_file_transfer_filepath(msg) != NULL) {
if (linphone_buffer_is_empty(buffer)) {
stats* counters = get_stats(lc);
counters->number_of_LinphoneFileTransferDownloadSuccessful++;
return;
}
} else {
receive_file = bc_tester_file("receive_file.dump");
if (!linphone_chat_message_get_user_data(msg)) {
/*first chunk, creating file*/
file = fopen(receive_file,"wb");
linphone_chat_message_set_user_data(msg,(void*)file); /*store fd for next chunks*/
}
bc_free(receive_file);
file = (FILE*)linphone_chat_message_get_user_data(msg);
BC_ASSERT_PTR_NOT_NULL(file);
if (linphone_buffer_is_empty(buffer)) { /* tranfer complete */
stats* counters = get_stats(lc);
counters->number_of_LinphoneFileTransferDownloadSuccessful++;
linphone_chat_message_set_user_data(msg, NULL);
fclose(file);
} else { /* store content on a file*/
if (fwrite(linphone_buffer_get_content(buffer),linphone_buffer_get_size(buffer),1,file)==0){
ms_error("file_transfer_received(): write() failed: %s",strerror(errno));
}
}
}
}
@ -196,7 +206,7 @@ void compare_files(const char *path1, const char *path2) {
buf2 = (uint8_t*)ms_load_path_content(path2, &size2);
BC_ASSERT_PTR_NOT_NULL(buf1);
BC_ASSERT_PTR_NOT_NULL(buf2);
BC_ASSERT_EQUAL((uint8_t)size1, (uint8_t)size2, uint8_t, "%u");
BC_ASSERT_EQUAL((uint8_t)size2, (uint8_t)size1, uint8_t, "%u");
BC_ASSERT_EQUAL(memcmp(buf1, buf2, size1), 0, int, "%d");
ms_free(buf1);
ms_free(buf2);
@ -234,6 +244,30 @@ LinphoneChatMessage* create_message_from_nowebcam(LinphoneChatRoom *chat_room) {
return msg;
}
LinphoneChatMessage* create_file_transfer_message_from_nowebcam(LinphoneChatRoom *chat_room) {
LinphoneChatMessageCbs *cbs;
LinphoneContent* content;
LinphoneChatMessage* msg;
char *send_filepath = bc_tester_res("images/nowebcamCIF.jpg");
content = linphone_core_create_content(chat_room->lc);
belle_sip_object_set_name(&content->base, "nowebcam content");
linphone_content_set_type(content,"image");
linphone_content_set_subtype(content,"jpeg");
linphone_content_set_name(content,"nowebcamCIF.jpg");
msg = linphone_chat_room_create_file_transfer_message(chat_room, content);
linphone_chat_message_set_file_transfer_filepath(msg, send_filepath);
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_message_cbs_set_file_transfer_progress_indication(cbs, file_transfer_progress_indication);
linphone_content_unref(content);
ms_free(send_filepath);
return msg;
}
void text_message_base(LinphoneCoreManager* marie, LinphoneCoreManager* pauline) {
LinphoneChatMessage* msg = linphone_chat_room_create_message(linphone_core_get_chat_room(pauline->lc,marie->identity),"Bli bli bli \n blu");
LinphoneChatMessageCbs *cbs = linphone_chat_message_get_callbacks(msg);
@ -411,20 +445,28 @@ static void text_message_with_external_body(void) {
linphone_core_manager_destroy(pauline);
}
void transfer_message_base2(LinphoneCoreManager* marie, LinphoneCoreManager* pauline, bool_t upload_error, bool_t download_error) {
void transfer_message_base2(LinphoneCoreManager* marie, LinphoneCoreManager* pauline, bool_t upload_error, bool_t download_error, bool_t use_file_body_handler_in_upload, bool_t use_file_body_handler_in_download) {
char *send_filepath = bc_tester_res("images/nowebcamCIF.jpg");
char *receive_filepath = bc_tester_file("receive_file.dump");
LinphoneChatRoom* chat_room;
LinphoneChatMessage* msg;
LinphoneChatMessageCbs *cbs;
/* Remove any previously downloaded file */
remove(receive_filepath);
/* Globally configure an http file transfer server. */
linphone_core_set_file_transfer_server(pauline->lc,"https://www.linphone.org:444/lft.php");
/* create a chatroom on pauline's side */
chat_room = linphone_core_get_chat_room(pauline->lc, marie->identity);
/* create a file transfer msg */
msg = create_message_from_nowebcam(chat_room);
if (use_file_body_handler_in_upload) {
msg = create_file_transfer_message_from_nowebcam(chat_room);
} else {
msg = create_message_from_nowebcam(chat_room);
}
linphone_chat_room_send_chat_message(chat_room,msg);
if (upload_error) {
@ -448,6 +490,9 @@ void transfer_message_base2(LinphoneCoreManager* marie, LinphoneCoreManager* pau
linphone_chat_message_cbs_set_msg_state_changed(cbs, liblinphone_tester_chat_message_msg_state_changed);
linphone_chat_message_cbs_set_file_transfer_recv(cbs, file_transfer_received);
linphone_chat_message_cbs_set_file_transfer_progress_indication(cbs, file_transfer_progress_indication);
if (use_file_body_handler_in_download) {
linphone_chat_message_set_file_transfer_filepath(marie->stat.last_received_chat_message, receive_filepath);
}
linphone_chat_message_download_file(marie->stat.last_received_chat_message);
if (download_error) {
@ -471,25 +516,37 @@ void transfer_message_base2(LinphoneCoreManager* marie, LinphoneCoreManager* pau
bc_free(receive_filepath);
}
void transfer_message_base(bool_t upload_error, bool_t download_error) {
void transfer_message_base(bool_t upload_error, bool_t download_error, bool_t use_file_body_handler_in_upload, bool_t use_file_body_handler_in_download) {
if (transport_supported(LinphoneTransportTls)) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_tcp_rc");
transfer_message_base2(marie,pauline,upload_error,download_error);
transfer_message_base2(marie,pauline,upload_error,download_error, use_file_body_handler_in_upload, use_file_body_handler_in_download);
linphone_core_manager_destroy(pauline);
linphone_core_manager_destroy(marie);
}
}
static void transfer_message(void) {
transfer_message_base(FALSE, FALSE);
transfer_message_base(FALSE, FALSE, FALSE, FALSE);
}
static void transfer_message_2(void) {
transfer_message_base(FALSE, FALSE, TRUE, FALSE);
}
static void transfer_message_3(void) {
transfer_message_base(FALSE, FALSE, FALSE, TRUE);
}
static void transfer_message_4(void) {
transfer_message_base(FALSE, FALSE, TRUE, TRUE);
}
static void transfer_message_with_upload_io_error(void) {
transfer_message_base(TRUE, FALSE);
transfer_message_base(TRUE, FALSE, FALSE, FALSE);
}
static void transfer_message_with_download_io_error(void) {
transfer_message_base(FALSE, TRUE);
transfer_message_base(FALSE, TRUE, FALSE, FALSE);
}
static void transfer_message_upload_cancelled(void) {
@ -604,6 +661,9 @@ static void file_transfer_2_messages_simultaneously(void) {
char *receive_filepath = bc_tester_file("receive_file.dump");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_tcp_rc");
/* Remove any previously downloaded file */
remove(receive_filepath);
/* Globally configure an http file transfer server. */
linphone_core_set_file_transfer_server(pauline->lc,"https://www.linphone.org:444/lft.php");
@ -809,6 +869,10 @@ static void lime_text_message(void) {
linphone_chat_room_send_message(chat_room,"Bla bla bla bla");
BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceived,1));
BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceivedLegacy,1));
BC_ASSERT_PTR_NOT_NULL(marie->stat.last_received_chat_message);
if (marie->stat.last_received_chat_message) {
BC_ASSERT_STRING_EQUAL(linphone_chat_message_get_text(marie->stat.last_received_chat_message), "Bla bla bla bla");
}
BC_ASSERT_PTR_NOT_NULL(linphone_core_get_chat_room(marie->lc,pauline->identity));
/* TODO : check the msg arrived correctly deciphered */
@ -853,14 +917,19 @@ end:
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
void lime_transfer_message_base(bool_t encrypt_file,bool_t download_file_from_stored_msg) {
void lime_transfer_message_base(bool_t encrypt_file,bool_t download_file_from_stored_msg, bool_t use_file_body_handler_in_upload, bool_t use_file_body_handler_in_download) {
FILE *ZIDCacheMarieFD, *ZIDCachePaulineFD;
LinphoneCoreManager *marie, *pauline;
LinphoneChatMessage *msg;
LinphoneChatMessageCbs *cbs;
char *pauline_id, *marie_id;
char *filepath;
char *send_filepath = bc_tester_res("images/nowebcamCIF.jpg");
char *receive_filepath = bc_tester_file("receive_file.dump");
MSList * msg_list = NULL;
/* Remove any previously downloaded file */
remove(receive_filepath);
marie = linphone_core_manager_new( "marie_rc");
pauline = linphone_core_manager_new( "pauline_tcp_rc");
@ -901,7 +970,11 @@ void lime_transfer_message_base(bool_t encrypt_file,bool_t download_file_from_st
linphone_core_set_file_transfer_server(pauline->lc,"https://www.linphone.org:444/lft.php");
/* create a file transfer msg */
msg = create_message_from_nowebcam(linphone_core_get_chat_room(pauline->lc, marie->identity));
if (use_file_body_handler_in_upload) {
msg = create_file_transfer_message_from_nowebcam(linphone_core_get_chat_room(pauline->lc, marie->identity));
} else {
msg = create_message_from_nowebcam(linphone_core_get_chat_room(pauline->lc, marie->identity));
}
linphone_chat_room_send_chat_message(msg->chat_room, msg);
BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneMessageReceivedWithFile,1));
@ -926,8 +999,13 @@ void lime_transfer_message_base(bool_t encrypt_file,bool_t download_file_from_st
BC_ASSERT_PTR_NOT_NULL(linphone_content_get_key(content));
else
BC_ASSERT_PTR_NULL(linphone_content_get_key(content));
if (use_file_body_handler_in_download) {
linphone_chat_message_set_file_transfer_filepath(recv_msg, receive_filepath);
}
linphone_chat_message_download_file(recv_msg);
BC_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneFileTransferDownloadSuccessful,1));
compare_files(send_filepath, receive_filepath);
bctbx_list_free_with_data(msg_list, (bctbx_list_free_func)linphone_chat_message_unref);
}
@ -935,20 +1013,38 @@ void lime_transfer_message_base(bool_t encrypt_file,bool_t download_file_from_st
BC_ASSERT_EQUAL(pauline->stat.number_of_LinphoneMessageDelivered,1, int, "%d");
BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneFileTransferDownloadSuccessful,1, int, "%d");
end:
ms_free(send_filepath);
bc_free(receive_filepath);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
static void lime_transfer_message(void) {
lime_transfer_message_base(TRUE,FALSE);
lime_transfer_message_base(TRUE, FALSE, FALSE, FALSE);
}
static void lime_transfer_message_2(void) {
lime_transfer_message_base(TRUE, FALSE, TRUE, FALSE);
}
static void lime_transfer_message_3(void) {
lime_transfer_message_base(TRUE, FALSE, FALSE, TRUE);
}
static void lime_transfer_message_4(void) {
lime_transfer_message_base(TRUE, FALSE, TRUE, TRUE);
}
static void lime_transfer_message_from_history(void) {
lime_transfer_message_base(TRUE,TRUE);
lime_transfer_message_base(TRUE, TRUE, FALSE, FALSE);
}
static void lime_transfer_message_without_encryption(void) {
lime_transfer_message_base(FALSE,FALSE);
lime_transfer_message_base(FALSE, FALSE, FALSE, FALSE);
}
static void lime_transfer_message_without_encryption_2(void) {
lime_transfer_message_base(FALSE, FALSE, TRUE, FALSE);
}
static void printHex(char *title, uint8_t *data, size_t length) {
@ -1818,12 +1914,41 @@ void file_transfer_with_http_proxy(void) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_tcp_rc");
linphone_core_set_http_proxy_host(marie->lc, "sip.linphone.org");
transfer_message_base2(marie,pauline,FALSE,FALSE);
transfer_message_base2(marie,pauline,FALSE,FALSE,FALSE,FALSE);
linphone_core_manager_destroy(pauline);
linphone_core_manager_destroy(marie);
}
}
void chat_message_custom_headers(void) {
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 = linphone_chat_room_create_message(chat_room, "Lorem Ipsum");
LinphoneChatMessageCbs *cbs = linphone_chat_message_get_callbacks(msg);
linphone_chat_message_add_custom_header(msg, "Test1", "Value1");
linphone_chat_message_add_custom_header(msg, "Test2", "Value2");
linphone_chat_message_remove_custom_header(msg, "Test1");
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) {
const char *header = linphone_chat_message_get_custom_header(marie->stat.last_received_chat_message, "Test2");
BC_ASSERT_STRING_EQUAL(header, "Value2");
header = linphone_chat_message_get_custom_header(marie->stat.last_received_chat_message, "Test1");
BC_ASSERT_PTR_NULL(header);
BC_ASSERT_STRING_EQUAL(marie->stat.last_received_chat_message->message, "Lorem Ipsum");
}
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
test_t message_tests[] = {
TEST_NO_TAG("Text message", text_message),
TEST_NO_TAG("Text message within call dialog", text_message_within_call_dialog),
@ -1834,6 +1959,9 @@ test_t message_tests[] = {
TEST_NO_TAG("Text message with send error", text_message_with_send_error),
TEST_NO_TAG("Text message with external body", text_message_with_external_body),
TEST_NO_TAG("Transfer message", transfer_message),
TEST_NO_TAG("Transfer message 2", transfer_message_2),
TEST_NO_TAG("Transfer message 3", transfer_message_3),
TEST_NO_TAG("Transfer message 4", transfer_message_4),
TEST_NO_TAG("Transfer message with http proxy", file_transfer_with_http_proxy),
TEST_NO_TAG("Transfer message with upload io error", transfer_message_with_upload_io_error),
TEST_NO_TAG("Transfer message with download io error", transfer_message_with_download_io_error),
@ -1848,8 +1976,12 @@ test_t message_tests[] = {
TEST_NO_TAG("Lime text message", lime_text_message),
TEST_NO_TAG("Lime text message to non lime", lime_text_message_to_non_lime),
TEST_NO_TAG("Lime transfer message", lime_transfer_message),
TEST_NO_TAG("Lime transfer message 2", lime_transfer_message_2),
TEST_NO_TAG("Lime transfer message 3", lime_transfer_message_3),
TEST_NO_TAG("Lime transfer message 4", lime_transfer_message_4),
TEST_NO_TAG("Lime transfer message from history", lime_transfer_message_from_history),
TEST_NO_TAG("Lime transfer message without encryption", lime_transfer_message_without_encryption),
TEST_NO_TAG("Lime transfer message without encryption 2", lime_transfer_message_without_encryption_2),
TEST_NO_TAG("Lime unitary", lime_unit),
#ifdef SQLITE_STORAGE_ENABLED
TEST_NO_TAG("Database migration", database_migration),
@ -1874,6 +2006,7 @@ 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_suite_t message_test_suite = {