From e07a927d7e45d6ab50184e670692d1540c5cfc55 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Wed, 20 Oct 2010 17:48:14 +0200 Subject: [PATCH] implemented attended transfer (untested yet) --- console/commands.c | 32 +++++++--- coreapi/callbacks.c | 4 +- coreapi/linphonecall.c | 3 + coreapi/linphonecore.c | 45 +++++++++++--- coreapi/linphonecore.h | 2 + coreapi/private.h | 1 + coreapi/sal.h | 7 ++- coreapi/sal_eXosip2.c | 137 ++++++++++++++++++++++++++++++++--------- coreapi/sal_eXosip2.h | 1 + mediastreamer2 | 2 +- oRTP | 2 +- 11 files changed, 184 insertions(+), 52 deletions(-) diff --git a/console/commands.c b/console/commands.c index d22d4b81..a7aa22f7 100644 --- a/console/commands.c +++ b/console/commands.c @@ -170,6 +170,12 @@ static LPC_COMMAND commands[] = { { "resume", lpc_cmd_resume, "resume a call", "'resume' : resume the unique call\n" "'resume ' : hold off the call with given id\n"}, + { "transfer", lpc_cmd_transfer, + "Transfer a call to a specified destination.", + "'transfer ' : transfers the current active call to the destination sip-uri\n" + "'transfer ': transfers the call with 'id' to the destination sip-uri\n" + "'transfer --to-call ': transfers the call with 'id1' to the destination of call 'id2' (attended transfer)\n" + }, { "mute", lpc_cmd_mute_mic, "Mute microphone and suspend voice transmission."}, #ifdef VIDEO_ENABLED @@ -209,11 +215,6 @@ static LPC_COMMAND commands[] = { "'ipv6 enable' : enable the use of the ipv6 network.\n" "'ipv6 disable' : do not use ipv6 network." }, - { "transfer", lpc_cmd_transfer, - "Transfer a call to a specified destination.", - "'transfer ' : transfers the current active call to the destination sip-uri" - "'transfer ': transfers the call with 'id' to the destination sip-uri" - }, { "nat", lpc_cmd_nat, "Set nat address", "'nat' : show nat settings.\n" "'nat ' : set nat address.\n" @@ -637,10 +638,12 @@ lpc_cmd_transfer(LinphoneCore *lc, char *args) { if (args){ LinphoneCall *call; + LinphoneCall *call2; const char *refer_to=NULL; char arg1[256]={0}; char arg2[266]={0}; - int n=sscanf(args,"%s %s",arg1,arg2); + long id2=0; + int n=sscanf(args,"%s %s %li",arg1,arg2,&id2); if (n==1 || isalpha(*arg1)){ call=linphone_core_get_current_call(lc); if (call==NULL && ms_list_size(linphone_core_get_calls(lc))==1){ @@ -651,13 +654,24 @@ lpc_cmd_transfer(LinphoneCore *lc, char *args) linphonec_out("No active call, please specify a call id among the ones listed by 'calls' command.\n"); return 0; } - }else{ + linphone_core_transfer_call(lc, call, refer_to); + }else if (n==2){ long id=atoi(arg1); refer_to=args+strlen(arg1)+1; call=linphonec_get_call(id); if (call==NULL) return 0; - } - linphone_core_transfer_call(lc, call, refer_to); + linphone_core_transfer_call(lc, call, refer_to); + }else if (n==3){ + long id=atoi(arg1); + call=linphonec_get_call(id); + call2=linphonec_get_call(id2); + if (call==NULL || call2==NULL) return 0; + if (strcmp(arg2,"--to-call")!=0){ + return 0; + } + linphonec_out("Performing attended transfer of call %i to call %i",id,id2); + linphone_core_transfer_call_to_another (lc,call,call2); + }else return 0; }else{ linphonec_out("Transfer command requires at least one argument\n"); return 0; diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index add616d8..9f7cd265 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -557,10 +557,10 @@ static void refer_received(Sal *sal, SalOp *op, const char *referto){ ms_free(msg); } if (lc->current_call==NULL) linphone_core_start_pending_refered_calls (lc); - sal_refer_accept(op); + sal_call_accept_refer(op); }else if (lc->vtable.refer_received){ lc->vtable.refer_received(lc,referto); - sal_refer_accept(op); + sal_call_accept_refer(op); } } diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 8139bbdb..1cdc41ff 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -161,6 +161,9 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr if (linphone_core_get_firewall_policy(call->core)==LinphonePolicyUseStun) linphone_core_run_stun_tests(call->core,call); discover_mtu(lc,linphone_address_get_domain (to)); + if (params->referer){ + sal_call_set_referer (call->op,params->referer->op); + } return call; } diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index b9e7fe51..9abea19a 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -1844,9 +1844,12 @@ void linphone_core_start_pending_refered_calls(LinphoneCore *lc){ for(elem=lc->calls;elem!=NULL;elem=elem->next){ LinphoneCall *call=(LinphoneCall*)elem->data; if (call->refer_pending){ + LinphoneCallParams *cp=linphone_core_create_default_call_parameters(lc); + cp->referer=call; ms_message("Starting new call to refered address %s",call->refer_to); call->refer_pending=FALSE; - linphone_core_invite(lc,call->refer_to); + linphone_core_invite_with_params(lc,call->refer_to,cp); + linphone_call_params_destroy(cp); break; } } @@ -2143,12 +2146,28 @@ int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char } //lc->call=NULL; //Do not do that you will lose the call afterward . . . real_url=linphone_address_as_string (real_parsed_url); - sal_refer(call->op,real_url); + sal_call_refer(call->op,real_url); ms_free(real_url); linphone_address_destroy(real_parsed_url); return 0; } +/** + * Transfer a call to destination of another running call. This is used for "attended transfer" scenarios. + * @param lc linphone core object + * @param call a running call you want to transfer + * @param dest a running call whose remote person will receive the transfer + * + * The transfered call is supposed to be in paused state, so that it is able to accept the transfer immediately. + * The destination call is a call previously established to introduce the transfered person. + * This method will send a transfer request to the transfered person. The phone of the transfered is then + * expected to automatically call to the destination of the transfer. The receiver of the transfer will then automatically + * close the call with us (the 'dest' call). +**/ +int linphone_core_transfer_call_to_another(LinphoneCore *lc, LinphoneCall *call, LinphoneCall *dest){ + return sal_call_refer_with_replaces (call->op,dest->op); +} + bool_t linphone_core_inc_invite_pending(LinphoneCore*lc){ LinphoneCall *call = linphone_core_get_current_call(lc); if(call != NULL) @@ -2198,6 +2217,7 @@ int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call) { LinphoneProxyConfig *cfg=NULL; const char *contact=NULL; + SalOp *replaced; if (call==NULL){ //if just one call is present answer the only one ... @@ -2207,16 +2227,27 @@ int linphone_core_accept_call(LinphoneCore *lc, LinphoneCall *call) call = (LinphoneCall*)linphone_core_get_calls(lc)->data; } - if (lc->current_call!=NULL && lc->current_call!=call){ - ms_warning("Cannot accept this call, there is already one running."); - return -1; - } - if (call->state==LinphoneCallConnected){ /*call already accepted*/ return -1; } + + /* check if this call is supposed to replace an already running one*/ + replaced=sal_call_get_replaces(call->op); + if (replaced){ + LinphoneCall *rc=(LinphoneCall*)sal_op_get_user_pointer (replaced); + if (rc){ + ms_message("Call %p replaces call %p. This last one is going to be terminated automatically.", + call,rc); + linphone_core_terminate_call (lc,rc); + } + } + if (lc->current_call!=NULL && lc->current_call!=call){ + ms_warning("Cannot accept this call, there is already one running."); + return -1; + } + /*can accept a new call only if others are on hold */ { MSList *elem; diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index 1a75f5ba..313907ad 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -591,6 +591,8 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const int linphone_core_transfer_call(LinphoneCore *lc, LinphoneCall *call, const char *refer_to); +int linphone_core_transfer_call_to_another(LinphoneCore *lc, LinphoneCall *call, LinphoneCall *dest); + bool_t linphone_core_inc_invite_pending(LinphoneCore*lc); bool_t linphone_core_in_call(const LinphoneCore *lc); diff --git a/coreapi/private.h b/coreapi/private.h index 9bee983b..b41c772d 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -57,6 +57,7 @@ struct _LinphoneCallParams{ + LinphoneCall *referer; /*in case this call creation is consecutive to an incoming transfer, this points to the original call */ bool_t has_video; bool_t pad[3]; }; diff --git a/coreapi/sal.h b/coreapi/sal.h index 0fcd6e5a..b0b3c757 100644 --- a/coreapi/sal.h +++ b/coreapi/sal.h @@ -282,8 +282,11 @@ int sal_call_decline(SalOp *h, SalReason reason, const char *redirection /*optio int sal_call_hold(SalOp *h, bool_t holdon); int sal_call_update(SalOp *h); SalMediaDescription * sal_call_get_final_media_description(SalOp *h); -int sal_refer(SalOp *h, const char *refer_to); -int sal_refer_accept(SalOp *h); +int sal_call_refer(SalOp *h, const char *refer_to); +int sal_call_refer_with_replaces(SalOp *h, SalOp *other_call_h); +int sal_call_accept_refer(SalOp *h); +/*informs this call is consecutive to an incoming refer */ +int sal_call_set_referer(SalOp *h, SalOp *refered_call); /* returns the SalOp of a call that should be replaced by h, if any */ SalOp *sal_call_get_replaces(SalOp *h); int sal_call_send_dtmf(SalOp *h, char dtmf); diff --git a/coreapi/sal_eXosip2.c b/coreapi/sal_eXosip2.c index c61ec96a..dfa4744c 100644 --- a/coreapi/sal_eXosip2.c +++ b/coreapi/sal_eXosip2.c @@ -162,6 +162,7 @@ SalOp * sal_op_new(Sal *sal){ op->reinvite=FALSE; op->call_id=NULL; op->replaces=NULL; + op->referred_by=NULL; op->masquerade_via=FALSE; op->auto_answer_asked=FALSE; return op; @@ -205,6 +206,9 @@ void sal_op_release(SalOp *op){ if (op->replaces){ ms_free(op->replaces); } + if (op->referred_by){ + ms_free(op->referred_by); + } __sal_op_free(op); } @@ -494,6 +498,12 @@ int sal_call(SalOp *h, const char *from, const char *to){ h->sdp_offering=TRUE; set_sdp_from_desc(invite,h->base.local_media); }else h->sdp_offering=FALSE; + if (h->replaces){ + osip_message_set_header(invite,"Replaces",h->replaces); + if (h->referred_by) + osip_message_set_header(invite,"Referred-By",h->referred_by); + } + eXosip_lock(); err=eXosip_call_send_initial_invite(invite); eXosip_unlock(); @@ -610,6 +620,14 @@ SalMediaDescription * sal_call_get_final_media_description(SalOp *h){ return h->result; } +int sal_call_set_referer(SalOp *h, SalOp *refered_call){ + if (refered_call->replaces) + h->replaces=ms_strdup(refered_call->replaces); + if (refered_call->referred_by) + h->referred_by=ms_strdup(refered_call->referred_by); + return 0; +} + int sal_ping(SalOp *op, const char *from, const char *to){ osip_message_t *options=NULL; @@ -628,7 +646,7 @@ int sal_ping(SalOp *op, const char *from, const char *to){ return -1; } -int sal_refer_accept(SalOp *op){ +int sal_call_accept_refer(SalOp *op){ osip_message_t *msg=NULL; int err=0; eXosip_lock(); @@ -648,7 +666,7 @@ int sal_refer_accept(SalOp *op){ return err; } -int sal_refer(SalOp *h, const char *refer_to){ +int sal_call_refer(SalOp *h, const char *refer_to){ osip_message_t *msg=NULL; int err=0; eXosip_lock(); @@ -659,6 +677,24 @@ int sal_refer(SalOp *h, const char *refer_to){ return err; } +int sal_call_refer_with_replaces(SalOp *h, SalOp *other_call_h){ + osip_message_t *msg=NULL; + char referto[256]={0}; + int err=0; + eXosip_lock(); + if (eXosip_call_get_referto(other_call_h->did,referto,sizeof(referto)-1)!=0){ + ms_error("eXosip_call_get_referto() failed for did=%i",other_call_h->did); + eXosip_unlock(); + return -1; + } + eXosip_call_build_refer(h->did,referto, &msg); + osip_message_set_header(msg,"Referred-By",h->base.from); + if (msg) err=eXosip_call_send_request(h->did, msg); + else err=-1; + eXosip_unlock(); + return err; +} + SalOp *sal_call_get_replaces(SalOp *h){ if (h->replaces!=NULL){ int cid; @@ -1240,6 +1276,59 @@ static void process_dtmf_relay(Sal *sal, eXosip_event_t *ev){ } } +static void fill_options_answer(osip_message_t *options){ + osip_message_set_allow(options,"INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, SUBSCRIBE, NOTIFY, INFO"); + osip_message_set_accept(options,"application/sdp"); +} + +static void process_refer(Sal *sal, SalOp *op, eXosip_event_t *ev){ + osip_header_t *h=NULL; + osip_message_t *ans=NULL; + ms_message("Receiving REFER request !"); + osip_message_header_get_byname(ev->request,"Refer-To",0,&h); + + if (h){ + osip_from_t *from=NULL; + char *tmp; + osip_from_init(&from); + + if (osip_from_parse(from,h->hvalue)==0){ + if (op ){ + osip_uri_header_t *uh=NULL; + osip_header_t *referred_by=NULL; + osip_uri_header_get_byname(&from->url->url_headers,(char*)"Replaces",&uh); + if (uh!=NULL && uh->gvalue && uh->gvalue[0]!='\0'){ + ms_message("Found replaces in Refer-To"); + if (op->replaces){ + ms_free(op->replaces); + } + op->replaces=ms_strdup(uh->gvalue); + } + osip_message_header_get_byname(ev->request,"Referred-By",0,&referred_by); + if (referred_by && referred_by->hvalue && referred_by->hvalue[0]!='\0'){ + if (op->referred_by) + ms_free(op->referred_by); + op->referred_by=ms_strdup(referred_by->hvalue); + } + } + osip_uri_header_freelist(&from->url->url_headers); + osip_from_to_str(from,&tmp); + sal->callbacks.refer_received(sal,op,tmp); + osip_free(tmp); + osip_from_free(from); + } + eXosip_lock(); + eXosip_call_build_answer(ev->tid,202,&ans); + if (ans) + eXosip_call_send_answer(ev->tid,202,ans); + eXosip_unlock(); + } + else + { + ms_warning("cannot do anything with the refer without destination\n"); + } +} + static void call_message_new(Sal *sal, eXosip_event_t *ev){ osip_message_t *ans=NULL; if (ev->request){ @@ -1268,8 +1357,7 @@ static void call_message_new(Sal *sal, eXosip_event_t *ev){ eXosip_call_send_answer(ev->tid,200,ans); eXosip_unlock(); } - } - if(MSG_IS_MESSAGE(ev->request)){ + }else if(MSG_IS_MESSAGE(ev->request)){ /* SIP messages could be received into call */ text_received(sal, ev); eXosip_lock(); @@ -1277,27 +1365,12 @@ static void call_message_new(Sal *sal, eXosip_event_t *ev){ if (ans) eXosip_call_send_answer(ev->tid,200,ans); eXosip_unlock(); - } - if(MSG_IS_REFER(ev->request)){ - osip_header_t *h=NULL; + }else if(MSG_IS_REFER(ev->request)){ SalOp *op=find_op(sal,ev); ms_message("Receiving REFER request !"); - osip_message_header_get_byname(ev->request,"Refer-To",0,&h); - eXosip_lock(); - eXosip_call_build_answer(ev->tid,202,&ans); - if (ans) - eXosip_call_send_answer(ev->tid,202,ans); - eXosip_unlock(); - if (h){ - sal->callbacks.refer_received(sal,op,h->hvalue); - } - else - { - ms_warning("cannot do anything with the refer without destination\n"); - } - } - if(MSG_IS_NOTIFY(ev->request)){ + process_refer(sal,op,ev); + }else if(MSG_IS_NOTIFY(ev->request)){ osip_header_t *h=NULL; char *from=NULL; SalOp *op=find_op(sal,ev); @@ -1314,6 +1387,14 @@ static void call_message_new(Sal *sal, eXosip_event_t *ev){ eXosip_call_send_answer(ev->tid,200,ans); eXosip_unlock(); osip_free(from); + }else if (MSG_IS_OPTIONS(ev->request)){ + eXosip_lock(); + eXosip_call_build_answer(ev->tid,200,&ans); + if (ans){ + fill_options_answer(ans); + eXosip_call_send_answer(ev->tid,200,ans); + } + eXosip_unlock(); } }else ms_warning("call_message_new: No request ?"); } @@ -1362,6 +1443,8 @@ static void text_received(Sal *sal, eXosip_event_t *ev){ osip_free(from); } + + static void other_request(Sal *sal, eXosip_event_t *ev){ ms_message("in other_request"); if (ev->request==NULL) return; @@ -1371,8 +1454,7 @@ static void other_request(Sal *sal, eXosip_event_t *ev){ }else if (strcmp(ev->request->sip_method,"OPTIONS")==0){ osip_message_t *options=NULL; eXosip_options_build_answer(ev->tid,200,&options); - osip_message_set_allow(options,"INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, SUBSCRIBE, NOTIFY, INFO"); - osip_message_set_accept(options,"application/sdp"); + fill_options_answer(options); eXosip_options_send_answer(ev->tid,200,options); }else if (strcmp(ev->request->sip_method,"WAKEUP")==0 && comes_from_local_if(ev->request)) { @@ -1382,12 +1464,7 @@ static void other_request(Sal *sal, eXosip_event_t *ev){ }else if (strncmp(ev->request->sip_method, "REFER", 5) == 0){ ms_message("Receiving REFER request !"); if (comes_from_local_if(ev->request)) { - osip_header_t *h=NULL; - osip_message_header_get_byname(ev->request,"Refer-To",0,&h); - eXosip_message_send_answer(ev->tid,200,NULL); - if (h){ - sal->callbacks.refer_received(sal,NULL,h->hvalue); - } + process_refer(sal,NULL,ev); }else ms_warning("Ignored REFER not coming from this local loopback interface."); }else if (strncmp(ev->request->sip_method, "UPDATE", 6) == 0){ inc_update(sal,ev); diff --git a/coreapi/sal_eXosip2.h b/coreapi/sal_eXosip2.h index bfb3d10f..10c8b46b 100644 --- a/coreapi/sal_eXosip2.h +++ b/coreapi/sal_eXosip2.h @@ -57,6 +57,7 @@ struct SalOp{ osip_call_id_t *call_id; /*used for out of calls transaction in order to retrieve the operation when receiving a response*/ char *replaces; + char *referred_by; bool_t supports_session_timers; bool_t sdp_offering; bool_t reinvite; diff --git a/mediastreamer2 b/mediastreamer2 index af119e5c..e3a50ec4 160000 --- a/mediastreamer2 +++ b/mediastreamer2 @@ -1 +1 @@ -Subproject commit af119e5c828e81a822d63af8ffb860764b8f20b4 +Subproject commit e3a50ec460494101e925fa112e97adbc82c45306 diff --git a/oRTP b/oRTP index 461dd13a..930fac6f 160000 --- a/oRTP +++ b/oRTP @@ -1 +1 @@ -Subproject commit 461dd13a0aad2a075a075bf618e68443475f7a24 +Subproject commit 930fac6f59b13cdd6cbb5f370911a65f98bad7de -- 2.39.2