From ad337fffefbc372b1098735b1802f3d767fa0a41 Mon Sep 17 00:00:00 2001 From: Sandrine Avakian Date: Mon, 27 Mar 2017 15:27:42 +0200 Subject: [PATCH] Adding new API int linphone_call_terminate_with_error_info(LinphoneCall *call, const LinphoneErrorInfo *ei) and other functions to handle RFC3326 reason header. --- coreapi/bellesip_sal/sal_op_call.c | 20 +++++++++-- coreapi/bellesip_sal/sal_op_impl.c | 5 ++- coreapi/callbacks.c | 2 +- coreapi/error_info.c | 14 ++++++-- coreapi/factory.c | 6 ++++ coreapi/linphonecall.c | 42 +++++++++++++++++++++-- coreapi/linphonecore.c | 2 +- include/linphone/call.h | 9 ++++- include/linphone/error_info.h | 21 ++++++++++-- include/linphone/factory.h | 5 +++ include/sal/sal.h | 1 + tester/call_single_tester.c | 53 +++++++++++++++++++++++++++++- 12 files changed, 166 insertions(+), 14 deletions(-) diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index 6b4bc13d6..825c01642 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -1039,7 +1039,8 @@ int sal_call_send_dtmf(SalOp *h, char dtmf){ return 0; } -int sal_call_terminate(SalOp *op){ + +int sal_call_terminate_with_error(SalOp *op, const SalErrorInfo *info){ belle_sip_dialog_state_t dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL; if (op->state==SalOpStateTerminating || op->state==SalOpStateTerminated) { ms_error("Cannot terminate op [%p] in state [%s]",op,sal_op_state_to_string(op->state)); @@ -1047,10 +1048,19 @@ int sal_call_terminate(SalOp *op){ } switch(dialog_state) { case BELLE_SIP_DIALOG_CONFIRMED: { - sal_op_send_request(op,belle_sip_dialog_create_request(op->dialog,"BYE")); + belle_sip_request_t * req = belle_sip_dialog_create_request(op->dialog,"BYE"); + if (info != NULL){ + belle_sip_header_reason_t* reason = BELLE_SIP_HEADER_REASON(belle_sip_header_reason_new()); + belle_sip_header_reason_set_text(reason, info->status_string); + belle_sip_header_reason_set_protocol(reason,info->protocol); + belle_sip_header_reason_set_cause(reason,info->protocol_code); + belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(reason)); + } + sal_op_send_request(op,req); op->state=SalOpStateTerminating; break; } + case BELLE_SIP_DIALOG_NULL: { if (op->dir == SalOpDirIncoming) { sal_call_decline(op, SalReasonDeclined,NULL); @@ -1085,6 +1095,12 @@ int sal_call_terminate(SalOp *op){ return 0; } + +int sal_call_terminate(SalOp *op){ + return sal_call_terminate_with_error(op, NULL); +} + + bool_t sal_call_autoanswer_asked(SalOp *op){ return op->auto_answer_asked; } diff --git a/coreapi/bellesip_sal/sal_op_impl.c b/coreapi/bellesip_sal/sal_op_impl.c index d7ddaa959..a8e263d37 100644 --- a/coreapi/bellesip_sal/sal_op_impl.c +++ b/coreapi/bellesip_sal/sal_op_impl.c @@ -606,7 +606,7 @@ void sal_error_info_set(SalErrorInfo *ei, SalReason reason, const char *protocol void sal_op_set_reason_error_info(SalOp *op, belle_sip_message_t *msg){ belle_sip_header_reason_t* reason_header = belle_sip_message_get_header_by_type(msg,belle_sip_header_reason_t); if (reason_header){ - SalErrorInfo *ei=&op->reason_error_info; + SalErrorInfo *ei=&op->reason_error_info; // ?// const char *protocol = belle_sip_header_reason_get_protocol(reason_header); int code = belle_sip_header_reason_get_cause(reason_header); const char *text = belle_sip_header_reason_get_text(reason_header); @@ -633,6 +633,9 @@ const SalErrorInfo * sal_op_get_reason_error_info(const SalOp *op){ return &op->reason_error_info; } + + + static void unlink_op_with_dialog(SalOp *op, belle_sip_dialog_t* dialog){ belle_sip_dialog_set_application_data(dialog,NULL); sal_op_unref(op); diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 3ec88f4a8..1d46cd3a0 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -818,7 +818,7 @@ static void call_terminated(SalOp *op, const char *from){ break; case LinphoneCallIncomingReceived: case LinphoneCallIncomingEarlyMedia: - linphone_error_info_set(call->ei, LinphoneReasonNotAnswered, 0, "Incoming call cancelled", NULL); + linphone_error_info_set(call->ei,NULL, LinphoneReasonNotAnswered, 0, "Incoming call cancelled", NULL); call->non_op_error = TRUE; break; default: diff --git a/coreapi/error_info.c b/coreapi/error_info.c index 288b8e793..0311ac497 100644 --- a/coreapi/error_info.c +++ b/coreapi/error_info.c @@ -216,12 +216,20 @@ void linphone_error_info_from_sal_op(LinphoneErrorInfo *ei, const SalOp *op){ } } -void linphone_error_info_set(LinphoneErrorInfo *ei, LinphoneReason reason, int code, const char *status_string, const char *warning){ +void linphone_error_info_set(LinphoneErrorInfo *ei, const char *protocol, LinphoneReason reason, int code, const char *status_string, const char *warning){ linphone_error_info_reset(ei); ei->reason = reason; ei->protocol_code = code; - ei->phrase = ms_strdup_safe(status_string); - ei->warnings = ms_strdup_safe(warning); + if (protocol != NULL){ + ei->protocol = bctbx_strdup(protocol); + } + else{ + const char* prot = "SIP"; + ei->protocol = bctbx_strdup(prot); + } + + ei->phrase = bctbx_strdup(status_string); + ei->warnings = bctbx_strdup(warning); } diff --git a/coreapi/factory.c b/coreapi/factory.c index 867d6be24..1ac43dc9f 100644 --- a/coreapi/factory.c +++ b/coreapi/factory.c @@ -169,3 +169,9 @@ void linphone_factory_set_msplugins_dir(LinphoneFactory *factory, const char *pa if (factory->msplugins_dir) bctbx_free(factory->msplugins_dir); factory->msplugins_dir = bctbx_strdup(path); } + +LinphoneErrorInfo *linphone_factory_create_error_info(void){ + + return linphone_error_info_new(); + +} diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 8e97cd567..750afb677 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -4327,7 +4327,7 @@ static void linphone_call_lost(LinphoneCall *call){ ms_message("LinphoneCall [%p]: %s", call, temp); linphone_core_notify_display_warning(lc, temp); call->non_op_error = TRUE; - linphone_error_info_set(call->ei, LinphoneReasonIOError, 503, "Media lost", NULL); + linphone_error_info_set(call->ei,NULL, LinphoneReasonIOError, 503, "Media lost", NULL); linphone_call_terminate(call); linphone_core_play_named_tone(lc, LinphoneToneCallLost); ms_free(temp); @@ -5088,6 +5088,44 @@ int linphone_call_terminate(LinphoneCall *call) { return 0; } +static void linphone_call_error_info_to_sal_op(const LinphoneErrorInfo* ei, SalErrorInfo* sei){ + + sei->reason = linphone_error_info_get_reason(ei); + sei->status_string = ms_strdup_safe(ei->phrase); + sei->full_string = ms_strdup_safe(ei->full_string); + sei->warnings = ms_strdup_safe(ei->warnings); + sei->protocol_code = ei->protocol_code; + sei->protocol = ms_strdup_safe(ei->protocol); +} + +int linphone_call_terminate_with_error(LinphoneCall *call , const LinphoneErrorInfo *ei){ + SalErrorInfo sei; + linphone_call_error_info_to_sal_op(ei, &sei); + + ms_message("Terminate call [%p] which is currently in state %s", call, linphone_call_state_to_string(call->state)); + switch (call->state) { + case LinphoneCallReleased: + case LinphoneCallEnd: + case LinphoneCallError: + ms_warning("No need to terminate a call [%p] in state [%s]", call, linphone_call_state_to_string(call->state)); + return -1; + case LinphoneCallIncomingReceived: + case LinphoneCallIncomingEarlyMedia: + return linphone_call_decline(call, LinphoneReasonDeclined); + case LinphoneCallOutgoingInit: + /* In state OutgoingInit, op has to be destroyed */ + sal_op_release(call->op); + call->op = NULL; + break; + default: + sal_call_terminate_with_error(call->op, &sei); + break; + } + terminate_call(call); + return 0; + +} + int linphone_call_redirect(LinphoneCall *call, const char *redirect_uri) { char *real_url = NULL; LinphoneCore *lc; @@ -5109,7 +5147,7 @@ int linphone_call_redirect(LinphoneCall *call, const char *redirect_uri) { real_url = linphone_address_as_string(real_parsed_url); sal_call_decline(call->op, SalReasonRedirect, real_url); ms_free(real_url); - linphone_error_info_set(call->ei, LinphoneReasonMovedPermanently, 302, "Call redirected", NULL); + linphone_error_info_set(call->ei, NULL, LinphoneReasonMovedPermanently, 302, "Call redirected", NULL); call->non_op_error = TRUE; terminate_call(call); linphone_address_unref(real_parsed_url); diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index d4c64e416..576d99095 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -2980,7 +2980,7 @@ void linphone_core_iterate(LinphoneCore *lc){ decline_reason = (lc->current_call != call) ? LinphoneReasonBusy : LinphoneReasonDeclined; call->log->status=LinphoneCallMissed; call->non_op_error = TRUE; - linphone_error_info_set(call->ei, decline_reason, linphone_reason_to_error_code(decline_reason), "Not answered", NULL); + linphone_error_info_set(call->ei, NULL, decline_reason, linphone_reason_to_error_code(decline_reason), "Not answered", NULL); linphone_call_decline(call, decline_reason); } } diff --git a/include/linphone/call.h b/include/linphone/call.h index 8d5985274..c1b86fa69 100644 --- a/include/linphone/call.h +++ b/include/linphone/call.h @@ -371,12 +371,19 @@ LINPHONE_PUBLIC int linphone_call_pause(LinphoneCall *call); **/ LINPHONE_PUBLIC int linphone_call_resume(LinphoneCall *call); +/** + * Terminates a call. + * @param[in] call LinphoneCall object + * @return 0 on success, -1 on failure +**/LINPHONE_PUBLIC int linphone_call_terminate(LinphoneCall *call); + + /** * Terminates a call. * @param[in] call LinphoneCall object * @return 0 on success, -1 on failure **/ -LINPHONE_PUBLIC int linphone_call_terminate(LinphoneCall *call); +LINPHONE_PUBLIC int linphone_call_terminate_with_error(LinphoneCall *call, const LinphoneErrorInfo *ei); /** * Redirect the specified call to the given redirect URI. diff --git a/include/linphone/error_info.h b/include/linphone/error_info.h index 22f5e59fa..49980608c 100644 --- a/include/linphone/error_info.h +++ b/include/linphone/error_info.h @@ -69,6 +69,13 @@ LINPHONE_PUBLIC LinphoneReason linphone_error_info_get_reason(const LinphoneErro * @return The error phrase **/ LINPHONE_PUBLIC const char * linphone_error_info_get_phrase(const LinphoneErrorInfo *ei); + +/** + * Get protocol from the error info. + * @param[in] ei ErrorInfo object + * @return The protocol + */ +LINPHONE_PUBLIC const char *linphone_error_info_get_protocol(const LinphoneErrorInfo *ei); /** * Provides additional information regarding the failure. @@ -88,10 +95,20 @@ LINPHONE_PUBLIC int linphone_error_info_get_protocol_code(const LinphoneErrorInf /** * Assign information to a LinphoneErrorInfo object. - * @param[in] ei ErrorInfo object + * @param[in] ei ErrorInfo object + * @param[in] protocol protocol name + * @param[in] reason reason from LinphoneReason enum + * @param[in] code protocol code + * @param[in] status_string description of the reason + * @param[in] warning warning message */ -LINPHONE_PUBLIC void linphone_error_info_set(LinphoneErrorInfo *ei, LinphoneReason reason, int code, const char *status_string, const char *warning); +LINPHONE_PUBLIC void linphone_error_info_set(LinphoneErrorInfo *ei, const char *protocol, LinphoneReason reason, int code, const char *status_string, const char *warning); +/** + * Assign reason LinphoneReason to a LinphoneErrorUnfo object. + * @param[in] ei ErrorInfo object + * @param[in] reason reason from LinphoneReason enum + */ LINPHONE_PUBLIC void linphone_error_info_set_reason(LinphoneErrorInfo *ei, LinphoneReason reason); /** diff --git a/include/linphone/factory.h b/include/linphone/factory.h index c24d9005f..f570278da 100644 --- a/include/linphone/factory.h +++ b/include/linphone/factory.h @@ -203,6 +203,11 @@ LINPHONE_PUBLIC char * linphone_factory_get_msplugins_dir(const LinphoneFactory */ LINPHONE_PUBLIC void linphone_factory_set_msplugins_dir(LinphoneFactory *factory, const char *path); +/** + * Creates an object LinphoneErrorInfo. + * @return LinphoneErrorInfo object. + */ +LINPHONE_PUBLIC LinphoneErrorInfo *linphone_factory_create_error_info(void); /** * @} */ diff --git a/include/sal/sal.h b/include/sal/sal.h index 40ed02341..09df18d47 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -757,6 +757,7 @@ int sal_call_set_referer(SalOp *h, SalOp *refered_call); SalOp *sal_call_get_replaces(SalOp *h); int sal_call_send_dtmf(SalOp *h, char dtmf); int sal_call_terminate(SalOp *h); +int sal_call_terminate_with_error(SalOp *op, const SalErrorInfo *info); bool_t sal_call_autoanswer_asked(SalOp *op); void sal_call_send_vfu_request(SalOp *h); int sal_call_is_offerer(const SalOp *h); diff --git a/tester/call_single_tester.c b/tester/call_single_tester.c index ab29c52c3..315c49742 100644 --- a/tester/call_single_tester.c +++ b/tester/call_single_tester.c @@ -980,6 +980,56 @@ static void simple_call_compatibility_mode(void) { linphone_core_manager_destroy(pauline); } +static void terminate_call_with_error(void) { + LinphoneCoreManager *callee_mgr = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager *caller_mgr = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); + + LinphoneCall* out_call = linphone_core_invite_address(caller_mgr->lc,callee_mgr->identity); + LinphoneCall* call_callee ; + + linphone_call_ref(out_call); + LinphoneErrorInfo *ei = linphone_error_info_new(); + linphone_error_info_set(ei, NULL, LinphoneReasonNone, 200, "Call completed elsewhere", NULL); + + BC_ASSERT_TRUE(wait_for(caller_mgr->lc, callee_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallOutgoingInit,1)); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc, callee_mgr->lc, &callee_mgr->stat.number_of_LinphoneCallIncomingReceived, 1)); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc, callee_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallOutgoingProgress, 1)); + + call_callee = linphone_core_get_current_call(callee_mgr->lc); + BC_ASSERT_PTR_NOT_NULL(call_callee); + + BC_ASSERT_EQUAL( linphone_core_accept_call(callee_mgr->lc,call_callee), 0 , int, "%d"); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc,&caller_mgr->stat.number_of_LinphoneCallConnected,1)); + + + + BC_ASSERT_TRUE(wait_for(caller_mgr->lc, callee_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallStreamsRunning, 1)); + + + const LinphoneErrorInfo *rei = ei; + + linphone_call_terminate_with_error(out_call,rei); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc,&caller_mgr->stat.number_of_LinphoneCallEnd,1)); + + BC_ASSERT_PTR_NOT_NULL(rei); + if (rei){ + BC_ASSERT_EQUAL(linphone_error_info_get_protocol_code(rei),200, int, "%d"); + BC_ASSERT_PTR_NOT_NULL(linphone_error_info_get_phrase(rei)); + BC_ASSERT_STRING_EQUAL(linphone_error_info_get_phrase(rei), "Call completed elsewhere"); + BC_ASSERT_STRING_EQUAL(linphone_error_info_get_protocol(ei), "SIP"); + + } + + + BC_ASSERT_EQUAL(caller_mgr->stat.number_of_LinphoneCallEnd,1, int, "%d"); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc,&caller_mgr->stat.number_of_LinphoneCallReleased,1)); + + linphone_error_info_unref(ei); + linphone_call_unref(out_call); + linphone_core_manager_destroy(callee_mgr); + linphone_core_manager_destroy(caller_mgr); +} + static void cancelled_call(void) { LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); @@ -5825,7 +5875,8 @@ test_t call_tests[] = { TEST_NO_TAG("Call with ZRTP configured receiver side only", call_with_zrtp_configured_callee_side), TEST_NO_TAG("Call from plain RTP to ZRTP mandatory should be silent", call_from_plain_rtp_to_zrtp), TEST_NO_TAG("Call ZRTP mandatory to plain RTP should be silent", call_from_zrtp_to_plain_rtp), - TEST_NO_TAG("Call with network reachable down in callback", call_with_network_reachable_down_in_callback) + TEST_NO_TAG("Call with network reachable down in callback", call_with_network_reachable_down_in_callback), + TEST_NO_TAG("Call terminated with reason", terminate_call_with_error) }; test_suite_t call_test_suite = {"Single Call", NULL, NULL, liblinphone_tester_before_each, liblinphone_tester_after_each,