Commit f4a4a644 authored by Simon Morlat's avatar Simon Morlat

Support for incoming UPDATEs within dialog.

For tests, the possibility to send an UPDATE with linphone_core_update_call() has been added thanks to a $
Added possibility to configure Supported SIP header.
parent 46c932e6
......@@ -29,7 +29,7 @@ Download lastest linphone-deps-win32 zip from
http://download.savannah.gnu.org/releases-noredirect/linphone/misc
using your browser.
Download lastest gtk+-2.24.10 win32 _bundle_ from http://www.gtk.org
Download gtk+-2.24.10 win32 _bundle_ from http://www.gtk.org, direct link: http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.24/gtk+-bundle_2.24.10-20120208_win32.zip
Install all these three package in /:
......
......@@ -534,6 +534,8 @@ void sal_uninit(Sal* sal){
belle_sip_object_unref(sal->prov);
belle_sip_object_unref(sal->stack);
belle_sip_object_unref(sal->listener);
if (sal->supported) belle_sip_object_unref(sal->supported);
ms_list_free_with_data(sal->supported_tags,ms_free);
if (sal->uuid) ms_free(sal->uuid);
if (sal->root_ca) ms_free(sal->root_ca);
ms_free(sal);
......@@ -932,10 +934,79 @@ int sal_create_uuid(Sal*ctx, char *uuid, size_t len){
return 0;
}
static void make_supported_header(Sal *sal){
MSList *it;
char *alltags=NULL;
size_t buflen=64;
size_t written=0;
if (sal->supported){
belle_sip_object_unref(sal->supported);
sal->supported=NULL;
}
for(it=sal->supported_tags;it!=NULL;it=it->next){
const char *tag=(const char*)it->data;
size_t taglen=strlen(tag);
if (alltags==NULL || (written+taglen+1>=buflen)) alltags=ms_realloc(alltags,(buflen=buflen*2));
snprintf(alltags+written,buflen-written,it->next ? "%s, " : "%s",tag);
}
if (alltags){
sal->supported=belle_sip_header_create("Supported",alltags);
if (sal->supported){
belle_sip_object_ref(sal->supported);
}
ms_free(alltags);
}
}
void sal_set_supported_tags(Sal *ctx, const char* tags){
ctx->supported_tags=ms_list_free_with_data(ctx->supported_tags,ms_free);
if (tags){
char *iter;
char *buffer=ms_strdup(tags);
char *tag;
char *context=NULL;
iter=buffer;
while((tag=strtok_r(iter,", ",&context))!=NULL){
iter=NULL;
ctx->supported_tags=ms_list_append(ctx->supported_tags,ms_strdup(tag));
}
ms_free(buffer);
}
make_supported_header(ctx);
}
const char *sal_get_supported_tags(Sal *ctx){
if (ctx->supported){
return belle_sip_header_get_unparsed_value(ctx->supported);
}
return NULL;
}
void sal_add_supported_tag(Sal *ctx, const char* tag){
MSList *elem=ms_list_find_custom(ctx->supported_tags,(MSCompareFunc)strcasecmp,NULL);
if (!elem){
ctx->supported_tags=ms_list_append(ctx->supported_tags,ms_strdup(tag));
make_supported_header(ctx);
}
}
void sal_remove_supported_tag(Sal *ctx, const char* tag){
MSList *elem=ms_list_find_custom(ctx->supported_tags,(MSCompareFunc)strcasecmp,NULL);
if (elem){
ms_free(elem->data);
ctx->supported_tags=ms_list_remove_link(ctx->supported_tags,elem);
make_supported_header(ctx);
}
}
belle_sip_response_t* sal_create_response_from_request ( Sal* sal, belle_sip_request_t* req, int code ) {
belle_sip_response_t *resp=belle_sip_response_create_from_request(req,code);
belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp),BELLE_SIP_HEADER(sal->user_agent));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp),sal_make_supported_header(sal));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(resp),sal->supported);
return resp;
}
......
......@@ -39,6 +39,8 @@ struct Sal{
char *root_ca;
char *uuid;
int refresher_retry_after; /*retry after value for refresher*/
MSList *supported_tags;/*list of char * */
belle_sip_header_t *supported;
bool_t one_matching_codec;
bool_t use_tcp_tls_keep_alive;
bool_t nat_helper_enabled;
......@@ -165,8 +167,6 @@ bool_t sal_op_get_body(SalOp *op, belle_sip_message_t *msg, SalBody *salbody);
SalReason sal_reason_to_sip_code(SalReason r);
belle_sip_header_t * sal_make_supported_header(Sal *sal);
void _sal_op_add_custom_headers(SalOp *op, belle_sip_message_t *msg);
#endif /* SAL_IMPL_H_ */
......@@ -185,6 +185,7 @@ static void call_process_response(void *op_base, const belle_sip_response_event_
int code = belle_sip_response_get_status_code(response);
belle_sip_header_content_type_t *header_content_type=NULL;
belle_sip_dialog_t *dialog=belle_sip_response_event_get_dialog(event);
const char *method;
if (!client_transaction) {
ms_warning("Discarding stateless response [%i] on op [%p]",code,op);
......@@ -193,13 +194,13 @@ static void call_process_response(void *op_base, const belle_sip_response_event_
req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction));
set_or_update_dialog(op,dialog);
dialog_state=dialog ? belle_sip_dialog_get_state(dialog) : BELLE_SIP_DIALOG_NULL;
method=belle_sip_request_get_method(req);
ms_message("Op [%p] receiving call response [%i], dialog is [%p] in state [%s]",op,code,dialog,belle_sip_dialog_state_to_string(dialog_state));
switch(dialog_state) {
case BELLE_SIP_DIALOG_NULL:
case BELLE_SIP_DIALOG_EARLY: {
if (strcmp("INVITE",belle_sip_request_get_method(req))==0 ) {
if (strcmp("INVITE",method)==0 ) {
if (op->state == SalOpStateTerminating) {
/*check if CANCEL was sent before*/
if (strcmp("CANCEL",belle_sip_request_get_method(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_client_trans))))!=0) {
......@@ -238,28 +239,28 @@ static void call_process_response(void *op_base, const belle_sip_response_event_
case BELLE_SIP_DIALOG_CONFIRMED: {
switch (op->state) {
case SalOpStateEarly:/*invite case*/
case SalOpStateActive: /*re-invite case*/
if (code >=200
&& code<300
&& strcmp("INVITE",belle_sip_request_get_method(req))==0) {
handle_sdp_from_response(op,response);
ack=belle_sip_dialog_create_ack(op->dialog,belle_sip_dialog_get_local_seq_number(op->dialog));
if (ack==NULL) {
ms_error("This call has been already terminated.");
return ;
case SalOpStateActive: /*re-invite, INFO, UPDATE case*/
if (strcmp("INVITE",method)==0){
if (code >=200 && code<300) {
handle_sdp_from_response(op,response);
ack=belle_sip_dialog_create_ack(op->dialog,belle_sip_dialog_get_local_seq_number(op->dialog));
if (ack==NULL) {
ms_error("This call has been already terminated.");
return ;
}
if (op->sdp_answer){
set_sdp(BELLE_SIP_MESSAGE(ack),op->sdp_answer);
belle_sip_object_unref(op->sdp_answer);
op->sdp_answer=NULL;
}
belle_sip_dialog_send_ack(op->dialog,ack);
op->base.root->callbacks.call_accepted(op); /*INVITE*/
op->state=SalOpStateActive;
}else if (code >= 300){
call_set_error(op,response);
}
if (op->sdp_answer){
set_sdp(BELLE_SIP_MESSAGE(ack),op->sdp_answer);
belle_sip_object_unref(op->sdp_answer);
op->sdp_answer=NULL;
}
belle_sip_dialog_send_ack(op->dialog,ack);
op->base.root->callbacks.call_accepted(op); /*INVITE*/
op->state=SalOpStateActive;
} else if (code >= 300 && strcmp("INVITE",belle_sip_request_get_method(req))==0){
call_set_error(op,response);
} else if (code == 491
&& strcmp("INFO",belle_sip_request_get_method(req)) == 0
}else if (strcmp("INFO",method)==0){
if (code == 491
&& (header_content_type = belle_sip_message_get_header_by_type(req,belle_sip_header_content_type_t))
&& strcmp("application",belle_sip_header_content_type_get_type(header_content_type))==0
&& strcmp("media_control+xml",belle_sip_header_content_type_get_subtype(header_content_type))==0) {
......@@ -267,8 +268,11 @@ static void call_process_response(void *op_base, const belle_sip_response_event_
belle_sip_source_t *s=sal_create_timer(op->base.root,vfu_retry,sal_op_ref(op), retry_in, "vfu request retry");
ms_message("Rejected vfu request on op [%p], just retry in [%ui] ms",op,retry_in);
belle_sip_object_unref(s);
}else {
/*ignoring*/
}else {
/*ignoring*/
}
}else if (strcmp("UPDATE",method)==0){
op->base.root->callbacks.call_accepted(op); /*INVITE*/
}
break;
case SalOpStateTerminating:
......@@ -419,6 +423,7 @@ 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;
const char *method=belle_sip_request_get_method(req);
bool_t is_update=FALSE;
if (strcmp("ACK",method)!=0){ /*ACK does'nt create srv transaction*/
server_transaction = belle_sip_provider_create_server_transaction(op->base.root->prov,belle_sip_request_event_get_request(event));
......@@ -490,7 +495,7 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
} else if (strcmp("UPDATE",method)==0) {
sal_op_reset_descriptions(op);
if (process_sdp_for_invite(op,req)==0)
op->base.root->callbacks.call_updating(op);
op->base.root->callbacks.call_updating(op,TRUE);
} else {
belle_sip_error("Unexpected method [%s] for dialog state BELLE_SIP_DIALOG_EARLY",belle_sip_request_get_method(req));
unsupported_method(server_transaction,req);
......@@ -522,11 +527,11 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
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",method)==0) {
} else if(strcmp("INVITE",method)==0 || (is_update=(strcmp("UPDATE",method)==0)) ) {
/*re-invite*/
sal_op_reset_descriptions(op);
if (process_sdp_for_invite(op,req)==0)
op->base.root->callbacks.call_updating(op);
op->base.root->callbacks.call_updating(op,is_update);
} else if (strcmp("INFO",method)==0){
if (belle_sip_message_get_body(BELLE_SIP_MESSAGE(req))
&& strstr(belle_sip_message_get_body(BELLE_SIP_MESSAGE(req)),"picture_fast_update")) {
......@@ -564,22 +569,6 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t
belle_sip_server_transaction_send_response(server_transaction,sal_op_create_response_from_request(op,req,481));
} else if (strcmp("MESSAGE",method)==0){
sal_process_incoming_message(op,event);
} else if (strcmp("UPDATE",method)==0) {
/*FIXME jehan: It might be better to silently accept UPDATE which do not modify either the number or the nature of streams*/
/*rfc 3311
* 5.2 Receiving an UPDATE
* ...
* If the UAS cannot change the session parameters without prompting the user, it SHOULD reject
* the request with a 504 response.
*/
resp=sal_op_create_response_from_request(op,req,504);
belle_sip_response_set_reason_phrase(resp,"Cannot change the session parameters without prompting the user");
/*belle_sip_message_add_header( BELLE_SIP_MESSAGE(resp)
,belle_sip_header_create( "Warning", "Cannot change the session parameters without prompting the user"));*/
belle_sip_server_transaction_send_response(server_transaction,resp);
return;
}else{
ms_error("unexpected method [%s] for dialog [%p]",belle_sip_request_get_method(req),op->dialog);
unsupported_method(server_transaction,req);
......@@ -796,13 +785,15 @@ int sal_call_decline(SalOp *op, SalReason reason, const char *redirection /*opti
return 0;
}
int sal_call_update(SalOp *op, const char *subject){
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=belle_sip_dialog_get_state(op->dialog);
/*check for dialog state*/
if ( state == BELLE_SIP_DIALOG_CONFIRMED) {
update=belle_sip_dialog_create_request(op->dialog,"INVITE");
if (no_user_consent)
update=belle_sip_dialog_create_request(op->dialog,"UPDATE");
else
update=belle_sip_dialog_create_request(op->dialog,"INVITE");
} else if (state == BELLE_SIP_DIALOG_EARLY) {
update=belle_sip_dialog_create_request(op->dialog,"UPDATE");
} else {
......
......@@ -118,9 +118,7 @@ belle_sip_header_contact_t* sal_op_create_contact(SalOp *op){
return contact_header;
}
belle_sip_header_t * sal_make_supported_header(Sal *sal){
return belle_sip_header_create("Supported","replaces, outbound");
}
static void add_initial_route_set(belle_sip_request_t *request, const MSList *list){
const MSList *elem;
......@@ -201,7 +199,7 @@ belle_sip_request_t* sal_op_build_request(SalOp *op,const char* method) {
belle_sip_header_privacy_add_privacy(privacy_header,sal_privacy_to_string(SalPrivacyUser));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(privacy_header));
}
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),sal_make_supported_header(op->base.root));
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),op->base.root->supported);
return req;
}
......
......@@ -503,58 +503,53 @@ static void call_ack(SalOp *op){
}
}
static void call_accept_update(LinphoneCore *lc, LinphoneCall *call){
SalMediaDescription *md;
SalMediaDescription *rmd=sal_call_get_remote_media_description(call->op);
if (rmd!=NULL && call->ice_session!=NULL) {
linphone_core_update_ice_from_remote_media_description(call,rmd);
linphone_core_update_local_media_description_from_ice(call->localdesc,call->ice_session);
}
#ifdef BUILD_UPNP
if(call->upnp_session != NULL) {
linphone_core_update_upnp_from_remote_media_description(call, rmd);
linphone_core_update_local_media_description_from_upnp(call->localdesc,call->upnp_session);
}
#endif //BUILD_UPNP
linphone_call_update_remote_session_id_and_ver(call);
sal_call_accept(call->op);
md=sal_call_get_final_media_description(call->op);
if (md && !sal_media_description_empty(md)){
linphone_core_update_streams(lc,call,md);
}
}
static void call_resumed(LinphoneCore *lc, LinphoneCall *call){
/*when we are resumed, increment session id, because sdp is changed (a=recvonly disapears)*/
linphone_call_increment_local_media_description(call);
call_accept_update(lc,call);
if(lc->vtable.display_status)
lc->vtable.display_status(lc,_("We have been resumed."));
linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)");
_linphone_core_accept_call_update(lc,call,NULL,LinphoneCallStreamsRunning,"Connected (streams running)");
}
static void call_paused_by_remote(LinphoneCore *lc, LinphoneCall *call){
/*when we are resumed, increment session id, because sdp is changed (a=recvonly appears)*/
linphone_call_increment_local_media_description(call);
call_accept_update(lc,call);
/* we are being paused */
if(lc->vtable.display_status)
lc->vtable.display_status(lc,_("We are paused by other party."));
linphone_call_set_state (call,LinphoneCallPausedByRemote,"Call paused by remote");
_linphone_core_accept_call_update(lc,call,NULL,LinphoneCallPausedByRemote,"Call paused by remote");
}
static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call,bool_t notify_application){
static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call, bool_t is_update){
/*first check if media capabilities are compatible*/
SalMediaDescription* md;
linphone_call_make_local_media_description(lc,call);
sal_call_set_local_media_description(call->op,call->localdesc);
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);
return;
SalMediaDescription *md;
SalMediaDescription *rmd=sal_call_get_remote_media_description(call->op);
SalMediaDescription *prev_result_desc=call->resultdesc;
if (rmd!=NULL){
if (call->state!=LinphoneCallPaused){
/*in paused state, we must stay in paused state.*/
linphone_call_make_local_media_description(lc,call);
sal_call_set_local_media_description(call->op,call->localdesc);
}
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);
return;
}
if (is_update && prev_result_desc && md){
int diff=sal_media_description_equals(prev_result_desc,md);
if (diff & (SAL_MEDIA_DESCRIPTION_CRYPTO_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"*/
return;
}
}
}
if (notify_application) {
if (call->state==LinphoneCallStreamsRunning) {
/*reINVITE and in-dialogs UPDATE go here*/
if(lc->vtable.display_status)
lc->vtable.display_status(lc,_("Call is updated by remote."));
call->defer_update=FALSE;
......@@ -562,22 +557,22 @@ static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call,bool_t n
if (call->defer_update==FALSE){
linphone_core_accept_call_update(lc,call,NULL);
}
} else { /*SIP UPDATE case*/
/*can be call from any state*/
_linphone_core_accept_call_update(lc,call,NULL);
if (rmd==NULL)
call->expect_media_in_ack=TRUE;
} else if (is_update){ /*SIP UPDATE case, can occur in early states*/
_linphone_core_accept_call_update(lc,call,NULL,call->state,linphone_call_state_to_string(call->state));
}
}
/* this callback is called when an incoming re-INVITE/ SIP UPDATE modifies the session*/
static void call_updating(SalOp *op){
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);
if (rmd==NULL){
/* case of a reINVITE without SDP */
call_accept_update(lc,call);
call->expect_media_in_ack=TRUE;
/* case of a reINVITE or UPDATE without SDP */
call_updated_by_remote(lc,call,is_update);
return;
}
......@@ -588,19 +583,38 @@ static void call_updating(SalOp *op){
}else call_paused_by_remote(lc,call);
break;
/*SIP UPDATE CASE*/
case LinphoneCallOutgoingRinging:
case LinphoneCallOutgoingEarlyMedia:
call_updated_by_remote(lc,call,FALSE);
case LinphoneCallIncomingEarlyMedia:
if (is_update) call_updated_by_remote(lc,call,is_update);
break;
case LinphoneCallStreamsRunning:
case LinphoneCallConnected:
if (sal_media_description_has_dir(rmd,SalStreamSendOnly) || sal_media_description_has_dir(rmd,SalStreamInactive)){
call_paused_by_remote(lc,call);
}else{
call_updated_by_remote(lc,call,TRUE);
call_updated_by_remote(lc,call,is_update);
}
break;
default:
call_accept_update(lc,call);
case LinphoneCallPaused:
call_updated_by_remote(lc,call,is_update);
break;
case LinphoneCallUpdating:
case LinphoneCallPausing:
case LinphoneCallResuming:
case LinphoneCallUpdatedByRemote:
sal_call_decline(call->op,SalReasonNotImplemented,NULL);
/*no break*/
case LinphoneCallIdle:
case LinphoneCallOutgoingInit:
case LinphoneCallEnd:
case LinphoneCallIncomingReceived:
case LinphoneCallOutgoingProgress:
case LinphoneCallRefered:
case LinphoneCallError:
case LinphoneCallReleased:
ms_warning("Receiving reINVITE or UPDATE while in state [%s], should not happen.",linphone_call_state_to_string(call->state));
break;
}
}
......
......@@ -635,6 +635,7 @@ void linphone_core_message_received(LinphoneCore *lc, SalOp *op, const SalMessag
linphone_address_destroy(addr);
msg->storage_id=linphone_chat_message_store(msg);
linphone_chat_room_message_received(cr,lc,msg);
linphone_chat_message_unref(msg);
ms_free(cleanfrom);
ms_free(from);
}
......
......@@ -1424,7 +1424,7 @@ static void video_stream_event_cb(void *user_pointer, const MSFilter *f, const u
case MS_VIDEO_DECODER_FIRST_IMAGE_DECODED:
ms_message("First video frame decoded successfully");
if (call->nextVideoFrameDecoded._func != NULL)
call->nextVideoFrameDecoded._func(call, call->nextVideoFrameDecoded._user_data);
call->nextVideoFrameDecoded._func(call, call->nextVideoFrameDecoded._user_data);
break;
case MS_VIDEO_DECODER_SEND_PLI:
case MS_VIDEO_DECODER_SEND_SLI:
......@@ -2766,7 +2766,7 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){
linphone_core_start_update_call(call->core, call);
break;
case LinphoneCallUpdatedByRemote:
linphone_core_start_accept_call_update(call->core, call);
linphone_core_start_accept_call_update(call->core, call,call->prevstate,linphone_call_state_to_string(call->prevstate));
break;
case LinphoneCallOutgoingInit:
linphone_call_stop_media_streams_for_ice_gathering(call);
......@@ -2781,7 +2781,7 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){
}
} else if (evt == ORTP_EVENT_ICE_LOSING_PAIRS_COMPLETED) {
if (call->state==LinphoneCallUpdatedByRemote){
linphone_core_start_accept_call_update(call->core, call);
linphone_core_start_accept_call_update(call->core, call,call->prevstate,linphone_call_state_to_string(call->prevstate));
linphone_core_update_ice_state_in_call_stats(call);
}
} else if (evt == ORTP_EVENT_ICE_RESTART_NEEDED) {
......
......@@ -464,6 +464,7 @@ static void sip_config_read(LinphoneCore *lc)
sal_enable_sip_update_method(lc->sal,lp_config_get_int(lc->config,"sip","sip_update",1));
lc->sip_conf.vfu_with_info=lp_config_get_int(lc->config,"sip","vfu_with_info",1);
linphone_core_set_sip_transport_timeout(lc, lp_config_get_int(lc->config, "sip", "transport_timeout", 63000));
sal_set_supported_tags(lc->sal,lp_config_get_string(lc->config,"sip","supported","replaces, outbound"));
}
static void rtp_config_read(LinphoneCore *lc)
......@@ -2853,8 +2854,9 @@ int linphone_core_accept_early_media(LinphoneCore* lc, LinphoneCall* call){
int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call){
const char *subject;
bool_t no_user_consent=call->params.no_user_consent;
linphone_call_make_local_media_description(lc,call);
if (!no_user_consent) linphone_call_make_local_media_description(lc,call);
#ifdef BUILD_UPNP
if(call->upnp_session != NULL) {
linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session);
......@@ -2862,8 +2864,10 @@ int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call){
#endif //BUILD_UPNP
if (call->params->in_conference){
subject="Conference";
}else{
}else if (!no_user_consent){
subject="Media change";
}else{
subject="Refreshing";
}
if (lc->vtable.display_status)
lc->vtable.display_status(lc,_("Modifying call parameters..."));
......@@ -2872,7 +2876,7 @@ int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call){
/*give a chance to update the contact address if connectivity has changed*/
sal_op_set_contact_address(call->op,sal_op_get_contact_address(call->dest_proxy->op));
}else sal_op_set_contact_address(call->op,NULL);
return sal_call_update(call->op,subject);
return sal_call_update(call->op,subject,no_user_consent);
}
/**
......@@ -2984,7 +2988,7 @@ int linphone_core_defer_call_update(LinphoneCore *lc, LinphoneCall *call){
return -1;
}
int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call){
int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState next_state, const char *state_info){
SalMediaDescription *md;
if (call->ice_session != NULL) {
if (ice_session_nb_losing_pairs(call->ice_session) > 0) {
......@@ -3002,8 +3006,7 @@ int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call)
linphone_core_update_streams (lc,call,md);
linphone_call_fix_call_parameters(call);
}
if (call->state != LinphoneCallOutgoingEarlyMedia) /*don't change the state in case of outgoing early (SIP UPDATE)*/
linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)");
linphone_call_set_state(call,next_state,state_info);
return 0;
}
......@@ -3032,10 +3035,10 @@ int linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const
linphone_call_state_to_string(call->state));
return -1;
}
return _linphone_core_accept_call_update(lc, call, params);
return _linphone_core_accept_call_update(lc, call, params, call->prevstate, linphone_call_state_to_string(call->prevstate));
}
int _linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params){
int _linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params, LinphoneCallState next_state, const char *state_info){
SalMediaDescription *remote_desc;
bool_t keep_sdp_version;
#if defined(VIDEO_ENABLED) && defined(BUILD_UPNP)
......@@ -3048,7 +3051,7 @@ int _linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, cons
/* Remote has sent an INVITE with the same SDP as before, so send a 200 OK with the same SDP as before. */
ms_warning("SDP version has not changed, send same SDP as before.");
sal_call_accept(call->op);
linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)");
linphone_call_set_state(call,next_state,state_info);
return 0;
}
if (params==NULL){
......@@ -3086,7 +3089,7 @@ int _linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, cons
}
#endif //BUILD_UPNP
linphone_core_start_accept_call_update(lc, call);
linphone_core_start_accept_call_update(lc, call, next_state, state_info);
return 0;
}
......@@ -3413,7 +3416,7 @@ int _linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call)
return -1;
}
sal_call_set_local_media_description(call->op,call->localdesc);
if (sal_call_update(call->op,subject) != 0){
if (sal_call_update(call->op,subject,FALSE) != 0){
if (lc->vtable.display_warning)
lc->vtable.display_warning(lc,_("Could not pause the call"));
}
......@@ -3498,7 +3501,7 @@ int linphone_core_resume_call(LinphoneCore *lc, LinphoneCall *call){
sal_call_set_local_media_description(call->op,call->localdesc);
sal_media_description_set_dir(call->localdesc,SalStreamSendRecv);
if (call->params->in_conference && !call->current_params->in_conference) subject="Conference";
if(sal_call_update(call->op,subject) != 0){
if ( sal_call_update(call->op,subject,FALSE) != 0){
return -1;
}
linphone_call_set_state(call,LinphoneCallResuming,"Resuming");
......@@ -6404,6 +6407,29 @@ void linphone_core_set_file_transfer_server(LinphoneCore *core, const char * ser
core->file_transfer_server=ms_strdup(server_url);
}
/**
* This function controls signaling features supported by the core.
* They are typically included in a SIP Supported header.
* @param lc the LinphoneCore
* @param tag the feature tag name
* @ingroup initializing
**/
void linphone_core_add_supported_tag(LinphoneCore *lc, const char *tag){
sal_add_supported_tag(lc->sal,tag);
lp_config_set_string(lc->config,"sip","supported",sal_get_supported_tags(lc->sal));
}
/**
* Remove a supported tag. @see linphone_core_add_supported_tag()
* @param lc the LinphoneCore
* @param tag the tag to remove
* @ingroup initializing
**/
void linphone_core_remove_supported_tag(LinphoneCore *lc, const char *tag){
sal_remove_supported_tag(lc->sal,tag);
lp_config_set_string(lc->config,"sip","supported",sal_get_supported_tags(lc->sal));
}
int linphone_payload_type_get_type(const LinphonePayloadType *pt) {
return pt->type;
......
......@@ -2861,6 +2861,10 @@ LINPHONE_PUBLIC void linphone_core_set_file_transfer_server(LinphoneCore *core,
**/
LINPHONE_PUBLIC const char ** linphone_core_get_supported_file_formats(LinphoneCore *core);
LINPHONE_PUBLIC void linphone_core_add_supported_tag(LinphoneCore *core, const char *tag);
LINPHONE_PUBLIC void linphone_core_remove_supported_tag(LinphoneCore *core, const char *tag);
#ifdef __cplusplus
}
#endif
......
......@@ -102,8 +102,9 @@ struct _LinphoneCallParams{
bool_t real_early_media; /*send real media even during early media (for outgoing calls)*/
bool_t in_conference; /*in conference mode */
bool_t low_bandwidth;
LinphonePrivacyMask privacy;
bool_t no_user_consent;/*when set to TRUE an UPDATE request will be used instead of reINVITE*/
uint16_t avpf_rr_interval;
LinphonePrivacyMask privacy;
};
BELLE_SIP_DECLARE_VPTR(LinphoneCallParams);
......@@ -404,7 +405,7 @@ int linphone_core_proceed_with_invite_if_ready(LinphoneCore *lc, LinphoneCall *c
int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, const LinphoneAddress* destination/* = NULL if to be taken from the call log */);
int linphone_core_restart_invite(LinphoneCore *lc, LinphoneCall *call);
int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call);
int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call);
int linphone_core_start_accept_call_update(LinphoneCore *lc, LinphoneCall *call, LinphoneCallState next_state, const char *state_info);
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;
......@@ -656,7 +657,7 @@ LinphoneToneDescription *linphone_core_get_call_error_tone(const LinphoneCore *l
void linphone_core_play_call_error_tone(LinphoneCore *lc, LinphoneReason reason);
void _linphone_core_set_tone(LinphoneCore *lc, LinphoneReason reason, LinphoneToneID id, const char *audiofile);
const char *linphone_core_get_tone_file(const LinphoneCore *lc, LinphoneToneID id);
int _linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params);
int _linphone_core_accept_call_update(LinphoneCore *lc, LinphoneCall *call, const LinphoneCallParams *params, LinphoneCallState next_state, const char *state_info);
typedef struct _LinphoneConference LinphoneConference;
struct _LinphoneCore
......
......@@ -307,7 +307,7 @@ int sal_media_description_equals(const SalMediaDescription *md1, const SalMediaD
int i;
if (strcmp(md1->addr, md2->addr) != 0) result |= SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED;
if (md1->nb_streams != md2->nb_streams) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
if (md1->nb_streams != md2->nb_streams) result |= SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED;
if (md1->bandwidth != md2->bandwidth) result |= SAL_MEDIA_DESCRIPTION_CODEC_CHANGED;
for(i = 0; i < md1->nb_streams; ++i){
result |= sal_stream_description_equals(&md1->streams[i], &md2->streams[i]);
......
......@@ -834,7 +834,7 @@ int linphone_upnp_call_process(LinphoneCall *call) {
linphone_core_start_update_call(lc, call);
break;
case LinphoneCallUpdatedByRemote:
linphone_core_start_accept_call_update(lc, call);
linphone_core_start_accept_call_update(lc, call,call->prevstate,linphone_call_state_to_string(call->prevstate));
break;
case LinphoneCallOutgoingInit:
linphone_core_proceed_with_invite_if_ready(lc, call, NULL);
......
......@@ -72,7 +72,9 @@ typedef enum {
#define SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED 0x01
#define SAL_MEDIA_DESCRIPTION_CODEC_CHANGED 0x02
#define SAL_MEDIA_DESCRIPTION_CRYPTO_CHANGED 0x04
#define SAL_MEDIA_DESCRIPTION_CHANGED (SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED | SAL_MEDIA_DESCRIPTION_CODEC_CHANGED | SAL_MEDIA_DESCRIPTION_CRYPTO_CHANGED)
#define SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED 0x08
#define SAL_MEDIA_DESCRIPTION_CHANGED (SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED | SAL_MEDIA_DESCRIPTION_CODEC_CHANGED |\
SAL_MEDIA_DESCRIPTION_CRYPTO_CHANGED |SAL_MEDIA_DESCRIPTION_STREAMS_CHANGED)
const char* sal_transport_to_string(SalTransport transport);
SalTransport sal_transport_parse(const char*);
......@@ -413,7 +415,7 @@ typedef void (*SalOnCallReceived)(SalOp *op);
typedef void (*SalOnCallRinging)(SalOp *op);
typedef void (*SalOnCallAccepted)(SalOp *op);
typedef void (*SalOnCallAck)(SalOp *op);
typedef void (*SalOnCallUpdating)(SalOp *op);/*< Called when a reINVITE/UPDATE is received*/
typedef void (*SalOnCallUpdating)(SalOp *op, bool_t is_update);/*< Called when a reINVITE/UPDATE is received*/
typedef void (*SalOnCallTerminated)(SalOp *op, const char *from);
typedef void (*SalOnCallFailure)(SalOp *op);
typedef void (*SalOnCallReleased)(SalOp *salop);
......@@ -517,6 +519,10 @@ int sal_get_listening_port(Sal *ctx, SalTransport tr);
int sal_unlisten_ports(Sal *ctx);