diff --git a/coreapi/CMakeLists.txt b/coreapi/CMakeLists.txt index d8af5289a..92d19ff9a 100644 --- a/coreapi/CMakeLists.txt +++ b/coreapi/CMakeLists.txt @@ -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 diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index 06965ae44..c960d84d1 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -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 \ diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 8ccde1378..e01faa9a9 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -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); diff --git a/coreapi/bellesip_sal/sal_op_message.c b/coreapi/bellesip_sal/sal_op_message.c index a17a65194..39e98e65b 100644 --- a/coreapi/bellesip_sal/sal_op_message.c +++ b/coreapi/bellesip_sal/sal_op_message.c @@ -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 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; } diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index c7f01119e..428368dc4 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -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); } diff --git a/coreapi/chat.c b/coreapi/chat.c index 2123a7cae..cd4ed54e7 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -27,7 +27,6 @@ #include "linphone/lpconfig.h" #include "belle-sip/belle-sip.h" #include "ortp/b64.h" -#include "lime.h" #include #include @@ -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; } diff --git a/coreapi/chat_file_transfer.c b/coreapi/chat_file_transfer.c index 0fc974085..c73dc2450 100644 --- a/coreapi/chat_file_transfer.c +++ b/coreapi/chat_file_transfer.c @@ -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); diff --git a/coreapi/im_encryption_engine.c b/coreapi/im_encryption_engine.c new file mode 100644 index 000000000..3a675648c --- /dev/null +++ b/coreapi/im_encryption_engine.c @@ -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; +} diff --git a/coreapi/im_encryption_engine.h b/coreapi/im_encryption_engine.h new file mode 100644 index 000000000..68a14b7bb --- /dev/null +++ b/coreapi/im_encryption_engine.h @@ -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 + +#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 */ \ No newline at end of file diff --git a/coreapi/lime.c b/coreapi/lime.c index 3f25951ee..9d3511af5 100644 --- a/coreapi/lime.c +++ b/coreapi/lime.c @@ -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 */ diff --git a/coreapi/lime.h b/coreapi/lime.h index 277f57cbe..4487d3860 100644 --- a/coreapi/lime.h +++ b/coreapi/lime.h @@ -38,6 +38,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include #include +#include "linphone/core.h" #include #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 */ diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index da64b4b00..28c1cdc3e 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -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; +} diff --git a/coreapi/private.h b/coreapi/private.h index bca1a9a87..b83415e9d 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -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; }; diff --git a/include/linphone/core.h b/include/linphone/core.h index f2207b3af..d7fe93f77 100644 --- a/include/linphone/core.h +++ b/include/linphone/core.h @@ -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 diff --git a/include/sal/sal.h b/include/sal/sal.h index f9fd31e4a..65de05736 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -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); diff --git a/tester/message_tester.c b/tester/message_tester.c index 98386a987..7662dd290 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -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 = {