add new API to obtain full details about failures (calls, registration, events).

Fix bug when receiving a 487 after cancelling call, resulting in a call waiting tone to be played.
This commit is contained in:
Simon Morlat 2014-03-21 18:13:54 +01:00
parent 9073389641
commit 269f8d1c4e
21 changed files with 451 additions and 341 deletions

View file

@ -162,8 +162,8 @@ void sal_process_authentication(SalOp *op) {
static void process_dialog_terminated(void *sal, const belle_sip_dialog_terminated_event_t *event){
belle_sip_dialog_t* dialog = belle_sip_dialog_terminated_event_get_dialog(event);
SalOp* op = belle_sip_dialog_get_application_data(dialog);
if (op && op->callbacks.process_dialog_terminated) {
op->callbacks.process_dialog_terminated(op,event);
if (op && op->callbacks && op->callbacks->process_dialog_terminated) {
op->callbacks->process_dialog_terminated(op,event);
} else {
ms_error("sal process_dialog_terminated no op found for this dialog [%p], ignoring",dialog);
}
@ -177,8 +177,8 @@ static void process_io_error(void *user_ctx, const belle_sip_io_error_event_t *e
op = (SalOp*)belle_sip_transaction_get_application_data(BELLE_SIP_TRANSACTION(client_transaction));
/*also reset auth count on IO error*/
op->auth_requests=0;
if (op->callbacks.process_io_error) {
op->callbacks.process_io_error(op,event);
if (op->callbacks && op->callbacks->process_io_error) {
op->callbacks->process_io_error(op,event);
}
} else {
/*ms_error("sal process_io_error not implemented yet for non transaction");*/
@ -290,8 +290,8 @@ static void process_request_event(void *ud, const belle_sip_request_event_t *eve
sal_op_set_privacy_from_message(op,(belle_sip_message_t*)req);
sal_op_assign_recv_headers(op,(belle_sip_message_t*)req);
if (op->callbacks.process_request_event) {
op->callbacks.process_request_event(op,event);
if (op->callbacks && op->callbacks->process_request_event) {
op->callbacks->process_request_event(op,event);
} else {
ms_error("sal process_request_event not implemented yet");
}
@ -329,7 +329,7 @@ static void process_response_event(void *user_ctx, const belle_sip_response_even
sal_op_assign_recv_headers(op,(belle_sip_message_t*)response);
if (op->callbacks.process_response_event) {
if (op->callbacks && op->callbacks->process_response_event) {
/*handle authorization*/
switch (response_code) {
case 200:
@ -365,7 +365,7 @@ static void process_response_event(void *user_ctx, const belle_sip_response_even
/*not an auth request*/
op->auth_requests=0;
}
op->callbacks.process_response_event(op,event);
op->callbacks->process_response_event(op,event);
} else {
ms_error("Unhandled event response [%p]",event);
}
@ -375,8 +375,8 @@ static void process_response_event(void *user_ctx, const belle_sip_response_even
static void process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) {
belle_sip_client_transaction_t* client_transaction = belle_sip_timeout_event_get_client_transaction(event);
SalOp* op = (SalOp*)belle_sip_transaction_get_application_data(BELLE_SIP_TRANSACTION(client_transaction));
if (op && op->callbacks.process_timeout) {
op->callbacks.process_timeout(op,event);
if (op && op->callbacks && op->callbacks->process_timeout) {
op->callbacks->process_timeout(op,event);
} else {
ms_error("Unhandled event timeout [%p]",event);
}
@ -393,8 +393,8 @@ static void process_transaction_terminated(void *user_ctx, const belle_sip_trans
trans=BELLE_SIP_TRANSACTION(server_transaction);
op = (SalOp*)belle_sip_transaction_get_application_data(trans);
if (op && op->callbacks.process_transaction_terminated) {
op->callbacks.process_transaction_terminated(op,event);
if (op && op->callbacks && op->callbacks->process_transaction_terminated) {
op->callbacks->process_transaction_terminated(op,event);
} else {
ms_message("Unhandled transaction terminated [%p]",trans);
}
@ -443,6 +443,7 @@ Sal * sal_init(){
sal->refresher_retry_after=60000; /*default value in ms*/
return sal;
}
void sal_set_user_pointer(Sal *sal, void *user_data){
sal->up=user_data;
}

View file

@ -76,7 +76,8 @@ const char* sal_op_type_to_string(SalOpType type);
struct SalOp{
SalOpBase base;
belle_sip_listener_callbacks_t callbacks;
const belle_sip_listener_callbacks_t *callbacks;
SalErrorInfo error_info;
belle_sip_client_transaction_t *pending_auth_transaction;
belle_sip_server_transaction_t* pending_server_trans;
belle_sip_server_transaction_t* pending_update_server_trans;
@ -132,8 +133,10 @@ bool_t sal_op_is_secure(const SalOp* op);
void sal_process_authentication(SalOp *op);
belle_sip_header_contact_t* sal_op_create_contact(SalOp *op) ;
bool_t sal_compute_sal_errors(belle_sip_response_t* response,SalError* sal_err,SalReason* sal_reason,char* reason, size_t reason_size);
void sal_compute_sal_errors_from_code(int code ,SalError* sal_err,SalReason* sal_reason) ;
bool_t _sal_compute_sal_errors(belle_sip_response_t* response, SalReason* sal_reason, char* reason, size_t reason_size);
SalReason _sal_reason_from_sip_code(int code);
void sal_op_set_error_info_from_response(SalOp *op, belle_sip_response_t *response);
/*presence*/
void sal_op_presence_fill_cbs(SalOp*op);
/*messaging*/

View file

@ -38,19 +38,8 @@ static void call_set_released_and_unref(SalOp* 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_get_unparsed_value(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);
}
sal_op_set_error_info_from_response(op,response);
op->base.root->callbacks.call_failure(op);
}
static void sdp_process(SalOp *h){
@ -127,7 +116,8 @@ 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);
sal_error_info_set(&op->error_info,SalReasonIOError,503,"IO error",NULL);
op->base.root->callbacks.call_failure(op);
call_set_released(op);
} else {
/*dialog will terminated shortly, nothing to do*/
@ -290,7 +280,8 @@ static void call_process_timeout(void *user_ctx, const belle_sip_timeout_event_t
if (!op->dialog) {
/*call terminated very early*/
op->base.root->callbacks.call_failure(op,SalErrorNoResponse,SalReasonUnknown,"Request Timeout",408);
sal_error_info_set(&op->error_info,SalReasonRequestTimeout,408,"Request timeout",NULL);
op->base.root->callbacks.call_failure(op);
call_set_released(op);
} else {
/*dialog will terminated shortly, nothing to do*/
@ -631,13 +622,18 @@ int sal_call(SalOp *op, const char *from, const char *to){
return sal_op_send_request(op,invite);
}
static belle_sip_listener_callbacks_t call_op_callbacks={0};
void sal_op_call_fill_cbs(SalOp*op) {
op->callbacks.process_io_error=call_process_io_error;
op->callbacks.process_response_event=call_process_response;
op->callbacks.process_timeout=call_process_timeout;
op->callbacks.process_transaction_terminated=call_process_transaction_terminated;
op->callbacks.process_request_event=process_request_event;
op->callbacks.process_dialog_terminated=process_dialog_terminated;
if (call_op_callbacks.process_response_event==NULL){
call_op_callbacks.process_io_error=call_process_io_error;
call_op_callbacks.process_response_event=call_process_response;
call_op_callbacks.process_timeout=call_process_timeout;
call_op_callbacks.process_transaction_terminated=call_process_transaction_terminated;
call_op_callbacks.process_request_event=process_request_event;
call_op_callbacks.process_dialog_terminated=process_dialog_terminated;
}
op->callbacks=&call_op_callbacks;
op->type=SalOpCall;
}

View file

@ -37,8 +37,6 @@ static void subscribe_refresher_listener (belle_sip_refresher_t* refresher
,unsigned int status_code
,const char* reason_phrase) {
SalOp* op = (SalOp*)user_pointer;
SalError error=SalErrorUnknown;
SalReason sr=SalReasonUnknown;
belle_sip_transaction_t *tr=BELLE_SIP_TRANSACTION(belle_sip_refresher_get_transaction(refresher));
/*belle_sip_response_t* response=belle_sip_transaction_get_response(tr);*/
SalSubscribeStatus sss=SalSubscribeTerminated;
@ -50,8 +48,8 @@ static void subscribe_refresher_listener (belle_sip_refresher_t* refresher
set_or_update_dialog(op,belle_sip_transaction_get_dialog(tr));
}
if (status_code>=200){
sal_compute_sal_errors_from_code(status_code,&error,&sr);
op->base.root->callbacks.subscribe_response(op,sss,error,sr);
sal_error_info_set(&op->error_info,SalReasonUnknown,status_code,reason_phrase,NULL);
op->base.root->callbacks.subscribe_response(op,sss);
}else if (status_code==0){
op->base.root->callbacks.on_expire(op);
}
@ -172,13 +170,18 @@ static void subscribe_process_request_event(void *op_base, const belle_sip_reque
}
}
static belle_sip_listener_callbacks_t op_subscribe_callbacks={ 0 };
void sal_op_subscribe_fill_cbs(SalOp*op) {
op->callbacks.process_io_error=subscribe_process_io_error;
op->callbacks.process_response_event=subscribe_response_event;
op->callbacks.process_timeout=subscribe_process_timeout;
op->callbacks.process_transaction_terminated=subscribe_process_transaction_terminated;
op->callbacks.process_request_event=subscribe_process_request_event;
op->callbacks.process_dialog_terminated=subscribe_process_dialog_terminated;
if (op_subscribe_callbacks.process_io_error==NULL){
op_subscribe_callbacks.process_io_error=subscribe_process_io_error;
op_subscribe_callbacks.process_response_event=subscribe_response_event;
op_subscribe_callbacks.process_timeout=subscribe_process_timeout;
op_subscribe_callbacks.process_transaction_terminated=subscribe_process_transaction_terminated;
op_subscribe_callbacks.process_request_event=subscribe_process_request_event;
op_subscribe_callbacks.process_dialog_terminated=subscribe_process_dialog_terminated;
}
op->callbacks=&op_subscribe_callbacks;
op->type=SalOpSubscribe;
}

View file

@ -62,6 +62,7 @@ void sal_op_release_impl(SalOp *op){
if (op->pending_server_trans) belle_sip_object_unref(op->pending_server_trans);
if (op->pending_update_server_trans) belle_sip_object_unref(op->pending_update_server_trans);
if (op->event) belle_sip_object_unref(op->event);
sal_error_info_reset(&op->error_info);
__sal_op_free(op);
return ;
}
@ -334,6 +335,12 @@ int sal_op_send_request(SalOp* op, belle_sip_request_t* request) {
SalReason sal_reason_to_sip_code(SalReason r){
int ret=500;
switch(r){
case SalReasonNone:
ret=200;
break;
case SalReasonIOError:
ret=503;
break;
case SalReasonUnknown:
ret=400;
break;
@ -401,110 +408,123 @@ SalReason sal_reason_to_sip_code(SalReason r){
return ret;
}
void sal_compute_sal_errors_from_code(int code ,SalError* sal_err,SalReason* sal_reason) {
*sal_err=SalErrorFailure;
SalReason _sal_reason_from_sip_code(int code) {
if (code>=100 && code<300) return SalReasonNone;
switch(code) {
case 0:
return SalReasonIOError;
case 301:
*sal_reason=SalReasonMovedPermanently;
break;
return SalReasonMovedPermanently;
case 302:
*sal_reason=SalReasonRedirect;
break;
return SalReasonRedirect;
case 401:
case 407:
*sal_reason=SalReasonUnauthorized;
break;
return SalReasonUnauthorized;
case 403:
*sal_reason=SalReasonForbidden;
break;
return SalReasonForbidden;
case 404:
*sal_reason=SalReasonNotFound;
break;
return SalReasonNotFound;
case 408:
*sal_reason=SalReasonRequestTimeout;
break;
return SalReasonRequestTimeout;
case 410:
*sal_reason=SalReasonGone;
break;
return SalReasonGone;
case 415:
*sal_reason=SalReasonUnsupportedContent;
break;
return SalReasonUnsupportedContent;
case 422:
ms_error ("422 not implemented yet");;
break;
case 480:
*sal_reason=SalReasonTemporarilyUnavailable;
break;
return SalReasonTemporarilyUnavailable;
case 481:
*sal_reason=SalReasonNoMatch;
break;
return SalReasonNoMatch;
case 484:
*sal_reason=SalReasonAddressIncomplete;
break;
return SalReasonAddressIncomplete;
case 486:
*sal_reason=SalReasonBusy;
break;
return SalReasonBusy;
case 487:
break;
return SalReasonNone;
case 488:
*sal_reason=SalReasonNotAcceptable;
break;
return SalReasonNotAcceptable;
case 491:
*sal_reason=SalReasonRequestPending;
break;
return SalReasonRequestPending;
case 501:
*sal_reason=SalReasonNotImplemented;
break;
return SalReasonNotImplemented;
case 502:
*sal_reason=SalReasonBadGateway;
break;
return SalReasonBadGateway;
case 504:
*sal_reason=SalReasonServerTimeout;
break;
return SalReasonServerTimeout;
case 600:
*sal_reason=SalReasonDoNotDisturb;
break;
return SalReasonDoNotDisturb;
case 603:
*sal_reason=SalReasonDeclined;
break;
return SalReasonDeclined;
case 503:
*sal_reason=SalReasonServiceUnavailable;
break;
return SalReasonServiceUnavailable;
default:
if (code>=300){
*sal_err=SalErrorFailure;
*sal_reason=SalReasonUnknown;
}else if (code>=100){
*sal_err=SalErrorNone;
*sal_reason=SalReasonUnknown;
}else if (code==0){
*sal_err=SalErrorNoResponse;
}
/* no break */
return SalReasonUnknown;
}
return SalReasonUnknown;
}
const SalErrorInfo *sal_error_info_none(void){
static SalErrorInfo none={
SalReasonNone,
"Ok",
200,
NULL,
NULL
};
return &none;
}
void sal_error_info_reset(SalErrorInfo *ei){
if (ei->status_string){
ms_free(ei->status_string);
ei->status_string=NULL;
}
if (ei->warnings){
ms_free(ei->warnings);
ei->warnings=NULL;
}
if (ei->full_string){
ms_free(ei->full_string);
ei->full_string=NULL;
}
ei->protocol_code=0;
ei->reason=SalReasonNone;
}
void sal_error_info_set(SalErrorInfo *ei, SalReason reason, int code, const char *status_string, const char *warning){
sal_error_info_reset(ei);
if (reason==SalReasonUnknown) ei->reason=_sal_reason_from_sip_code(code);
else ei->reason=reason;
ei->protocol_code=code;
ei->status_string=status_string ? ms_strdup(status_string) : NULL;
ei->warnings=warning ? ms_strdup(warning) : NULL;
if (ei->status_string){
if (ei->warnings)
ei->full_string=ms_strdup_printf("%s %s",ei->status_string,ei->warnings);
else ei->full_string=ms_strdup(ei->status_string);
}
}
/*return TRUE if error code*/
bool_t sal_compute_sal_errors(belle_sip_response_t* response,SalError* sal_err,SalReason* sal_reason,char* reason, size_t reason_size) {
int code = belle_sip_response_get_status_code(response);
belle_sip_header_t* reason_header = belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),"Reason");
*sal_err=SalErrorUnknown;
*sal_reason = SalReasonUnknown;
if (reason_header){
snprintf(reason
,reason_size
,"%s %s"
,belle_sip_response_get_reason_phrase(response)
,belle_sip_header_get_unparsed_value(reason_header));
} else {
strncpy(reason,belle_sip_response_get_reason_phrase(response),reason_size);
}
if (code>=400) {
sal_compute_sal_errors_from_code(code,sal_err,sal_reason);
return TRUE;
} else {
return FALSE;
}
void sal_op_set_error_info_from_response(SalOp *op, belle_sip_response_t *response){
int code = belle_sip_response_get_status_code(response);
const char *reason_phrase=belle_sip_response_get_reason_phrase(response);
/*Remark: the reason header is to be used mainly in SIP requests, thus the use and prototype of this function should be changed.*/
belle_sip_header_t* reason_header = belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),"Reason");
belle_sip_header_t *warning=belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),"Warning");
SalErrorInfo *ei=&op->error_info;
const char *warnings;
warnings=warning ? belle_sip_header_get_unparsed_value(warning) : NULL;
if (warnings==NULL) warnings=reason_header ? belle_sip_header_get_unparsed_value(reason_header) : NULL;
sal_error_info_set(ei,SalReasonUnknown,code,reason_phrase,warnings);
}
const SalErrorInfo *sal_op_get_error_info(const SalOp *op){
return &op->error_info;
}
void set_or_update_dialog(SalOp* op, belle_sip_dialog_t* dialog) {

View file

@ -20,7 +20,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
static void process_error( SalOp* op) {
if (op->dir == SalOpDirOutgoing) {
op->base.root->callbacks.text_delivery_update(op, SalTextDeliveryFailed, SalReasonUnknown);
op->base.root->callbacks.text_delivery_update(op, SalTextDeliveryFailed);
} else {
ms_warning("unexpected io error for incoming message on op [%p]",op);
}
@ -30,33 +30,20 @@ static void process_error( SalOp* op) {
static void process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){
SalOp* op = (SalOp*)user_ctx;
// belle_sip_object_t* source = belle_sip_io_error_event_get_source(event);
// if (BELLE_SIP_IS_INSTANCE_OF(source,belle_sip_transaction_t)) {
// /*reset op to make sure transaction terminated does not need op*/
// belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(source),NULL);
// }
sal_error_info_set(&op->error_info,SalReasonIOError,503,"IO Error",NULL);
process_error(op);
}
static void process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) {
SalOp* op=(SalOp*)user_ctx;
// belle_sip_client_transaction_t *client_transaction=belle_sip_timeout_event_get_client_transaction(event);
// belle_sip_server_transaction_t *server_transaction=belle_sip_timeout_event_get_server_transaction(event);
// /*reset op to make sure transaction terminated does not need op*/
// if (client_transaction) {
// belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(client_transaction),NULL);
// } else {
// belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(server_transaction),NULL);
// }
sal_error_info_set(&op->error_info,SalReasonRequestTimeout,408,"Request timeout",NULL);
process_error(op);
}
static void process_response_event(void *op_base, const belle_sip_response_event_t *event){
SalOp* op = (SalOp*)op_base;
/*belle_sip_client_transaction_t *client_transaction=belle_sip_response_event_get_client_transaction(event);*/
int code = belle_sip_response_get_status_code(belle_sip_response_event_get_response(event));
SalTextDeliveryStatus status;
SalReason reason=SalReasonUnknown;
SalError err=SalErrorNone;
sal_op_set_error_info_from_response(op,belle_sip_response_event_get_response(event));
if (code>=100 && code <200)
status=SalTextDeliveryInProgress;
@ -64,13 +51,10 @@ static void process_response_event(void *op_base, const belle_sip_response_event
status=SalTextDeliveryDone;
else
status=SalTextDeliveryFailed;
if (status != SalTextDeliveryInProgress) {
/*reset op to make sure transaction terminated does not need op
belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(client_transaction),NULL);*/
}
sal_compute_sal_errors_from_code(code,&err,&reason);
op->base.root->callbacks.text_delivery_update(op,status, reason);
op->base.root->callbacks.text_delivery_update(op,status);
}
static bool_t is_plain_text(belle_sip_header_content_type_t* content_type) {
return strcmp("text",belle_sip_header_content_type_get_type(content_type))==0
&& strcmp("plain",belle_sip_header_content_type_get_subtype(content_type))==0;
@ -191,10 +175,15 @@ int sal_text_send(SalOp *op, const char *from, const char *to, const char *msg)
return sal_message_send(op,from,to,"text/plain",msg);
}
static belle_sip_listener_callbacks_t op_message_callbacks={0};
void sal_op_message_fill_cbs(SalOp*op) {
op->callbacks.process_io_error=process_io_error;
op->callbacks.process_response_event=process_response_event;
op->callbacks.process_timeout=process_timeout;
op->callbacks.process_request_event=process_request_event;
if (op_message_callbacks.process_io_error==NULL){
op_message_callbacks.process_io_error=process_io_error;
op_message_callbacks.process_response_event=process_response_event;
op_message_callbacks.process_timeout=process_timeout;
op_message_callbacks.process_request_event=process_request_event;
}
op->callbacks=&op_message_callbacks;
op->type=SalOpMessage;
}

View file

@ -94,13 +94,12 @@ static void presence_response_event(void *op_base, const belle_sip_response_even
belle_sip_response_t* response=belle_sip_response_event_get_response(event);
belle_sip_request_t* request=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction));
int code = belle_sip_response_get_status_code(response);
char reason[256]={0};
SalError error=SalErrorUnknown;
SalReason sr=SalReasonUnknown;
belle_sip_header_expires_t* expires;
sal_op_set_error_info_from_response(op,response);
if (sal_compute_sal_errors(response,&error,&sr,reason, sizeof(reason))) {
ms_error("subscription to [%s] rejected reason [%s]",sal_op_get_to(op),reason[0]!=0?reason:sal_reason_to_string(sr));
if (code>=300) {
ms_message("subscription to [%s] rejected",sal_op_get_to(op));
op->base.root->callbacks.notify_presence(op,SalSubscribeTerminated, NULL,NULL); /*NULL = offline*/
return;
}
@ -267,13 +266,18 @@ static void presence_process_request_event(void *op_base, const belle_sip_reques
}
}
static belle_sip_listener_callbacks_t op_presence_callbacks={0};
void sal_op_presence_fill_cbs(SalOp*op) {
op->callbacks.process_io_error=presence_process_io_error;
op->callbacks.process_response_event=presence_response_event;
op->callbacks.process_timeout=presence_process_timeout;
op->callbacks.process_transaction_terminated=presence_process_transaction_terminated;
op->callbacks.process_request_event=presence_process_request_event;
op->callbacks.process_dialog_terminated=presence_process_dialog_terminated;
if (op_presence_callbacks.process_request_event==NULL){
op_presence_callbacks.process_io_error=presence_process_io_error;
op_presence_callbacks.process_response_event=presence_response_event;
op_presence_callbacks.process_timeout=presence_process_timeout;
op_presence_callbacks.process_transaction_terminated=presence_process_transaction_terminated;
op_presence_callbacks.process_request_event=presence_process_request_event;
op_presence_callbacks.process_dialog_terminated=presence_process_dialog_terminated;
}
op->callbacks=&op_presence_callbacks;
op->type=SalOpPresence;
}

View file

@ -35,24 +35,26 @@ static void publish_refresher_listener (belle_sip_refresher_t* refresher
}else if (status_code==0){
op->base.root->callbacks.on_expire(op);
}else if (status_code>=200){
SalError err;
SalReason reason;
sal_compute_sal_errors_from_code(status_code,&err,&reason);
op->base.root->callbacks.on_publish_response(op,err,reason);
sal_error_info_set(&op->error_info,SalReasonUnknown,status_code,reason_phrase,NULL);
op->base.root->callbacks.on_publish_response(op);
}
}
static void publish_response_event(void *userctx, const belle_sip_response_event_t *event){
SalOp *op=(SalOp*)userctx;
int code=belle_sip_response_get_status_code(belle_sip_response_event_get_response(event));
SalError err;
SalReason reason;
sal_compute_sal_errors_from_code(code,&err,&reason);
op->base.root->callbacks.on_publish_response(op,err,reason);
sal_op_set_error_info_from_response(op,belle_sip_response_event_get_response(event));
if (op->error_info.protocol_code>=200){
op->base.root->callbacks.on_publish_response(op);
}
}
void sal_op_publish_fill_cbs(SalOp*op) {
op->callbacks.process_response_event=publish_response_event;
static belle_sip_listener_callbacks_t op_publish_callbacks={0};
void sal_op_publish_fill_cbs(SalOp *op) {
if (op_publish_callbacks.process_response_event==NULL){
op_publish_callbacks.process_response_event=publish_response_event;
}
op->callbacks=&op_publish_callbacks;
op->type=SalOpPublish;
}

View file

@ -24,8 +24,6 @@ static void register_refresher_listener (belle_sip_refresher_t* refresher
,unsigned int status_code
,const char* reason_phrase) {
SalOp* op = (SalOp*)user_pointer;
SalError sal_err;
SalReason sal_reason;
belle_sip_response_t* response=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(belle_sip_refresher_get_transaction(refresher)));
ms_message("Register refresher [%i] reason [%s] for proxy [%s]",status_code,reason_phrase,sal_op_get_proxy(op));
@ -57,9 +55,8 @@ static void register_refresher_listener (belle_sip_refresher_t* refresher
chooses not to re-register, the UA SHOULD discard any stored service
route for that address-of-record. */
sal_op_set_service_route(op,NULL);
sal_compute_sal_errors_from_code(status_code,&sal_err,&sal_reason);
op->base.root->callbacks.register_failure(op,sal_err,sal_reason,reason_phrase);
sal_error_info_set(&op->error_info,SalReasonUnknown,status_code,reason_phrase,NULL);
op->base.root->callbacks.register_failure(op);
if (op->auth_info) {
/*add pending auth*/
sal_add_pending_auth(op->base.root,op);

View file

@ -32,7 +32,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include <unistd.h>
#endif
static void register_failure(SalOp *op, SalError error, SalReason reason, const char *details);
static void register_failure(SalOp *op);
static int media_parameters_changed(LinphoneCall *call, SalMediaDescription *oldmd, SalMediaDescription *newmd) {
if (call->params.in_conference != call->current_params.in_conference) return SAL_MEDIA_DESCRIPTION_CHANGED;
@ -550,7 +550,7 @@ static void call_terminated(SalOp *op, const char *from){
break;
case LinphoneCallIncomingReceived:
case LinphoneCallIncomingEarlyMedia:
call->reason=LinphoneReasonNotAnswered;
sal_error_info_set(&call->non_op_error,SalReasonRequestTimeout,0,"Incoming call cancelled",NULL);
break;
default:
break;
@ -593,14 +593,15 @@ static int resume_call_after_failed_transfer(LinphoneCall *call){
return BELLE_SIP_STOP;
}
static void call_failure(SalOp *op, SalError error, SalReason sr, const char *details, int code){
static void call_failure(SalOp *op){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
const SalErrorInfo *ei=sal_op_get_error_info(op);
char *msg486=_("User is busy.");
char *msg480=_("User is temporarily unavailable.");
/*char *retrymsg=_("%s. Retry after %i minute(s).");*/
char *msg600=_("User does not want to be disturbed.");
char *msg603=_("Call declined.");
const char *msg=details;
const char *msg=ei->full_string;
LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
LinphoneCall *referer=call->referer;
@ -610,102 +611,95 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de
}
if (lc->vtable.show) lc->vtable.show(lc);
if (error==SalErrorNoResponse){
msg=_("No response.");
if (lc->vtable.display_status)
lc->vtable.display_status(lc,msg);
}else if (error==SalErrorProtocol){
msg=details ? details : _("Protocol error.");
if (lc->vtable.display_status)
lc->vtable.display_status(lc, msg);
}else if (error==SalErrorFailure){
switch(sr){
case SalReasonDeclined:
msg=msg603;
if (lc->vtable.display_status)
lc->vtable.display_status(lc,msg603);
break;
case SalReasonBusy:
msg=msg486;
if (lc->vtable.display_status)
lc->vtable.display_status(lc,msg486);
break;
case SalReasonRedirect:
{
ms_error("case SalReasonRedirect");
switch(ei->reason){
case SalReasonNone:
break;
case SalReasonRequestTimeout:
msg=_("Request timeout.");
if (lc->vtable.display_status)
lc->vtable.display_status(lc,msg);
break;
case SalReasonDeclined:
msg=msg603;
if (lc->vtable.display_status)
lc->vtable.display_status(lc,msg603);
break;
case SalReasonBusy:
msg=msg486;
if (lc->vtable.display_status)
lc->vtable.display_status(lc,msg486);
break;
case SalReasonRedirect:
{
linphone_call_stop_media_streams(call);
if ( call->state==LinphoneCallOutgoingInit
|| call->state==LinphoneCallOutgoingProgress
|| call->state==LinphoneCallOutgoingRinging /*push case*/
|| call->state==LinphoneCallOutgoingEarlyMedia){
LinphoneAddress* redirection_to = (LinphoneAddress*)sal_op_get_remote_contact_address(call->op);
if( redirection_to ){
char* url = linphone_address_as_string(redirection_to);
ms_warning("Redirecting call [%p] to %s",call, url);
ms_free(url);
linphone_call_create_op(call);
linphone_core_start_invite(lc, call, redirection_to);
return;
}
}
msg=_("Redirected");
if (lc->vtable.display_status)
lc->vtable.display_status(lc,msg);
}
break;
case SalReasonTemporarilyUnavailable:
msg=msg480;
if (lc->vtable.display_status)
lc->vtable.display_status(lc,msg480);
break;
case SalReasonNotFound:
if (lc->vtable.display_status)
lc->vtable.display_status(lc,msg);
break;
case SalReasonDoNotDisturb:
msg=msg600;
if (lc->vtable.display_status)
lc->vtable.display_status(lc,msg600);
break;
case SalReasonUnsupportedContent: /*<this is for compatibility: linphone sent 415 because of SDP offer answer failure*/
case SalReasonNotAcceptable:
//media_encryption_mandatory
if (call->params.media_encryption == LinphoneMediaEncryptionSRTP &&
!linphone_core_is_media_encryption_mandatory(lc)) {
int i;
ms_message("Outgoing call [%p] failed with SRTP (SAVP) enabled",call);
linphone_call_stop_media_streams(call);
if ( call->state==LinphoneCallOutgoingInit
if (call->state==LinphoneCallOutgoingInit
|| call->state==LinphoneCallOutgoingProgress
|| call->state==LinphoneCallOutgoingRinging /*push case*/
|| call->state==LinphoneCallOutgoingEarlyMedia){
LinphoneAddress* redirection_to = (LinphoneAddress*)sal_op_get_remote_contact_address(call->op);
if( redirection_to ){
char* url = linphone_address_as_string(redirection_to);
ms_error("Redirecting call [%p] to %s",call, url);
ms_free(url);
linphone_call_create_op(call);
linphone_core_start_invite(lc, call, redirection_to);
return;
ms_message("Retrying call [%p] with AVP",call);
/* clear SRTP local params */
call->params.media_encryption = LinphoneMediaEncryptionNone;
for(i=0; i<call->localdesc->n_active_streams; i++) {
call->localdesc->streams[i].proto = SalProtoRtpAvp;
memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto));
}
linphone_core_restart_invite(lc, call);
return;
}
msg=_("Redirected");
if (lc->vtable.display_status)
lc->vtable.display_status(lc,msg);
}
break;
case SalReasonTemporarilyUnavailable:
msg=msg480;
if (lc->vtable.display_status)
lc->vtable.display_status(lc,msg480);
break;
case SalReasonNotFound:
if (lc->vtable.display_status)
lc->vtable.display_status(lc,msg);
break;
case SalReasonDoNotDisturb:
msg=msg600;
if (lc->vtable.display_status)
lc->vtable.display_status(lc,msg600);
break;
case SalReasonUnsupportedContent: /*<this is for compatibility: linphone sent 415 because of SDP offer answer failure*/
case SalReasonNotAcceptable:
//media_encryption_mandatory
if (call->params.media_encryption == LinphoneMediaEncryptionSRTP &&
!linphone_core_is_media_encryption_mandatory(lc)) {
int i;
ms_message("Outgoing call [%p] failed with SRTP (SAVP) enabled",call);
linphone_call_stop_media_streams(call);
if ( call->state==LinphoneCallOutgoingInit
|| call->state==LinphoneCallOutgoingProgress
|| call->state==LinphoneCallOutgoingRinging /*push case*/
|| call->state==LinphoneCallOutgoingEarlyMedia){
ms_message("Retrying call [%p] with AVP",call);
/* clear SRTP local params */
call->params.media_encryption = LinphoneMediaEncryptionNone;
for(i=0; i<call->localdesc->n_active_streams; i++) {
call->localdesc->streams[i].proto = SalProtoRtpAvp;
memset(call->localdesc->streams[i].crypto, 0, sizeof(call->localdesc->streams[i].crypto));
}
linphone_core_restart_invite(lc, call);
return;
}
}
msg=_("Incompatible media parameters.");
if (lc->vtable.display_status)
lc->vtable.display_status(lc,msg);
break;
case SalReasonRequestPending:
/*restore previous state, the application will decide to resubmit the action if relevant*/
call->reason=linphone_reason_from_sal(sr);
linphone_call_set_state(call,call->prevstate,msg);
return;
break;
default:
if (lc->vtable.display_status)
lc->vtable.display_status(lc,_("Call failed."));
}
msg=_("Incompatible media parameters.");
if (lc->vtable.display_status)
lc->vtable.display_status(lc,msg);
break;
case SalReasonRequestPending:
/*restore previous state, the application will decide to resubmit the action if relevant*/
linphone_call_set_state(call,call->prevstate,msg);
return;
break;
default:
if (lc->vtable.display_status)
lc->vtable.display_status(lc,_("Call failed."));
}
/*some call error are not fatal*/
@ -714,8 +708,7 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de
case LinphoneCallPausing:
case LinphoneCallResuming:
ms_message("Call error on state [%s], restoring previous state",linphone_call_state_to_string(call->prevstate));
call->reason=linphone_reason_from_sal(sr);
linphone_call_set_state(call, call->prevstate,details);
linphone_call_set_state(call, call->prevstate,ei->full_string);
return;
default:
break; /*nothing to do*/
@ -728,13 +721,14 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de
linphone_call_delete_upnp_session(call);
#endif //BUILD_UPNP
call->reason=linphone_reason_from_sal(sr);
if (sr==SalReasonDeclined){
linphone_call_set_state(call,LinphoneCallEnd,"Call declined.");
}else{
linphone_call_set_state(call,LinphoneCallError,details);
if (call->state!=LinphoneCallEnd && call->state!=LinphoneCallError){
if (ei->reason==SalReasonDeclined){
linphone_call_set_state(call,LinphoneCallEnd,"Call declined.");
}else{
linphone_call_set_state(call,LinphoneCallError,ei->full_string);
}
if (ei->reason!=SalReasonNone) linphone_core_play_call_error_tone(lc,linphone_reason_from_sal(ei->reason));
}
linphone_core_play_call_error_tone(lc,call->reason);
if (referer){
/*notify referer of the failure*/
@ -773,7 +767,6 @@ static void register_success(SalOp *op, bool_t registered){
ms_message("Registration success for removed proxy config, ignored");
return;
}
linphone_proxy_config_set_error(cfg,LinphoneReasonNone);
linphone_proxy_config_set_state(cfg, registered ? LinphoneRegistrationOk : LinphoneRegistrationCleared ,
registered ? "Registration successful" : "Unregistration done");
if (lc->vtable.display_status){
@ -785,9 +778,11 @@ static void register_success(SalOp *op, bool_t registered){
}
static void register_failure(SalOp *op, SalError error, SalReason reason, const char *details){
static void register_failure(SalOp *op){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)sal_op_get_user_pointer(op);
const SalErrorInfo *ei=sal_op_get_error_info(op);
const char *details=ei->full_string;
if (cfg==NULL){
ms_warning("Registration failed for unknown proxy config.");
@ -801,15 +796,12 @@ static void register_failure(SalOp *op, SalError error, SalReason reason, const
details=_("no response timeout");
if (lc->vtable.display_status) {
char *msg=ortp_strdup_printf(_("Registration on %s failed: %s"),sal_op_get_proxy(op),details );
char *msg=ortp_strdup_printf(_("Registration on %s failed: %s"),sal_op_get_proxy(op), details);
lc->vtable.display_status(lc,msg);
ms_free(msg);
}
linphone_proxy_config_set_error(cfg,linphone_reason_from_sal(reason));
if (error== SalErrorFailure
&& reason == SalReasonServiceUnavailable
if ((ei->reason == SalReasonServiceUnavailable || ei->reason == SalReasonIOError)
&& linphone_proxy_config_get_state(cfg) == LinphoneRegistrationOk) {
linphone_proxy_config_set_state(cfg,LinphoneRegistrationProgress,_("Service unavailable, retrying"));
} else {
@ -1031,22 +1023,15 @@ static LinphoneChatMessageState chatStatusSal2Linphone(SalTextDeliveryStatus sta
return LinphoneChatMessageStateIdle;
}
static int op_equals(LinphoneCall *a, SalOp *b) {
return a->op !=b; /*return 0 if equals*/
}
static void text_delivery_update(SalOp *op, SalTextDeliveryStatus status, SalReason reason){
static void text_delivery_update(SalOp *op, SalTextDeliveryStatus status){
LinphoneChatMessage *chat_msg=(LinphoneChatMessage* )sal_op_get_user_pointer(op);
const MSList* calls;
if (chat_msg == NULL) {
// Do not handle delivery status for isComposing messages.
return;
}
calls = linphone_core_get_calls(chat_msg->chat_room->lc);
chat_msg->state=chatStatusSal2Linphone(status);
chat_msg->reason=linphone_reason_from_sal(reason);
linphone_chat_message_store_state(chat_msg);
if (chat_msg && chat_msg->cb) {
ms_message("Notifying text delivery with status %i",chat_msg->state);
@ -1056,11 +1041,6 @@ static void text_delivery_update(SalOp *op, SalTextDeliveryStatus status, SalRea
}
if (status != SalTextDeliveryInProgress) { /*don't release op if progress*/
linphone_chat_message_destroy(chat_msg);
if (!ms_list_find_custom((MSList*)calls, (MSCompareFunc) op_equals, op)) {
/*op was only create for messaging purpose, destroying*/
sal_op_release(op);
}
}
}
@ -1069,7 +1049,7 @@ static void info_received(SalOp *op, const SalBody *body){
linphone_core_notify_info_message(lc,op,body);
}
static void subscribe_response(SalOp *op, SalSubscribeStatus status, SalError error, SalReason reason){
static void subscribe_response(SalOp *op, SalSubscribeStatus status){
LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op);
if (lev==NULL) return;
@ -1079,7 +1059,6 @@ static void subscribe_response(SalOp *op, SalSubscribeStatus status, SalError er
}else if (status==SalSubscribePending){
linphone_event_set_state(lev,LinphoneSubscriptionPending);
}else{
linphone_event_set_reason(lev, linphone_reason_from_sal(reason));
linphone_event_set_state(lev,LinphoneSubscriptionError);
}
}
@ -1121,18 +1100,18 @@ static void subscribe_closed(SalOp *op){
linphone_event_set_state(lev,LinphoneSubscriptionTerminated);
}
static void on_publish_response(SalOp* op, SalError err, SalReason reason){
static void on_publish_response(SalOp* op){
LinphoneEvent *lev=(LinphoneEvent*)sal_op_get_user_pointer(op);
const SalErrorInfo *ei=sal_op_get_error_info(op);
if (lev==NULL) return;
if (err==SalErrorNone){
if (ei->reason==SalReasonNone){
if (!lev->terminating)
linphone_event_set_publish_state(lev,LinphonePublishOk);
else
linphone_event_set_publish_state(lev,LinphonePublishCleared);
}else{
linphone_event_set_reason(lev,linphone_reason_from_sal(reason));
linphone_event_set_publish_state(lev,LinphonePublishError);
}
}

View file

@ -160,7 +160,7 @@ static void _linphone_chat_room_send_message(LinphoneChatRoom *cr, LinphoneChatM
identity=linphone_proxy_config_get_identity(proxy);
}else identity=linphone_core_get_primary_contact(cr->lc);
/*sending out of calls*/
op = sal_op_new(cr->lc->sal);
msg->op = op = sal_op_new(cr->lc->sal);
linphone_configure_op(cr->lc,op,cr->peer_url,msg->custom_headers,lp_config_get_int(cr->lc->config,"sip","chat_msg_with_contact",0));
sal_op_set_user_pointer(op, msg); /*if out of call, directly store msg*/
}
@ -779,6 +779,7 @@ LinphoneChatMessage* linphone_chat_message_clone(const LinphoneChatMessage* msg)
* Destroys a LinphoneChatMessage.
**/
void linphone_chat_message_destroy(LinphoneChatMessage* msg) {
if (msg->op) sal_op_release(msg->op);
if (msg->message) ms_free(msg->message);
if (msg->external_body_url) ms_free(msg->external_body_url);
if (msg->from) linphone_address_destroy(msg->from);
@ -787,9 +788,17 @@ void linphone_chat_message_destroy(LinphoneChatMessage* msg) {
ms_free(msg);
}
/**
* Get full details about delivery error of a chat message.
* @param msg a LinphoneChatMessage
* @return a LinphoneErrorInfo describing the details.
**/
const LinphoneErrorInfo *linphone_chat_message_get_error_info(const LinphoneChatMessage *msg){
return linphone_error_info_from_sal_op(msg->op);
}
LinphoneReason linphone_chat_message_get_reason(LinphoneChatMessage* msg) {
return msg->reason;
return linphone_error_info_get_reason(linphone_chat_message_get_error_info(msg));
}
/**

View file

@ -114,12 +114,12 @@ LinphonePublishState linphone_event_get_publish_state(const LinphoneEvent *lev){
return lev->publish_state;
}
void linphone_event_set_reason(LinphoneEvent *lev, LinphoneReason reason){
lev->reason=reason;
const LinphoneErrorInfo *linphone_event_get_error_info(const LinphoneEvent *lev){
return linphone_error_info_from_sal_op(lev->op);
}
LinphoneReason linphone_event_get_reason(const LinphoneEvent *lev){
return lev->reason;
return linphone_error_info_get_reason(linphone_event_get_error_info(lev));
}
LinphoneEvent *linphone_core_create_subscribe(LinphoneCore *lc, const LinphoneAddress *resource, const char *event, int expires){

View file

@ -207,6 +207,11 @@ LINPHONE_PUBLIC int linphone_event_update_publish(LinphoneEvent *lev, const Linp
**/
LINPHONE_PUBLIC LinphoneReason linphone_event_get_reason(const LinphoneEvent *lev);
/**
* Get full details about an error occured.
**/
const LinphoneErrorInfo *linphone_event_get_error_info(const LinphoneEvent *lev);
/**
* Get subscription state. If the event object was not created by a subscription mechanism, #LinphoneSubscriptionNone is returned.
**/

View file

@ -793,11 +793,11 @@ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const
call->state=cstate;
}
if (cstate==LinphoneCallEnd || cstate==LinphoneCallError){
switch(call->reason){
case LinphoneReasonDeclined:
switch(call->non_op_error.reason){
case SalReasonDeclined:
call->log->status=LinphoneCallDeclined;
break;
case LinphoneReasonNotAnswered:
case SalReasonRequestTimeout:
call->log->status=LinphoneCallMissed;
break;
default:
@ -814,6 +814,11 @@ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const
lc->vtable.call_state_changed(lc,call,cstate,message);
if (cstate==LinphoneCallReleased){
if (call->op!=NULL) {
/*transfer the last error so that it can be obtained even in Released state*/
if (call->non_op_error.reason==SalReasonNone){
const SalErrorInfo *ei=sal_op_get_error_info(call->op);
sal_error_info_set(&call->non_op_error,ei->reason,ei->protocol_code,ei->status_string,ei->warnings);
}
/* so that we cannot have anymore upcalls for SAL
concerning this call*/
sal_op_release(call->op);
@ -877,6 +882,7 @@ static void linphone_call_destroy(LinphoneCall *obj)
}
linphone_call_params_uninit(&obj->params);
linphone_call_params_uninit(&obj->current_params);
sal_error_info_reset(&obj->non_op_error);
ms_free(obj);
}
@ -998,7 +1004,16 @@ LinphoneCallState linphone_call_get_state(const LinphoneCall *call){
* Returns the reason for a call termination (either error or normal termination)
**/
LinphoneReason linphone_call_get_reason(const LinphoneCall *call){
return call->reason;
return linphone_error_info_get_reason(linphone_call_get_error_info(call));
}
/**
* Returns full details about call errors or termination reasons.
**/
const LinphoneErrorInfo *linphone_call_get_error_info(const LinphoneCall *call){
if (call->non_op_error.reason!=SalReasonNone){
return (const LinphoneErrorInfo*)&call->non_op_error;
}else return linphone_error_info_from_sal_op(call->op);
}
/**

View file

@ -2281,7 +2281,7 @@ void linphone_core_iterate(LinphoneCore *lc){
ms_message("incoming call timeout (%i)",lc->sip_conf.inc_timeout);
decline_reason=lc->current_call ? LinphoneReasonBusy : LinphoneReasonDeclined;
call->log->status=LinphoneCallMissed;
call->reason=LinphoneReasonNotAnswered;
sal_error_info_set(&call->non_op_error,SalReasonRequestTimeout,408,"Not answered",NULL);
linphone_core_decline_call(lc,call,decline_reason);
}
}
@ -3470,8 +3470,8 @@ int linphone_core_abort_call(LinphoneCore *lc, LinphoneCall *call, const char *e
static void terminate_call(LinphoneCore *lc, LinphoneCall *call){
if (call->state==LinphoneCallIncomingReceived){
if (call->reason!=LinphoneReasonNotAnswered)
call->reason=LinphoneReasonDeclined;
if (call->non_op_error.reason!=SalReasonRequestTimeout)
call->non_op_error.reason=SalReasonDeclined;
}
/*stop ringing*/
linphone_core_stop_ringing(lc);
@ -3490,7 +3490,7 @@ static void terminate_call(LinphoneCore *lc, LinphoneCall *call){
int linphone_core_redirect_call(LinphoneCore *lc, LinphoneCall *call, const char *redirect_uri){
if (call->state==LinphoneCallIncomingReceived){
sal_call_decline(call->op,SalReasonRedirect,redirect_uri);
call->reason=LinphoneReasonDeclined;
sal_error_info_set(&call->non_op_error,SalReasonRedirect,603,"Call redirected",NULL);
terminate_call(lc,call);
}else{
ms_error("Bad state for call redirection.");
@ -6152,6 +6152,8 @@ const char *linphone_reason_to_string(LinphoneReason err){
return "Bad gateway";
case LinphoneReasonServerTimeout:
return "Server timeout";
case LinphoneReasonUnknown:
return "Unknown error";
}
return "unknown error";
}

View file

@ -159,6 +159,7 @@ typedef struct _LinphoneCall LinphoneCall;
* Enum describing various failure reasons or contextual information for some events.
* @see linphone_call_get_reason()
* @see linphone_proxy_config_get_error()
* @see linphone_error_info_get_reason()
* @ingroup misc
**/
enum _LinphoneReason{
@ -181,7 +182,8 @@ enum _LinphoneReason{
LinphoneReasonAddressIncomplete, /**<Address incomplete*/
LinphoneReasonNotImplemented, /**<Not implemented*/
LinphoneReasonBadGateway, /**<Bad gateway*/
LinphoneReasonServerTimeout /**<Server timeout*/
LinphoneReasonServerTimeout, /**<Server timeout*/
LinphoneReasonUnknown /**Unknown reason*/
};
#define LinphoneReasonBadCredentials LinphoneReasonForbidden
@ -201,6 +203,18 @@ typedef enum _LinphoneReason LinphoneReason;
**/
const char *linphone_reason_to_string(LinphoneReason err);
/**
* Object representing full details about a signaling error or status.
* All LinphoneErrorInfo object returned by the liblinphone API are readonly and transcients. For safety they must be used immediately
* after obtaining them. Any other function call to the liblinphone may change their content or invalidate the pointer.
* @ingroup misc
**/
typedef struct _LinphoneErrorInfo LinphoneErrorInfo;
LINPHONE_PUBLIC LinphoneReason linphone_error_info_get_reason(const LinphoneErrorInfo *ei);
LINPHONE_PUBLIC const char *linphone_error_info_get_phrase(const LinphoneErrorInfo *ei);
LINPHONE_PUBLIC const char *linphone_error_info_get_details(const LinphoneErrorInfo *ei);
LINPHONE_PUBLIC int linphone_error_info_get_protocol_code(const LinphoneErrorInfo *ei);
/* linphone dictionary */
LINPHONE_PUBLIC LinphoneDictionary* linphone_dictionary_new();
@ -676,6 +690,7 @@ LINPHONE_PUBLIC void linphone_call_enable_camera(LinphoneCall *lc, bool_t enable
LINPHONE_PUBLIC bool_t linphone_call_camera_enabled(const LinphoneCall *lc);
LINPHONE_PUBLIC int linphone_call_take_video_snapshot(LinphoneCall *call, const char *file);
LINPHONE_PUBLIC LinphoneReason linphone_call_get_reason(const LinphoneCall *call);
LINPHONE_PUBLIC const LinphoneErrorInfo *linphone_call_get_error_info(const LinphoneCall *call);
LINPHONE_PUBLIC const char *linphone_call_get_remote_user_agent(LinphoneCall *call);
LINPHONE_PUBLIC const char *linphone_call_get_remote_contact(LinphoneCall *call);
LINPHONE_PUBLIC LinphoneAddress *linphone_call_get_remote_contact_address(LinphoneCall *call);
@ -851,6 +866,13 @@ LINPHONE_PUBLIC const char * linphone_proxy_config_get_dial_prefix(const Linphon
**/
LINPHONE_PUBLIC LinphoneReason linphone_proxy_config_get_error(const LinphoneProxyConfig *cfg);
/**
* Get detailed information why registration failed when the proxy config state is LinphoneRegistrationFailed.
* @param[in] cfg #LinphoneProxyConfig object.
* @returns The details why registration failed for this proxy config.
**/
LINPHONE_PUBLIC const LinphoneErrorInfo *linphone_proxy_config_get_error_info(const LinphoneProxyConfig *cfg);
/*
* return the transport from either : service route, route, or addr
* @returns cfg object
@ -1071,6 +1093,7 @@ LINPHONE_PUBLIC bool_t linphone_chat_message_is_read(LinphoneChatMessage* messag
LINPHONE_PUBLIC bool_t linphone_chat_message_is_outgoing(LinphoneChatMessage* message);
LINPHONE_PUBLIC unsigned int linphone_chat_message_get_storage_id(LinphoneChatMessage* message);
LINPHONE_PUBLIC LinphoneReason linphone_chat_message_get_reason(LinphoneChatMessage* msg);
LINPHONE_PUBLIC const LinphoneErrorInfo *linphone_chat_message_get_error_info(const LinphoneChatMessage *msg);
/**
* @}
*/

View file

@ -1139,6 +1139,8 @@ SalReason linphone_reason_to_sal(LinphoneReason reason){
return SalReasonServerTimeout;
case LinphoneReasonNotAnswered:
return SalReasonRequestTimeout;
case LinphoneReasonUnknown:
return SalReasonUnknown;
}
return SalReasonUnknown;
}
@ -1146,9 +1148,15 @@ SalReason linphone_reason_to_sal(LinphoneReason reason){
LinphoneReason linphone_reason_from_sal(SalReason r){
LinphoneReason ret=LinphoneReasonNone;
switch(r){
case SalReasonUnknown:
case SalReasonNone:
ret=LinphoneReasonNone;
break;
case SalReasonIOError:
ret=LinphoneReasonIOError;
break;
case SalReasonUnknown:
ret=LinphoneReasonUnknown;
break;
case SalReasonBusy:
ret=LinphoneReasonBusy;
break;
@ -1213,6 +1221,52 @@ LinphoneReason linphone_reason_from_sal(SalReason r){
return ret;
}
/**
* Get reason code from the error info.
* @param ei the error info.
* @return a #LinphoneReason
* @ingroup misc
**/
LinphoneReason linphone_error_info_get_reason(const LinphoneErrorInfo *ei){
const SalErrorInfo *sei=(const SalErrorInfo*)ei;
return linphone_reason_from_sal(sei->reason);
}
/**
* Get textual phrase from the error info.
* This is the text that is provided by the peer in the protocol (SIP).
* @param ei the error info.
* @return the error phrase
* @ingroup misc
**/
const char *linphone_error_info_get_phrase(const LinphoneErrorInfo *ei){
const SalErrorInfo *sei=(const SalErrorInfo*)ei;
return sei->status_string;
}
/**
* Provides additional information regarding the failure.
* With SIP protocol, the "Reason" and "Warning" headers are returned.
* @param ei the error info.
* @return more details about the failure.
* @ingroup misc
**/
const char *linphone_error_info_get_details(const LinphoneErrorInfo *ei){
const SalErrorInfo *sei=(const SalErrorInfo*)ei;
return sei->warnings;
}
/**
* Get the status code from the low level protocol (ex a SIP status code).
* @param ei the error info.
* @return the status code.
* @ingroup misc
**/
int linphone_error_info_get_protocol_code(const LinphoneErrorInfo *ei){
const SalErrorInfo *sei=(const SalErrorInfo*)ei;
return sei->protocol_code;
}
/**
* Set the name of the mediastreamer2 filter to be used for rendering video.
* This is for advanced users of the library, mainly to workaround hardware/driver bugs.

View file

@ -146,7 +146,7 @@ struct _LinphoneChatMessage {
LinphoneChatMessageState state;
bool_t is_read;
unsigned int storage_id;
LinphoneReason reason;
SalOp *op;
};
typedef struct StunCandidate{
@ -159,6 +159,7 @@ struct _LinphoneCall
{
int magic; /*used to distinguish from proxy config*/
struct _LinphoneCore *core;
SalErrorInfo non_op_error;
int af; /*the address family to prefer for RTP path, guessed from signaling path*/
LinphoneCallDir dir;
SalMediaDescription *biggestdesc; /*media description with all already proposed streams, used to remember the mapping of streams*/
@ -174,7 +175,6 @@ struct _LinphoneCall
LinphoneCallState state;
LinphoneCallState prevstate;
LinphoneCallState transfer_state; /*idle if no transfer*/
LinphoneReason reason;
LinphoneProxyConfig *dest_proxy;
int refcnt;
void * user_pointer;
@ -204,7 +204,7 @@ struct _LinphoneCall
unsigned int remote_session_ver;
LinphoneCall *referer; /*when this call is the result of a transfer, referer is set to the original call that caused the transfer*/
LinphoneCall *transfer_target;/*if this call received a transfer request, then transfer_target points to the new call created to the refer target */
int localdesc_changed;
int localdesc_changed;/*not a boolean, contains a mask representing changes*/
bool_t refer_pending;
bool_t expect_media_in_ack;
@ -373,7 +373,6 @@ int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call)
void linphone_core_notify_incoming_call(LinphoneCore *lc, LinphoneCall *call);
bool_t linphone_core_incompatible_security(LinphoneCore *lc, SalMediaDescription *md);
extern SalCallbacks linphone_sal_callbacks;
void linphone_proxy_config_set_error(LinphoneProxyConfig *cfg, LinphoneReason error);
bool_t linphone_core_rtcp_enabled(const LinphoneCore *lc);
LinphoneCall * is_a_linphone_call(void *user_pointer);
@ -413,7 +412,6 @@ struct _LinphoneProxyConfig
bool_t pad[3];
void* user_data;
time_t deletion_date;
LinphoneReason error;
LinphonePrivacyMask privacy;
};
@ -693,7 +691,6 @@ struct _LinphoneEvent{
SalCustomHeader *send_custom_headers;
LinphoneSubscriptionState subscription_state;
LinphonePublishState publish_state;
LinphoneReason reason;
void *userdata;
int refcnt;
char *name;
@ -806,7 +803,6 @@ LinphoneEvent *linphone_event_new(LinphoneCore *lc, LinphoneSubscriptionDir dir,
LinphoneEvent *linphone_event_new_with_op(LinphoneCore *lc, SalOp *op, LinphoneSubscriptionDir dir, const char *name);
void linphone_event_set_state(LinphoneEvent *lev, LinphoneSubscriptionState state);
void linphone_event_set_publish_state(LinphoneEvent *lev, LinphonePublishState state);
void linphone_event_set_reason(LinphoneEvent *lev, LinphoneReason reason);
LinphoneSubscriptionState linphone_subscription_state_from_sal(SalSubscribeStatus ss);
const LinphoneContent *linphone_content_from_sal_body(LinphoneContent *obj, const SalBody *ref);
void linphone_core_invalidate_friend_subscriptions(LinphoneCore *lc);
@ -847,6 +843,10 @@ char * linphone_get_xml_text_content(xmlparsing_context_t *xml_ctx, const char *
void linphone_free_xml_text_content(const char *text);
xmlXPathObjectPtr linphone_get_xml_xpath_object_for_node_list(xmlparsing_context_t *xml_ctx, const char *xpath_expression);
static inline const LinphoneErrorInfo *linphone_error_info_from_sal_op(const SalOp *op){
if (op==NULL) return (LinphoneErrorInfo*)sal_error_info_none();
return (const LinphoneErrorInfo*)sal_op_get_error_info(op);
}
/** Belle Sip-based objects need unique ids
*/

View file

@ -1368,11 +1368,11 @@ LinphoneRegistrationState linphone_proxy_config_get_state(const LinphoneProxyCon
}
LinphoneReason linphone_proxy_config_get_error(const LinphoneProxyConfig *cfg) {
return cfg->error;
return linphone_error_info_get_reason(linphone_proxy_config_get_error_info(cfg));
}
void linphone_proxy_config_set_error(LinphoneProxyConfig *cfg,LinphoneReason error) {
cfg->error = error;
const LinphoneErrorInfo *linphone_proxy_config_get_error_info(const LinphoneProxyConfig *cfg){
return linphone_error_info_from_sal_op(cfg->op);
}
const LinphoneAddress* linphone_proxy_config_get_service_route(const LinphoneProxyConfig* cfg) {

View file

@ -254,6 +254,7 @@ SalStreamDescription *sal_media_description_find_stream(SalMediaDescription *md,
SalMediaProto proto, SalStreamType type);
void sal_media_description_set_dir(SalMediaDescription *md, SalStreamDir stream_dir);
/*this structure must be at the first byte of the SalOp structure defined by implementors*/
typedef struct SalOpBase{
Sal *root;
@ -279,15 +280,8 @@ typedef struct SalOpBase{
} SalOpBase;
typedef enum SalError{
SalErrorNone,
SalErrorNoResponse,
SalErrorProtocol,
SalErrorFailure, /* see SalReason for more details */
SalErrorUnknown
} SalError;
typedef enum SalReason{
SalReasonNone, /*no error, please leave first so that it takes 0 value*/
SalReasonDeclined,
SalReasonBusy,
SalReasonRedirect,
@ -308,11 +302,20 @@ typedef enum SalReason{
SalReasonAddressIncomplete,
SalReasonNotImplemented,
SalReasonBadGateway,
SalReasonServerTimeout
SalReasonServerTimeout,
SalReasonIOError
}SalReason;
const char* sal_reason_to_string(const SalReason reason);
typedef struct SalErrorInfo{
SalReason reason;
char *status_string;
int protocol_code;
char *warnings;
char *full_string; /*concatenation of status_string + warnings*/
}SalErrorInfo;
typedef enum SalPresenceStatus{
SalPresenceOffline,
SalPresenceOnline,
@ -400,21 +403,21 @@ typedef void (*SalOnCallAccepted)(SalOp *op);
typedef void (*SalOnCallAck)(SalOp *op);
typedef void (*SalOnCallUpdating)(SalOp *op);/*< Called when a reINVITE/UPDATE is received*/
typedef void (*SalOnCallTerminated)(SalOp *op, const char *from);
typedef void (*SalOnCallFailure)(SalOp *op, SalError error, SalReason reason, const char *details, int code);
typedef void (*SalOnCallFailure)(SalOp *op);
typedef void (*SalOnCallReleased)(SalOp *salop);
typedef void (*SalOnAuthRequestedLegacy)(SalOp *op, const char *realm, const char *username);
typedef bool_t (*SalOnAuthRequested)(Sal *sal,SalAuthInfo* info);
typedef void (*SalOnAuthFailure)(SalOp *op, SalAuthInfo* info);
typedef void (*SalOnRegisterSuccess)(SalOp *op, bool_t registered);
typedef void (*SalOnRegisterFailure)(SalOp *op, SalError error, SalReason reason, const char *details);
typedef void (*SalOnRegisterFailure)(SalOp *op);
typedef void (*SalOnVfuRequest)(SalOp *op);
typedef void (*SalOnDtmfReceived)(SalOp *op, char dtmf);
typedef void (*SalOnRefer)(Sal *sal, SalOp *op, const char *referto);
typedef void (*SalOnTextReceived)(SalOp *op, const SalMessage *msg);
typedef void (*SalOnTextDeliveryUpdate)(SalOp *op, SalTextDeliveryStatus, SalReason);
typedef void (*SalOnTextDeliveryUpdate)(SalOp *op, SalTextDeliveryStatus);
typedef void (*SalOnIsComposingReceived)(SalOp *op, const SalIsComposing *is_composing);
typedef void (*SalOnNotifyRefer)(SalOp *op, SalReferStatus state);
typedef void (*SalOnSubscribeResponse)(SalOp *op, SalSubscribeStatus status, SalError error, SalReason reason);
typedef void (*SalOnSubscribeResponse)(SalOp *op, SalSubscribeStatus status);
typedef void (*SalOnNotify)(SalOp *op, SalSubscribeStatus status, const char *event, const SalBody *body);
typedef void (*SalOnSubscribeReceived)(SalOp *salop, const char *event, const SalBody *body);
typedef void (*SalOnSubscribeClosed)(SalOp *salop);
@ -425,7 +428,7 @@ typedef void (*SalOnSubscribePresenceReceived)(SalOp *salop, const char *from);
typedef void (*SalOnSubscribePresenceClosed)(SalOp *salop, const char *from);
typedef void (*SalOnPingReply)(SalOp *salop);
typedef void (*SalOnInfoReceived)(SalOp *salop, const SalBody *body);
typedef void (*SalOnPublishResponse)(SalOp *salop, SalError error, SalReason reason);
typedef void (*SalOnPublishResponse)(SalOp *salop);
typedef void (*SalOnExpire)(SalOp *salop);
/*allows sal implementation to access auth info if available, return TRUE if found*/
@ -577,6 +580,11 @@ bool_t sal_op_is_ipv6(SalOp *op);
/*returns TRUE if there is no pending request that may block a future one */
bool_t sal_op_is_idle(SalOp *op);
const SalErrorInfo *sal_error_info_none(void);
const SalErrorInfo *sal_op_get_error_info(const SalOp *op);
void sal_error_info_reset(SalErrorInfo *ei);
void sal_error_info_set(SalErrorInfo *ei, SalReason reason, int code, const char *status_string, const char *warning);
/*Call API*/
int sal_call_set_local_media_description(SalOp *h, SalMediaDescription *desc);
int sal_call(SalOp *h, const char *from, const char *to);

View file

@ -914,7 +914,7 @@ static void call_waiting_indication_with_param(bool_t enable_caller_privacy) {
snprintf(hellopath,sizeof(hellopath), "%s/sounds/hello8000.wav", liblinphone_tester_file_prefix);
linphone_core_set_play_file(laure->lc,hellopath);
if (enable_caller_privacy)
linphone_call_params_set_privacy(laure_params,LinphonePrivacyId);
linphone_call_params_set_privacy(laure_params,LinphonePrivacyId);
CU_ASSERT_PTR_NOT_NULL(linphone_core_invite_address_with_params(laure->lc,pauline->identity,laure_params));
@ -1185,7 +1185,7 @@ static void early_media_call_with_ringing(void){
marie_call = linphone_core_invite_address(marie->lc, pauline->identity);
CU_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingReceived,1,1000));
CU_ASSERT_TRUE(wait_for_list(lcs, &pauline->stat.number_of_LinphoneCallIncomingReceived,1,3000));
CU_ASSERT_TRUE(wait_for_list(lcs, &marie->stat.number_of_LinphoneCallOutgoingRinging,1,1000));