lc->vtable.show(lc);
}
+static void ping_reply(SalOp *op){
+ LinphoneCall *call=(LinphoneCall*) sal_op_get_user_pointer(op);
+ ms_message("ping reply !");
+ if (call){
+ if (call->state==LCStatePreEstablishing){
+ linphone_core_start_invite(call->core,call,NULL);
+ }
+ }
+}
+
SalCallbacks linphone_sal_callbacks={
call_received,
call_ringing,
notify,
subscribe_received,
subscribe_closed,
- internal_message
+ internal_message,
+ ping_reply
};
}
-static MSList *make_codec_list(const MSList *codecs){
+static MSList *make_codec_list(const MSList *codecs, bool_t only_one_codec){
MSList *l=NULL;
const MSList *it;
for(it=codecs;it!=NULL;it=it->next){
PayloadType *pt=(PayloadType*)it->data;
if (pt->flags & PAYLOAD_TYPE_ENABLED){
l=ms_list_append(l,payload_type_clone(pt));
+ if (only_one_codec) break;
}
}
return l;
}
static SalMediaDescription *create_local_media_description(LinphoneCore *lc,
- const char *localip, const char *username){
+ const char *localip, const char *username, bool_t only_one_codec){
MSList *l;
PayloadType *pt;
SalMediaDescription *md=sal_media_description_new();
md->streams[0].port=linphone_core_get_audio_port(lc);
md->streams[0].proto=SalProtoRtpAvp;
md->streams[0].type=SalAudio;
- l=make_codec_list(lc->codecs_conf.audio_codecs);
+ l=make_codec_list(lc->codecs_conf.audio_codecs,only_one_codec);
pt=payload_type_clone(rtp_profile_get_payload_from_mime(&av_profile,"telephone-event"));
l=ms_list_append(l,pt);
md->streams[0].payloads=l;
md->streams[1].port=linphone_core_get_video_port(lc);
md->streams[1].proto=SalProtoRtpAvp;
md->streams[1].type=SalVideo;
- l=make_codec_list(lc->codecs_conf.video_codecs);
+ l=make_codec_list(lc->codecs_conf.video_codecs,only_one_codec);
md->streams[1].payloads=l;
if (lc->dw_video_bw)
md->streams[1].bandwidth=lc->dw_video_bw;
call->core=lc;
linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip);
call->localdesc=create_local_media_description (lc,call->localip,
- linphone_address_get_username(from));
+ linphone_address_get_username(from),FALSE);
linphone_call_init_common(call,from,to);
discover_mtu(lc,linphone_address_get_domain (to));
return call;
LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){
LinphoneCall *call=ms_new0(LinphoneCall,1);
LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc);
+ char *to_str;
+ char *from_str;
call->dir=LinphoneCallIncoming;
sal_op_set_user_pointer(op,call);
call->op=op;
call->core=lc;
+
+ if (lc->sip_conf.ping_with_options){
+ /*the following sends an option request back to the caller so that
+ we get a chance to discover our nat'd address before answering.*/
+ call->ping_op=sal_op_new(lc->sal);
+ to_str=linphone_address_as_string(to);
+ from_str=linphone_address_as_string(from);
+ sal_op_set_route(call->ping_op,sal_op_get_network_origin(call->op));
+ sal_ping(call->ping_op,to_str,from_str);
+ ms_free(to_str);
+ ms_free(from_str);
+ }
linphone_address_clean(from);
linphone_core_get_local_ip(lc,linphone_address_get_domain(from),call->localip);
call->localdesc=create_local_media_description (lc,call->localip,
- linphone_address_get_username(me));
+ linphone_address_get_username(me),lc->sip_conf.only_one_codec);
linphone_call_init_common(call, from, to);
discover_mtu(lc,linphone_address_get_domain(from));
linphone_address_destroy(me);
linphone_core_notify_all_friends(obj->core,obj->core->prev_mode);
linphone_call_log_completed(obj->log,obj);
linphone_core_update_allocated_audio_bandwidth(obj->core);
- if (obj->op!=NULL) sal_op_release(obj->op);
- if (obj->resultdesc!=NULL) sal_media_description_unref(obj->resultdesc);
- if (obj->localdesc!=NULL) sal_media_description_unref(obj->localdesc);
+ if (obj->op!=NULL) {
+ sal_op_release(obj->op);
+ obj->op=NULL;
+ }
+ if (obj->resultdesc!=NULL) {
+ sal_media_description_unref(obj->resultdesc);
+ obj->resultdesc=NULL;
+ }
+ if (obj->localdesc!=NULL) {
+ sal_media_description_unref(obj->localdesc);
+ obj->localdesc=NULL;
+ }
+ if (obj->ping_op) {
+ sal_op_release(obj->ping_op);
+ }
ms_free(obj);
}
calllog->duration=time(NULL)-call->start_time;
switch(call->state){
case LCStateInit:
+ case LCStatePreEstablishing:
calllog->status=LinphoneCallAborted;
break;
case LCStateRinging:
}
}
- /*for test*/
+ /*for tuning or test*/
lc->sip_conf.sdp_200_ack=lp_config_get_int(lc->config,"sip","sdp_200_ack",0);
lc->sip_conf.only_one_codec=lp_config_get_int(lc->config,"sip","only_one_codec",0);
lc->sip_conf.register_only_when_network_is_up=
lp_config_get_int(lc->config,"sip","register_only_when_network_is_up",1);
+ lc->sip_conf.ping_with_options=lp_config_get_int(lc->config,"sip","ping_with_options",1);
}
static void rtp_config_read(LinphoneCore *lc)
if (lc->call!=NULL){
LinphoneCall *call=lc->call;
-
+ if (call->state==LCStatePreEstablishing && (curtime-call->start_time>=2)){
+ /*start the call even if the OPTIONS reply did not arrive*/
+ linphone_core_start_invite(lc,call,NULL);
+ }
if (call->dir==LinphoneCallIncoming && call->state==LCStateRinging){
elapsed=curtime-call->start_time;
ms_message("incoming call ringing for %i seconds",elapsed);
LinphoneAddress *ctt;
const char *localip=call->localip;
+ /* first use user's supplied ip address if asked*/
if (linphone_core_get_firewall_policy(lc)==LINPHONE_POLICY_USE_NAT_ADDRESS){
ctt=linphone_core_get_primary_contact_parsed(lc);
return ms_strdup_printf("sip:%s@%s",linphone_address_get_username(ctt),
linphone_core_get_nat_address(lc));
}
-
+
+ /* if already choosed, don't change it */
if (call->op && sal_op_get_contact(call->op)!=NULL){
return NULL;
}
-
+ /*if using a proxy, use the contact address as guessed with the REGISTERs*/
if (dest_proxy && dest_proxy->op){
const char *fixed_contact=sal_op_get_contact(dest_proxy->op);
if (fixed_contact) {
return ms_strdup(fixed_contact);
}
}
+ /* if the ping OPTIONS request succeeded use the contact guessed from the
+ received, rport*/
+ if (call->ping_op){
+ const char *guessed=sal_op_get_contact(call->ping_op);
+ if (guessed){
+ ms_message("Contact has been fixed using OPTIONS to %s",guessed);
+ return ms_strdup(guessed);
+ }
+ }
+
ctt=linphone_core_get_primary_contact_parsed(lc);
if (ctt!=NULL){
return NULL;
}
+int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy){
+ int err;
+ char *contact;
+ char *real_url,*barmsg;
+ char *from;
+ /*try to be best-effort in giving real local or routable contact address */
+ contact=get_fixed_contact(lc,call,dest_proxy);
+ if (contact){
+ sal_op_set_contact(call->op, contact);
+ ms_free(contact);
+ }
+ call->state=LCStateInit;
+ linphone_core_init_media_streams(lc,lc->call);
+ if (!lc->sip_conf.sdp_200_ack){
+ call->media_pending=TRUE;
+ sal_call_set_local_media_description(call->op,call->localdesc);
+ }
+ real_url=linphone_address_as_string(call->log->to);
+ from=linphone_address_as_string(call->log->from);
+ err=sal_call(call->op,from,real_url);
+
+ if (lc->sip_conf.sdp_200_ack){
+ call->media_pending=TRUE;
+ sal_call_set_local_media_description(call->op,call->localdesc);
+ }
+ barmsg=ortp_strdup_printf("%s %s", _("Contacting"), real_url);
+ lc->vtable.display_status(lc,barmsg);
+ ms_free(barmsg);
+
+ if (err<0){
+ ms_warning("Could not initiate call.");
+ lc->vtable.display_status(lc,_("could not call"));
+ linphone_core_stop_media_streams(lc,call);
+ linphone_call_destroy(call);
+ lc->call=NULL;
+ }else gstate_new_state(lc, GSTATE_CALL_OUT_INVITE, real_url);
+ ms_free(real_url);
+ ms_free(from);
+ return err;
+}
+
/**
* Initiates an outgoing call
*
**/
int linphone_core_invite(LinphoneCore *lc, const char *url)
{
- char *barmsg;
int err=0;
char *route=NULL;
const char *from=NULL;
- char *contact=NULL;
LinphoneProxyConfig *proxy=NULL;
LinphoneAddress *parsed_url2=NULL;
LinphoneAddress *real_parsed_url=NULL;
call=linphone_call_new_outgoing(lc,parsed_url2,real_parsed_url);
sal_op_set_route(call->op,route);
-
- /*try to be best-effort in giving real local or routable contact address */
- contact=get_fixed_contact(lc,call,dest_proxy);
- if (contact){
- sal_op_set_contact(call->op, contact);
- ms_free(contact);
- }
-
+
lc->call=call;
-
- linphone_core_init_media_streams(lc,lc->call);
- if (!lc->sip_conf.sdp_200_ack){
- call->media_pending=TRUE;
- sal_call_set_local_media_description(call->op,call->localdesc);
- }
- err=sal_call(call->op,from,real_url);
-
- if (lc->sip_conf.sdp_200_ack){
- call->media_pending=TRUE;
- sal_call_set_local_media_description(call->op,call->localdesc);
+ if (dest_proxy!=NULL || lc->sip_conf.ping_with_options==FALSE){
+ err=linphone_core_start_invite(lc,call,dest_proxy);
+ }else{
+ /*defer the start of the call after the OPTIONS ping*/
+ call->state=LCStatePreEstablishing;
+ call->ping_op=sal_op_new(lc->sal);
+ sal_ping(call->ping_op,from,real_url);
+ call->start_time=time(NULL);
}
- barmsg=ortp_strdup_printf("%s %s", _("Contacting"), real_url);
- lc->vtable.display_status(lc,barmsg);
- ms_free(barmsg);
- if (err<0){
- ms_warning("Could not initiate call.");
- lc->vtable.display_status(lc,_("could not call"));
- linphone_core_stop_media_streams(lc,call);
- linphone_call_destroy(call);
- lc->call=NULL;
- }else gstate_new_state(lc, GSTATE_CALL_OUT_INVITE, url);
-
if (real_url!=NULL) ms_free(real_url);
if (route!=NULL) ms_free(route);
- return (err<0) ? -1 : 0;
+ return err;
}
int linphone_core_refer(LinphoneCore *lc, const char *url)
typedef enum _LCState{
LCStateInit,
+ LCStatePreEstablishing,
LCStateRinging,
LCStateAVRunning
}LCState;
struct _RtpProfile *video_profile;
struct _LinphoneCallLog *log;
SalOp *op;
+ SalOp *ping_op;
char localip[LINPHONE_IPADDR_SIZE]; /* our best guess for local ipaddress for this call */
time_t start_time; /*time at which the call was initiated*/
time_t media_start_time; /*time at which it was accepted, media streams established*/
void linphone_core_update_progress(LinphoneCore *lc, const char *purpose, float progresses);
void linphone_core_stop_waiting(LinphoneCore *lc);
+int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy);
extern SalCallbacks linphone_sal_callbacks;
bool_t sdp_200_ack;
bool_t only_one_codec; /*in SDP answers*/
bool_t register_only_when_network_is_up;
+ bool_t ping_with_options;
} sip_config_t;
typedef struct rtp_config
return ((SalOpBase*)op)->route;
}
+const char *sal_op_get_network_origin(const SalOp *op){
+ return ((SalOpBase*)op)->origin;
+}
+
void __sal_op_init(SalOp *b, Sal *sal){
memset(b,0,sizeof(SalOpBase));
((SalOpBase*)b)->root=sal;
}
+void __sal_op_set_network_origin(SalOp *op, const char *origin){
+ assign_string(&((SalOpBase*)op)->origin,origin);
+}
+
+
void __sal_op_free(SalOp *op){
SalOpBase *b=(SalOpBase *)op;
if (b->from) {
ms_free(b->contact);
b->contact=NULL;
}
+ if (b->origin){
+ ms_free(b->origin);
+ b->origin=NULL;
+ }
if (b->local_media)
sal_media_description_unref(b->local_media);
if (b->remote_media)
char *contact;
char *from;
char *to;
+ char *origin;
SalMediaDescription *local_media;
SalMediaDescription *remote_media;
void *user_pointer;
typedef void (*SalOnSubscribeReceived)(SalOp *salop, const char *from);
typedef void (*SalOnSubscribeClosed)(SalOp *salop, const char *from);
typedef void (*SalOnInternalMsg)(Sal *sal, const char *msg);
+typedef void (*SalOnPingReply)(SalOp *salop);
typedef struct SalCallbacks{
SalOnCallReceived call_received;
SalOnSubscribeReceived subscribe_received;
SalOnSubscribeClosed subscribe_closed;
SalOnInternalMsg internal_message;
+ SalOnPingReply ping_reply;
}SalCallbacks;
typedef struct SalAuthInfo{
void sal_set_callbacks(Sal *ctx, const SalCallbacks *cbs);
int sal_listen_port(Sal *ctx, const char *addr, int port, SalTransport tr, int is_secure);
void sal_set_user_agent(Sal *ctx, const char *user_agent);
-void sal_masquerade(Sal *ctx, const char *ip);
void sal_use_session_timers(Sal *ctx, int expires);
int sal_iterate(Sal *sal);
MSList * sal_get_pending_auths(Sal *sal);
const char *sal_op_get_contact(const SalOp *op);
const char *sal_op_get_route(const SalOp *op);
const char *sal_op_get_proxy(const SalOp *op);
+/*for incoming requests, returns the origin of the packet as a sip uri*/
+const char *sal_op_get_network_origin(const SalOp *op);
void *sal_op_get_user_pointer(const SalOp *op);
/*Call API*/
/*presence publish */
int sal_publish(SalOp *op, const char *from, const char *to, SalPresenceStatus status);
+
+/*ping: main purpose is to obtain its own contact address behind firewalls*/
+int sal_ping(SalOp *op, const char *from, const char *to);
+
#define payload_type_set_number(pt,n) (pt)->user_data=(void*)((long)n);
#define payload_type_get_number(pt) ((int)(long)(pt)->user_data)
/*internal API */
void __sal_op_init(SalOp *b, Sal *sal);
+void __sal_op_set_network_origin(SalOp *op, const char *origin /*a sip uri*/);
void __sal_op_free(SalOp *b);
}
}
+static SalOp * sal_find_other(Sal *sal, osip_message_t *response){
+ const MSList *elem;
+ SalOp *op;
+ osip_call_id_t *callid=osip_message_get_call_id(response);
+ if (callid==NULL) {
+ ms_error("There is no call-id in this response !");
+ return NULL;
+ }
+ for(elem=sal->other_transactions;elem!=NULL;elem=elem->next){
+ op=(SalOp*)elem->data;
+ if (osip_call_id_match(callid,op->call_id)==0) return op;
+ }
+ return NULL;
+}
+
+static void sal_add_other(Sal *sal, SalOp *op, osip_message_t *request){
+ osip_call_id_t *callid=osip_message_get_call_id(request);
+ if (callid==NULL) {
+ ms_error("There is no call id in the request !");
+ return;
+ }
+ osip_call_id_clone(callid,&op->call_id);
+ sal->other_transactions=ms_list_append(sal->other_transactions,op);
+}
+
+static void sal_remove_other(Sal *sal, SalOp *op){
+ sal->other_transactions=ms_list_remove(sal->other_transactions,op);
+}
+
+
static void sal_add_pending_auth(Sal *sal, SalOp *op){
sal->pending_auths=ms_list_append(sal->pending_auths,op);
}
op->pending_auth=NULL;
op->sdp_answer=NULL;
op->reinvite=FALSE;
+ op->call_id=NULL;
return op;
}
}
if (op->result)
sal_media_description_unref(op->result);
+ if (op->call_id){
+ sal_remove_other(op->base.root,op);
+ osip_call_id_free(op->call_id);
+ }
__sal_op_free(op);
}
return sal->up;
}
-void sal_masquerade(Sal *ctx, const char *ip){
- ms_message("Masquerading SIP with %s",ip);
- eXosip_set_option(EXOSIP_OPT_SET_IPV4_FOR_GATEWAY,ip);
-}
-
static void unimplemented_stub(){
ms_warning("Unimplemented SAL callback");
}
ctx->callbacks.text_received=(SalOnTextReceived)unimplemented_stub;
if (ctx->callbacks.internal_message==NULL)
ctx->callbacks.internal_message=(SalOnInternalMsg)unimplemented_stub;
+ if (ctx->callbacks.ping_reply==NULL)
+ ctx->callbacks.ping_reply=(SalOnPingReply)unimplemented_stub;
}
int sal_listen_port(Sal *ctx, const char *addr, int port, SalTransport tr, int is_secure){
return h->result;
}
+int sal_ping(SalOp *op, const char *from, const char *to){
+ osip_message_t *options=NULL;
+
+ sal_op_set_from(op,from);
+ sal_op_set_to(op,to);
+ eXosip_options_build_request (&options, sal_op_get_to(op),
+ sal_op_get_from(op),sal_op_get_route(op));
+ if (options){
+ if (op->base.root->session_expires!=0){
+ osip_message_set_header(options, "Session-expires", "200");
+ osip_message_set_supported(options, "timer");
+ }
+ sal_add_other(sal_op_get_sal(op),op,options);
+ return eXosip_options_send_request(options);
+ }
+ return -1;
+}
+
int sal_refer(SalOp *h, const char *refer_to){
osip_message_t *msg=NULL;
int err=0;
}
}
+static void set_network_origin(SalOp *op, osip_message_t *req){
+ const char *received=NULL;
+ int rport=5060;
+ char origin[64];
+ if (extract_received_rport(req,&received,&rport)!=0){
+ osip_via_t *via=NULL;
+ char *tmp;
+ osip_message_get_via(req,0,&via);
+ received=osip_via_get_host(via);
+ tmp=osip_via_get_port(via);
+ if (tmp) rport=atoi(tmp);
+ }
+ snprintf(origin,sizeof(origin)-1,"sip:%s:%i",received,rport);
+ __sal_op_set_network_origin(op,origin);
+}
+
static void inc_new_call(Sal *sal, eXosip_event_t *ev){
SalOp *op=sal_op_new(sal);
osip_from_t *from,*to;
char *tmp;
sdp_message_t *sdp=eXosip_get_sdp_info(ev->request);
+
+ set_network_origin(op,ev->request);
+
if (sdp){
op->sdp_offering=FALSE;
op->base.remote_media=sal_media_description_new();
}
}
-static int call_proceeding(Sal *sal, eXosip_event_t *ev){
- SalOp *op=(SalOp*)ev->external_reference;
+static void update_contact_from_response(SalOp *op, osip_message_t *response){
const char *received;
int rport;
-
- if (op==NULL) {
- ms_warning("This call has been canceled.");
- eXosip_lock();
- eXosip_call_terminate(ev->cid,ev->did);
- eXosip_unlock();
- return -1;
- }
- op->did=ev->did;
- op->tid=ev->tid;
- /* update contact if received and rport are set by the server */
- if (extract_received_rport(ev->response,&received,&rport)==0){
+ if (extract_received_rport(response,&received,&rport)==0){
const char *contact=sal_op_get_contact(op);
if (!contact){
/*no contact given yet, use from instead*/
sal_address_set_domain(addr,received);
sal_address_set_port_int(addr,rport);
tmp=sal_address_as_string(addr);
- ms_message("Contact address automatically updated to %s for this call",tmp);
+ ms_message("Contact address updated to %s for this dialog",tmp);
sal_op_set_contact(op,tmp);
ms_free(tmp);
}
}
+}
+
+static int call_proceeding(Sal *sal, eXosip_event_t *ev){
+ SalOp *op=(SalOp*)ev->external_reference;
+
+ if (op==NULL) {
+ ms_warning("This call has been canceled.");
+ eXosip_lock();
+ eXosip_call_terminate(ev->cid,ev->did);
+ eXosip_unlock();
+ return -1;
+ }
+ op->did=ev->did;
+ op->tid=ev->tid;
+
+ /* update contact if received and rport are set by the server
+ note: will only be used by remote for next INVITE, if any...*/
+ update_contact_from_response(op,ev->response);
return 0;
}
osip_message_t *msg=NULL;
SalOp *op;
const char *contact;
+
op=(SalOp*)ev->external_reference;
if (op==NULL){
ms_error("A closed call is accepted ?");
if (ev->rid>0){
return sal_find_register(sal,ev->rid);
}
+ if (ev->response) return sal_find_other(sal,ev->response);
return NULL;
}
return TRUE;
}
+static void other_request_reply(Sal *sal,eXosip_event_t *ev){
+ SalOp *op=find_op(sal,ev);
+
+ if (op==NULL){
+ ms_warning("other_request_reply(): Receiving response to unknown request.");
+ return;
+ }
+ if (ev->response){
+ update_contact_from_response(op,ev->response);
+ sal->callbacks.ping_reply(op);
+ }
+}
+
static bool_t process_event(Sal *sal, eXosip_event_t *ev){
switch(ev->type){
case EXOSIP_CALL_ANSWERED:
case EXOSIP_MESSAGE_NEW:
other_request(sal,ev);
break;
+ case EXOSIP_MESSAGE_PROCEEDING:
+ case EXOSIP_MESSAGE_ANSWERED:
+ case EXOSIP_MESSAGE_REDIRECTED:
+ case EXOSIP_MESSAGE_SERVERFAILURE:
+ case EXOSIP_MESSAGE_GLOBALFAILURE:
+ other_request_reply(sal,ev);
+ break;
case EXOSIP_MESSAGE_REQUESTFAILURE:
if (ev->response && (ev->response->status_code == 407 || ev->response->status_code == 401)){
return process_authentication(sal,ev);
MSList *out_subscribes;/*MSList of SalOp */
MSList *in_subscribes;/*MSList of SalOp */
MSList *pending_auths;/*MSList of SalOp */
+ MSList *other_transactions; /*MSList of SalOp */
int running;
int session_expires;
void *up;
SalMediaDescription *result;
sdp_message_t *sdp_answer;
eXosip_event_t *pending_auth;
+ osip_call_id_t *call_id; /*used for out of calls transaction in order
+ to retrieve the operation when receiving a response*/
bool_t supports_session_timers;
bool_t sdp_offering;
bool_t reinvite;