linphone-ios/coreapi/sal_eXosip2.c
2010-12-13 14:24:33 +01:00

2009 lines
54 KiB
C

/*
linphone
Copyright (C) 2010 Simon MORLAT (simon.morlat@free.fr)
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "sal_eXosip2.h"
#include "offeranswer.h"
static void text_received(Sal *sal, eXosip_event_t *ev);
void _osip_list_set_empty(osip_list_t *l, void (*freefunc)(void*)){
void *data;
while((data=osip_list_get(l,0))!=NULL){
osip_list_remove(l,0);
freefunc(data);
}
}
void sal_get_default_local_ip(Sal *sal, int address_family,char *ip, size_t iplen){
if (eXosip_guess_localip(address_family,ip,iplen)<0){
/*default to something */
strncpy(ip,address_family==AF_INET6 ? "::1" : "127.0.0.1",iplen);
ms_error("Could not find default routable ip address !");
}
}
static SalOp * sal_find_call(Sal *sal, int cid){
const MSList *elem;
SalOp *op;
for(elem=sal->calls;elem!=NULL;elem=elem->next){
op=(SalOp*)elem->data;
if (op->cid==cid) return op;
}
return NULL;
}
static void sal_add_call(Sal *sal, SalOp *op){
sal->calls=ms_list_append(sal->calls,op);
}
static void sal_remove_call(Sal *sal, SalOp *op){
sal->calls=ms_list_remove(sal->calls, op);
}
static SalOp * sal_find_register(Sal *sal, int rid){
const MSList *elem;
SalOp *op;
for(elem=sal->registers;elem!=NULL;elem=elem->next){
op=(SalOp*)elem->data;
if (op->rid==rid) return op;
}
return NULL;
}
static void sal_add_register(Sal *sal, SalOp *op){
sal->registers=ms_list_append(sal->registers,op);
}
static void sal_remove_register(Sal *sal, int rid){
MSList *elem;
SalOp *op;
for(elem=sal->registers;elem!=NULL;elem=elem->next){
op=(SalOp*)elem->data;
if (op->rid==rid) {
sal->registers=ms_list_remove_link(sal->registers,elem);
return;
}
}
}
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;
}
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);
}
static void sal_remove_pending_auth(Sal *sal, SalOp *op){
sal->pending_auths=ms_list_remove(sal->pending_auths,op);
}
void sal_exosip_fix_route(SalOp *op){
if (sal_op_get_route(op)!=NULL){
osip_route_t *rt=NULL;
osip_uri_param_t *lr_param=NULL;
osip_route_init(&rt);
if (osip_route_parse(rt,sal_op_get_route(op))<0){
ms_warning("Bad route %s!",sal_op_get_route(op));
sal_op_set_route(op,NULL);
}else{
/* check if the lr parameter is set , if not add it */
osip_uri_uparam_get_byname(rt->url, "lr", &lr_param);
if (lr_param==NULL){
char *tmproute;
osip_uri_uparam_add(rt->url,osip_strdup("lr"),NULL);
osip_route_to_str(rt,&tmproute);
sal_op_set_route(op,tmproute);
osip_free(tmproute);
}
}
osip_route_free(rt);
}
}
SalOp * sal_op_new(Sal *sal){
SalOp *op=ms_new(SalOp,1);
__sal_op_init(op,sal);
op->cid=op->did=op->tid=op->rid=op->nid=op->sid=-1;
op->result=NULL;
op->supports_session_timers=FALSE;
op->sdp_offering=TRUE;
op->pending_auth=NULL;
op->sdp_answer=NULL;
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;
}
bool_t sal_call_autoanswer_asked(SalOp *op)
{
return op->auto_answer_asked;
}
void sal_op_release(SalOp *op){
if (op->sdp_answer)
sdp_message_free(op->sdp_answer);
if (op->pending_auth)
eXosip_event_free(op->pending_auth);
if (op->rid!=-1){
sal_remove_register(op->base.root,op->rid);
}
if (op->cid!=-1){
ms_message("Cleaning cid %i",op->cid);
sal_remove_call(op->base.root,op);
}
if (op->sid!=-1){
sal_remove_out_subscribe(op->base.root,op);
}
if (op->nid!=-1){
sal_remove_in_subscribe(op->base.root,op);
if (op->call_id)
osip_call_id_free(op->call_id);
op->call_id=NULL;
}
if (op->pending_auth){
sal_remove_pending_auth(op->base.root,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);
}
if (op->replaces){
ms_free(op->replaces);
}
if (op->referred_by){
ms_free(op->referred_by);
}
__sal_op_free(op);
}
static void _osip_trace_func(char *fi, int li, osip_trace_level_t level, char *chfr, va_list ap){
int ortp_level=ORTP_DEBUG;
switch(level){
case OSIP_INFO1:
case OSIP_INFO2:
case OSIP_INFO3:
case OSIP_INFO4:
ortp_level=ORTP_MESSAGE;
break;
case OSIP_WARNING:
ortp_level=ORTP_WARNING;
break;
case OSIP_ERROR:
case OSIP_BUG:
ortp_level=ORTP_ERROR;
break;
case OSIP_FATAL:
ortp_level=ORTP_FATAL;
break;
case END_TRACE_LEVEL:
break;
}
if (ortp_log_level_enabled(level)){
int len=strlen(chfr);
char *chfrdup=ortp_strdup(chfr);
/*need to remove endline*/
if (len>1){
if (chfrdup[len-1]=='\n')
chfrdup[len-1]='\0';
if (chfrdup[len-2]=='\r')
chfrdup[len-2]='\0';
}
ortp_logv(ortp_level,chfrdup,ap);
ortp_free(chfrdup);
}
}
Sal * sal_init(){
static bool_t firsttime=TRUE;
Sal *sal;
if (firsttime){
osip_trace_initialize_func (OSIP_INFO4,&_osip_trace_func);
firsttime=FALSE;
}
eXosip_init();
sal=ms_new0(Sal,1);
sal->keepalive_period=30;
return sal;
}
void sal_uninit(Sal* sal){
eXosip_quit();
ms_free(sal);
}
void sal_set_user_pointer(Sal *sal, void *user_data){
sal->up=user_data;
}
void *sal_get_user_pointer(const Sal *sal){
return sal->up;
}
static void unimplemented_stub(){
ms_warning("Unimplemented SAL callback");
}
void sal_set_callbacks(Sal *ctx, const SalCallbacks *cbs){
memcpy(&ctx->callbacks,cbs,sizeof(*cbs));
if (ctx->callbacks.call_received==NULL)
ctx->callbacks.call_received=(SalOnCallReceived)unimplemented_stub;
if (ctx->callbacks.call_ringing==NULL)
ctx->callbacks.call_ringing=(SalOnCallRinging)unimplemented_stub;
if (ctx->callbacks.call_accepted==NULL)
ctx->callbacks.call_accepted=(SalOnCallAccepted)unimplemented_stub;
if (ctx->callbacks.call_failure==NULL)
ctx->callbacks.call_failure=(SalOnCallFailure)unimplemented_stub;
if (ctx->callbacks.call_terminated==NULL)
ctx->callbacks.call_terminated=(SalOnCallTerminated)unimplemented_stub;
if (ctx->callbacks.call_updating==NULL)
ctx->callbacks.call_updating=(SalOnCallUpdating)unimplemented_stub;
if (ctx->callbacks.auth_requested==NULL)
ctx->callbacks.auth_requested=(SalOnAuthRequested)unimplemented_stub;
if (ctx->callbacks.auth_success==NULL)
ctx->callbacks.auth_success=(SalOnAuthSuccess)unimplemented_stub;
if (ctx->callbacks.register_success==NULL)
ctx->callbacks.register_success=(SalOnRegisterSuccess)unimplemented_stub;
if (ctx->callbacks.register_failure==NULL)
ctx->callbacks.register_failure=(SalOnRegisterFailure)unimplemented_stub;
if (ctx->callbacks.dtmf_received==NULL)
ctx->callbacks.dtmf_received=(SalOnDtmfReceived)unimplemented_stub;
if (ctx->callbacks.notify==NULL)
ctx->callbacks.notify=(SalOnNotify)unimplemented_stub;
if (ctx->callbacks.notify_presence==NULL)
ctx->callbacks.notify_presence=(SalOnNotifyPresence)unimplemented_stub;
if (ctx->callbacks.subscribe_received==NULL)
ctx->callbacks.subscribe_received=(SalOnSubscribeReceived)unimplemented_stub;
if (ctx->callbacks.text_received==NULL)
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_unlisten_ports(Sal *ctx){
if (ctx->running){
eXosip_quit();
eXosip_init();
}
return 0;
}
int sal_listen_port(Sal *ctx, const char *addr, int port, SalTransport tr, int is_secure){
int err;
bool_t ipv6;
int proto=IPPROTO_UDP;
switch (tr) {
case SalTransportDatagram:
proto=IPPROTO_UDP;
break;
case SalTransportStream:
proto= IPPROTO_TCP;
break;
default:
ms_warning("unexpected proto, using datagram");
}
err=0;
eXosip_set_option(13,&err); /*13=EXOSIP_OPT_SRV_WITH_NAPTR, as it is an enum value, we can't use it unless we are sure of the
version of eXosip, which is not the case*/
/*see if it looks like an IPv6 address*/
ipv6=strchr(addr,':')!=NULL;
eXosip_enable_ipv6(ipv6);
if (is_secure){
ms_fatal("SIP over TLS or DTLS is not supported yet.");
return -1;
}
err=eXosip_listen_addr(proto, addr, port, ipv6 ? PF_INET6 : PF_INET, 0);
#ifdef HAVE_EXOSIP_GET_SOCKET
ms_message("Exosip has socket number %i",eXosip_get_socket(proto));
#endif
eXosip_set_option (EXOSIP_OPT_UDP_KEEP_ALIVE, &ctx->keepalive_period);
ctx->running=TRUE;
return err;
}
ortp_socket_t sal_get_socket(Sal *ctx){
#ifdef HAVE_EXOSIP_GET_SOCKET
return eXosip_get_socket(IPPROTO_UDP);
#else
ms_warning("Sorry, eXosip does not have eXosip_get_socket() method");
return -1;
#endif
}
void sal_set_user_agent(Sal *ctx, const char *user_agent){
eXosip_set_user_agent(user_agent);
}
void sal_use_session_timers(Sal *ctx, int expires){
ctx->session_expires=expires;
}
void sal_use_one_matching_codec_policy(Sal *ctx, bool_t one_matching_codec){
ctx->one_matching_codec=one_matching_codec;
}
MSList *sal_get_pending_auths(Sal *sal){
return ms_list_copy(sal->pending_auths);
}
static int extract_received_rport(osip_message_t *msg, const char **received, int *rportval){
osip_via_t *via=NULL;
osip_generic_param_t *param=NULL;
const char *rport=NULL;
*rportval=5060;
*received=NULL;
osip_message_get_via(msg,0,&via);
if (!via) return -1;
/* it is useless to do that with tcp since client socket might have a different port
than the server socket.
*/
if (strcasecmp(via->protocol,"tcp")==0) return -1;
if (via->port && via->port[0]!='\0')
*rportval=atoi(via->port);
osip_via_param_get_byname(via,"rport",&param);
if (param) {
rport=param->gvalue;
if (rport && rport[0]!='\0') *rportval=atoi(rport);
else *rportval=5060;
*received=via->host;
}
param=NULL;
osip_via_param_get_byname(via,"received",&param);
if (param) *received=param->gvalue;
if (rport==NULL && *received==NULL) return -1;
return 0;
}
static void set_sdp(osip_message_t *sip,sdp_message_t *msg){
int sdplen;
char clen[10];
char *sdp=NULL;
sdp_message_to_str(msg,&sdp);
sdplen=strlen(sdp);
snprintf(clen,sizeof(clen),"%i",sdplen);
osip_message_set_body(sip,sdp,sdplen);
osip_message_set_content_type(sip,"application/sdp");
osip_message_set_content_length(sip,clen);
osip_free(sdp);
}
static void set_sdp_from_desc(osip_message_t *sip, const SalMediaDescription *desc){
sdp_message_t *msg=media_description_to_sdp(desc);
if (msg==NULL) {
ms_error("Fail to print sdp message !");
return;
}
set_sdp(sip,msg);
sdp_message_free(msg);
}
static void sdp_process(SalOp *h){
ms_message("Doing SDP offer/answer process");
if (h->result){
sal_media_description_unref(h->result);
}
h->result=sal_media_description_new();
if (h->sdp_offering){
offer_answer_initiate_outgoing(h->base.local_media,h->base.remote_media,h->result);
}else{
int i;
offer_answer_initiate_incoming(h->base.local_media,h->base.remote_media,h->result,h->base.root->one_matching_codec);
h->sdp_answer=media_description_to_sdp(h->result);
/*once we have generated the SDP answer, we modify the result description for processing by the upper layer.
It should contains media parameters constraint from the remote offer, not our response*/
strcpy(h->result->addr,h->base.remote_media->addr);
h->result->bandwidth=h->base.remote_media->bandwidth;
for(i=0;i<h->result->nstreams;++i){
if (h->result->streams[i].port>0){
strcpy(h->result->streams[i].addr,h->base.remote_media->streams[i].addr);
h->result->streams[i].ptime=h->base.remote_media->streams[i].ptime;
h->result->streams[i].bandwidth=h->base.remote_media->streams[i].bandwidth;
h->result->streams[i].port=h->base.remote_media->streams[i].port;
}
}
}
}
int sal_call_set_local_media_description(SalOp *h, SalMediaDescription *desc){
if (desc)
sal_media_description_ref(desc);
if (h->base.local_media)
sal_media_description_unref(h->base.local_media);
h->base.local_media=desc;
return 0;
}
int sal_call(SalOp *h, const char *from, const char *to){
int err;
osip_message_t *invite=NULL;
sal_op_set_from(h,from);
sal_op_set_to(h,to);
sal_exosip_fix_route(h);
err=eXosip_call_build_initial_invite(&invite,to,from,sal_op_get_route(h),"Phone call");
if (err!=0){
ms_error("Could not create call.");
return -1;
}
osip_message_set_allow(invite, "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO");
if (h->base.contact){
_osip_list_set_empty(&invite->contacts,(void (*)(void*))osip_contact_free);
osip_message_set_contact(invite,h->base.contact);
}
if (h->base.root->session_expires!=0){
osip_message_set_header(invite, "Session-expires", "200");
osip_message_set_supported(invite, "timer");
}
if (h->base.local_media){
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();
h->cid=err;
if (err<0){
ms_error("Fail to send invite !");
return -1;
}else{
sal_add_call(h->base.root,h);
}
return 0;
}
int sal_call_notify_ringing(SalOp *h, bool_t early_media){
osip_message_t *msg;
int err;
/*if early media send also 180 and 183 */
if (early_media && h->sdp_answer){
msg=NULL;
eXosip_lock();
err=eXosip_call_build_answer(h->tid,180,&msg);
if (msg){
set_sdp(msg,h->sdp_answer);
eXosip_call_send_answer(h->tid,180,msg);
}
msg=NULL;
err=eXosip_call_build_answer(h->tid,183,&msg);
if (msg){
set_sdp(msg,h->sdp_answer);
eXosip_call_send_answer(h->tid,183,msg);
}
eXosip_unlock();
}else{
eXosip_lock();
eXosip_call_send_answer(h->tid,180,NULL);
eXosip_unlock();
}
return 0;
}
int sal_call_accept(SalOp * h){
osip_message_t *msg;
const char *contact=sal_op_get_contact(h);
/* sends a 200 OK */
int err=eXosip_call_build_answer(h->tid,200,&msg);
if (err<0 || msg==NULL){
ms_error("Fail to build answer for call: err=%i",err);
return -1;
}
if (h->base.root->session_expires!=0){
if (h->supports_session_timers) osip_message_set_supported(msg, "timer");
}
if (contact) {
_osip_list_set_empty(&msg->contacts,(void (*)(void*))osip_contact_free);
osip_message_set_contact(msg,contact);
}
if (h->base.local_media){
/*this is the case where we received an invite without SDP*/
if (h->sdp_offering) {
set_sdp_from_desc(msg,h->base.local_media);
}else{
if (h->sdp_answer){
set_sdp(msg,h->sdp_answer);
sdp_message_free(h->sdp_answer);
h->sdp_answer=NULL;
}
}
}else{
ms_error("You are accepting a call but not defined any media capabilities !");
}
eXosip_call_send_answer(h->tid,200,msg);
return 0;
}
int sal_call_decline(SalOp *h, SalReason reason, const char *redirect){
if (reason==SalReasonBusy){
eXosip_lock();
eXosip_call_send_answer(h->tid,486,NULL);
eXosip_unlock();
}
else if (reason==SalReasonTemporarilyUnavailable){
eXosip_lock();
eXosip_call_send_answer(h->tid,480,NULL);
eXosip_unlock();
}else if (reason==SalReasonDoNotDisturb){
eXosip_lock();
eXosip_call_send_answer(h->tid,600,NULL);
eXosip_unlock();
}else if (reason==SalReasonMedia){
eXosip_lock();
eXosip_call_send_answer(h->tid,415,NULL);
eXosip_unlock();
}else if (redirect!=NULL && reason==SalReasonRedirect){
osip_message_t *msg;
int code;
if (strstr(redirect,"sip:")!=0) code=302;
else code=380;
eXosip_lock();
eXosip_call_build_answer(h->tid,code,&msg);
osip_message_set_contact(msg,redirect);
eXosip_call_send_answer(h->tid,code,msg);
eXosip_unlock();
}else sal_call_terminate(h);
return 0;
}
SalMediaDescription * sal_call_get_final_media_description(SalOp *h){
if (h->base.local_media && h->base.remote_media && !h->result){
sdp_process(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;
sal_op_set_from(op,from);
sal_op_set_to(op,to);
/*bug here: eXosip2 does not honor the route argument*/
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_call_accept_refer(SalOp *op){
osip_message_t *msg=NULL;
int err=0;
eXosip_lock();
err = eXosip_call_build_notify(op->did,EXOSIP_SUBCRSTATE_ACTIVE,&msg);
if(msg != NULL)
{
osip_message_set_header(msg,(const char *)"event","refer");
osip_message_set_content_type(msg,"message/sipfrag");
osip_message_set_body(msg,"SIP/2.0 100 Trying",sizeof("SIP/2.0 100 Trying"));
eXosip_call_send_request(op->did,msg);
}
else
{
ms_error("could not get a notify built\n");
}
eXosip_unlock();
return err;
}
int sal_call_refer(SalOp *h, const char *refer_to){
osip_message_t *msg=NULL;
int err=0;
eXosip_lock();
eXosip_call_build_refer(h->did,refer_to, &msg);
if (msg) err=eXosip_call_send_request(h->did, msg);
else err=-1;
eXosip_unlock();
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;
eXosip_lock();
cid=eXosip_call_find_by_replaces(h->replaces);
eXosip_unlock();
if (cid>0){
SalOp *ret=sal_find_call(h->base.root,cid);
return ret;
}
}
return NULL;
}
int sal_call_send_dtmf(SalOp *h, char dtmf){
osip_message_t *msg=NULL;
char dtmf_body[128];
char clen[10];
eXosip_lock();
eXosip_call_build_info(h->did,&msg);
if (msg){
snprintf(dtmf_body, sizeof(dtmf_body), "Signal=%c\r\nDuration=250\r\n", dtmf);
osip_message_set_body(msg,dtmf_body,strlen(dtmf_body));
osip_message_set_content_type(msg,"application/dtmf-relay");
snprintf(clen,sizeof(clen),"%lu",(unsigned long)strlen(dtmf_body));
osip_message_set_content_length(msg,clen);
eXosip_call_send_request(h->did,msg);
}
eXosip_unlock();
return 0;
}
int sal_call_terminate(SalOp *h){
int err;
eXosip_lock();
err=eXosip_call_terminate(h->cid,h->did);
eXosip_unlock();
if (err!=0){
ms_warning("Exosip could not terminate the call: cid=%i did=%i", h->cid,h->did);
}
sal_remove_call(h->base.root,h);
h->cid=-1;
return 0;
}
void sal_op_authenticate(SalOp *h, const SalAuthInfo *info){
if (h->pending_auth){
const char *userid;
if (info->userid==NULL || info->userid[0]=='\0') userid=info->username;
else userid=info->userid;
ms_message("Authentication info for %s %s added to eXosip", info->username,info->realm);
eXosip_add_authentication_info (info->username,userid,
info->password, NULL,info->realm);
eXosip_lock();
eXosip_default_action(h->pending_auth);
eXosip_unlock();
ms_message("eXosip_default_action() done");
eXosip_clear_authentication_info();
eXosip_event_free(h->pending_auth);
sal_remove_pending_auth(sal_op_get_sal(h),h);
h->pending_auth=NULL;
}
}
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 set_remote_ua(SalOp* op, osip_message_t *req){
if (op->base.remote_ua==NULL){
osip_header_t *h=NULL;
osip_message_get_user_agent(req,0,&h);
if (h){
op->base.remote_ua=ms_strdup(h->hvalue);
}
}
}
static void set_replaces(SalOp *op, osip_message_t *req){
osip_header_t *h=NULL;
if (op->replaces){
ms_free(op->replaces);
op->replaces=NULL;
}
osip_message_header_get_byname(req,"replaces",0,&h);
if (h){
if (h->hvalue && h->hvalue[0]!='\0'){
op->replaces=ms_strdup(h->hvalue);
}
}
}
static SalOp *find_op(Sal *sal, eXosip_event_t *ev){
if (ev->cid>0){
return sal_find_call(sal,ev->cid);
}
if (ev->rid>0){
return sal_find_register(sal,ev->rid);
}
if (ev->sid>0){
return sal_find_out_subscribe(sal,ev->sid);
}
if (ev->response) return sal_find_other(sal,ev->response);
return NULL;
}
static void inc_new_call(Sal *sal, eXosip_event_t *ev){
SalOp *op=sal_op_new(sal);
osip_from_t *from,*to;
osip_call_info_t *call_info;
char *tmp;
sdp_message_t *sdp=eXosip_get_sdp_info(ev->request);
set_network_origin(op,ev->request);
set_remote_ua(op,ev->request);
set_replaces(op,ev->request);
if (sdp){
op->sdp_offering=FALSE;
op->base.remote_media=sal_media_description_new();
sdp_to_media_description(sdp,op->base.remote_media);
sdp_message_free(sdp);
}else op->sdp_offering=TRUE;
from=osip_message_get_from(ev->request);
to=osip_message_get_to(ev->request);
osip_from_to_str(from,&tmp);
sal_op_set_from(op,tmp);
osip_free(tmp);
osip_from_to_str(to,&tmp);
sal_op_set_to(op,tmp);
osip_free(tmp);
osip_message_get_call_info(ev->request,0,&call_info);
if(call_info)
{
osip_call_info_to_str(call_info,&tmp);
if( strstr(tmp,"answer-after=") != NULL)
{
op->auto_answer_asked=TRUE;
ms_message("The caller asked to automatically answer the call(Emergency?)\n");
}
osip_free(tmp);
}
op->tid=ev->tid;
op->cid=ev->cid;
op->did=ev->did;
sal_add_call(op->base.root,op);
sal->callbacks.call_received(op);
}
static void handle_reinvite(Sal *sal, eXosip_event_t *ev){
SalOp *op=find_op(sal,ev);
sdp_message_t *sdp;
osip_message_t *msg=NULL;
if (op==NULL) {
ms_warning("Reinvite for non-existing operation !");
return;
}
op->reinvite=TRUE;
op->tid=ev->tid;
sdp=eXosip_get_sdp_info(ev->request);
if (op->base.remote_media){
sal_media_description_unref(op->base.remote_media);
op->base.remote_media=NULL;
}
if (op->result){
sal_media_description_unref(op->result);
op->result=NULL;
}
if (sdp){
op->sdp_offering=FALSE;
op->base.remote_media=sal_media_description_new();
sdp_to_media_description(sdp,op->base.remote_media);
sdp_message_free(sdp);
sal->callbacks.call_updating(op);
}else {
op->sdp_offering=TRUE;
eXosip_lock();
eXosip_call_build_answer(ev->tid,200,&msg);
if (msg!=NULL){
set_sdp_from_desc(msg,op->base.local_media);
eXosip_call_send_answer(ev->tid,200,msg);
}
eXosip_unlock();
}
}
static void handle_ack(Sal *sal, eXosip_event_t *ev){
SalOp *op=find_op(sal,ev);
sdp_message_t *sdp;
if (op==NULL) {
ms_warning("ack for non-existing call !");
return;
}
sdp=eXosip_get_sdp_info(ev->ack);
if (sdp){
op->base.remote_media=sal_media_description_new();
sdp_to_media_description(sdp,op->base.remote_media);
sdp_process(op);
sdp_message_free(sdp);
}
if (op->reinvite){
if (sdp) sal->callbacks.call_updating(op);
op->reinvite=FALSE;
}else{
sal->callbacks.call_ack(op);
}
}
static void update_contact_from_response(SalOp *op, osip_message_t *response){
const char *received;
int rport;
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*/
contact=sal_op_get_from(op);
}
if (contact){
SalAddress *addr=sal_address_new(contact);
char *tmp;
sal_address_set_domain(addr,received);
sal_address_set_port_int(addr,rport);
tmp=sal_address_as_string(addr);
ms_message("Contact address updated to %s for this dialog",tmp);
sal_op_set_contact(op,tmp);
sal_address_destroy(addr);
ms_free(tmp);
}
}
}
static int call_proceeding(Sal *sal, eXosip_event_t *ev){
SalOp *op=find_op(sal,ev);
if (op==NULL) {
ms_warning("This call has been canceled.");
eXosip_lock();
eXosip_call_terminate(ev->cid,ev->did);
eXosip_unlock();
return -1;
}
if (ev->did>0)
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;
}
static void call_ringing(Sal *sal, eXosip_event_t *ev){
sdp_message_t *sdp;
SalOp *op=find_op(sal,ev);
if (call_proceeding(sal, ev)==-1) return;
set_remote_ua(op,ev->response);
sdp=eXosip_get_sdp_info(ev->response);
if (sdp){
op->base.remote_media=sal_media_description_new();
sdp_to_media_description(sdp,op->base.remote_media);
sdp_message_free(sdp);
if (op->base.local_media) sdp_process(op);
}
sal->callbacks.call_ringing(op);
}
static void call_accepted(Sal *sal, eXosip_event_t *ev){
sdp_message_t *sdp;
osip_message_t *msg=NULL;
SalOp *op=find_op(sal,ev);
const char *contact;
if (op==NULL){
ms_error("A closed call is accepted ?");
return;
}
op->did=ev->did;
set_remote_ua(op,ev->response);
sdp=eXosip_get_sdp_info(ev->response);
if (sdp){
op->base.remote_media=sal_media_description_new();
sdp_to_media_description(sdp,op->base.remote_media);
sdp_message_free(sdp);
if (op->base.local_media) sdp_process(op);
}
eXosip_call_build_ack(ev->did,&msg);
contact=sal_op_get_contact(op);
if (contact) {
_osip_list_set_empty(&msg->contacts,(void (*)(void*))osip_contact_free);
osip_message_set_contact(msg,contact);
}
if (op->sdp_answer){
set_sdp(msg,op->sdp_answer);
sdp_message_free(op->sdp_answer);
op->sdp_answer=NULL;
}
eXosip_call_send_ack(ev->did,msg);
sal->callbacks.call_accepted(op);
}
static void call_terminated(Sal *sal, eXosip_event_t *ev){
char *from=NULL;
SalOp *op=find_op(sal,ev);
if (op==NULL){
ms_warning("Call terminated for already closed call ?");
return;
}
if (ev->request){
osip_from_to_str(ev->request->from,&from);
}
sal_remove_call(sal,op);
op->cid=-1;
sal->callbacks.call_terminated(op,from!=NULL ? from : sal_op_get_from(op));
if (from) osip_free(from);
}
static void call_released(Sal *sal, eXosip_event_t *ev){
SalOp *op=find_op(sal,ev);
if (op==NULL){
ms_warning("No op associated to this call_released()");
return;
}
op->cid=-1;
if (op->did==-1)
sal->callbacks.call_failure(op,SalErrorNoResponse,SalReasonUnknown,NULL, 487);
}
static int get_auth_data_from_response(osip_message_t *resp, const char **realm, const char **username){
const char *prx_realm=NULL,*www_realm=NULL;
osip_proxy_authenticate_t *prx_auth;
osip_www_authenticate_t *www_auth;
*username=osip_uri_get_username(resp->from->url);
prx_auth=(osip_proxy_authenticate_t*)osip_list_get(&resp->proxy_authenticates,0);
www_auth=(osip_proxy_authenticate_t*)osip_list_get(&resp->www_authenticates,0);
if (prx_auth!=NULL)
prx_realm=osip_proxy_authenticate_get_realm(prx_auth);
if (www_auth!=NULL)
www_realm=osip_www_authenticate_get_realm(www_auth);
if (prx_realm){
*realm=prx_realm;
}else if (www_realm){
*realm=www_realm;
}else{
return -1;
}
return 0;
}
static int get_auth_data_from_request(osip_message_t *msg, const char **realm, const char **username){
osip_authorization_t *auth=NULL;
osip_proxy_authorization_t *prx_auth=NULL;
*username=osip_uri_get_username(msg->from->url);
osip_message_get_authorization(msg, 0, &auth);
if (auth){
*realm=osip_authorization_get_realm(auth);
return 0;
}
osip_message_get_proxy_authorization(msg,0,&prx_auth);
if (prx_auth){
*realm=osip_proxy_authorization_get_realm(prx_auth);
return 0;
}
return -1;
}
static int get_auth_data(eXosip_event_t *ev, const char **realm, const char **username){
if (ev->response && get_auth_data_from_response(ev->response,realm,username)==0) return 0;
if (ev->request && get_auth_data_from_request(ev->request,realm,username)==0) return 0;
return -1;
}
int sal_op_get_auth_requested(SalOp *op, const char **realm, const char **username){
if (op->pending_auth){
return get_auth_data(op->pending_auth,realm,username);
}
return -1;
}
static bool_t process_authentication(Sal *sal, eXosip_event_t *ev){
SalOp *op;
const char *username,*realm;
op=find_op(sal,ev);
if (op==NULL){
ms_warning("No operation associated with this authentication !");
return TRUE;
}
if (get_auth_data(ev,&realm,&username)==0){
if (op->pending_auth!=NULL)
eXosip_event_free(op->pending_auth);
op->pending_auth=ev;
sal_add_pending_auth (sal,op);
sal->callbacks.auth_requested(op,realm,username);
return FALSE;
}
return TRUE;
}
static void authentication_ok(Sal *sal, eXosip_event_t *ev){
SalOp *op;
const char *username,*realm;
op=find_op(sal,ev);
if (op==NULL){
ms_warning("No operation associated with this authentication_ok!");
return ;
}
if (get_auth_data(ev,&realm,&username)==0){
sal->callbacks.auth_success(op,realm,username);
}
}
static bool_t call_failure(Sal *sal, eXosip_event_t *ev){
SalOp *op;
int code=0;
char* computedReason=NULL;
const char *reason=NULL;
SalError error=SalErrorUnknown;
SalReason sr=SalReasonUnknown;
op=(SalOp*)find_op(sal,ev);
if (op==NULL) {
ms_warning("Call failure reported for a closed call, ignored.");
return TRUE;
}
if (ev->response){
code=osip_message_get_status_code(ev->response);
reason=osip_message_get_reason_phrase(ev->response);
osip_header_t *h=NULL;
if (!osip_message_header_get_byname( ev->response
,"Reason"
,0
,&h)) {
computedReason = ms_strdup_printf("%s %s",reason,osip_header_get_value(h));
reason = computedReason;
}
}
switch(code)
{
case 401:
case 407:
return process_authentication(sal,ev);
break;
case 400:
error=SalErrorUnknown;
break;
case 404:
error=SalErrorFailure;
sr=SalReasonNotFound;
break;
case 415:
error=SalErrorFailure;
sr=SalReasonMedia;
break;
case 422:
eXosip_default_action(ev);
return TRUE;
break;
case 480:
error=SalErrorFailure;
sr=SalReasonTemporarilyUnavailable;
case 486:
error=SalErrorFailure;
sr=SalReasonBusy;
break;
case 487:
break;
case 600:
error=SalErrorFailure;
sr=SalReasonDoNotDisturb;
break;
case 603:
error=SalErrorFailure;
sr=SalReasonDeclined;
break;
default:
if (code>0){
error=SalErrorFailure;
sr=SalReasonUnknown;
}else error=SalErrorNoResponse;
}
sal->callbacks.call_failure(op,error,sr,reason,code);
if (computedReason != NULL){
ms_free(computedReason);
}
return TRUE;
}
static void process_media_control_xml(Sal *sal, eXosip_event_t *ev){
SalOp *op=find_op(sal,ev);
osip_body_t *body=NULL;
if (op==NULL){
ms_warning("media control xml received without operation context!");
return ;
}
osip_message_get_body(ev->request,0,&body);
if (body && body->body!=NULL &&
strstr(body->body,"picture_fast_update")){
osip_message_t *ans=NULL;
ms_message("Receiving VFU request !");
if (sal->callbacks.vfu_request){
sal->callbacks.vfu_request(op);
eXosip_call_build_answer(ev->tid,200,&ans);
if (ans)
eXosip_call_send_answer(ev->tid,200,ans);
return;
}
}
/*in all other cases we must say it is not implemented.*/
{
osip_message_t *ans=NULL;
eXosip_lock();
eXosip_call_build_answer(ev->tid,501,&ans);
if (ans)
eXosip_call_send_answer(ev->tid,501,ans);
eXosip_unlock();
}
}
static void process_dtmf_relay(Sal *sal, eXosip_event_t *ev){
SalOp *op=find_op(sal,ev);
osip_body_t *body=NULL;
if (op==NULL){
ms_warning("media dtmf relay received without operation context!");
return ;
}
osip_message_get_body(ev->request,0,&body);
if (body && body->body!=NULL){
osip_message_t *ans=NULL;
const char *name=strstr(body->body,"Signal");
if (name==NULL) name=strstr(body->body,"signal");
if (name==NULL) {
ms_warning("Could not extract the dtmf name from the SIP INFO.");
}else{
char tmp[2];
name+=strlen("signal");
if (sscanf(name," = %1s",tmp)==1){
ms_message("Receiving dtmf %s via SIP INFO.",tmp);
if (sal->callbacks.dtmf_received != NULL)
sal->callbacks.dtmf_received(op, tmp[0]);
}
}
eXosip_call_build_answer(ev->tid,200,&ans);
if (ans)
eXosip_call_send_answer(ev->tid,200,ans);
}
}
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){
if (MSG_IS_INFO(ev->request)){
osip_content_type_t *ct;
ct=osip_message_get_content_type(ev->request);
if (ct && ct->subtype){
if (strcmp(ct->subtype,"media_control+xml")==0)
process_media_control_xml(sal,ev);
else if (strcmp(ct->subtype,"dtmf-relay")==0)
process_dtmf_relay(sal,ev);
else {
ms_message("Unhandled SIP INFO.");
/*send an "Not implemented" answer*/
eXosip_lock();
eXosip_call_build_answer(ev->tid,501,&ans);
if (ans)
eXosip_call_send_answer(ev->tid,501,ans);
eXosip_unlock();
}
}else{
/*empty SIP INFO, probably to test we are alive. Send an empty answer*/
eXosip_lock();
eXosip_call_build_answer(ev->tid,200,&ans);
if (ans)
eXosip_call_send_answer(ev->tid,200,ans);
eXosip_unlock();
}
}else if(MSG_IS_MESSAGE(ev->request)){
/* SIP messages could be received into call */
text_received(sal, ev);
eXosip_lock();
eXosip_call_build_answer(ev->tid,200,&ans);
if (ans)
eXosip_call_send_answer(ev->tid,200,ans);
eXosip_unlock();
}else if(MSG_IS_REFER(ev->request)){
SalOp *op=find_op(sal,ev);
ms_message("Receiving REFER 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);
ms_message("Receiving NOTIFY request !");
osip_from_to_str(ev->request->from,&from);
osip_message_header_get_byname(ev->request,"Event",0,&h);
if(h)
sal->callbacks.notify(op,from,h->hvalue);
/*answer that we received the notify*/
eXosip_lock();
eXosip_call_build_answer(ev->tid,200,&ans);
if (ans)
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 ?");
}
static void inc_update(Sal *sal, eXosip_event_t *ev){
osip_message_t *msg=NULL;
ms_message("Processing incoming UPDATE");
eXosip_lock();
eXosip_message_build_answer(ev->tid,200,&msg);
if (msg!=NULL)
eXosip_message_send_answer(ev->tid,200,msg);
eXosip_unlock();
}
static bool_t comes_from_local_if(osip_message_t *msg){
osip_via_t *via=NULL;
osip_message_get_via(msg,0,&via);
if (via){
const char *host;
host=osip_via_get_host(via);
if (strcmp(host,"127.0.0.1")==0 || strcmp(host,"::1")==0){
osip_generic_param_t *param=NULL;
osip_via_param_get_byname(via,"received",&param);
if (param==NULL) return TRUE;
if (param->gvalue &&
(strcmp(param->gvalue,"127.0.0.1")==0 || strcmp(param->gvalue,"::1")==0)){
return TRUE;
}
}
}
return FALSE;
}
static void text_received(Sal *sal, eXosip_event_t *ev){
osip_body_t *body=NULL;
char *from=NULL,*msg;
osip_message_get_body(ev->request,0,&body);
if (body==NULL){
ms_error("Could not get text message from SIP body");
return;
}
msg=body->body;
osip_from_to_str(ev->request->from,&from);
sal->callbacks.text_received(sal,from,msg);
osip_free(from);
}
static void other_request(Sal *sal, eXosip_event_t *ev){
ms_message("in other_request");
if (ev->request==NULL) return;
if (strcmp(ev->request->sip_method,"MESSAGE")==0){
text_received(sal,ev);
eXosip_message_send_answer(ev->tid,200,NULL);
}else if (strcmp(ev->request->sip_method,"OPTIONS")==0){
osip_message_t *options=NULL;
eXosip_options_build_answer(ev->tid,200,&options);
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)) {
eXosip_message_send_answer(ev->tid,200,NULL);
ms_message("Receiving WAKEUP request !");
sal->callbacks.internal_message(sal,"WAKEUP");
}else if (strncmp(ev->request->sip_method, "REFER", 5) == 0){
ms_message("Receiving REFER request !");
if (comes_from_local_if(ev->request)) {
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);
}else {
char *tmp=NULL;
size_t msglen=0;
osip_message_to_str(ev->request,&tmp,&msglen);
if (tmp){
ms_message("Unsupported request received:\n%s",tmp);
osip_free(tmp);
}
/*answer with a 501 Not implemented*/
eXosip_message_send_answer(ev->tid,501,NULL);
}
}
static void masquerade_via(osip_message_t *msg, const char *ip, const char *port){
osip_via_t *via=NULL;
osip_message_get_via(msg,0,&via);
if (via){
osip_free(via->port);
via->port=osip_strdup(port);
osip_free(via->host);
via->host=osip_strdup(ip);
}
}
static bool_t register_again_with_updated_contact(SalOp *op, osip_message_t *orig_request, osip_message_t *last_answer){
osip_message_t *msg;
const char *received;
int rport;
osip_contact_t *ctt=NULL;
char *tmp;
char port[20];
SalAddress *addr;
if (extract_received_rport(last_answer,&received,&rport)==-1) return FALSE;
osip_message_get_contact(orig_request,0,&ctt);
if (strcmp(ctt->url->host,received)==0){
/*ip address matches, check ports*/
const char *contact_port=ctt->url->port;
if (contact_port==NULL || contact_port[0]=='\0')
contact_port="5060";
if (atoi(contact_port)==rport){
ms_message("Register has up to date contact, doing nothing.");
return FALSE;
}else ms_message("ports do not match, need to update the register (%s <> %i)", contact_port,rport);
}
eXosip_lock();
msg=NULL;
eXosip_register_build_register(op->rid,op->expires,&msg);
if (msg==NULL){
eXosip_unlock();
ms_warning("Fail to create a contact updated register.");
return FALSE;
}
osip_message_get_contact(msg,0,&ctt);
if (ctt->url->host!=NULL){
osip_free(ctt->url->host);
}
ctt->url->host=osip_strdup(received);
if (ctt->url->port!=NULL){
osip_free(ctt->url->port);
}
snprintf(port,sizeof(port),"%i",rport);
ctt->url->port=osip_strdup(port);
if (op->masquerade_via) masquerade_via(msg,received,port);
eXosip_register_send_register(op->rid,msg);
eXosip_unlock();
osip_contact_to_str(ctt,&tmp);
addr=sal_address_new(tmp);
osip_free(tmp);
sal_address_clean(addr);
tmp=sal_address_as_string(addr);
sal_op_set_contact(op,tmp);
sal_address_destroy(addr);
ms_message("Resending new register with updated contact %s",tmp);
ms_free(tmp);
return TRUE;
}
static void registration_success(Sal *sal, eXosip_event_t *ev){
SalOp *op=sal_find_register(sal,ev->rid);
osip_header_t *h=NULL;
bool_t registered;
if (op==NULL){
ms_error("Receiving register response for unknown operation");
return;
}
osip_message_get_expires(ev->request,0,&h);
if (h!=NULL && atoi(h->hvalue)!=0){
registered=TRUE;
if (!register_again_with_updated_contact(op,ev->request,ev->response)){
sal->callbacks.register_success(op,registered);
}
}else {
sal->callbacks.register_success(op,FALSE);
}
}
static bool_t registration_failure(Sal *sal, eXosip_event_t *ev){
int status_code=0;
const char *reason=NULL;
SalOp *op=sal_find_register(sal,ev->rid);
SalReason sr=SalReasonUnknown;
SalError se=SalErrorUnknown;
if (op==NULL){
ms_error("Receiving register failure for unknown operation");
return TRUE;
}
if (ev->response){
status_code=osip_message_get_status_code(ev->response);
reason=osip_message_get_reason_phrase(ev->response);
}
switch(status_code){
case 401:
case 407:
return process_authentication(sal,ev);
break;
case 606: /*Not acceptable, workaround for proxies that don't like private addresses
in vias, such as ekiga.net
On the opposite, freephonie.net bugs when via are masqueraded.
*/
op->masquerade_via=TRUE;
default:
/* if contact is up to date, process the failure, otherwise resend a new register with
updated contact first, just in case the faillure is due to incorrect contact */
if (ev->response && register_again_with_updated_contact(op,ev->request,ev->response))
return TRUE; /*we are retrying with an updated contact*/
if (status_code==403){
se=SalErrorFailure;
sr=SalReasonForbidden;
}else if (status_code==0){
se=SalErrorNoResponse;
}
sal->callbacks.register_failure(op,se,sr,reason);
}
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);
if (ev->request && strcmp(osip_message_get_method(ev->request),"OPTIONS")==0)
sal->callbacks.ping_reply(op);
}
}
static bool_t process_event(Sal *sal, eXosip_event_t *ev){
ms_message("linphone process event get a message %d\n",ev->type);
switch(ev->type){
case EXOSIP_CALL_ANSWERED:
ms_message("CALL_ANSWERED\n");
call_accepted(sal,ev);
authentication_ok(sal,ev);
break;
case EXOSIP_CALL_CLOSED:
case EXOSIP_CALL_CANCELLED:
ms_message("CALL_CLOSED or CANCELLED\n");
call_terminated(sal,ev);
break;
case EXOSIP_CALL_TIMEOUT:
case EXOSIP_CALL_NOANSWER:
ms_message("CALL_TIMEOUT or NOANSWER\n");
return call_failure(sal,ev);
break;
case EXOSIP_CALL_REQUESTFAILURE:
case EXOSIP_CALL_GLOBALFAILURE:
case EXOSIP_CALL_SERVERFAILURE:
ms_message("CALL_REQUESTFAILURE or GLOBALFAILURE or SERVERFAILURE\n");
return call_failure(sal,ev);
break;
case EXOSIP_CALL_RELEASED:
ms_message("CALL_RELEASED\n");
call_released(sal, ev);
break;
case EXOSIP_CALL_INVITE:
ms_message("CALL_NEW\n");
inc_new_call(sal,ev);
break;
case EXOSIP_CALL_REINVITE:
handle_reinvite(sal,ev);
break;
case EXOSIP_CALL_ACK:
ms_message("CALL_ACK");
handle_ack(sal,ev);
break;
case EXOSIP_CALL_REDIRECTED:
ms_message("CALL_REDIRECTED");
eXosip_default_action(ev);
break;
case EXOSIP_CALL_PROCEEDING:
ms_message("CALL_PROCEEDING");
call_proceeding(sal,ev);
break;
case EXOSIP_CALL_RINGING:
ms_message("CALL_RINGING");
call_ringing(sal,ev);
break;
case EXOSIP_CALL_MESSAGE_NEW:
ms_message("EXOSIP_CALL_MESSAGE_NEW");
call_message_new(sal,ev);
break;
case EXOSIP_CALL_MESSAGE_REQUESTFAILURE:
if (ev->did<0 && ev->response &&
(ev->response->status_code==407 || ev->response->status_code==401)){
return process_authentication(sal,ev);
}
break;
case EXOSIP_IN_SUBSCRIPTION_NEW:
ms_message("CALL_IN_SUBSCRIPTION_NEW ");
sal_exosip_subscription_recv(sal,ev);
break;
case EXOSIP_IN_SUBSCRIPTION_RELEASED:
ms_message("CALL_SUBSCRIPTION_NEW ");
sal_exosip_in_subscription_closed(sal,ev);
break;
case EXOSIP_SUBSCRIPTION_UPDATE:
ms_message("CALL_SUBSCRIPTION_UPDATE");
break;
case EXOSIP_SUBSCRIPTION_NOTIFY:
ms_message("CALL_SUBSCRIPTION_NOTIFY");
sal_exosip_notify_recv(sal,ev);
break;
case EXOSIP_SUBSCRIPTION_ANSWERED:
ms_message("EXOSIP_SUBSCRIPTION_ANSWERED, ev->sid=%i, ev->did=%i\n",ev->sid,ev->did);
sal_exosip_subscription_answered(sal,ev);
break;
case EXOSIP_SUBSCRIPTION_CLOSED:
ms_message("EXOSIP_SUBSCRIPTION_CLOSED\n");
sal_exosip_subscription_closed(sal,ev);
break;
case EXOSIP_SUBSCRIPTION_REQUESTFAILURE: /**< announce a request failure */
if (ev->response && (ev->response->status_code == 407 || ev->response->status_code == 401)){
return process_authentication(sal,ev);
}
case EXOSIP_SUBSCRIPTION_SERVERFAILURE:
case EXOSIP_SUBSCRIPTION_GLOBALFAILURE:
sal_exosip_subscription_closed(sal,ev);
break;
case EXOSIP_REGISTRATION_FAILURE:
ms_message("REGISTRATION_FAILURE\n");
return registration_failure(sal,ev);
break;
case EXOSIP_REGISTRATION_SUCCESS:
authentication_ok(sal,ev);
registration_success(sal,ev);
break;
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) {
switch (ev->response->status_code) {
case 407:
case 401:
return process_authentication(sal,ev);
case 412: {
eXosip_automatic_action ();
return 1;
}
}
}
other_request_reply(sal,ev);
break;
default:
ms_message("Unhandled exosip event ! %i",ev->type);
break;
}
return TRUE;
}
int sal_iterate(Sal *sal){
eXosip_event_t *ev;
while((ev=eXosip_event_wait(0,0))!=NULL){
if (process_event(sal,ev))
eXosip_event_free(ev);
}
eXosip_lock();
eXosip_automatic_refresh();
eXosip_unlock();
return 0;
}
int sal_register(SalOp *h, const char *proxy, const char *from, int expires){
osip_message_t *msg;
sal_op_set_route(h,proxy);
if (h->rid==-1){
eXosip_lock();
h->rid=eXosip_register_build_initial_register(from,proxy,sal_op_get_contact(h),expires,&msg);
sal_add_register(h->base.root,h);
}else{
eXosip_lock();
eXosip_register_build_register(h->rid,expires,&msg);
}
eXosip_register_send_register(h->rid,msg);
eXosip_unlock();
h->expires=expires;
return 0;
}
int sal_unregister(SalOp *h){
osip_message_t *msg=NULL;
eXosip_lock();
eXosip_register_build_register(h->rid,0,&msg);
if (msg) eXosip_register_send_register(h->rid,msg);
else ms_warning("Could not build unREGISTER !");
eXosip_unlock();
return 0;
}
SalAddress * sal_address_new(const char *uri){
osip_from_t *from;
osip_from_init(&from);
if (osip_from_parse(from,uri)!=0){
osip_from_free(from);
return NULL;
}
if (from->displayname!=NULL && from->displayname[0]=='"'){
char *unquoted=osip_strdup_without_quote(from->displayname);
osip_free(from->displayname);
from->displayname=unquoted;
}
return (SalAddress*)from;
}
SalAddress * sal_address_clone(const SalAddress *addr){
osip_from_t *ret=NULL;
osip_from_clone((osip_from_t*)addr,&ret);
return (SalAddress*)ret;
}
#define null_if_empty(s) (((s)!=NULL && (s)[0]!='\0') ? (s) : NULL )
const char *sal_address_get_scheme(const SalAddress *addr){
const osip_from_t *u=(const osip_from_t*)addr;
return null_if_empty(u->url->scheme);
}
const char *sal_address_get_display_name(const SalAddress* addr){
const osip_from_t *u=(const osip_from_t*)addr;
return null_if_empty(u->displayname);
}
const char *sal_address_get_username(const SalAddress *addr){
const osip_from_t *u=(const osip_from_t*)addr;
return null_if_empty(u->url->username);
}
const char *sal_address_get_domain(const SalAddress *addr){
const osip_from_t *u=(const osip_from_t*)addr;
return null_if_empty(u->url->host);
}
void sal_address_set_display_name(SalAddress *addr, const char *display_name){
osip_from_t *u=(osip_from_t*)addr;
if (u->displayname!=NULL){
osip_free(u->displayname);
u->displayname=NULL;
}
if (display_name!=NULL && display_name[0]!='\0'){
u->displayname=osip_strdup(display_name);
}
}
void sal_address_set_username(SalAddress *addr, const char *username){
osip_from_t *uri=(osip_from_t*)addr;
if (uri->url->username!=NULL){
osip_free(uri->url->username);
uri->url->username=NULL;
}
if (username)
uri->url->username=osip_strdup(username);
}
void sal_address_set_domain(SalAddress *addr, const char *host){
osip_from_t *uri=(osip_from_t*)addr;
if (uri->url->host!=NULL){
osip_free(uri->url->host);
uri->url->host=NULL;
}
if (host)
uri->url->host=osip_strdup(host);
}
void sal_address_set_port(SalAddress *addr, const char *port){
osip_from_t *uri=(osip_from_t*)addr;
if (uri->url->port!=NULL){
osip_free(uri->url->port);
uri->url->port=NULL;
}
if (port)
uri->url->port=osip_strdup(port);
}
void sal_address_set_port_int(SalAddress *uri, int port){
char tmp[12];
if (port==5060){
/*this is the default, special case to leave the port field blank*/
sal_address_set_port(uri,NULL);
return;
}
snprintf(tmp,sizeof(tmp),"%i",port);
sal_address_set_port(uri,tmp);
}
void sal_address_clean(SalAddress *addr){
osip_generic_param_freelist(& ((osip_from_t*)addr)->gen_params);
osip_uri_param_freelist(& ((osip_from_t*)addr)->url->url_params);
}
char *sal_address_as_string(const SalAddress *u){
char *tmp,*ret;
osip_from_t *from=(osip_from_t *)u;
char *old_displayname=NULL;
/* hack to force use of quotes around the displayname*/
if (from->displayname!=NULL
&& from->displayname[0]!='"'){
old_displayname=from->displayname;
from->displayname=osip_enquote(from->displayname);
}
osip_from_to_str(from,&tmp);
if (old_displayname!=NULL){
ms_free(from->displayname);
from->displayname=old_displayname;
}
ret=ms_strdup(tmp);
osip_free(tmp);
return ret;
}
char *sal_address_as_string_uri_only(const SalAddress *u){
char *tmp=NULL,*ret;
osip_uri_to_str(((osip_from_t*)u)->url,&tmp);
ret=ms_strdup(tmp);
osip_free(tmp);
return ret;
}
void sal_address_add_param(SalAddress *u,const char* name,const char* value) {
osip_uri_uparam_add (((osip_from_t*)u)->url,ms_strdup(name),ms_strdup(value));
}
void sal_address_destroy(SalAddress *u){
osip_from_free((osip_from_t*)u);
}
void sal_set_keepalive_period(Sal *ctx,unsigned int value) {
ctx->keepalive_period=value;
eXosip_set_option (EXOSIP_OPT_UDP_KEEP_ALIVE, &value);
}
const char * sal_address_get_port(const SalAddress *addr) {
const osip_from_t *u=(const osip_from_t*)addr;
return null_if_empty(u->url->port);
}
int sal_address_get_port_int(const SalAddress *uri) {
const char* port = sal_address_get_port(uri);
if (port != NULL) {
return atoi(port);
} else {
return 5060;
}
}
/*
* Send a re-Invite used to hold the current call
*/
int sal_call_hold(SalOp *h, bool_t holdon)
{
int err=0;
osip_message_t *reinvite=NULL;
if(eXosip_call_build_request(h->did,"INVITE",&reinvite) != OSIP_SUCCESS || reinvite==NULL)
return -1;
osip_message_set_subject(reinvite,holdon ? "Phone call hold" : "Phone call resume" );
osip_message_set_allow(reinvite, "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO");
if (h->base.root->session_expires!=0){
osip_message_set_header(reinvite, "Session-expires", "200");
osip_message_set_supported(reinvite, "timer");
}
//add something to say that the distant sip phone will be in sendonly/sendrecv mode
if (h->base.local_media){
h->sdp_offering=TRUE;
sal_media_description_set_dir(h->base.local_media, holdon ? SalStreamSendOnly : SalStreamSendRecv);
set_sdp_from_desc(reinvite,h->base.local_media);
}else h->sdp_offering=FALSE;
eXosip_lock();
err = eXosip_call_send_request(h->did, reinvite);
eXosip_unlock();
return err;
}
/* sends a reinvite. Local media description may have changed by application since call establishment*/
int sal_call_update(SalOp *h){
int err=0;
osip_message_t *reinvite=NULL;
eXosip_lock();
if(eXosip_call_build_request(h->did,"INVITE",&reinvite) != OSIP_SUCCESS || reinvite==NULL){
eXosip_unlock();
return -1;
}
eXosip_unlock();
osip_message_set_subject(reinvite,osip_strdup("Phone call parameters updated"));
osip_message_set_allow(reinvite, "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO");
if (h->base.root->session_expires!=0){
osip_message_set_header(reinvite, "Session-expires", "200");
osip_message_set_supported(reinvite, "timer");
}
if (h->base.local_media){
h->sdp_offering=TRUE;
set_sdp_from_desc(reinvite,h->base.local_media);
}else h->sdp_offering=FALSE;
eXosip_lock();
err = eXosip_call_send_request(h->did, reinvite);
eXosip_unlock();
return err;
}