]> sjero.net Git - linphone/blobdiff - coreapi/callbacks.c
fix crash when receiving an incorrect SDP message in a 200Ok.
[linphone] / coreapi / callbacks.c
index 63327de66dad777ab968f49f98c3db9f54bc13cc..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){
@@ -181,7 +254,6 @@ 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);
        
        /* the call is acceptable so we can now add it to our list */
        linphone_core_add_call(lc,call);
@@ -192,6 +264,13 @@ static void call_received(SalOp *h){
                ms_message("Defer ringing to gather ICE candidates");
                return;
        }
+#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
 
        linphone_core_notify_incoming_call(lc,call);
 }
@@ -261,13 +340,19 @@ static void call_accepted(SalOp *op){
                return ;
        }
 
-       if (call->ice_session == NULL) {
-               /* Ensure the ICE check list pointers for the call streams are resetted to prevent crashes */
-               if (call->audiostream != NULL) call->audiostream->ice_check_list = NULL;
-               if (call->videostream != NULL) call->videostream->ice_check_list = NULL;
+       /* 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 ||
@@ -275,7 +360,8 @@ static void call_accepted(SalOp *op){
                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){
@@ -301,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."));
@@ -328,7 +410,7 @@ static void call_accepted(SalOp *op){
        }else{
                /*send a bye*/
                ms_error("Incompatible SDP offer received in 200Ok, need to abort the call");
-               linphone_core_abort_call(lc,call,_("Incompatible, check codecs..."));
+               linphone_core_abort_call(lc,call,_("Incompatible, check codecs or security settings..."));
        }
 }
 
@@ -342,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{
@@ -360,6 +437,18 @@ 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))
@@ -455,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");
 }
 
@@ -524,11 +617,11 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de
                                        if (call->state==LinphoneCallOutgoingInit || call->state==LinphoneCallOutgoingProgress){
                                                /* clear SRTP local params */
                                                call->params.media_encryption = LinphoneMediaEncryptionNone;
-                                               for(i=0; i<call->localdesc->nstreams; i++) {
+                                               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, NULL);
+                                               linphone_core_start_invite(lc, call);
                                        }
                                        return;
                                }
@@ -551,12 +644,21 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de
                /*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);
        }
@@ -712,7 +814,7 @@ 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_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.
@@ -723,9 +825,33 @@ static void refer_received(Sal *sal, SalOp *op, const char *referto){
        }
 }
 
-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){
@@ -756,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
@@ -793,6 +920,40 @@ static void notify_refer(SalOp *op, SalReferStatus status){
        }
 }
 
+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,
@@ -810,12 +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,
 };