Commit 0d154471 authored by jehan's avatar jehan
Browse files

better management of call state machine

parent e69170d3
......@@ -301,7 +301,6 @@ static void process_response_event(void *user_ctx, const belle_sip_response_even
if (op->state == SalOpStateTerminating && strcmp("BYE",belle_sip_request_get_method(request))!=0) {
/*only bye are completed*/
belle_sip_message("Op is in state terminating, nothing else to do ");
return;
} else {
if (op->pending_auth_transaction){
belle_sip_object_unref(op->pending_auth_transaction);
......@@ -349,6 +348,7 @@ static void process_transaction_terminated(void *user_ctx, const belle_sip_trans
ms_error("Unhandled transaction terminated [%p]",trans);
}
if (op) sal_op_unref(op); /*no longuer need to ref op*/
}
static void process_auth_requested(void *sal, belle_sip_auth_event_t *auth_event) {
......
......@@ -19,6 +19,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "sal_impl.h"
#include "offeranswer.h"
static void call_set_released(SalOp* op){
op->state=SalOpStateTerminated;
op->base.root->callbacks.call_released(op);
}
static void call_set_error(SalOp* op,belle_sip_response_t* response){
SalError error=SalErrorUnknown;
SalReason sr=SalReasonUnknown;
belle_sip_header_t* reason_header = belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),"Reason");
char* reason=(char*)belle_sip_response_get_reason_phrase(response);
int code = belle_sip_response_get_status_code(response);
if (reason_header){
reason = ms_strdup_printf("%s %s",reason,belle_sip_header_extension_get_value(BELLE_SIP_HEADER_EXTENSION(reason_header)));
}
sal_compute_sal_errors_from_code(code,&error,&sr);
op->base.root->callbacks.call_failure(op,error,sr,reason,code);
if (reason_header != NULL){
ms_free(reason);
}
call_set_released(op);
}
static void sdp_process(SalOp *h){
ms_message("Doing SDP offer/answer process of type %s",h->sdp_offering ? "outgoing" : "incoming");
......@@ -85,7 +105,7 @@ static void call_process_io_error(void *user_ctx, const belle_sip_io_error_event
if (!op->dialog) {
/*call terminated very early*/
op->base.root->callbacks.call_failure(op,SalErrorNoResponse,SalReasonUnknown,"Service Unavailable",503);
op->state=SalOpStateTerminated;
call_set_released(op);
} else {
/*dialog will terminated shortly, nothing to do*/
}
......@@ -93,18 +113,51 @@ static void call_process_io_error(void *user_ctx, const belle_sip_io_error_event
static void process_dialog_terminated(void *ctx, const belle_sip_dialog_terminated_event_t *event) {
SalOp* op=(SalOp*)ctx;
if (op->dialog) {
if (belle_sip_dialog_get_previous_state(op->dialog) == BELLE_SIP_DIALOG_CONFIRMED) {
/*this is probably a "normal termination from a BYE*/
op->base.root->callbacks.call_terminated(op,op->dir==SalOpDirIncoming?sal_op_get_from(op):sal_op_get_to(op));
op->state=SalOpStateTerminating;
} else {
/*let the process response handle this case*/
}
if (op->dialog && op->dialog==belle_sip_dialog_terminated_get_dialog(event)) {
switch(belle_sip_dialog_get_previous_state(op->dialog)) {
case BELLE_SIP_DIALOG_CONFIRMED:
if (op->state!=SalOpStateTerminated && op->state!=SalOpStateTerminating) {
/*this is probably a "normal termination from a BYE*/
op->base.root->callbacks.call_terminated(op,op->dir==SalOpDirIncoming?sal_op_get_from(op):sal_op_get_to(op));
op->state=SalOpStateTerminating;
}
break;
default: {
belle_sip_transaction_t* trans=belle_sip_dialog_get_last_transaction(op->dialog);
belle_sip_response_t* response=belle_sip_transaction_get_response(trans);
int code = belle_sip_response_get_status_code(response);
if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(trans,belle_sip_client_transaction_t)) {
switch (code) {
case 487:
call_set_released(op);
break;
case 401:
case 407:
if (op->state!=SalOpStateTerminating) {
/*normal termination for chalanged dialog */
break;
}
default:
call_set_error(op,response);
}
} else {
belle_sip_main_loop_do_later(belle_sip_stack_get_main_loop(op->base.root->stack)
,(belle_sip_callback_t) call_set_released
, op);
}
}
}
belle_sip_object_unref(op->dialog);
op->dialog=NULL;
sal_op_unref(op);
} else {
ms_error("dialog unknown for op ");
}
ms_message("Dialog [%p] terminated for op [%p]",belle_sip_dialog_terminated_get_dialog(event)
,op);
}
static void handle_sdp_from_response(SalOp* op,belle_sip_response_t* response) {
belle_sdp_session_description_t* sdp;
......@@ -119,7 +172,7 @@ static void cancelling_invite(SalOp* op ){
ms_message("Cancelling INVITE requets from [%s] to [%s] ",sal_op_get_from(op), sal_op_get_to(op));
cancel = belle_sip_client_transaction_create_cancel(op->pending_inv_client_trans);
sal_op_send_request(op,cancel);
op->state=SalOpStateTerminated;
op->state=SalOpStateTerminating;
}
static void call_response_event(void *op_base, const belle_sip_response_event_t *event){
SalOp* op = (SalOp*)op_base;
......@@ -129,67 +182,45 @@ static void call_response_event(void *op_base, const belle_sip_response_event_t
belle_sip_request_t* req;
belle_sip_response_t* response=belle_sip_response_event_get_response(event);
int code = belle_sip_response_get_status_code(response);
char* reason;
SalError error=SalErrorUnknown;
SalReason sr=SalReasonUnknown;
belle_sip_header_t* reason_header = belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),"Reason");
if (!client_transaction) {
ms_warning("Discarding state less response [%i] on op [%p]",code,op);
return;
}
req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction));
reason=(char*)belle_sip_response_get_reason_phrase(response);
if (reason_header){
reason = ms_strdup_printf("%s %s",reason,belle_sip_header_extension_get_value(BELLE_SIP_HEADER_EXTENSION(reason_header)));
}
/*FIXME should be limited to early/NULL dialog*/
if (code >=400) {
sal_compute_sal_errors_from_code(code,&error,&sr);
op->base.root->callbacks.call_failure(op,error,sr,reason,code);
op->state=SalOpStateTerminated;
if (reason_header != NULL){
ms_free(reason);
}
return;
}
set_or_update_dialog(op,belle_sip_response_event_get_dialog(event));
/*check if op is terminating*/
if (op->state == SalOpStateTerminating) {
if( strcmp("INVITE",belle_sip_request_get_method(req))==0
&& (!op->dialog
|| belle_sip_dialog_get_state(op->dialog)==BELLE_SIP_DIALOG_NULL
|| belle_sip_dialog_get_state(op->dialog)==BELLE_SIP_DIALOG_EARLY)) {
/*FIXME if DIALOG_CONFIRM then ACK+BYE*/
cancelling_invite(op);
return;
} else if (strcmp("BYE",belle_sip_request_get_method(req))==0) {
return; /*probably a 200ok for a bye*/
}
}
if (!op->dialog) {
ms_message("call op [%p] receive out of dialog answer [%i]",op,code);
return;
}
dialog_state=belle_sip_dialog_get_state(op->dialog);
dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL;
switch(dialog_state) {
case BELLE_SIP_DIALOG_NULL: {
ms_error("call op [%p] receive an unexpected answer [%i]",op,code);
break;
}
case BELLE_SIP_DIALOG_NULL:
case BELLE_SIP_DIALOG_EARLY: {
if (code >= 180) {
handle_sdp_from_response(op,response);
op->base.root->callbacks.call_ringing(op);
if (strcmp("INVITE",belle_sip_request_get_method(req))==0 ) {
if ( op->state == SalOpStateTerminating) {
if (code <200) {
cancelling_invite(op);
} else if (code<400) {
sal_op_send_request(op,belle_sip_dialog_create_request(op->dialog,"BYE"));
} else {
/*nop ?*/
}
break;
} else if (code >= 180) {
handle_sdp_from_response(op,response);
op->base.root->callbacks.call_ringing(op);
break;
} else {
/*nop error*/
}
} else if (strcmp("CANCEL",belle_sip_request_get_method(req))==0
|| strcmp("BYE",belle_sip_request_get_method(req))==0) {
break;/*200ok for cancel or BYE*/
} else {
ms_error("call op [%p] receive an unexpected answer [%i]",op,code);
/*nop error*/
}
break;
ms_error("call op [%p] receive an unexpected answer [%i]",op,code);
}
case BELLE_SIP_DIALOG_CONFIRMED: {
switch (op->state) {
......@@ -213,18 +244,28 @@ static void call_response_event(void *op_base, const belle_sip_response_event_t
op->base.root->callbacks.call_accepted(op); /*INVITE*/
op->state=SalOpStateActive;
} else {
} else {
/*nop*/
}
break;
case SalOpStateTerminating:
//FIXME send bye
case SalOpStateTerminated:
default:
ms_error("call op [%p] receive answer [%i] not implemented",op,code);
}
break;
}
case BELLE_SIP_DIALOG_TERMINATED:
case BELLE_SIP_DIALOG_TERMINATED: {
/*if (code>=400 && strcmp("INVITE",belle_sip_request_get_method(req))==0){
call_set_error(op,response);
call_set_released(op);
op->state=SalOpStateTerminated;
break;
}*/
break;
}
/* no break */
default: {
ms_error("call op [%p] receive answer [%i] not implemented",op,code);
}
......@@ -234,15 +275,12 @@ static void call_response_event(void *op_base, const belle_sip_response_event_t
}
static void call_set_released(SalOp* op){
op->base.root->callbacks.call_released(op);
}
static void call_process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) {
SalOp* op=(SalOp*)user_ctx;
if (!op->dialog) {
/*call terminated very early*/
op->base.root->callbacks.call_failure(op,SalErrorNoResponse,SalReasonUnknown,"Request Timeout",408);
call_set_released(op);
op->state=SalOpStateTerminated;
} else {
/*dialog will terminated shortly, nothing to do*/
......@@ -261,9 +299,11 @@ static void call_process_transaction_terminated(void *user_ctx, const belle_sip_
req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(server_transaction));
resp=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(server_transaction));
}
if (strcmp("BYE",belle_sip_request_get_method(req))==0
if (op->state ==SalOpStateTerminating &&
strcmp("BYE",belle_sip_request_get_method(req))==0
&& (!resp || (belle_sip_response_get_status_code(resp) !=401
&& belle_sip_response_get_status_code(resp) !=407))) {
call_set_released(op);
}
......@@ -302,11 +342,17 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
belle_sip_response_t* resp;
belle_sip_header_t* call_info;
if (server_transaction) belle_sip_object_ref(server_transaction); /*ACK does'nt create srv transaction*/
if (server_transaction){
belle_sip_object_ref(server_transaction); /*ACK does'nt create srv transaction*/
belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(server_transaction),op);
sal_op_ref(op);
}
if (strcmp("INVITE",belle_sip_request_get_method(req))==0) {
if (op->pending_server_trans)belle_sip_object_unref(op->pending_server_trans);
/*updating pending invite transaction*/
op->pending_server_trans=server_transaction;
belle_sip_object_ref(op->pending_server_trans);
}
if (!op->dialog) {
......@@ -386,7 +432,9 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
} else if(strcmp("BYE",belle_sip_request_get_method(req))==0) {
resp=belle_sip_response_create_from_request(req,200);
belle_sip_server_transaction_send_response(server_transaction,resp);
/*call end is notified by dialog deletion*/
op->base.root->callbacks.call_terminated(op,op->dir==SalOpDirIncoming?sal_op_get_from(op):sal_op_get_to(op));
op->state=SalOpStateTerminating;
/*call end not notified by dialog deletion because transaction can end before dialog*/
} else if(strcmp("INVITE",belle_sip_request_get_method(req))==0) {
/*re-invite*/
if (op->base.remote_media){
......@@ -429,6 +477,7 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
/* no break */
}
if (server_transaction) belle_sip_object_unref(server_transaction);
}
......@@ -640,7 +689,10 @@ int sal_call_terminate(SalOp *op){
cancelling_invite(op);
break;
} else {
ms_error("Don't know how to termination NUlL dialog for op [%p]",op);
/*just schedule call released*/
belle_sip_main_loop_do_later(belle_sip_stack_get_main_loop(op->base.root->stack)
,(belle_sip_callback_t) call_set_released
, op);
}
break;
}
......
......@@ -268,12 +268,17 @@ bool_t sal_compute_sal_errors(belle_sip_response_t* response,SalError* sal_err,S
}
void set_or_update_dialog(SalOp* op, belle_sip_dialog_t* dialog) {
/*check if dialog has changed*/
if (dialog != op->dialog) {
if (dialog && dialog != op->dialog) {
ms_message("Dialog set from [%p] to [%p] for op [%p]",op->dialog,dialog,op);
/*fixme, shouldn't we cancel previous dialog*/
if (op->dialog)belle_sip_object_unref(op->dialog);
if (op->dialog) {
belle_sip_dialog_set_application_data(op->dialog,NULL);
belle_sip_object_unref(op->dialog);
sal_op_unref(op);
}
op->dialog=dialog;
belle_sip_dialog_set_application_data(op->dialog,op);
sal_op_ref(op);
belle_sip_object_ref(op->dialog);
}
}
......
......@@ -404,8 +404,12 @@ static void add_presence_info(belle_sip_message_t *notify, SalPresenceStatus onl
static void presence_process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){
ms_error("presence_process_io_error not implemented yet");
}
static void presence_process_dialog_terminated(void *op, const belle_sip_dialog_terminated_event_t *event) {
if (((SalOp*)op)->dialog) ((SalOp*)op)->dialog=NULL;
static void presence_process_dialog_terminated(void *ctx, const belle_sip_dialog_terminated_event_t *event) {
SalOp* op= (SalOp*)ctx;
if (op->dialog) {
sal_op_unref(op);
op->dialog=NULL;
}
}
static void presence_response_event(void *op_base, const belle_sip_response_event_t *event){
......
......@@ -159,6 +159,7 @@ int sdp_to_media_description(belle_sdp_session_description_t *session_desc, Sal
const char *mtype,*proto;
SalStreamDescription *stream;
belle_sdp_media_t* media;
belle_sip_list_t* mime_params=NULL;
belle_sip_list_t* mime_param_it=NULL;
belle_sdp_mime_parameter_t* mime_param;
PayloadType *pt;
......@@ -232,7 +233,8 @@ int sdp_to_media_description(belle_sdp_session_description_t *session_desc, Sal
}
/* for each payload type */
for(mime_param_it=belle_sdp_media_description_build_mime_parameters(media_desc)
mime_params=belle_sdp_media_description_build_mime_parameters(media_desc);
for(mime_param_it=mime_params
;mime_param_it!=NULL
;mime_param_it=mime_param_it->next) {
mime_param=BELLE_SDP_MIME_PARAMETER(mime_param_it->data)
......@@ -248,7 +250,7 @@ int sdp_to_media_description(belle_sdp_session_description_t *session_desc, Sal
ms_message("Found payload %s/%i fmtp=%s",pt->mime_type,pt->clock_rate,
pt->send_fmtp ? pt->send_fmtp : "");
}
if (mime_param_it) belle_sip_list_free_with_data(mime_param_it,belle_sip_object_unref);
if (mime_params) belle_sip_list_free_with_data(mime_params,belle_sip_object_unref);
/* read crypto lines if any */
if (stream->proto == SalProtoRtpSavp) {
......
......@@ -187,6 +187,13 @@ static void simple_call(void) {
CU_ASSERT_TRUE(wait_for(lc_pauline,lc_marie,&stat_pauline->number_of_LinphoneCallEnd,1));
CU_ASSERT_TRUE(wait_for(lc_pauline,lc_marie,&stat_marie->number_of_LinphoneCallEnd,1));
}
linphone_core_destroy(marie->lc);
marie->lc=NULL;
CU_ASSERT_EQUAL(stat_marie->number_of_LinphoneCallReleased,1);
linphone_core_destroy(pauline->lc);
pauline->lc=NULL;
CU_ASSERT_EQUAL(stat_pauline->number_of_LinphoneCallReleased,1);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
......@@ -268,6 +275,8 @@ static void cancelled_call(void) {
//CU_ASSERT_EQUAL(linphone_call_get_reason(out_call),LinphoneReasonCanceled);
CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallEnd,1);
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallIncomingReceived,0);
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallReleased,1));
linphone_call_unref(out_call);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
......@@ -285,6 +294,7 @@ static void call_with_dns_time_out(void) {
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallOutgoingInit,1);
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallOutgoingProgress,1);
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallError,1);
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallReleased,1);
linphone_core_manager_destroy(marie);
}
......@@ -297,10 +307,11 @@ static void cancelled_ringing_call(void) {
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallIncomingReceived,1));
linphone_core_terminate_call(pauline->lc,out_call);
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallEnd,1));
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallEnd,1));
//CU_ASSERT_EQUAL(linphone_call_get_reason(in_call),LinphoneReasonDeclined);
//CU_ASSERT_EQUAL(linphone_call_get_reason(out_call),LinphoneReasonDeclined);
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallReleased,1));
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallReleased,1));
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallEnd,1);
CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallEnd,1);
linphone_call_unref(out_call);
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
......@@ -318,8 +329,10 @@ static void early_declined_call(void) {
if (in_call) {
linphone_call_ref(in_call);
linphone_core_terminate_call(marie->lc,in_call);
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallEnd,1));
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallEnd,1));
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallReleased,1));
CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallReleased,1));
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallEnd,1);
CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallEnd,1);
CU_ASSERT_EQUAL(linphone_call_get_reason(in_call),LinphoneReasonDeclined);
CU_ASSERT_EQUAL(linphone_call_get_reason(out_call),LinphoneReasonDeclined);
linphone_call_unref(in_call);
......
......@@ -165,6 +165,7 @@ static void enable_codec(LinphoneCore* lc,const char* type,int rate) {
if((pt = linphone_core_find_payload_type(lc,type,rate,1))) {
linphone_core_enable_payload_type(lc,pt, 1);
}
ms_list_free(codecs);
}
LinphoneCoreManager* linphone_core_manager_new2(const char* path, const char* rc_file, int check_for_proxies) {
......@@ -194,7 +195,7 @@ LinphoneCoreManager* linphone_core_manager_new(const char* path, const char* rc_
}
void linphone_core_manager_destroy(LinphoneCoreManager* mgr) {
linphone_core_destroy(mgr->lc);
if (mgr->lc) linphone_core_destroy(mgr->lc);
if (mgr->identity) linphone_address_destroy(mgr->identity);
free(mgr);
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment