diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index 9676c9b73..8a53abdab 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -510,6 +510,7 @@ static int is_media_description_acceptable(SalMediaDescription *md){ static SalReason process_sdp_for_invite(SalOp* op,belle_sip_request_t* invite) { belle_sdp_session_description_t* sdp; SalReason reason = SalReasonNone; + SalErrorInfo sei; if (extract_sdp(op,BELLE_SIP_MESSAGE(invite),&sdp,&reason)==0) { if (sdp){ @@ -525,7 +526,10 @@ static SalReason process_sdp_for_invite(SalOp* op,belle_sip_request_t* invite) { } if (reason != SalReasonNone){ - sal_call_decline(op,reason,NULL); + sal_error_info_init_to_null(&sei); + sal_error_info_set(&sei, reason,"SIP", 0, NULL, NULL); + sal_call_decline_with_error_info(op, &sei,NULL); + } return reason; } @@ -943,6 +947,17 @@ int sal_call_accept(SalOp*h){ return 0; } +static belle_sip_header_reason_t *sal_call_make_reason_header( const SalErrorInfo *info){ + 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); + return reason; + } + return NULL; +} + int sal_call_decline(SalOp *op, SalReason reason, const char *redirection /*optional*/){ belle_sip_response_t* response; belle_sip_header_contact_t* contact=NULL; @@ -971,6 +986,41 @@ int sal_call_decline(SalOp *op, SalReason reason, const char *redirection /*opti return 0; } +int sal_call_decline_with_error_info(SalOp *op, const SalErrorInfo *info, const char *redirection /*optional*/){ + belle_sip_response_t* response; + belle_sip_header_contact_t* contact=NULL; + int status = info->protocol_code; + belle_sip_transaction_t *trans; + + if (info->reason==SalReasonRedirect){ + if (redirection!=NULL) { + if (strstr(redirection,"sip:")!=0) status=302; + else status=380; + contact= belle_sip_header_contact_new(); + belle_sip_header_address_set_uri(BELLE_SIP_HEADER_ADDRESS(contact),belle_sip_uri_parse(redirection)); + } else { + ms_error("Cannot redirect to null"); + } + } + trans=(belle_sip_transaction_t*)op->pending_server_trans; + if (!trans) trans=(belle_sip_transaction_t*)op->pending_update_server_trans; + if (!trans){ + ms_error("sal_call_decline_with_error_info(): no pending transaction to decline."); + return -1; + } + response = sal_op_create_response_from_request(op,belle_sip_transaction_get_request(trans),status); + belle_sip_header_reason_t* reason_header = sal_call_make_reason_header(info->sub_sei); + if (reason_header) { + belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_HEADER(reason_header)); + } + + if (contact) { + belle_sip_message_add_header(BELLE_SIP_MESSAGE(response),BELLE_SIP_HEADER(contact)); + } + belle_sip_server_transaction_send_response(BELLE_SIP_SERVER_TRANSACTION(trans),response); + return 0; +} + int sal_call_update(SalOp *op, const char *subject, bool_t no_user_consent){ belle_sip_request_t *update; belle_sip_dialog_state_t state; @@ -1040,7 +1090,18 @@ 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){ + SalErrorInfo sei; + const SalErrorInfo *p_sei; + if (info == NULL){ + sal_error_info_init_to_null(&sei); + sal_error_info_set(&sei,SalReasonDeclined, "SIP", 0, NULL, NULL); + p_sei = &sei; + } else{ + p_sei = 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)); @@ -1048,13 +1109,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 = sal_call_make_reason_header(info); + 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); + sal_call_decline_with_error_info(op, p_sei, NULL); 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){ @@ -1071,7 +1138,7 @@ int sal_call_terminate(SalOp *op){ } case BELLE_SIP_DIALOG_EARLY: { if (op->dir == SalOpDirIncoming) { - sal_call_decline(op, SalReasonDeclined,NULL); + sal_call_decline_with_error_info(op, p_sei,NULL); op->state=SalOpStateTerminated; } else { cancelling_invite(op); @@ -1086,6 +1153,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..f859b221e 100644 --- a/coreapi/bellesip_sal/sal_op_impl.c +++ b/coreapi/bellesip_sal/sal_op_impl.c @@ -561,11 +561,22 @@ const SalErrorInfo *sal_error_info_none(void){ "Ok", 200, NULL, - NULL + NULL, + }; return &none; } +void sal_error_info_init_to_null(SalErrorInfo *sei){ + sei->status_string = NULL; + sei->full_string = NULL; + sei->protocol = NULL; + sei->sub_sei = NULL; + sei->warnings = NULL; + sei->protocol_code=0; + sei->reason=SalReasonNone; +} + void sal_error_info_reset(SalErrorInfo *ei){ if (ei->status_string){ ms_free(ei->status_string); @@ -584,14 +595,23 @@ void sal_error_info_reset(SalErrorInfo *ei){ ms_free(ei->protocol); ei->protocol = NULL; } + if (ei->sub_sei){ + ms_free(ei->sub_sei); + ei->sub_sei = NULL; + } ei->protocol_code=0; ei->reason=SalReasonNone; } void sal_error_info_set(SalErrorInfo *ei, SalReason reason, const char *protocol, int code, const char *status_string, const char *warning){ sal_error_info_reset(ei); - if (reason==SalReasonUnknown && strcmp(protocol, "SIP") == 0) ei->reason=_sal_reason_from_sip_code(code); - else ei->reason=reason; + if (reason==SalReasonUnknown && strcmp(protocol, "SIP") == 0 && code != 0) ei->reason=_sal_reason_from_sip_code(code); + else{ + ei->reason=reason; + if (code == 0) { + code = sal_reason_to_sip_code(reason); + } + } ei->protocol_code=code; ei->status_string=status_string ? ms_strdup(status_string) : NULL; ei->warnings=warning ? ms_strdup(warning) : NULL; @@ -606,7 +626,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); @@ -623,6 +643,7 @@ void sal_op_set_error_info_from_response(SalOp *op, belle_sip_response_t *respon warnings=warning ? belle_sip_header_get_unparsed_value(warning) : NULL; sal_error_info_set(ei,SalReasonUnknown,"SIP", code,reason_phrase,warnings); + sal_op_set_reason_error_info(op, BELLE_SIP_MESSAGE(response)); } const SalErrorInfo *sal_op_get_error_info(const SalOp *op){ @@ -633,6 +654,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 867f493d5..26bcad581 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -269,6 +269,7 @@ static void call_received(SalOp *h){ LinphoneAddress *from_address_to_search_if_me=NULL; /*address used to know if I'm the caller*/ SalMediaDescription *md; const char * p_asserted_id; + SalErrorInfo sei; LinphoneErrorInfo *ei = NULL; /* Look if this INVITE is for a call that has already been notified but broken because of network failure */ @@ -306,10 +307,12 @@ static void call_received(SalOp *h){ case LinphonePresenceActivityPermanentAbsence: alt_contact = linphone_presence_model_get_contact(lc->presence_model); if (alt_contact != NULL) { - sal_call_decline(h,SalReasonRedirect,alt_contact); + sal_error_info_init_to_null(&sei); + sal_error_info_set(&sei,SalReasonRedirect, "SIP", 0, NULL, NULL); + sal_call_decline_with_error_info(h, &sei,alt_contact); ms_free(alt_contact); ei = linphone_error_info_new(); - linphone_error_info_set(ei, LinphoneReasonMovedPermanently, 302, "Moved permanently", NULL); + linphone_error_info_set(ei, NULL, LinphoneReasonMovedPermanently, 302, "Moved permanently", NULL); linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, from_addr, to_addr, ei); sal_op_release(h); return; @@ -324,7 +327,7 @@ static void call_received(SalOp *h){ if (!linphone_core_can_we_add_call(lc)){/*busy*/ sal_call_decline(h,SalReasonBusy,NULL); ei = linphone_error_info_new(); - linphone_error_info_set(ei, LinphoneReasonBusy, 486, "Busy - too many calls", NULL); + linphone_error_info_set(ei, NULL, LinphoneReasonBusy, 486, "Busy - too many calls", NULL); linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, from_addr, to_addr, ei); sal_op_release(h); return; @@ -344,7 +347,7 @@ static void call_received(SalOp *h){ ms_warning("Receiving a call while one with same address [%s] is initiated, refusing this one with busy message.",addr); sal_call_decline(h,SalReasonBusy,NULL); ei = linphone_error_info_new(); - linphone_error_info_set(ei, LinphoneReasonBusy, 486, "Busy - duplicated call", NULL); + linphone_error_info_set(ei, NULL, LinphoneReasonBusy, 486, "Busy - duplicated call", NULL); linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, from_addr, to_addr, ei); sal_op_release(h); linphone_address_unref(from_address_to_search_if_me); @@ -362,7 +365,7 @@ static void call_received(SalOp *h){ if (md){ if (sal_media_description_empty(md) || linphone_core_incompatible_security(lc,md)){ ei = linphone_error_info_new(); - linphone_error_info_set(ei, LinphoneReasonNotAcceptable, 488, "Not acceptable here", NULL); + linphone_error_info_set(ei, NULL, LinphoneReasonNotAcceptable, 488, "Not acceptable here", NULL); linphone_core_report_early_failed_call(lc, LinphoneCallIncoming, linphone_address_ref(from_addr), linphone_address_ref(to_addr), ei); sal_call_decline(call->op,SalReasonNotAcceptable,NULL); linphone_call_unref(call); @@ -698,6 +701,7 @@ static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call){ /* this callback is called when an incoming re-INVITE/ SIP UPDATE modifies the session*/ static void call_updated(LinphoneCore *lc, LinphoneCall *call, SalOp *op, bool_t is_update){ + SalErrorInfo sei; SalMediaDescription *rmd=sal_call_get_remote_media_description(op); call->defer_update = lp_config_get_int(lc->config, "sip", "defer_update_default", FALSE); @@ -735,7 +739,9 @@ static void call_updated(LinphoneCore *lc, LinphoneCall *call, SalOp *op, bool_t case LinphoneCallUpdating: case LinphoneCallPausing: case LinphoneCallResuming: - sal_call_decline(call->op,SalReasonInternalError,NULL); + sal_error_info_init_to_null(&sei); + sal_error_info_set(&sei,SalReasonInternalError, "SIP", 0, NULL, NULL); + sal_call_decline_with_error_info(call->op, &sei,NULL); /*no break*/ case LinphoneCallIdle: case LinphoneCallOutgoingInit: @@ -757,7 +763,8 @@ static void call_updating(SalOp *op, bool_t is_update){ LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); SalMediaDescription *rmd=sal_call_get_remote_media_description(op); - + SalErrorInfo sei; + if (!call) { ms_error("call_updating(): call doesn't exist anymore"); return ; @@ -768,7 +775,7 @@ static void call_updating(SalOp *op, bool_t is_update){ if (rmd == NULL && lp_config_get_int(call->core->config,"sip","sdp_200_ack_follow_video_policy",0)) { LinphoneCallParams *p=linphone_core_create_call_params(lc, NULL); ms_message("Applying default policy for offering SDP on call [%p]",call); - linphone_call_set_new_params(call, p); + _linphone_call_set_new_params(call, p); linphone_call_params_unref(p); } linphone_call_make_local_media_description(call); @@ -787,14 +794,18 @@ static void call_updating(SalOp *op, bool_t is_update){ md=sal_call_get_final_media_description(call->op); if (md && (sal_media_description_empty(md) || linphone_core_incompatible_security(lc,md))){ - sal_call_decline(call->op,SalReasonNotAcceptable,NULL); + sal_error_info_init_to_null(&sei); + sal_error_info_set(&sei,SalReasonNotAcceptable, "SIP", 0, NULL, NULL); + sal_call_decline_with_error_info(call->op, &sei,NULL); return; } if (is_update && prev_result_desc && md){ int diff=sal_media_description_equals(prev_result_desc,md); if (diff & (SAL_MEDIA_DESCRIPTION_CRYPTO_POLICY_CHANGED|SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED)){ ms_warning("Cannot accept this update, it is changing parameters that require user approval"); - sal_call_decline(call->op,SalReasonNotAcceptable,NULL); /*FIXME should send 504 Cannot change the session parameters without prompting the user"*/ + sal_error_info_init_to_null(&sei); + sal_error_info_set(&sei,SalReasonUnknown, "SIP", 504, "Cannot change the session parameters without prompting the user", NULL); + sal_call_decline_with_error_info(call->op, &sei,NULL); return; } } @@ -838,7 +849,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 4fc26252a..4883a0d5f 100644 --- a/coreapi/error_info.c +++ b/coreapi/error_info.c @@ -31,6 +31,7 @@ static void error_info_destroy(LinphoneErrorInfo *ei){ } static void error_info_clone(LinphoneErrorInfo *ei, const LinphoneErrorInfo *other){ + linphone_error_info_set_reason(ei, linphone_error_info_get_reason(other)); ei->protocol = bctbx_strdup(other->protocol); ei->phrase = bctbx_strdup(other->phrase); ei->warnings = bctbx_strdup(other->warnings); @@ -214,10 +215,29 @@ 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){ +LinphoneErrorInfo* linphone_error_info_get_sub(const LinphoneErrorInfo *ei){ + + return ei->sub_ei; +} + +void linphone_error_info_set_sub_error_info(LinphoneErrorInfo *ei, LinphoneErrorInfo *appended_ei){ + if (appended_ei != NULL){ + ei->sub_ei = linphone_error_info_ref(appended_ei); + } +} + +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; + 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 f5983291d..2f34ec4ca 100644 --- a/coreapi/factory.c +++ b/coreapi/factory.c @@ -52,6 +52,7 @@ struct _LinphoneFactory { char *cached_ring_resources_dir; char *cached_image_resources_dir; char *cached_msplugins_dir; + LinphoneErrorInfo* ei; }; static void linphone_factory_uninit(LinphoneFactory *obj){ @@ -209,3 +210,9 @@ const char * linphone_factory_get_msplugins_dir(LinphoneFactory *factory) { void linphone_factory_set_msplugins_dir(LinphoneFactory *factory, const char *path) { STRING_SET(factory->msplugins_dir, path); } + +LinphoneErrorInfo *linphone_factory_create_error_info(LinphoneFactory *factory){ + + return linphone_error_info_new(); + +} diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 322ef88d4..4760aee76 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -4419,7 +4419,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); @@ -4813,13 +4813,29 @@ LinphonePlayer *linphone_call_get_player(LinphoneCall *call){ return call->player; } -void linphone_call_set_new_params(LinphoneCall *call, const LinphoneCallParams *params){ + +void linphone_call_set_params(LinphoneCall *call, const LinphoneCallParams *params){ + if ( call->state == LinphoneCallOutgoingInit || call->state == LinphoneCallIncomingReceived){ + _linphone_call_set_new_params(call, params); + } + else { + ms_error("linphone_call_set_params() invalid state %s to call this function", linphone_call_state_to_string(call->state)); + } +} + + +void _linphone_call_set_new_params(LinphoneCall *call, const LinphoneCallParams *params){ LinphoneCallParams *cp=NULL; if (params) cp=linphone_call_params_copy(params); if (call->params) linphone_call_params_unref(call->params); call->params=cp; } +const LinphoneCallParams * linphone_call_get_params(LinphoneCall *call){ + return call->params; +} + + static int send_dtmf_handler(void *data, unsigned int revents){ LinphoneCall *call = (LinphoneCall*)data; /*By default we send DTMF RFC2833 if we do not have enabled SIP_INFO but we can also send RFC2833 and SIP_INFO*/ @@ -5141,10 +5157,70 @@ int linphone_call_terminate(LinphoneCall *call) { return 0; } +static void linphone_error_info_fields_to_sal(const LinphoneErrorInfo* ei, SalErrorInfo* sei){ + + sei->reason = linphone_error_info_get_reason(ei); + sei->status_string = bctbx_strdup(ei->phrase); + sei->full_string = bctbx_strdup(ei->full_string); + sei->warnings = bctbx_strdup(ei->warnings); + sei->protocol_code = ei->protocol_code; + sei->protocol = bctbx_strdup(ei->protocol); + +} + +static void linphone_error_info_to_sal(const LinphoneErrorInfo* ei, SalErrorInfo* sei){ + + linphone_error_info_fields_to_sal(ei, sei); + if (ei->sub_ei !=NULL) { + + linphone_error_info_to_sal(ei->sub_ei, sei->sub_sei); + } +} + +int linphone_call_terminate_with_error_info(LinphoneCall *call , const LinphoneErrorInfo *ei){ + SalErrorInfo sei ; + sal_error_info_init_to_null(&sei); + LinphoneErrorInfo* p_ei = (LinphoneErrorInfo*) ei; + + 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: + linphone_error_info_set_reason(p_ei, LinphoneReasonDeclined); + return linphone_call_decline_with_error_info(call, p_ei); + case LinphoneCallOutgoingInit: + /* In state OutgoingInit, op has to be destroyed */ + sal_op_release(call->op); + call->op = NULL; + break; + default: + + if (ei == NULL){ + sal_call_terminate(call->op); + } + else{ + linphone_error_info_to_sal(ei, &sei); + sal_call_terminate_with_error(call->op, &sei); + sal_error_info_reset(&sei); + } + break; + } + + terminate_call(call); + return 0; + +} + int linphone_call_redirect(LinphoneCall *call, const char *redirect_uri) { char *real_url = NULL; LinphoneCore *lc; LinphoneAddress *real_parsed_url; + SalErrorInfo sei; if (call->state != LinphoneCallIncomingReceived) { ms_error("Bad state for call redirection."); @@ -5160,9 +5236,11 @@ 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); + sal_error_info_init_to_null(&sei); + sal_error_info_set(&sei,SalReasonRedirect, "SIP", 0, NULL, NULL); + sal_call_decline_with_error_info(call->op, &sei, 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); @@ -5174,12 +5252,29 @@ int linphone_call_decline(LinphoneCall * call, LinphoneReason reason) { ms_error("Cannot decline a call that is in state %s", linphone_call_state_to_string(call->state)); return -1; } - sal_call_decline(call->op, linphone_reason_to_sal(reason), NULL); terminate_call(call); return 0; } + + +int linphone_call_decline_with_error_info(LinphoneCall * call, const LinphoneErrorInfo *ei) { + SalErrorInfo sei; + sal_error_info_init_to_null(&sei); + SalErrorInfo sub_sei; + sal_error_info_init_to_null(&sub_sei); + sei.sub_sei = &sub_sei; + + if ((call->state != LinphoneCallIncomingReceived) && (call->state != LinphoneCallIncomingEarlyMedia)) { + ms_error("Cannot decline a call that is in state %s", linphone_call_state_to_string(call->state)); + return -1; + } + linphone_error_info_to_sal(ei, &sei); + sal_call_decline_with_error_info(call->op, &sei , NULL); + terminate_call(call); + return 0; +} int linphone_call_accept(LinphoneCall *call) { return linphone_call_accept_with_params(call, NULL); } @@ -5248,7 +5343,7 @@ int linphone_call_accept_with_params(LinphoneCall *call, const LinphoneCallParam /* Try to be best-effort in giving real local or routable contact address */ linphone_call_set_contact_op(call); if (params) { - linphone_call_set_new_params(call, params); + _linphone_call_set_new_params(call, params); linphone_call_prepare_ice(call, TRUE); linphone_call_make_local_media_description(call); sal_call_set_local_media_description(call->op, call->localdesc); @@ -5304,7 +5399,7 @@ int linphone_call_accept_early_media_with_params(LinphoneCall *call, const Linph /* If parameters are passed, update the media description */ if (params) { - linphone_call_set_new_params(call, params); + _linphone_call_set_new_params(call, params); linphone_call_make_local_media_description(call); sal_call_set_local_media_description(call->op, call->localdesc); sal_op_set_sent_custom_header(call->op, params->custom_headers); @@ -5377,7 +5472,7 @@ int linphone_call_update(LinphoneCall *call, const LinphoneCallParams *params) { } } #endif /* defined(VIDEO_ENABLED) && defined(BUILD_UPNP) */ - linphone_call_set_new_params(call, params); + _linphone_call_set_new_params(call, params); err = linphone_call_prepare_ice(call, FALSE); if (err == 1) { ms_message("Defer call update to gather ICE candidates"); @@ -5525,7 +5620,7 @@ int _linphone_call_accept_update(LinphoneCall *call, const LinphoneCallParams *p linphone_call_params_enable_video_multicast(call->params, FALSE); } } else { - linphone_call_set_new_params(call, params); + _linphone_call_set_new_params(call, params); } if (call->params->has_video && !linphone_core_video_enabled(lc)) { @@ -5774,6 +5869,7 @@ void linphone_call_reinvite_to_recover_from_connection_loss(LinphoneCall *call) } void linphone_call_repair_if_broken(LinphoneCall *call){ + SalErrorInfo sei; if (!call->broken) return; if (!call->core->media_network_reachable) return; @@ -5803,7 +5899,9 @@ void linphone_call_repair_if_broken(LinphoneCall *call){ break; case LinphoneCallUpdatedByRemote: if (sal_call_dialog_request_pending(call->op)) { - sal_call_decline(call->op, SalReasonServiceUnavailable, NULL); + sal_error_info_init_to_null(&sei); + sal_error_info_set(&sei, SalReasonServiceUnavailable,"SIP", 0, NULL, NULL); + sal_call_decline_with_error_info(call->op, &sei,NULL); } linphone_call_reinvite_to_recover_from_connection_loss(call); break; diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 8ec7b2dd3..48230c32c 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -3081,7 +3081,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/coreapi/private.h b/coreapi/private.h index b0c9d47e9..036a44203 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -410,7 +410,9 @@ void linphone_call_notify_info_message_received(LinphoneCall *call, const Linpho LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, const LinphoneCallParams *params, LinphoneProxyConfig *cfg); LinphoneCall * linphone_call_new_incoming(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op); -void linphone_call_set_new_params(LinphoneCall *call, const LinphoneCallParams *params); +void _linphone_call_set_new_params(LinphoneCall *call, const LinphoneCallParams *params); +void linphone_call_set_params(LinphoneCall *call, const LinphoneCallParams *params); +const LinphoneCallParams * linphone_call_get_params(LinphoneCall *call); void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const char *message); void linphone_call_set_contact_op(LinphoneCall* call); void linphone_call_set_compatible_incoming_call_parameters(LinphoneCall *call, SalMediaDescription *md); @@ -1783,8 +1785,6 @@ void linphone_core_notify_display_status(LinphoneCore *lc, const char *message); void linphone_core_notify_display_message(LinphoneCore *lc, const char *message); void linphone_core_notify_display_warning(LinphoneCore *lc, const char *message); void linphone_core_notify_display_url(LinphoneCore *lc, const char *message, const char *url); -void linphone_core_notify_notify_presence_received(LinphoneCore *lc, LinphoneFriend * lf); -void linphone_core_notify_notify_presence_received_for_uri_or_tel(LinphoneCore *lc, LinphoneFriend *lf, const char *uri_or_tel, const LinphonePresenceModel *presence_model); void linphone_core_notify_new_subscription_requested(LinphoneCore *lc, LinphoneFriend *lf, const char *url); void linphone_core_notify_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username, const char *domain); void linphone_core_notify_authentication_requested(LinphoneCore *lc, LinphoneAuthInfo *auth_info, LinphoneAuthMethod method); diff --git a/include/linphone/call.h b/include/linphone/call.h index 453ea4411..56bd6480a 100644 --- a/include/linphone/call.h +++ b/include/linphone/call.h @@ -203,9 +203,12 @@ LINPHONE_PUBLIC int linphone_call_take_preview_snapshot(LinphoneCall *call, cons **/ LINPHONE_PUBLIC LinphoneReason linphone_call_get_reason(const LinphoneCall *call); + /** * Returns full details about call errors or termination reasons. -**/ + * @param call LinphoneCall object on which we want the information error + * @return LinphoneErrorInfo object holding the reason error. + */ LINPHONE_PUBLIC const LinphoneErrorInfo *linphone_call_get_error_info(const LinphoneCall *call); /** @@ -376,8 +379,16 @@ 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 + * @param[in] ei LinphoneErrorInfo + * @return 0 on success, -1 on failure **/ -LINPHONE_PUBLIC int linphone_call_terminate(LinphoneCall *call); +LINPHONE_PUBLIC int linphone_call_terminate_with_error_info(LinphoneCall *call, const LinphoneErrorInfo *ei); /** * Redirect the specified call to the given redirect URI. @@ -395,6 +406,14 @@ LINPHONE_PUBLIC int linphone_call_redirect(LinphoneCall *call, const char *redir **/ LINPHONE_PUBLIC int linphone_call_decline(LinphoneCall * call, LinphoneReason reason); +/** + * Decline a pending incoming call, with a LinphoneErrorInfo object. + * @param[in] call A LinphoneCall object that must be in the IncomingReceived state + * @param[in] ei LinphoneErrorInfo containing more information on the call rejection. + * @return 0 on success, -1 on failure + */ +LINPHONE_PUBLIC int linphone_call_decline_with_error_info(LinphoneCall * call, const LinphoneErrorInfo *ei); + /** * Accept an incoming call. * diff --git a/include/linphone/core.h b/include/linphone/core.h index e25a26b4c..c182f8ed6 100644 --- a/include/linphone/core.h +++ b/include/linphone/core.h @@ -4885,6 +4885,28 @@ LINPHONE_PUBLIC LinphonePresencePerson * linphone_core_create_presence_person(Li * @return The created #LinphonePresenceService object. */ LINPHONE_PUBLIC LinphonePresenceService * linphone_core_create_presence_service(LinphoneCore *lc, const char *id, LinphonePresenceBasicStatus basic_status, const char *contact); + + +/** + * Notifies the upper layer that a presence status has been received by calling the appropriate + * callback if one has been set. + * @param[in] lc the #LinphoneCore object. + * @param[in] lf the #LinphoneFriend whose presence information has been received. + */ +LINPHONE_PUBLIC void linphone_core_notify_notify_presence_received(LinphoneCore *lc, LinphoneFriend *lf); + + +/** + * Notifies the upper layer that a presence model change has been received for the uri or + * telephone number given as a parameter, by calling the appropriate callback if one has been set. + * @param[in] lc the #LinphoneCore object. + * @param[in] lf the #LinphoneFriend whose presence information has been received. + * @param[in] uri_or_tel telephone number or sip uri + * @param[in] presence_model the #LinphonePresenceModel that has been modified + */ +LINPHONE_PUBLIC void linphone_core_notify_notify_presence_received_for_uri_or_tel(LinphoneCore *lc, LinphoneFriend *lf, const char *uri_or_tel, const LinphonePresenceModel *presence_model); + + /** * @} diff --git a/include/linphone/error_info.h b/include/linphone/error_info.h index 22f5e59fa..025a18e66 100644 --- a/include/linphone/error_info.h +++ b/include/linphone/error_info.h @@ -62,6 +62,13 @@ LINPHONE_PUBLIC void linphone_error_info_unref(LinphoneErrorInfo *ei); **/ LINPHONE_PUBLIC LinphoneReason linphone_error_info_get_reason(const LinphoneErrorInfo *ei); +/** + * Get pointer to chained LinphoneErrorInfo set in sub_ei. + * @param ei ErrorInfo object + * @return LinphoneErrorInfo pointer defined in the ei object. + */ +LINPHONE_PUBLIC LinphoneErrorInfo* linphone_error_info_get_sub(const LinphoneErrorInfo *ei); + /** * Get textual phrase from the error info. * This is the text that is provided by the peer in the protocol (SIP). @@ -69,6 +76,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 +102,28 @@ 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); + +/** + * Set the sub_ei in LinphoneErrorInfo to another LinphoneErrorInfo. + * Used when there is more than one reason header. + * @param[in] ei LinphoneErrorInfo object to which the other LinphoneErrorInfo will be appended as ei->sub_ei. + * @param[in] appended_ei LinphoneErrorInfo to append + */ +LINPHONE_PUBLIC void linphone_error_info_set_sub_error_info(LinphoneErrorInfo *ei, LinphoneErrorInfo *appended_ei); +/** + * 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 fae018c94..8498b9e48 100644 --- a/include/linphone/factory.h +++ b/include/linphone/factory.h @@ -210,6 +210,12 @@ LINPHONE_PUBLIC const char * linphone_factory_get_msplugins_dir(LinphoneFactory */ LINPHONE_PUBLIC void linphone_factory_set_msplugins_dir(LinphoneFactory *factory, const char *path); +/** + * Creates an object LinphoneErrorInfo. + * @param[in] factory LinphoneFactory object + * @return LinphoneErrorInfo object. + */ +LINPHONE_PUBLIC LinphoneErrorInfo *linphone_factory_create_error_info(LinphoneFactory *factory); /** * @} */ diff --git a/include/sal/sal.h b/include/sal/sal.h index 07dbe2bcd..6ee695ce4 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -412,6 +412,7 @@ typedef struct SalErrorInfo{ char *warnings; char *protocol; char *full_string; /*concatenation of status_string + warnings*/ + struct SalErrorInfo *sub_sei; }SalErrorInfo; typedef enum SalPresenceStatus{ @@ -730,6 +731,7 @@ const SalErrorInfo *sal_error_info_none(void); LINPHONE_PUBLIC const SalErrorInfo *sal_op_get_error_info(const SalOp *op); const SalErrorInfo *sal_op_get_reason_error_info(const SalOp *op); void sal_error_info_reset(SalErrorInfo *ei); +void sal_error_info_init_to_null(SalErrorInfo *sei); void sal_error_info_set(SalErrorInfo *ei, SalReason reason, const char *protocol, int code, const char *status_string, const char *warning); /*entity tag used for publish (see RFC 3903)*/ @@ -745,6 +747,7 @@ int sal_call_notify_ringing(SalOp *h, bool_t early_media); /*accept an incoming call or, during a call accept a reINVITE*/ int sal_call_accept(SalOp*h); int sal_call_decline(SalOp *h, SalReason reason, const char *redirection /*optional*/); +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); SalMediaDescription * sal_call_get_remote_media_description(SalOp *h); @@ -758,6 +761,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 991f28482..c2cb29b7e 100644 --- a/tester/call_single_tester.c +++ b/tester/call_single_tester.c @@ -980,6 +980,59 @@ static void simple_call_compatibility_mode(void) { linphone_core_manager_destroy(pauline); } +static void terminate_call_with_error(void) { + LinphoneCall* call_callee ; + LinphoneErrorInfo *ei ; + const LinphoneErrorInfo *rei ; + 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); + + + linphone_call_ref(out_call); + 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)); + + + rei = ei; + + linphone_call_terminate_with_error_info(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"); @@ -1136,6 +1189,62 @@ static void call_busy_when_calling_self(void) { linphone_core_manager_destroy(marie); } +static void call_declined_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* in_call = NULL; + LinphoneCall* out_call = linphone_core_invite_address(caller_mgr->lc,callee_mgr->identity); + LinphoneFactory* factory = linphone_factory_get(); + const LinphoneErrorInfo* rcvd_ei; + const LinphoneErrorInfo* sub_rcvd_ei; + + LinphoneErrorInfo *ei = linphone_factory_create_error_info(factory); + LinphoneErrorInfo *reason_ei = linphone_factory_create_error_info(factory); + + linphone_error_info_set(ei, "SIP", LinphoneReasonUnknown, 603, "Decline", NULL); //ordre des arguments à vérifier + linphone_error_info_set(reason_ei, "hardware", LinphoneReasonUnknown, 66, "J'ai plus de batterie", NULL); + + linphone_error_info_set_sub_error_info(ei, reason_ei); + + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc,&callee_mgr->stat.number_of_LinphoneCallIncomingReceived,1)); + BC_ASSERT_PTR_NOT_NULL(in_call=linphone_core_get_current_call(callee_mgr->lc)); + + linphone_call_ref(out_call); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc,&callee_mgr->stat.number_of_LinphoneCallIncomingReceived,1)); + BC_ASSERT_PTR_NOT_NULL(in_call=linphone_core_get_current_call(callee_mgr->lc)); + if (in_call) { + linphone_call_ref(in_call); + linphone_call_decline_with_error_info(in_call, ei); + + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc,&callee_mgr->stat.number_of_LinphoneCallEnd,1)); + BC_ASSERT_TRUE(wait_for(callee_mgr->lc,caller_mgr->lc,&caller_mgr->stat.number_of_LinphoneCallEnd,1)); + + rcvd_ei = linphone_call_get_error_info(out_call); + sub_rcvd_ei = linphone_error_info_get_sub(rcvd_ei); + + BC_ASSERT_STRING_EQUAL(linphone_error_info_get_phrase(rcvd_ei), "Decline"); + BC_ASSERT_STRING_EQUAL(linphone_error_info_get_protocol(rcvd_ei), "SIP"); + BC_ASSERT_STRING_EQUAL(linphone_error_info_get_phrase(sub_rcvd_ei), "J'ai plus de batterie"); + BC_ASSERT_STRING_EQUAL(linphone_error_info_get_protocol(sub_rcvd_ei), "hardware"); + + BC_ASSERT_EQUAL(linphone_call_get_reason(in_call),LinphoneReasonDeclined, int, "%d"); + BC_ASSERT_EQUAL(linphone_call_log_get_status(linphone_call_get_call_log(in_call)),LinphoneCallDeclined, int, "%d"); + BC_ASSERT_EQUAL(linphone_call_get_reason(out_call),LinphoneReasonDeclined, int, "%d"); + BC_ASSERT_EQUAL(linphone_call_log_get_status(linphone_call_get_call_log(out_call)),LinphoneCallDeclined, int, "%d"); + + + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc,&callee_mgr->stat.number_of_LinphoneCallReleased,1)); + BC_ASSERT_TRUE(wait_for(caller_mgr->lc,callee_mgr->lc,&caller_mgr->stat.number_of_LinphoneCallReleased,1)); + linphone_call_unref(in_call); + } + linphone_call_unref(out_call); + linphone_error_info_unref(reason_ei); + linphone_error_info_unref(ei); + + linphone_core_manager_destroy(callee_mgr); + linphone_core_manager_destroy(caller_mgr); +} static void call_declined(void) { LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); @@ -1523,6 +1632,260 @@ static void call_with_custom_sdp_attributes(void) { linphone_core_manager_destroy(pauline); } + + + +static void call_with_custom_header_or_sdp_cb(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message) { + + + const char *value; + if (cstate == LinphoneCallOutgoingInit){ + LinphoneCallParams *params = linphone_call_params_copy(linphone_call_get_params(call)); + linphone_call_params_add_custom_sdp_attribute(params, "working", "maybe"); + linphone_call_set_params(call, params); + linphone_call_params_unref(params); + + } + + else if (cstate == LinphoneCallIncomingReceived){ + const LinphoneCallParams *tparams = linphone_call_get_remote_params(call); + LinphoneCallParams *params = linphone_call_params_copy(tparams); + //Check received params + //SDP + value = linphone_call_params_get_custom_sdp_attribute(params, "working"); + BC_ASSERT_PTR_NOT_NULL(value); + if (value) BC_ASSERT_STRING_EQUAL(value, "maybe"); + //header + value = linphone_call_params_get_custom_header(params, "weather"); + BC_ASSERT_PTR_NOT_NULL(value); + if (value) BC_ASSERT_STRING_EQUAL(value, "thunderstorm"); + //modify SDP + linphone_call_params_add_custom_sdp_attribute(params, "working", "yes"); + linphone_call_set_params(call, params); + linphone_call_params_unref(params); + + } + + + +} + +static void call_caller_with_custom_header_or_sdp_attributes(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 *call_caller = NULL, *call_callee = NULL; + LinphoneCallParams *caller_params; // *callee_params ; + + LinphoneCoreVTable *vtable; + + LinphoneCallTestParams caller_test_params = {0}; + LinphoneCallTestParams callee_test_params = {0}; + + stats initial_caller=caller_mgr->stat; + stats initial_callee=callee_mgr->stat; + bool_t result=FALSE; + bool_t did_receive_call; + + //Create caller params with custom header and custom SDP + caller_params = linphone_core_create_call_params(caller_mgr->lc, NULL); + linphone_call_params_add_custom_header(caller_params, "weather", "thunderstorm"); + linphone_call_params_add_custom_sdp_media_attribute(caller_params, LinphoneStreamTypeAudio, "sleeping", "almost"); + + caller_test_params.base = (LinphoneCallParams*)caller_params; + callee_test_params.base = NULL; + + /* TODO: This should be handled correctly inside the liblinphone library but meanwhile handle this here. */ + linphone_core_manager_wait_for_stun_resolution(caller_mgr); + linphone_core_manager_wait_for_stun_resolution(callee_mgr); + + setup_sdp_handling(&caller_test_params, caller_mgr); + setup_sdp_handling(&callee_test_params, callee_mgr); + + // Assign dedicated callback to vtable for caller and callee + vtable = linphone_core_v_table_new(); + vtable->call_state_changed = call_with_custom_header_or_sdp_cb; + linphone_core_add_listener(callee_mgr->lc, vtable); + linphone_core_add_listener(caller_mgr->lc, vtable); + + //Caller initates the call with INVITE + // caller params not null + BC_ASSERT_PTR_NOT_NULL((call_caller=linphone_core_invite_address_with_params(caller_mgr->lc,callee_mgr->identity,caller_params))); + + BC_ASSERT_PTR_NULL(linphone_call_get_remote_params(call_caller)); /*assert that remote params are NULL when no response is received yet*/ + + // Wait for Incoming received + did_receive_call = wait_for(callee_mgr->lc + ,caller_mgr->lc + ,&callee_mgr->stat.number_of_LinphoneCallIncomingReceived + ,initial_callee.number_of_LinphoneCallIncomingReceived+1); + BC_ASSERT_EQUAL(did_receive_call, !callee_test_params.sdp_simulate_error, int, "%d"); + + linphone_call_params_unref(caller_params); + + sal_default_set_sdp_handling(caller_mgr->lc->sal, SalOpSDPNormal); + sal_default_set_sdp_handling(callee_mgr->lc->sal, SalOpSDPNormal); + + // Wait for Outgoing Progress + if (linphone_core_get_calls_nb(callee_mgr->lc)<=1) + BC_ASSERT_TRUE(linphone_core_inc_invite_pending(callee_mgr->lc)); + BC_ASSERT_EQUAL(caller_mgr->stat.number_of_LinphoneCallOutgoingProgress,initial_caller.number_of_LinphoneCallOutgoingProgress+1, int, "%d"); + + + + + LinphoneCallParams *default_params=linphone_core_create_call_params(callee_mgr->lc,call_callee); + ms_message("Created default call params with video=%i", linphone_call_params_video_enabled(default_params)); + linphone_core_accept_call_with_params(callee_mgr->lc,call_callee,default_params); + linphone_call_params_unref(default_params); + + + BC_ASSERT_TRUE(wait_for(callee_mgr->lc,caller_mgr->lc,&callee_mgr->stat.number_of_LinphoneCallConnected,initial_callee.number_of_LinphoneCallConnected+1)); + BC_ASSERT_TRUE(wait_for(callee_mgr->lc,caller_mgr->lc,&caller_mgr->stat.number_of_LinphoneCallConnected,initial_caller.number_of_LinphoneCallConnected+1)); + + result = wait_for_until(callee_mgr->lc,caller_mgr->lc,&caller_mgr->stat.number_of_LinphoneCallStreamsRunning,initial_caller.number_of_LinphoneCallStreamsRunning+1, 2000) + && + wait_for_until(callee_mgr->lc,caller_mgr->lc,&callee_mgr->stat.number_of_LinphoneCallStreamsRunning,initial_callee.number_of_LinphoneCallStreamsRunning+1, 2000); + + + + caller_params = linphone_core_create_call_params(caller_mgr->lc, call_caller); + linphone_call_params_clear_custom_sdp_attributes(caller_params); + linphone_call_params_clear_custom_sdp_media_attributes(caller_params, LinphoneStreamTypeAudio); + linphone_call_params_add_custom_sdp_attribute(caller_params, "weather", "sunny"); + linphone_core_update_call(caller_mgr->lc, call_caller, caller_params); + linphone_call_params_unref(caller_params); + + + end_call(caller_mgr, callee_mgr); + + linphone_core_manager_destroy(callee_mgr); + linphone_core_manager_destroy(caller_mgr); +} + + + + +static void call_callee_with_custom_header_or_sdp_cb(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState cstate, const char *message) { + + + const char *value; + if (cstate == LinphoneCallOutgoingInit){ + LinphoneCallParams *params = linphone_call_params_copy(linphone_call_get_params(call)); + linphone_call_params_add_custom_sdp_attribute(params, "working", "maybe"); + linphone_call_set_params(call, params); + linphone_call_params_unref(params); + + } + + else if (cstate == LinphoneCallIncomingReceived){ + const LinphoneCallParams *tparams = linphone_call_get_remote_params(call); + LinphoneCallParams *params = linphone_call_params_copy(tparams); + value = linphone_call_params_get_custom_sdp_attribute(params, "working"); + BC_ASSERT_PTR_NOT_NULL(value); + if (value) BC_ASSERT_STRING_EQUAL(value, "maybe"); + linphone_call_set_params(call, params); + linphone_call_params_unref(params); + + } + +} + + +static void call_callee_with_custom_header_or_sdp_attributes(void) { + int result; + 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 *call_caller = NULL, *call_callee = NULL; + LinphoneCallParams *callee_params, *caller_params ; + + LinphoneCoreVTable *vtable; + const char *value; + LinphoneCallTestParams caller_test_params = {0}; + LinphoneCallTestParams callee_test_params = {0}; + + stats initial_caller=caller_mgr->stat; + stats initial_callee=callee_mgr->stat; + bool_t did_receive_call; + const LinphoneCallParams *caller_remote_params; + + caller_params = linphone_core_create_call_params(caller_mgr->lc, NULL); + + + callee_test_params.base = NULL; + caller_test_params.base = NULL; + + /* TODO: This should be handled correctly inside the liblinphone library but meanwhile handle this here. */ + linphone_core_manager_wait_for_stun_resolution(caller_mgr); + linphone_core_manager_wait_for_stun_resolution(callee_mgr); + + setup_sdp_handling(&caller_test_params, caller_mgr); + setup_sdp_handling(&callee_test_params, callee_mgr); + + // Assign dedicated callback to vtable for caller and callee + vtable = linphone_core_v_table_new(); + vtable->call_state_changed = call_callee_with_custom_header_or_sdp_cb; + linphone_core_add_listener(callee_mgr->lc, vtable); + linphone_core_add_listener(caller_mgr->lc, vtable); + + //Caller initates the call with INVITE + // caller params not null + BC_ASSERT_PTR_NOT_NULL((call_caller=linphone_core_invite_address_with_params(caller_mgr->lc,callee_mgr->identity,caller_params))); + + BC_ASSERT_PTR_NULL(linphone_call_get_remote_params(call_caller)); /*assert that remote params are NULL when no response is received yet*/ + + // Wait for Incoming received + did_receive_call = wait_for(callee_mgr->lc + ,caller_mgr->lc + ,&callee_mgr->stat.number_of_LinphoneCallIncomingReceived + ,initial_callee.number_of_LinphoneCallIncomingReceived+1); + BC_ASSERT_EQUAL(did_receive_call, !callee_test_params.sdp_simulate_error, int, "%d"); + + + + sal_default_set_sdp_handling(caller_mgr->lc->sal, SalOpSDPNormal); + sal_default_set_sdp_handling(callee_mgr->lc->sal, SalOpSDPNormal); + + // Wait for Outgoing Progress + if (linphone_core_get_calls_nb(callee_mgr->lc)<=1) + BC_ASSERT_TRUE(linphone_core_inc_invite_pending(callee_mgr->lc)); + BC_ASSERT_EQUAL(caller_mgr->stat.number_of_LinphoneCallOutgoingProgress,initial_caller.number_of_LinphoneCallOutgoingProgress+1, int, "%d"); + + + //Create callee params with custom header and custom SDP + + + callee_params = linphone_core_create_call_params(callee_mgr->lc,call_callee); + linphone_call_params_add_custom_header(callee_params, "weather", "thunderstorm"); + linphone_call_params_add_custom_sdp_media_attribute(callee_params, LinphoneStreamTypeAudio, "sleeping", "almost"); + linphone_call_params_add_custom_sdp_attribute(callee_params, "working", "yes"); + ms_message("Created default call params with video=%i", linphone_call_params_video_enabled(callee_params)); + linphone_core_accept_call_with_params(callee_mgr->lc,call_callee,callee_params); + linphone_call_params_unref(callee_params); + + + BC_ASSERT_TRUE(wait_for(callee_mgr->lc,caller_mgr->lc,&callee_mgr->stat.number_of_LinphoneCallConnected,initial_callee.number_of_LinphoneCallConnected+1)); + BC_ASSERT_TRUE(wait_for(callee_mgr->lc,caller_mgr->lc,&caller_mgr->stat.number_of_LinphoneCallConnected,initial_caller.number_of_LinphoneCallConnected+1)); + + result = wait_for_until(callee_mgr->lc,caller_mgr->lc,&caller_mgr->stat.number_of_LinphoneCallStreamsRunning,initial_caller.number_of_LinphoneCallStreamsRunning+1, 2000) + && + wait_for_until(callee_mgr->lc,caller_mgr->lc,&callee_mgr->stat.number_of_LinphoneCallStreamsRunning,initial_callee.number_of_LinphoneCallStreamsRunning+1, 2000); + + caller_remote_params = linphone_call_get_remote_params(call_caller); + value = linphone_call_params_get_custom_sdp_attribute(caller_remote_params, "working"); + BC_ASSERT_PTR_NOT_NULL(value); + if (value) BC_ASSERT_STRING_EQUAL(value, "yes"); + //header + value = linphone_call_params_get_custom_header(caller_remote_params, "weather"); + BC_ASSERT_PTR_NOT_NULL(value); + if (value) BC_ASSERT_STRING_EQUAL(value, "thunderstorm"); + + linphone_call_params_unref(caller_params); + end_call(caller_mgr, callee_mgr); + + linphone_core_manager_destroy(callee_mgr); + linphone_core_manager_destroy(caller_mgr); +} + void call_paused_resumed_base(bool_t multicast, bool_t with_losses) { LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); @@ -5462,6 +5825,7 @@ static void call_with_network_reachable_down_in_callback(void){ test_t call_tests[] = { TEST_NO_TAG("Early declined call", early_declined_call), TEST_NO_TAG("Call declined", call_declined), + TEST_NO_TAG("Call declined with error", call_declined_with_error), TEST_NO_TAG("Cancelled call", cancelled_call), TEST_NO_TAG("Early cancelled call", early_cancelled_call), TEST_NO_TAG("Call with DNS timeout", call_with_dns_time_out), @@ -5535,6 +5899,8 @@ test_t call_tests[] = { TEST_ONE_TAG("Call with ICE added by reINVITE", ice_added_by_reinvite, "ICE"), TEST_NO_TAG("Call with custom headers", call_with_custom_headers), TEST_NO_TAG("Call with custom SDP attributes", call_with_custom_sdp_attributes), + TEST_NO_TAG("Call caller with custom header or sdp", call_caller_with_custom_header_or_sdp_attributes), + TEST_NO_TAG("Call callee with custom header or sdp", call_callee_with_custom_header_or_sdp_attributes), TEST_NO_TAG("Call established with rejected INFO", call_established_with_rejected_info), TEST_NO_TAG("Call established with rejected RE-INVITE", call_established_with_rejected_reinvite), TEST_NO_TAG("Call established with rejected incoming RE-INVITE", call_established_with_rejected_incoming_reinvite), @@ -5602,7 +5968,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,