]> sjero.net Git - linphone/blobdiff - coreapi/callbacks.c
fix crash when receiving an incorrect SDP message in a 200Ok.
[linphone] / coreapi / callbacks.c
index c57016b97ec0b67c113417e1b3c84551ecb03f05..a6b49a0e8e878eb76cee10453ce86a0ab55187bb 100644 (file)
@@ -27,9 +27,63 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 static void register_failure(SalOp *op, SalError error, SalReason reason, const char *details);
 
-static bool_t media_parameters_changed(LinphoneCall *call, SalMediaDescription *oldmd, SalMediaDescription *newmd){
-       if (call->params.in_conference!=call->current_params.in_conference) return TRUE;
-       return !sal_media_description_equals(oldmd,newmd)  || call->up_bw!=linphone_core_get_upload_bandwidth(call->core);
+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;
+       if (call->up_bw != linphone_core_get_upload_bandwidth(call->core)) return SAL_MEDIA_DESCRIPTION_CHANGED;
+       return sal_media_description_equals(oldmd, newmd);
+}
+
+void linphone_core_update_streams_destinations(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *old_md, SalMediaDescription *new_md) {
+       SalStreamDescription *old_audiodesc = NULL;
+       SalStreamDescription *old_videodesc = NULL;
+       SalStreamDescription *new_audiodesc = NULL;
+       SalStreamDescription *new_videodesc = NULL;
+       char *rtp_addr, *rtcp_addr;
+       int i;
+
+       for (i = 0; i < old_md->n_active_streams; i++) {
+               if (old_md->streams[i].type == SalAudio) {
+                       old_audiodesc = &old_md->streams[i];
+               } else if (old_md->streams[i].type == SalVideo) {
+                       old_videodesc = &old_md->streams[i];
+               }
+       }
+       for (i = 0; i < new_md->n_active_streams; i++) {
+               if (new_md->streams[i].type == SalAudio) {
+                       new_audiodesc = &new_md->streams[i];
+               } else if (new_md->streams[i].type == SalVideo) {
+                       new_videodesc = &new_md->streams[i];
+               }
+       }
+       if (call->audiostream && new_audiodesc) {
+               rtp_addr = (new_audiodesc->rtp_addr[0] != '\0') ? new_audiodesc->rtp_addr : new_md->addr;
+               rtcp_addr = (new_audiodesc->rtcp_addr[0] != '\0') ? new_audiodesc->rtcp_addr : new_md->addr;
+               ms_message("Change audio stream destination: RTP=%s:%d RTCP=%s:%d", rtp_addr, new_audiodesc->rtp_port, rtcp_addr, new_audiodesc->rtcp_port);
+               rtp_session_set_remote_addr_full(call->audiostream->ms.session, rtp_addr, new_audiodesc->rtp_port, rtcp_addr, new_audiodesc->rtcp_port);
+       }
+#ifdef VIDEO_ENABLED
+       if (call->videostream && new_videodesc) {
+               rtp_addr = (new_videodesc->rtp_addr[0] != '\0') ? new_videodesc->rtp_addr : new_md->addr;
+               rtcp_addr = (new_videodesc->rtcp_addr[0] != '\0') ? new_videodesc->rtcp_addr : new_md->addr;
+               ms_message("Change video stream destination: RTP=%s:%d RTCP=%s:%d", rtp_addr, new_videodesc->rtp_port, rtcp_addr, new_videodesc->rtcp_port);
+               rtp_session_set_remote_addr_full(call->videostream->ms.session, rtp_addr, new_videodesc->rtp_port, rtcp_addr, new_videodesc->rtcp_port);
+       }
+#endif
+
+       /* Copy address and port values from new_md to old_md since we will keep old_md as resultdesc */
+       strcpy(old_md->addr, new_md->addr);
+       if (old_audiodesc && new_audiodesc) {
+               strcpy(old_audiodesc->rtp_addr, new_audiodesc->rtp_addr);
+               strcpy(old_audiodesc->rtcp_addr, new_audiodesc->rtcp_addr);
+               old_audiodesc->rtp_port = new_audiodesc->rtp_port;
+               old_audiodesc->rtcp_port = new_audiodesc->rtcp_port;
+       }
+       if (old_videodesc && new_videodesc) {
+               strcpy(old_videodesc->rtp_addr, new_videodesc->rtp_addr);
+               strcpy(old_videodesc->rtcp_addr, new_videodesc->rtcp_addr);
+               old_videodesc->rtp_port = new_videodesc->rtp_port;
+               old_videodesc->rtcp_port = new_videodesc->rtcp_port;
+       }
 }
 
 void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMediaDescription *new_md){
@@ -46,28 +100,44 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia
                call->media_pending=TRUE;
        }
        call->resultdesc=new_md;
-       if (call->audiostream && call->audiostream->ticker){
+       if ((call->audiostream && call->audiostream->ms.ticker) || (call->videostream && call->videostream->ms.ticker)){
                /* we already started media: check if we really need to restart it*/
                if (oldmd){
-                       if (!media_parameters_changed(call,oldmd,new_md) && !call->playing_ringbacktone){
-                               /*as nothing has changed, keep the oldmd */
-                               call->resultdesc=oldmd;
-                               sal_media_description_unref(new_md);
-                               if (call->all_muted){
-                                       ms_message("Early media finished, unmuting inputs...");
-                                       /*we were in early media, now we want to enable real media */
-                                       linphone_call_enable_camera (call,linphone_call_camera_enabled (call));
-                                       if (call->audiostream)
-                                               linphone_core_mute_mic (lc, linphone_core_is_mic_muted(lc));
+                       int md_changed = media_parameters_changed(call, oldmd, new_md);
+                       if ((md_changed & SAL_MEDIA_DESCRIPTION_CODEC_CHANGED) || call->playing_ringbacktone) {
+                               ms_message("Media descriptions are different, need to restart the streams.");
+                       } else {
+                               if (md_changed == SAL_MEDIA_DESCRIPTION_UNCHANGED) {
+                                       /*as nothing has changed, keep the oldmd */
+                                       call->resultdesc=oldmd;
+                                       sal_media_description_unref(new_md);
+                                       if (call->all_muted){
+                                               ms_message("Early media finished, unmuting inputs...");
+                                               /*we were in early media, now we want to enable real media */
+                                               linphone_call_enable_camera (call,linphone_call_camera_enabled (call));
+                                               if (call->audiostream)
+                                                       linphone_core_mute_mic (lc, linphone_core_is_mic_muted(lc));
 #ifdef VIDEO_ENABLED
-                                       if (call->videostream && call->camera_active)
-                                               video_stream_change_camera(call->videostream,lc->video_conf.device );
+                                               if (call->videostream && call->camera_active)
+                                                       video_stream_change_camera(call->videostream,lc->video_conf.device );
 #endif
+                                       }
+                                       ms_message("No need to restart streams, SDP is unchanged.");
+                                       return;
+                               }
+                               else {
+                                       if (md_changed & SAL_MEDIA_DESCRIPTION_NETWORK_CHANGED) {
+                                               ms_message("Network parameters have changed, update them.");
+                                               linphone_core_update_streams_destinations(lc, call, oldmd, new_md);
+                                       }
+                                       if (md_changed & SAL_MEDIA_DESCRIPTION_CRYPTO_CHANGED) {
+                                               ms_message("Crypto parameters have changed, update them.");
+                                               linphone_call_update_crypto_parameters(call, oldmd, new_md);
+                                       }
+                                       call->resultdesc = oldmd;
+                                       sal_media_description_unref(new_md);
+                                       return;
                                }
-                               ms_message("No need to restart streams, SDP is unchanged.");
-                               return;
-                       }else{
-                               ms_message("Media descriptions are different, need to restart the streams.");
                        }
                }
                linphone_call_stop_media_streams (call);
@@ -81,7 +151,7 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia
                bool_t send_ringbacktone=FALSE;
                
                if (call->audiostream==NULL){
-                       /*this happens after pausing the call locally. The streams is destroyed and then we wait the 200Ok to recreate it*/
+                       /*this happens after pausing the call locally. The streams are destroyed and then we wait the 200Ok to recreate them*/
                        linphone_call_init_media_streams (call);
                }
                if (call->state==LinphoneCallIncomingEarlyMedia && linphone_core_get_remote_ringback_tone (lc)!=NULL){
@@ -93,6 +163,9 @@ void linphone_core_update_streams(LinphoneCore *lc, LinphoneCall *call, SalMedia
                }
                linphone_call_start_media_streams(call,all_muted,send_ringbacktone);
        }
+       if (call->state==LinphoneCallPausing && call->paused_by_app && ms_list_size(lc->calls)==1){
+               linphone_core_play_named_tone(lc,LinphoneToneCallOnHold);
+       }
 }
 #if 0
 static bool_t is_duplicate_call(LinphoneCore *lc, const LinphoneAddress *from, const LinphoneAddress *to){
@@ -108,6 +181,21 @@ static bool_t is_duplicate_call(LinphoneCore *lc, const LinphoneAddress *from, c
 }
 #endif
 
+static bool_t already_a_call_with_remote_address(const LinphoneCore *lc, const LinphoneAddress *remote) {
+       ms_warning(" searching for already_a_call_with_remote_address.");
+
+       MSList *elem;
+       for(elem=lc->calls;elem!=NULL;elem=elem->next){
+               const LinphoneCall *call=(LinphoneCall*)elem->data;
+               const LinphoneAddress *cRemote=linphone_call_get_remote_address(call);
+               if (linphone_address_weak_equal(cRemote,remote)) {
+                       ms_warning("already_a_call_with_remote_address found.");
+                       return TRUE;
+               }
+       }
+       return FALSE;
+}
+
 static bool_t already_a_call_pending(LinphoneCore *lc){
        MSList *elem;
        for(elem=lc->calls;elem!=NULL;elem=elem->next){
@@ -125,15 +213,10 @@ static bool_t already_a_call_pending(LinphoneCore *lc){
 
 static void call_received(SalOp *h){
        LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
-       char *barmesg;
        LinphoneCall *call;
        const char *from,*to;
-       char *tmp;
-       LinphoneAddress *from_parsed;
        LinphoneAddress *from_addr, *to_addr;
-       SalMediaDescription *md;
-       bool_t propose_early_media=lp_config_get_int(lc->config,"sip","incoming_calls_early_media",FALSE);
-       const char *ringback_tone=linphone_core_get_remote_ringback_tone (lc);
+       bool_t prevent_colliding_calls=lp_config_get_int(lc->config,"sip","prevent_colliding_calls",TRUE);
        
        /* first check if we can answer successfully to this invite */
        if (lc->presence_mode==LinphoneStatusBusy ||
@@ -161,7 +244,7 @@ static void call_received(SalOp *h){
        from_addr=linphone_address_new(from);
        to_addr=linphone_address_new(to);
 
-       if (already_a_call_pending(lc)){
+       if ((already_a_call_with_remote_address(lc,from_addr) && prevent_colliding_calls) || already_a_call_pending(lc)){
                ms_warning("Receiving another call while one is ringing or initiated, refusing this one with busy message.");
                sal_call_decline(h,SalReasonBusy,NULL);
                sal_op_release(h);
@@ -171,72 +254,25 @@ static void call_received(SalOp *h){
        }
        
        call=linphone_call_new_incoming(lc,from_addr,to_addr,h);
-       sal_call_set_local_media_description(h,call->localdesc);
-       md=sal_call_get_final_media_description(h);
-
-       if (md && sal_media_description_empty(md)){
-               sal_call_decline(h,SalReasonMedia,NULL);
-               linphone_call_unref(call);
-               return;
-       }
        
        /* the call is acceptable so we can now add it to our list */
        linphone_core_add_call(lc,call);
-       
-       from_parsed=linphone_address_new(sal_op_get_from(h));
-       linphone_address_clean(from_parsed);
-       tmp=linphone_address_as_string(from_parsed);
-       linphone_address_destroy(from_parsed);
-       barmesg=ortp_strdup_printf("%s %s%s",tmp,_("is contacting you"),
-           (sal_call_autoanswer_asked(h)) ?_(" and asked autoanswer."):_("."));
-       if (lc->vtable.show) lc->vtable.show(lc);
-       if (lc->vtable.display_status) 
-           lc->vtable.display_status(lc,barmesg);
-
-       /* play the ring if this is the only call*/
-       if (ms_list_size(lc->calls)==1){
-               lc->current_call=call;
-               if (lc->ringstream && lc->dmfs_playing_start_time!=0){
-                       ring_stop(lc->ringstream);
-                       lc->ringstream=NULL;
-                       lc->dmfs_playing_start_time=0;
-               }
-               if (lc->sound_conf.ring_sndcard!=NULL){
-                       if(lc->ringstream==NULL && lc->sound_conf.local_ring){
-                               MSSndCard *ringcard=lc->sound_conf.lsd_card ?lc->sound_conf.lsd_card : lc->sound_conf.ring_sndcard;
-                               ms_message("Starting local ring...");
-                               lc->ringstream=ring_start(lc->sound_conf.local_ring,2000,ringcard);
-                       }
-                       else
-                       {
-                               ms_message("the local ring is already started");
-                       }
-               }
-       }else{
-               /* else play a tone within the context of the current call */
-               call->ringing_beep=TRUE;
-               linphone_core_play_tone(lc);
-       }
-
-       
        linphone_call_ref(call); /*prevent the call from being destroyed while we are notifying, if the user declines within the state callback */
-       linphone_call_set_state(call,LinphoneCallIncomingReceived,"Incoming call");
-       
-       if (call->state==LinphoneCallIncomingReceived){
-               sal_call_notify_ringing(h,propose_early_media || ringback_tone!=NULL);
 
-               if (propose_early_media || ringback_tone!=NULL){
-                       linphone_call_set_state(call,LinphoneCallIncomingEarlyMedia,"Incoming call early media");
-                       linphone_core_update_streams(lc,call,md);
-               }
-               if (sal_call_get_replaces(call->op)!=NULL && lp_config_get_int(lc->config,"sip","auto_answer_replacing_calls",1)){
-                       linphone_core_accept_call(lc,call);
-               }
+       if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseIce) && (call->ice_session != NULL)) {
+               /* Defer ringing until the end of the ICE candidates gathering process. */
+               ms_message("Defer ringing to gather ICE candidates");
+               return;
        }
-       linphone_call_unref(call);
+#ifdef BUILD_UPNP
+       if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseUpnp) && (call->upnp_session != NULL)) {
+               /* Defer ringing until the end of the ICE candidates gathering process. */
+               ms_message("Defer ringing to gather uPnP candidates");
+               return;
+       }
+#endif //BUILD_UPNP
 
-       ms_free(barmesg);
-       ms_free(tmp);
+       linphone_core_notify_incoming_call(lc,call);
 }
 
 static void call_ringing(SalOp *h){
@@ -259,6 +295,10 @@ static void call_ringing(SalOp *h){
                if (lc->ringstream!=NULL) return;       /*already ringing !*/
                if (lc->sound_conf.play_sndcard!=NULL){
                        MSSndCard *ringcard=lc->sound_conf.lsd_card ? lc->sound_conf.lsd_card : lc->sound_conf.play_sndcard;
+                       if (call->localdesc->streams[0].max_rate>0) ms_snd_card_set_preferred_sample_rate(ringcard, call->localdesc->streams[0].max_rate);
+                       /*we release sound before playing ringback tone*/
+                       if (call->audiostream)
+                               audio_stream_unprepare_sound(call->audiostream);
                        lc->ringstream=ring_start(lc->sound_conf.remote_ring,2000,ringcard);
                }
                ms_message("Remote ringing...");
@@ -267,7 +307,7 @@ static void call_ringing(SalOp *h){
                linphone_call_set_state(call,LinphoneCallOutgoingRinging,"Remote ringing");
        }else{
                /*accept early media */
-               if (call->audiostream && call->audiostream->ticker!=NULL){
+               if (call->audiostream && audio_stream_started(call->audiostream)){
                        /*streams already started */
                        ms_message("Early media already started.");
                        return;
@@ -281,7 +321,7 @@ static void call_ringing(SalOp *h){
                        lc->ringstream=NULL;
                }
                ms_message("Doing early media...");
-               linphone_core_update_streams (lc,call,md);
+               linphone_core_update_streams(lc,call,md);
        }
 }
 
@@ -299,15 +339,29 @@ static void call_accepted(SalOp *op){
                ms_warning("No call to accept.");
                return ;
        }
-       
+
+       /* Handle remote ICE attributes if any. */
+       if (call->ice_session != NULL) {
+               linphone_core_update_ice_from_remote_media_description(call, sal_call_get_remote_media_description(op));
+       }
+#ifdef BUILD_UPNP
+       if (call->upnp_session != NULL) {
+               linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(op));
+       }
+#endif //BUILD_UPNP
+
        md=sal_call_get_final_media_description(op);
+       if (md)
+               call->params.has_video &= linphone_core_media_description_contains_video_stream(md);
        
        if (call->state==LinphoneCallOutgoingProgress ||
            call->state==LinphoneCallOutgoingRinging ||
            call->state==LinphoneCallOutgoingEarlyMedia){
                linphone_call_set_state(call,LinphoneCallConnected,"Connected");
+               if (call->referer) linphone_core_notify_refer_state(lc,call->referer,call);
        }
-       if (md && !sal_media_description_empty(md)){
+       if (md && !sal_media_description_empty(md) && !linphone_core_incompatible_security(lc,md)){
+               linphone_call_update_remote_session_id_and_ver(call);
                if (sal_media_description_has_dir(md,SalStreamSendOnly) ||
                    sal_media_description_has_dir(md,SalStreamInactive)){
                        if (lc->vtable.display_status){
@@ -319,6 +373,8 @@ static void call_accepted(SalOp *op){
                        }
                        linphone_core_update_streams (lc,call,md);
                        linphone_call_set_state(call,LinphoneCallPaused,"Call paused");
+                       if (call->refer_pending)
+                               linphone_core_start_refered_call(lc,call);
                }else if (sal_media_description_has_dir(md,SalStreamRecvOnly)){
                        /*we are put on hold when the call is initially accepted */
                        if (lc->vtable.display_status){
@@ -331,11 +387,7 @@ static void call_accepted(SalOp *op){
                        linphone_core_update_streams (lc,call,md);
                        linphone_call_set_state(call,LinphoneCallPausedByRemote,"Call paused by remote");
                }else{
-                       if (call->state==LinphoneCallStreamsRunning){
-                               /*media was running before, the remote as acceted a call modification (that is
-                                       a reinvite made by us. We must notify the application this reinvite was accepted*/
-                               linphone_call_set_state(call, LinphoneCallUpdated, "Call updated");
-                       }else{
+                       if (call->state!=LinphoneCallUpdating){
                                if (call->state==LinphoneCallResuming){
                                        if (lc->vtable.display_status){
                                                lc->vtable.display_status(lc,_("Call resumed."));
@@ -351,14 +403,14 @@ static void call_accepted(SalOp *op){
                                }
                        }
                        linphone_core_update_streams (lc,call,md);
-                       linphone_call_set_state(call, LinphoneCallStreamsRunning, "Streams running");
                        if (!call->current_params.in_conference)
                                lc->current_call=call;
+                       linphone_call_set_state(call, LinphoneCallStreamsRunning, "Streams running");
                }
        }else{
                /*send a bye*/
                ms_error("Incompatible SDP offer received in 200Ok, need to abort the call");
-               linphone_core_abort_call(lc,call,"No codec intersection");
+               linphone_core_abort_call(lc,call,_("Incompatible, check codecs or security settings..."));
        }
 }
 
@@ -372,11 +424,6 @@ static void call_ack(SalOp *op){
        if (call->media_pending){
                SalMediaDescription *md=sal_call_get_final_media_description(op);
                if (md && !sal_media_description_empty(md)){
-                       if (call->state==LinphoneCallStreamsRunning){
-                               /*media was running before, the remote as acceted a call modification (that is
-                                       a reinvite made by us. We must notify the application this reinvite was accepted*/
-                               linphone_call_set_state(call, LinphoneCallUpdated, "Call updated");
-                       }
                        linphone_core_update_streams (lc,call,md);
                        linphone_call_set_state (call,LinphoneCallStreamsRunning,"Connected (streams running)");
                }else{
@@ -388,49 +435,81 @@ 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){
+       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_call_set_transfer_state(call, LinphoneCallIdle);
+}
+
+static void call_paused_by_remote(LinphoneCore *lc, LinphoneCall *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");
+}
+
+static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call){
+       if(lc->vtable.display_status)
+               lc->vtable.display_status(lc,_("Call is updated by remote."));
+       call->defer_update=FALSE;
+       linphone_call_set_state(call, LinphoneCallUpdatedByRemote,"Call updated by remote");
+       if (call->defer_update==FALSE){
+               linphone_core_accept_call_update(lc,call,NULL);
+       }
+}
 
 /* this callback is called when an incoming re-INVITE modifies the session*/
 static void call_updating(SalOp *op){
        LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
        LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op);
-       LinphoneCallState prevstate=LinphoneCallIdle;
-       SalMediaDescription *md;
-       
-       md=sal_call_get_final_media_description(op);
-       
-       if (md && !sal_media_description_empty(md))
-       {
-               linphone_core_update_streams (lc,call,md);
-
-               if (sal_media_description_has_dir(call->localdesc,SalStreamSendRecv)){
-                       ms_message("Our local status is SalStreamSendRecv");
-                       if (sal_media_description_has_dir (md,SalStreamRecvOnly) || sal_media_description_has_dir(md,SalStreamInactive)){
-                               /* we are being paused */
-                               if(lc->vtable.display_status)
-                                       lc->vtable.display_status(lc,_("We are being paused..."));
-                               linphone_call_set_state (call,LinphoneCallPausedByRemote,"Call paused by remote");
-                       }else if (!sal_media_description_has_dir(call->resultdesc,SalStreamSendRecv) && sal_media_description_has_dir(md,SalStreamSendRecv)){
-                               if(lc->vtable.display_status)
-                                       lc->vtable.display_status(lc,_("We have been resumed..."));
-                               linphone_call_set_state (call,LinphoneCallStreamsRunning,"Connected (streams running)");
-                               if (!call->current_params.in_conference)
-                                       lc->current_call=call;
+       SalMediaDescription *rmd=sal_call_get_remote_media_description(op);
+
+       if (rmd==NULL){
+               /* case of a reINVITE without SDP */
+               call_accept_update(lc,call);
+               call->media_pending=TRUE;
+               return;
+       }
+
+       switch(call->state){
+               case LinphoneCallPausedByRemote:
+                       if (sal_media_description_has_dir(rmd,SalStreamSendRecv) || sal_media_description_has_dir(rmd,SalStreamRecvOnly)){
+                               call_resumed(lc,call);
+                       }else call_paused_by_remote(lc,call);
+               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{
-                               prevstate=call->state;
-                               if(lc->vtable.display_status)
-                                       lc->vtable.display_status(lc,_("Call has been updated by remote..."));
-                               linphone_call_set_state(call, LinphoneCallUpdatedByRemote,"Call updated by remote");
+                               call_updated_by_remote(lc,call);
                        }
-               }
-               /*accept the modification (sends a 200Ok)*/
-               sal_call_accept(op);
-<<<<<<< HEAD
-
-=======
->>>>>>> 949b3cd84135d234abab27a023885ad977f715c9
-               if (prevstate!=LinphoneCallIdle){
-                       linphone_call_set_state (call,prevstate,"Connected (streams running)");
-               }
+               break;
+               default:
+                       call_accept_update(lc,call);
        }
 }
 
@@ -440,9 +519,18 @@ static void call_terminated(SalOp *op, const char *from){
 
        if (call==NULL) return;
        
-       if (linphone_call_get_state(call)==LinphoneCallEnd || linphone_call_get_state(call)==LinphoneCallError){
-               ms_warning("call_terminated: ignoring.");
-               return;
+       switch(linphone_call_get_state(call)){
+               case LinphoneCallEnd:
+               case LinphoneCallError:
+                       ms_warning("call_terminated: ignoring.");
+                       return;
+               break;
+               case LinphoneCallIncomingReceived:
+               case LinphoneCallIncomingEarlyMedia:
+                       call->reason=LinphoneReasonNotAnswered;
+               break;
+               default:
+               break;
        }
        ms_message("Current call terminated...");
        //we stop the call only if we have this current call or if we are in call
@@ -456,6 +544,10 @@ static void call_terminated(SalOp *op, const char *from){
        if (lc->vtable.display_status!=NULL)
                lc->vtable.display_status(lc,_("Call terminated."));
 
+#ifdef BUILD_UPNP
+       linphone_call_delete_upnp_session(call);
+#endif //BUILD_UPNP
+
        linphone_call_set_state(call, LinphoneCallEnd,"Call ended");
 }
 
@@ -507,7 +599,6 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de
                                        lc->vtable.display_status(lc,msg480);
                        break;
                        case SalReasonNotFound:
-                               msg=_("Not found");
                                if (lc->vtable.display_status)
                                        lc->vtable.display_status(lc,msg);
                        break;
@@ -517,7 +608,24 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de
                                        lc->vtable.display_status(lc,msg600);
                        break;
                        case SalReasonMedia:
-                               msg=_("No common codecs");
+                       //media_encryption_mandatory
+                               if (call->params.media_encryption == LinphoneMediaEncryptionSRTP && 
+                                       !linphone_core_is_media_encryption_mandatory(lc)) {
+                                       int i;
+                                       ms_message("Outgoing call failed with SRTP (SAVP) enabled - retrying with AVP");
+                                       linphone_call_stop_media_streams(call);
+                                       if (call->state==LinphoneCallOutgoingInit || call->state==LinphoneCallOutgoingProgress){
+                                               /* 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_start_invite(lc, call);
+                                       }
+                                       return;
+                               }
+                               msg=_("Incompatible media parameters.");
                                if (lc->vtable.display_status)
                                        lc->vtable.display_status(lc,msg);
                        break;
@@ -532,10 +640,27 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de
                lc->ringstream=NULL;
        }
        linphone_call_stop_media_streams (call);
-       if (sr!=SalReasonDeclined) linphone_call_set_state(call,LinphoneCallError,msg);
-       else{
+       if (call->referer && linphone_call_get_state(call->referer)==LinphoneCallPaused && call->referer->was_automatically_paused){
+               /*resume to the call that send us the refer automatically*/
+               linphone_core_resume_call(lc,call->referer);
+       }
+
+#ifdef BUILD_UPNP
+       linphone_call_delete_upnp_session(call);
+#endif //BUILD_UPNP
+
+       if (sr == SalReasonDeclined) {
                call->reason=LinphoneReasonDeclined;
                linphone_call_set_state(call,LinphoneCallEnd,"Call declined.");
+       } else if (sr == SalReasonNotFound) {
+               call->reason=LinphoneReasonNotFound;
+               linphone_call_set_state(call,LinphoneCallError,"User not found.");
+       } else if (sr == SalReasonBusy) {
+               call->reason=LinphoneReasonBusy;
+               linphone_call_set_state(call,LinphoneCallError,"User is busy.");
+               linphone_core_play_named_tone(lc,LinphoneToneBusy);
+       } else {
+               linphone_call_set_state(call,LinphoneCallError,msg);
        }
 }
 
@@ -549,6 +674,16 @@ static void call_released(SalOp *op){
 static void auth_requested(SalOp *h, const char *realm, const char *username){
        LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
        LinphoneAuthInfo *ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,realm,username);
+       LinphoneCall *call=is_a_linphone_call(sal_op_get_user_pointer(h));
+
+       if (call && call->ping_op==h){
+               /*don't request authentication for ping requests. Their purpose is just to get any
+                * answer to get the Via's received and rport parameters.
+                */
+               ms_message("auth_requested(): ignored for ping request.");
+               return;
+       }
+       
        ms_message("auth_requested() for realm=%s, username=%s",realm,username);
 
        if (ai && ai->works==FALSE && ai->usecount>=3){
@@ -592,12 +727,15 @@ static void register_success(SalOp *op, bool_t registered){
        LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)sal_op_get_user_pointer(op);
        char *msg;
        
-       cfg->registered=registered;
+       if (cfg->deletion_date!=0){
+               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 sucessful" : "Unregistration done");
        if (lc->vtable.display_status){
-               if (cfg->registered) msg=ms_strdup_printf(_("Registration on %s successful."),sal_op_get_proxy(op));
+               if (registered) msg=ms_strdup_printf(_("Registration on %s successful."),sal_op_get_proxy(op));
                else msg=ms_strdup_printf(_("Unregistration on %s done."),sal_op_get_proxy(op));
                lc->vtable.display_status(lc,msg);
                ms_free(msg);
@@ -613,6 +751,10 @@ static void register_failure(SalOp *op, SalError error, SalReason reason, const
                ms_warning("Registration failed for unknown proxy config.");
                return ;
        }
+       if (cfg->deletion_date!=0){
+               ms_message("Registration failed for removed proxy config, ignored");
+               return;
+       }
        if (details==NULL)
                details=_("no response timeout");
        
@@ -672,19 +814,44 @@ static void refer_received(Sal *sal, SalOp *op, const char *referto){
                }
                if (call->state!=LinphoneCallPaused){
                        ms_message("Automatically pausing current call to accept transfer.");
-                       linphone_core_pause_call(lc,call);
-               }
-               linphone_core_start_refered_call(lc,call);
-               sal_call_accept_refer(op);
+                       _linphone_core_pause_call(lc,call);
+                       call->was_automatically_paused=TRUE;
+                       /*then we will start the refered when the pause is accepted, in order to serialize transactions within the dialog.
+                        * Indeed we need to avoid to send a NOTIFY to inform about of state of the refered call while the pause isn't completed.
+                       **/
+               }else linphone_core_start_refered_call(lc,call);
        }else if (lc->vtable.refer_received){
                lc->vtable.refer_received(lc,referto);
-               sal_call_accept_refer(op);
        }
 }
 
-static void text_received(Sal *sal, const char *from, const char *msg){
-       LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal);
-       linphone_core_text_received(lc,from,msg);
+static bool_t is_duplicate_msg(LinphoneCore *lc, const char *msg_id){
+       MSList *elem=lc->last_recv_msg_ids;
+       MSList *tail=NULL;
+       int i;
+       bool_t is_duplicate=FALSE;
+       for(i=0;elem!=NULL;elem=elem->next,i++){
+               if (strcmp((const char*)elem->data,msg_id)==0){
+                       is_duplicate=TRUE;
+               }
+               tail=elem;
+       }
+       if (!is_duplicate){
+               lc->last_recv_msg_ids=ms_list_prepend(lc->last_recv_msg_ids,ms_strdup(msg_id));
+       }
+       if (i>=10){
+               ms_free(tail->data);
+               lc->last_recv_msg_ids=ms_list_remove_link(lc->last_recv_msg_ids,tail);
+       }
+       return is_duplicate;
+}
+
+
+static void text_received(SalOp *op, const SalMessage *msg){
+       LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
+       if (is_duplicate_msg(lc,msg->message_id)==FALSE){
+               linphone_core_message_received(lc,op,msg);
+       }
 }
 
 static void notify(SalOp *op, const char *from, const char *msg){
@@ -695,7 +862,7 @@ static void notify(SalOp *op, const char *from, const char *msg){
                lc->vtable.notify_recv(lc,call,from,msg);
 }
 
-static void notify_presence(SalOp *op, SalSubscribeState ss, SalPresenceStatus status, const char *msg){
+static void notify_presence(SalOp *op, SalSubscribeStatus ss, SalPresenceStatus status, const char *msg){
        LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
        linphone_notify_recv(lc,op,ss,status);
 }
@@ -715,7 +882,8 @@ static void ping_reply(SalOp *op){
        ms_message("ping reply !");
        if (call){
                if (call->state==LinphoneCallOutgoingInit){
-                       linphone_core_start_invite(call->core,call,NULL);
+                       call->ping_replied=TRUE;
+                       linphone_core_proceed_with_invite_if_ready(call->core,call,NULL);
                }
        }
        else
@@ -724,6 +892,68 @@ static void ping_reply(SalOp *op){
        }
 }
 
+static void notify_refer(SalOp *op, SalReferStatus status){
+       LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op));
+       LinphoneCall *call=(LinphoneCall*) sal_op_get_user_pointer(op);
+       LinphoneCallState cstate;
+       if (call==NULL) {
+               ms_warning("Receiving notify_refer for unknown call.");
+               return ;
+       }
+       switch(status){
+               case SalReferTrying:
+                       cstate=LinphoneCallOutgoingProgress;
+               break;
+               case SalReferSuccess:
+                       cstate=LinphoneCallConnected;
+               break;
+               case SalReferFailed:
+                       cstate=LinphoneCallError;
+               break;
+               default:
+                       cstate=LinphoneCallError;
+       }
+       linphone_call_set_transfer_state(call, cstate);
+       if (cstate==LinphoneCallConnected){
+               /*automatically terminate the call as the transfer is complete.*/
+               linphone_core_terminate_call(lc,call);
+       }
+}
+
+static LinphoneChatMessageState chatStatusSal2Linphone(SalTextDeliveryStatus status){
+       switch(status){
+               case SalTextDeliveryInProgress:
+                       return LinphoneChatMessageStateInProgress;
+               case SalTextDeliveryDone:
+                       return LinphoneChatMessageStateDelivered;
+               case SalTextDeliveryFailed:
+                       return LinphoneChatMessageStateNotDelivered;
+       }
+       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){
+       LinphoneChatMessage *chat_msg=(LinphoneChatMessage* )sal_op_get_user_pointer(op);
+       const MSList* calls = linphone_core_get_calls(chat_msg->chat_room->lc);
+       
+       chat_msg->state=chatStatusSal2Linphone(status);
+       linphone_chat_message_store_state(chat_msg);
+       if (chat_msg && chat_msg->cb) {
+               chat_msg->cb(chat_msg
+                       ,chat_msg->state
+                       ,chat_msg->cb_ud);
+       }
+       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);
+       }
+}
+
 SalCallbacks linphone_sal_callbacks={
        call_received,
        call_ringing,
@@ -741,11 +971,13 @@ SalCallbacks linphone_sal_callbacks={
        dtmf_received,
        refer_received,
        text_received,
+       text_delivery_update,
        notify,
        notify_presence,
+       notify_refer,
        subscribe_received,
        subscribe_closed,
-       ping_reply
+       ping_reply,
 };