From 5193bb34e1f11a11cbd061f4fd3e04dc8f4c1580 Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Tue, 21 Feb 2017 10:52:17 +0100 Subject: [PATCH] Add API to resend a chat message that has not been delivered. --- coreapi/bellesip_sal/sal_op_impl.c | 7 +++- coreapi/callbacks.c | 26 +----------- coreapi/chat.c | 67 +++++++++++++++++++++++------- coreapi/linphonecore.c | 3 -- coreapi/private.h | 1 - include/linphone/chat.h | 6 +++ include/linphone/wrapper_utils.h | 12 +++++- tester/message_tester.c | 32 +++++++++----- wrappers/cpp/abstractapi.py | 1 + 9 files changed, 101 insertions(+), 54 deletions(-) diff --git a/coreapi/bellesip_sal/sal_op_impl.c b/coreapi/bellesip_sal/sal_op_impl.c index 634bd22fb..135b53c4c 100644 --- a/coreapi/bellesip_sal/sal_op_impl.c +++ b/coreapi/bellesip_sal/sal_op_impl.c @@ -159,6 +159,7 @@ belle_sip_request_t* sal_op_build_request(SalOp *op,const char* method) { belle_sip_request_t *req; belle_sip_uri_t* req_uri; belle_sip_uri_t* to_uri; + belle_sip_header_call_id_t *call_id_header; const SalAddress* to_address; const MSList *elem=sal_op_get_route_addresses(op); @@ -189,11 +190,15 @@ belle_sip_request_t* sal_op_build_request(SalOp *op,const char* method) { belle_sip_uri_set_secure(req_uri,sal_op_is_secure(op)); to_header = belle_sip_header_to_create(BELLE_SIP_HEADER_ADDRESS(to_address),NULL); + call_id_header = belle_sip_provider_create_call_id(prov); + if (sal_op_get_call_id(op)) { + belle_sip_header_call_id_set_call_id(call_id_header, sal_op_get_call_id(op)); + } req=belle_sip_request_create( req_uri, method, - belle_sip_provider_create_call_id(prov), + call_id_header, belle_sip_header_cseq_create(20,method), from_header, to_header, diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index c4a95a8d8..ffa76485c 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -1148,34 +1148,12 @@ static void refer_received(Sal *sal, SalOp *op, const char *referto){ } } -static bool_t is_duplicate_msg(LinphoneCore *lc, const char *msg_id){ - bctbx_list_t *elem=lc->last_recv_msg_ids; - bctbx_list_t *tail=NULL; - int i; - bool_t is_duplicate=FALSE; - for(i=0;elem!=NULL;elem=elem->next,i++){ - if (strcmp((const char*)elem->data,msg_id)==0){ - is_duplicate=TRUE; - } - tail=elem; - } - if (!is_duplicate){ - lc->last_recv_msg_ids=bctbx_list_prepend(lc->last_recv_msg_ids,ms_strdup(msg_id)); - } - if (i>=10){ - ms_free(tail->data); - lc->last_recv_msg_ids=bctbx_list_erase_link(lc->last_recv_msg_ids,tail); - } - return is_duplicate; -} - - static void message_received(SalOp *op, const SalMessage *msg){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); LinphoneReason reason = lc->chat_deny_code; - if (reason == LinphoneReasonNone && is_duplicate_msg(lc, msg->message_id) == FALSE) { - reason = linphone_core_message_received(lc, op, msg); + if (reason == LinphoneReasonNone) { + linphone_core_message_received(lc, op, msg); } 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 eb0839631..646b2a311 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -383,7 +383,6 @@ void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage msg->dir = LinphoneChatMessageOutgoing; - /* Check if we shall upload a file to a server */ if (msg->file_transfer_information != NULL && msg->content_type == NULL) { /* open a transaction with the server and send an empty request(RCS5.1 section 3.5.4.8.3.1) */ @@ -397,16 +396,20 @@ void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage return; } } else { - SalOp *op = NULL; + SalOp *op = msg->op; LinphoneCall *call=NULL; char *content_type; const char *identity = NULL; - char *message_not_encrypted = NULL; - + char *clear_text_message = NULL; + char *clear_text_content_type = NULL; + if (msg->message) { - message_not_encrypted = ms_strdup(msg->message); + clear_text_message = ms_strdup(msg->message); } - + if (msg->content_type) { + clear_text_content_type = ms_strdup(msg->content_type); + } + /* Add to transient list */ linphone_chat_room_add_transient_message(cr, msg); msg->time = ms_time(0); @@ -421,7 +424,7 @@ void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage } } } - + if (!identity) { LinphoneProxyConfig *proxy = linphone_core_lookup_known_proxy(cr->lc, cr->peer_url); if (proxy) { @@ -438,7 +441,7 @@ void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage linphone_address_unref(msg->from); } msg->from = linphone_address_new(identity); - + if (imee) { LinphoneImEncryptionEngineCbs *imee_cbs = linphone_im_encryption_engine_get_callbacks(imee); LinphoneImEncryptionEngineCbsOutgoingMessageCb cb_process_outgoing_message = linphone_im_encryption_engine_cbs_get_process_outgoing_message(imee_cbs); @@ -449,7 +452,7 @@ void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage } } } - + if (op == NULL) { /*sending out of calls*/ msg->op = op = sal_op_new(cr->lc->sal); @@ -457,7 +460,7 @@ 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); store_or_update_chat_message(msg); @@ -481,10 +484,15 @@ void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage ms_free(peer_uri); } - if (msg->message && message_not_encrypted && strcmp(msg->message, message_not_encrypted) != 0) { + if (msg->message && clear_text_message && strcmp(msg->message, clear_text_message) != 0) { // We replace the encrypted message by the original one so it can be correctly stored and displayed by the application ms_free(msg->message); - msg->message = ms_strdup(message_not_encrypted); + msg->message = ms_strdup(clear_text_message); + } + if (msg->content_type && clear_text_content_type && (strcmp(msg->content_type, clear_text_content_type) != 0)) { + /* We replace the encrypted content type by the original one */ + ms_free(msg->content_type); + msg->content_type = ms_strdup(clear_text_content_type); } msg->message_id = ms_strdup(sal_op_get_call_id(op)); /* must be known at that time */ store_or_update_chat_message(msg); @@ -495,8 +503,11 @@ void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatMessage linphone_chat_room_delete_composing_idle_timer(cr); linphone_chat_room_delete_composing_refresh_timer(cr); - if (message_not_encrypted) { - ms_free(message_not_encrypted); + if (clear_text_message) { + ms_free(clear_text_message); + } + if (clear_text_content_type) { + ms_free(clear_text_content_type); } if (call && call->op == op) { @@ -648,6 +659,12 @@ LinphoneReason linphone_core_message_received(LinphoneCore *lc, SalOp *op, const linphone_address_clean(addr); cr = linphone_core_get_chat_room(lc, addr); + /* Check if this is a duplicate message */ + if (linphone_chat_room_find_message(cr, sal_op_get_call_id(op)) != NULL) { + reason = lc->chat_deny_code; + goto end; + } + msg = linphone_chat_room_create_message(cr, sal_msg->text); linphone_chat_message_set_content_type(msg, sal_msg->content_type); linphone_chat_message_set_from(msg, cr->peer_url); @@ -950,6 +967,28 @@ void linphone_chat_room_send_chat_message(LinphoneChatRoom *cr, LinphoneChatMess _linphone_chat_room_send_message(cr, msg); } +void _linphone_chat_message_resend(LinphoneChatMessage *msg, bool_t ref_msg) { + LinphoneChatMessageState state = linphone_chat_message_get_state(msg); + LinphoneChatRoom *cr; + + if (state != LinphoneChatMessageStateNotDelivered) { + ms_warning("Cannot resend chat message in state %s", linphone_chat_message_state_to_string(state)); + return; + } + + cr = linphone_chat_message_get_chat_room(msg); + if (ref_msg) linphone_chat_message_ref(msg); + _linphone_chat_room_send_message(cr, msg); +} + +void linphone_chat_message_resend(LinphoneChatMessage *msg) { + _linphone_chat_message_resend(msg, FALSE); +} + +void linphone_chat_message_resend_2(LinphoneChatMessage *msg) { + _linphone_chat_message_resend(msg, TRUE); +} + static char *linphone_chat_room_create_is_composing_xml(LinphoneChatRoom *cr) { xmlBufferPtr buf; xmlTextWriterPtr writer; diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 0a53e60bf..baf6c3ee3 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -5523,9 +5523,6 @@ static void linphone_core_uninit(LinphoneCore *lc) bctbx_list_for_each(lc->call_logs,(void (*)(void*))linphone_call_log_unref); lc->call_logs=bctbx_list_free(lc->call_logs); - bctbx_list_for_each(lc->last_recv_msg_ids,ms_free); - lc->last_recv_msg_ids=bctbx_list_free(lc->last_recv_msg_ids); - if(lc->zrtp_secrets_cache != NULL) { ms_free(lc->zrtp_secrets_cache); } diff --git a/coreapi/private.h b/coreapi/private.h index 0ba063bc8..233ad7c08 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -1051,7 +1051,6 @@ struct _LinphoneCore int max_calls; LinphoneTunnel *tunnel; char* device_id; - MSList *last_recv_msg_ids; char *chat_db_file; char *logs_db_file; char *friends_db_file; diff --git a/include/linphone/chat.h b/include/linphone/chat.h index 30b7f207a..b041674fa 100644 --- a/include/linphone/chat.h +++ b/include/linphone/chat.h @@ -412,6 +412,12 @@ LINPHONE_PUBLIC int linphone_chat_message_download_file(LinphoneChatMessage *mes */ LINPHONE_PUBLIC void linphone_chat_message_cancel_file_transfer(LinphoneChatMessage* msg); +/** + * Resend a chat message if it is in the 'not delivered' state for whatever reason. + * @param[in] msg LinphoneChatMessage object + */ +LINPHONE_PUBLIC void linphone_chat_message_resend(LinphoneChatMessage *msg); + /** * Linphone message has an app-specific field that can store a text. The application might want * to use it for keeping data over restarts, like thumbnail image path. diff --git a/include/linphone/wrapper_utils.h b/include/linphone/wrapper_utils.h index 1201468f7..5c05a1cc0 100644 --- a/include/linphone/wrapper_utils.h +++ b/include/linphone/wrapper_utils.h @@ -44,12 +44,22 @@ extern "C" { * @note Unlike linphone_chat_room_send_chat_message(), that function only takes a reference on the #LinphoneChatMessage * instead of totaly takes ownership on it. Thus, the #LinphoneChatMessage object must be released by the API user after calling * that function. - * + * * @param[in] cr A chat room. * @param[in] msg The message to send. */ LINPHONE_PUBLIC void linphone_chat_room_send_chat_message_2(LinphoneChatRoom *cr, LinphoneChatMessage *msg); +/** + * Resend a chat message if it is in the 'not delivered' state for whatever reason. + * @note Unlike linphone_chat_message_resend(), that function only takes a reference on the #LinphoneChatMessage + * instead of totaly takes ownership on it. Thus, the #LinphoneChatMessage object must be released by the API user after calling + * that function. + * + * @param[in] msg LinphoneChatMessage object + */ +LINPHONE_PUBLIC void linphone_chat_message_resend(LinphoneChatMessage *msg); + /** * Accessor for the shared_ptr<BelCard> stored by a #LinphoneVcard */ diff --git a/tester/message_tester.c b/tester/message_tester.c index 4931dce6c..a28c8ff7d 100644 --- a/tester/message_tester.c +++ b/tester/message_tester.c @@ -1090,7 +1090,8 @@ static void im_notification_policy_with_lime(void) { static void _im_error_delivery_notification(bool_t online) { FILE *ZIDCacheMarieFD, *ZIDCachePaulineFD; LinphoneChatRoom *chat_room; - char *filepath; + char *marie_filepath; + char *pauline_filepath; LinphoneCoreManager *marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager *pauline = linphone_core_manager_new( "pauline_tcp_rc"); LinphoneChatMessage *msg; @@ -1114,13 +1115,12 @@ static void _im_error_delivery_notification(bool_t online) { fclose(ZIDCacheMarieFD); fclose(ZIDCachePaulineFD); - filepath = bc_tester_file("tmpZIDCacheMarie.xml"); - linphone_core_set_zrtp_secrets_file(marie->lc, filepath); - bc_free(filepath); + marie_filepath = bc_tester_file("tmpZIDCacheMarie.xml"); + linphone_core_set_zrtp_secrets_file(marie->lc, marie_filepath); - filepath = bc_tester_file("tmpZIDCachePauline.xml"); - linphone_core_set_zrtp_secrets_file(pauline->lc, filepath); - bc_free(filepath); + pauline_filepath = bc_tester_file("tmpZIDCachePauline.xml"); + linphone_core_set_zrtp_secrets_file(pauline->lc, pauline_filepath); + bc_free(pauline_filepath); chat_room = linphone_core_get_chat_room(pauline->lc, marie->identity); @@ -1154,13 +1154,21 @@ static void _im_error_delivery_notification(bool_t online) { wait_for_until(pauline->lc, marie->lc, &dummy, 1, 1500); /* Just to sleep while iterating */ BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneMessageReceived, 1, int, "%d"); /* Check the new message is not considered as received */ BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &pauline->stat.number_of_LinphoneMessageNotDelivered, 1)); + + /* Restore the ZID cache of the receiver and resend the chat message */ + linphone_core_set_zrtp_secrets_file(marie->lc, marie_filepath); + linphone_chat_message_ref(msg); + linphone_chat_message_resend(msg); + BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &marie->stat.number_of_LinphoneMessageReceived, 2)); /* Check the new message is now received */ + BC_ASSERT_TRUE(wait_for(pauline->lc, marie->lc, &pauline->stat.number_of_LinphoneMessageDeliveredToUser, 1)); linphone_chat_message_unref(msg); + bc_free(marie_filepath); end: - remove("tmpZIDCacheMarie.xml"); - remove("tmpZIDCachePauline.xml"); linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); + remove("tmpZIDCacheMarie.xml"); + remove("tmpZIDCachePauline.xml"); } static void im_error_delivery_notification_online(void) { @@ -1260,7 +1268,11 @@ static void lime_text_message_to_non_lime(bool_t sender_policy_mandatory, bool_t if (chat_room_size == 1) { bctbx_list_t *history = linphone_chat_room_get_history(chat_room, 0); LinphoneChatMessage *sent_msg = (LinphoneChatMessage *)bctbx_list_get_data(history); - BC_ASSERT_EQUAL((int)linphone_chat_message_get_state(sent_msg), (int)LinphoneChatMessageStateDelivered, int, "%d"); + if (lime_key_available) { + BC_ASSERT_EQUAL((int)linphone_chat_message_get_state(sent_msg), (int)LinphoneChatMessageStateDelivered, int, "%d"); + } else { + BC_ASSERT_EQUAL((int)linphone_chat_message_get_state(sent_msg), (int)LinphoneChatMessageStateNotDelivered, int, "%d"); + } bctbx_list_free_with_data(history, (bctbx_list_free_func)linphone_chat_message_unref); } } else { diff --git a/wrappers/cpp/abstractapi.py b/wrappers/cpp/abstractapi.py index 3a319bb22..bc6da71bd 100644 --- a/wrappers/cpp/abstractapi.py +++ b/wrappers/cpp/abstractapi.py @@ -440,6 +440,7 @@ class CParser(object): 'linphone_factory_create_core_with_config', # manualy wrapped 'linphone_buffer_get_content', 'linphone_chat_room_send_chat_message', # overloaded + 'linphone_chat_message_resend', # overloaded 'linphone_config_read_relative_file', 'linphone_vcard_get_belcard', # manualy wrapped 'linphone_chat_room_destroy', # was deprecated when the wrapper generator was made