From 5d566fcc326753ba0562fa806ef521235276eb53 Mon Sep 17 00:00:00 2001 From: Benjamin Reis Date: Mon, 5 Jun 2017 10:29:40 +0200 Subject: [PATCH] Add reason header to CANCEL and add tests for reason headers --- coreapi/bellesip_sal/sal_op_call.c | 75 +++++++++--------- coreapi/callbacks.c | 8 +- include/sal/sal.h | 1 + tester/call_single_tester.c | 122 +++++++++++++++++++++++++---- 4 files changed, 154 insertions(+), 52 deletions(-) diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index 1b081eade..e4cef0ab4 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -217,31 +217,12 @@ static void handle_sdp_from_response(SalOp* op,belle_sip_response_t* response) { if (op->base.local_media) sdp_process(op); } -void sal_call_cancel_invite(SalOp* op) { - belle_sip_request_t* cancel; - ms_message("Cancelling INVITE request from [%s] to [%s] ",sal_op_get_from(op), sal_op_get_to(op)); - cancel = belle_sip_client_transaction_create_cancel(op->pending_client_trans); - if (cancel){ - sal_op_send_request(op,cancel); - }else if (op->dialog){ - belle_sip_dialog_state_t state = belle_sip_dialog_get_state(op->dialog);; - /*case where the response received is invalid (could not establish a dialog), but the transaction is not cancellable - * because already terminated*/ - switch(state){ - case BELLE_SIP_DIALOG_EARLY: - case BELLE_SIP_DIALOG_NULL: - /*force kill the dialog*/ - ms_warning("op [%p]: force kill of dialog [%p]", op, op->dialog); - belle_sip_dialog_delete(op->dialog); - break; - default: - break; - } - } +void sal_call_cancel_invite(SalOp *op) { + sal_call_cancel_invite_with_info(op,NULL); } -static void cancelling_invite(SalOp *op) { - sal_call_cancel_invite(op); +static void cancelling_invite(SalOp *op, const SalErrorInfo *info) { + sal_call_cancel_invite_with_info(op, info); op->state=SalOpStateTerminating; } @@ -284,7 +265,7 @@ static void call_process_response(void *op_base, const belle_sip_response_event_ if (strcmp("CANCEL",belle_sip_request_get_method(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_client_trans))))!=0) { /*it wasn't sent */ if (code<200) { - cancelling_invite(op); + cancelling_invite(op, NULL); }else{ /* no need to send the INVITE because the UAS rejected the INVITE*/ if (op->dialog==NULL) call_set_released(op); @@ -441,12 +422,13 @@ static void call_process_transaction_terminated(void *user_ctx, const belle_sip_ if (release_call) call_set_released(op); } -static void call_terminated(SalOp* op,belle_sip_server_transaction_t* server_transaction, belle_sip_request_t* request,int status_code) { +static void call_terminated(SalOp* op,belle_sip_server_transaction_t* server_transaction, int status_code, belle_sip_request_t* cancel_request) { belle_sip_response_t* resp; + belle_sip_request_t* server_req = belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(server_transaction)); op->state = SalOpStateTerminating; op->base.root->callbacks.call_terminated(op,op->dir==SalOpDirIncoming?sal_op_get_from(op):sal_op_get_to(op)); - sal_op_set_reason_error_info(op, BELLE_SIP_MESSAGE(request)); - resp=sal_op_create_response_from_request(op,request,status_code); + sal_op_set_reason_error_info(op, BELLE_SIP_MESSAGE(cancel_request ? cancel_request : server_req)); + resp=sal_op_create_response_from_request(op,server_req,status_code); belle_sip_server_transaction_send_response(server_transaction,resp); } @@ -623,11 +605,7 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t belle_sip_server_transaction_send_response(server_transaction ,sal_op_create_response_from_request(op,req,200)); /*terminate invite transaction*/ - call_terminated(op - ,op->pending_server_trans - ,belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_server_trans)),487); - - + call_terminated(op,op->pending_server_trans,487,req); } else { /*call leg does not exist*/ belle_sip_server_transaction_send_response(server_transaction @@ -671,7 +649,7 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t ms_message("Ignored received ack since a new client transaction has been started since."); } } else if(strcmp("BYE",method)==0) { - call_terminated(op,server_transaction,req,200); + call_terminated(op,server_transaction,200,req); /*call end not notified by dialog deletion because transaction can end before dialog*/ } else if(strcmp("INVITE",method)==0 || (is_update=(strcmp("UPDATE",method)==0)) ) { if (is_update && !belle_sip_message_get_body(BELLE_SIP_MESSAGE(req))) { @@ -957,6 +935,33 @@ static belle_sip_header_reason_t *sal_call_make_reason_header( const SalErrorInf return NULL; } +void sal_call_cancel_invite_with_info(SalOp* op, const SalErrorInfo *info) { + belle_sip_request_t* cancel; + ms_message("Cancelling INVITE request from [%s] to [%s] ",sal_op_get_from(op), sal_op_get_to(op)); + cancel = belle_sip_client_transaction_create_cancel(op->pending_client_trans); + if (cancel){ + if (info != NULL){ + belle_sip_header_reason_t* reason = sal_call_make_reason_header(info); + belle_sip_message_add_header(BELLE_SIP_MESSAGE(cancel),BELLE_SIP_HEADER(reason)); + } + sal_op_send_request(op,cancel); + }else if (op->dialog){ + belle_sip_dialog_state_t state = belle_sip_dialog_get_state(op->dialog);; + /*case where the response received is invalid (could not establish a dialog), but the transaction is not cancellable + * because already terminated*/ + switch(state){ + case BELLE_SIP_DIALOG_EARLY: + case BELLE_SIP_DIALOG_NULL: + /*force kill the dialog*/ + ms_warning("op [%p]: force kill of dialog [%p]", op, op->dialog); + belle_sip_dialog_delete(op->dialog); + break; + default: + break; + } + } +} + int sal_call_decline(SalOp *op, SalReason reason, const char *redirection /*optional*/){ belle_sip_response_t* response; belle_sip_header_contact_t* contact=NULL; @@ -1123,7 +1128,7 @@ int sal_call_terminate_with_error(SalOp *op, const SalErrorInfo *info){ op->state=SalOpStateTerminated; } else if (op->pending_client_trans){ if (belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(op->pending_client_trans)) == BELLE_SIP_TRANSACTION_PROCEEDING){ - cancelling_invite(op); + cancelling_invite(op, p_sei); }else{ /* Case where the CANCEL cannot be sent because no provisional response was received so far. * The Op must be kept for the time of the transaction in case a response is received later. @@ -1139,7 +1144,7 @@ int sal_call_terminate_with_error(SalOp *op, const SalErrorInfo *info){ sal_call_decline_with_error_info(op, p_sei,NULL); op->state=SalOpStateTerminated; } else { - cancelling_invite(op); + cancelling_invite(op, p_sei); } break; } diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 2453775fa..d49030348 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -850,9 +850,11 @@ static void call_terminated(SalOp *op, const char *from){ break; case LinphoneCallIncomingReceived: case LinphoneCallIncomingEarlyMedia: - linphone_error_info_set(call->ei,NULL, LinphoneReasonNotAnswered, 0, "Incoming call cancelled", NULL); - call->non_op_error = TRUE; - break; + if(sal_op_get_reason_error_info(op)->reason != SalReasonNone) { + linphone_error_info_set(call->ei,NULL, LinphoneReasonNotAnswered, 0, "Incoming call cancelled", NULL); + call->non_op_error = TRUE; + } + break; default: break; } diff --git a/include/sal/sal.h b/include/sal/sal.h index 6ee695ce4..0ca64c958 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -750,6 +750,7 @@ int sal_call_decline(SalOp *h, SalReason reason, const char *redirection /*optio int sal_call_decline_with_error_info(SalOp *h, const SalErrorInfo* info, const char *redirection /*optional*/); int sal_call_update(SalOp *h, const char *subject, bool_t no_user_consent); void sal_call_cancel_invite(SalOp *op); +void sal_call_cancel_invite_with_info(SalOp* op, const SalErrorInfo *info); SalMediaDescription * sal_call_get_remote_media_description(SalOp *h); LINPHONE_PUBLIC SalMediaDescription * sal_call_get_final_media_description(SalOp *h); int sal_call_refer(SalOp *h, const char *refer_to); diff --git a/tester/call_single_tester.c b/tester/call_single_tester.c index 45bb03cef..0364e1d3f 100644 --- a/tester/call_single_tester.c +++ b/tester/call_single_tester.c @@ -1008,10 +1008,9 @@ static void terminate_call_with_error(void) { LinphoneCall* out_call = linphone_core_invite_address(caller_mgr->lc,callee_mgr->identity); - linphone_call_ref(out_call); ei = linphone_error_info_new(); - linphone_error_info_set(ei, NULL, LinphoneReasonNone, 200, "Call completed elsewhere", NULL); + linphone_error_info_set(ei, NULL, LinphoneReasonNone, 200, "Call refused for security reason", 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)); @@ -1029,9 +1028,8 @@ static void terminate_call_with_error(void) { if (ei){ BC_ASSERT_EQUAL(linphone_error_info_get_protocol_code(ei),200, int, "%d"); BC_ASSERT_PTR_NOT_NULL(linphone_error_info_get_phrase(ei)); - BC_ASSERT_STRING_EQUAL(linphone_error_info_get_phrase(ei), "Call completed elsewhere"); + BC_ASSERT_STRING_EQUAL(linphone_error_info_get_phrase(ei), "Call refused for security reason"); BC_ASSERT_STRING_EQUAL(linphone_error_info_get_protocol(ei), "SIP"); - } linphone_call_terminate_with_error_info(out_call,ei); BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc,&callee_mgr->stat.number_of_LinphoneCallEnd,1)); @@ -1042,9 +1040,8 @@ static void terminate_call_with_error(void) { 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_phrase(rei), "Call refused for security reason"); BC_ASSERT_STRING_EQUAL(linphone_error_info_get_protocol(ei), "SIP"); - } BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc,&caller_mgr->stat.number_of_LinphoneCallReleased,1)); @@ -1067,7 +1064,7 @@ static void cancel_call_with_error(void) { linphone_call_ref(out_call); ei = linphone_error_info_new(); - linphone_error_info_set(ei, NULL, LinphoneReasonNone, 200, "Call completed elsewhere", NULL); + linphone_error_info_set(ei, NULL, LinphoneReasonNone, 200, "Call has been cancelled", 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)); @@ -1081,9 +1078,8 @@ static void cancel_call_with_error(void) { if (ei){ BC_ASSERT_EQUAL(linphone_error_info_get_protocol_code(ei),200, int, "%d"); BC_ASSERT_PTR_NOT_NULL(linphone_error_info_get_phrase(ei)); - BC_ASSERT_STRING_EQUAL(linphone_error_info_get_phrase(ei), "Call completed elsewhere"); + BC_ASSERT_STRING_EQUAL(linphone_error_info_get_phrase(ei), "Call has been cancelled"); BC_ASSERT_STRING_EQUAL(linphone_error_info_get_protocol(ei), "SIP"); - } linphone_call_terminate_with_error_info(out_call,ei); BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc,&callee_mgr->stat.number_of_LinphoneCallEnd,1)); @@ -1092,11 +1088,10 @@ static void cancel_call_with_error(void) { rei = linphone_call_get_error_info(call_callee); BC_ASSERT_PTR_NOT_NULL(rei); if (rei){ - BC_ASSERT_EQUAL(linphone_error_info_get_protocol_code(rei),0, int, "%d"); + 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), "Incoming call cancelled"); - BC_ASSERT_STRING_EQUAL(linphone_error_info_get_protocol(ei), "SIP"); - + BC_ASSERT_STRING_EQUAL(linphone_error_info_get_phrase(rei), "Call has been cancelled"); + BC_ASSERT_STRING_EQUAL(linphone_error_info_get_protocol(rei), "SIP"); } BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc,&caller_mgr->stat.number_of_LinphoneCallReleased,1)); @@ -1108,6 +1103,103 @@ static void cancel_call_with_error(void) { linphone_core_manager_destroy(caller_mgr); } +static void cancel_other_device_after_accept(void) { + LinphoneCall* call_callee; + LinphoneCall* call_callee_2; + const LinphoneErrorInfo *rei = NULL; + LinphoneCoreManager *callee_mgr = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager *callee_mgr_2 = 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); + linphone_call_ref(out_call); + + 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); + linphone_call_ref(call_callee); + BC_ASSERT_PTR_NOT_NULL(call_callee); + + BC_ASSERT_TRUE(wait_for(caller_mgr->lc, callee_mgr_2->lc, &callee_mgr_2->stat.number_of_LinphoneCallIncomingReceived, 1)); + call_callee_2 = linphone_core_get_current_call(callee_mgr_2->lc); + linphone_call_ref(call_callee_2); + BC_ASSERT_PTR_NOT_NULL(call_callee_2); + + BC_ASSERT_EQUAL( linphone_call_accept(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)); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr_2->lc,&callee_mgr_2->stat.number_of_LinphoneCallEnd,1)); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr_2->lc,&callee_mgr_2->stat.number_of_LinphoneCallReleased,1)); + + rei = linphone_call_get_error_info(call_callee_2); + 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(rei), "SIP"); + } + + linphone_call_terminate(out_call); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc,&caller_mgr->stat.number_of_LinphoneCallEnd,1)); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc,&caller_mgr->stat.number_of_LinphoneCallReleased,1)); + + linphone_call_unref(out_call); + linphone_call_unref(call_callee); + linphone_call_unref(call_callee_2); + linphone_core_manager_destroy(callee_mgr); + linphone_core_manager_destroy(callee_mgr_2); + linphone_core_manager_destroy(caller_mgr); +} + +static void cancel_other_device_after_decline(void) { + LinphoneCall* call_callee; + LinphoneCall* call_callee_2; + const LinphoneErrorInfo *rei = NULL; + LinphoneCoreManager *callee_mgr = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager *callee_mgr_2 = linphone_core_manager_new("marie_rc"); + //LinphoneCoreManager *caller_mgr = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); + LinphoneCoreManager *caller_mgr = linphone_core_manager_new("pauline_tcp_rc"); + + LinphoneCall* out_call = linphone_core_invite_address(caller_mgr->lc,callee_mgr->identity); + linphone_call_ref(out_call); + + 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); + linphone_call_ref(call_callee); + BC_ASSERT_PTR_NOT_NULL(call_callee); + + BC_ASSERT_TRUE(wait_for(caller_mgr->lc, callee_mgr_2->lc, &callee_mgr_2->stat.number_of_LinphoneCallIncomingReceived, 1)); + call_callee_2 = linphone_core_get_current_call(callee_mgr_2->lc); + linphone_call_ref(call_callee_2); + BC_ASSERT_PTR_NOT_NULL(call_callee_2); + + BC_ASSERT_EQUAL(linphone_call_decline(call_callee, LinphoneReasonDeclined), 0 , int, "%d"); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc,&caller_mgr->stat.number_of_LinphoneCallEnd,1)); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc, &caller_mgr->stat.number_of_LinphoneCallReleased, 1)); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr_2->lc,&callee_mgr_2->stat.number_of_LinphoneCallEnd,1)); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr_2->lc,&callee_mgr_2->stat.number_of_LinphoneCallReleased,1)); + + rei = linphone_call_get_error_info(call_callee_2); + BC_ASSERT_PTR_NOT_NULL(rei); + if (rei){ + BC_ASSERT_EQUAL(linphone_error_info_get_protocol_code(rei),600, int, "%d"); + BC_ASSERT_PTR_NOT_NULL(linphone_error_info_get_phrase(rei)); + BC_ASSERT_STRING_EQUAL(linphone_error_info_get_phrase(rei), "Busy Everywhere"); + BC_ASSERT_STRING_EQUAL(linphone_error_info_get_protocol(rei), "SIP"); + } + + linphone_call_unref(out_call); + linphone_call_unref(call_callee); + linphone_call_unref(call_callee_2); + linphone_core_manager_destroy(callee_mgr); + linphone_core_manager_destroy(callee_mgr_2); + linphone_core_manager_destroy(caller_mgr); +} + static void cancelled_call(void) { LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); @@ -6074,7 +6166,9 @@ test_t call_tests[] = { 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 terminated with reason", terminate_call_with_error), - TEST_NO_TAG("Call cancelled with reason", cancel_call_with_error) + TEST_NO_TAG("Call cancelled with reason", cancel_call_with_error), + TEST_NO_TAG("Call accepted, other ringing device receive CANCEL with reason", cancel_other_device_after_accept), + TEST_NO_TAG("Call declined, other ringing device receive CANCEL with reason", cancel_other_device_after_decline) }; test_suite_t call_test_suite = {"Single Call", NULL, NULL, liblinphone_tester_before_each, liblinphone_tester_after_each,