linphone-ios/coreapi/sal_eXosip2.c
2012-12-13 14:50:09 +01:00

2564 lines
71 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"
#ifdef ANDROID
// Necessary to make it linked
static void for_linker() { eXosip_transport_hook_register(NULL); }
#endif
static bool_t call_failure(Sal *sal, eXosip_event_t *ev);
static void text_received(Sal *sal, eXosip_event_t *ev);
static void masquerade_via(osip_message_t *msg, const char *ip, const char *port);
static bool_t fix_message_contact(SalOp *op, osip_message_t *request,osip_message_t *last_answer, bool_t expire_last_contact);
static void update_contact_from_response(SalOp *op, osip_message_t *response);
void _osip_list_set_empty(osip_list_t *l, void (*freefunc)(void*)){
void *data;
while(!osip_list_eol(l,0)) {
data=osip_list_get(l,0);
osip_list_remove(l,0);
if (data) 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 *message){
const MSList *elem;
SalOp *op;
osip_call_id_t *callid=osip_message_get_call_id(message);
if (callid==NULL) {
ms_error("There is no call-id in this message !");
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_new0(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;
op->auth_info=NULL;
op->terminated=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);
eXosip_register_remove(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);
}
if (op->auth_info) {
sal_auth_info_delete(op->auth_info);
}
__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;
sal->double_reg=TRUE;
sal->use_rports=TRUE;
sal->use_101=TRUE;
sal->reuse_authorization=FALSE;
sal->rootCa = 0;
sal->verify_server_certs=TRUE;
sal->expire_old_contact=FALSE;
sal->add_dates=FALSE;
sal->dscp=-1;
return sal;
}
void sal_uninit(Sal* sal){
eXosip_quit();
if (sal->rootCa)
ms_free(sal->rootCa);
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_released==NULL)
ctx->callbacks.call_released=(SalOnCallReleased)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.ping_reply==NULL)
ctx->callbacks.ping_reply=(SalOnPingReply)unimplemented_stub;
}
int sal_unlisten_ports(Sal *ctx){
if (ctx->running){
eXosip_quit();
eXosip_init();
ctx->running=FALSE;
}
return 0;
}
int sal_reset_transports(Sal *ctx){
#ifdef HAVE_EXOSIP_RESET_TRANSPORTS
if (ctx->running){
ms_message("Exosip transports reset.");
eXosip_reset_transports();
}
return 0;
#else
ms_warning("sal_reset_transports() not implemented in this version.");
return -1;
#endif
}
static void set_tls_options(Sal *ctx){
if (ctx->rootCa) {
eXosip_tls_ctx_t tlsCtx;
memset(&tlsCtx, 0, sizeof(tlsCtx));
snprintf(tlsCtx.root_ca_cert, sizeof(tlsCtx.client.cert), "%s", ctx->rootCa);
eXosip_set_tls_ctx(&tlsCtx);
}
#ifdef HAVE_EXOSIP_TLS_VERIFY_CERTIFICATE
eXosip_tls_verify_certificate(ctx->verify_server_certs);
#endif
}
void sal_set_dscp(Sal *ctx, int dscp){
ctx->dscp=dscp;
if (dscp!=-1)
eXosip_set_option(EXOSIP_OPT_SET_DSCP,&ctx->dscp);
}
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;
int keepalive = ctx->keepalive_period;
switch (tr) {
case SalTransportUDP:
proto=IPPROTO_UDP;
eXosip_set_option (EXOSIP_OPT_UDP_KEEP_ALIVE, &keepalive);
break;
case SalTransportTCP:
case SalTransportTLS:
proto= IPPROTO_TCP;
keepalive=-1;
eXosip_set_option (EXOSIP_OPT_UDP_KEEP_ALIVE,&keepalive);
set_tls_options(ctx);
break;
default:
ms_warning("unexpected proto, using datagram");
}
/*see if it looks like an IPv6 address*/
int use_rports = ctx->use_rports; // Copy char to int to avoid bad alignment
eXosip_set_option(EXOSIP_OPT_USE_RPORT,&use_rports);
int dont_use_101 = !ctx->use_101; // Copy char to int to avoid bad alignment
eXosip_set_option(EXOSIP_OPT_DONT_SEND_101,&dont_use_101);
sal_set_dscp(ctx,ctx->dscp);
sal_use_dates(ctx,ctx->add_dates);
ipv6=strchr(addr,':')!=NULL;
eXosip_enable_ipv6(ipv6);
if (is_secure && tr == SalTransportUDP){
ms_fatal("SIP over DTLS is not supported yet.");
return -1;
}
err=eXosip_listen_addr(proto, addr, port, ipv6 ? PF_INET6 : PF_INET, is_secure);
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);
}
void sal_use_double_registrations(Sal *ctx, bool_t enabled){
ctx->double_reg=enabled;
}
void sal_expire_old_registration_contacts(Sal *ctx, bool_t enabled){
ctx->expire_old_contact=enabled;
}
void sal_use_dates(Sal *ctx, bool_t enabled){
ctx->add_dates=enabled;
#ifdef EXOSIP_OPT_REGISTER_WITH_DATE
{
int tmp=enabled;
eXosip_set_option(EXOSIP_OPT_REGISTER_WITH_DATE,&tmp);
}
#else
if (enabled) ms_warning("Exosip does not support EXOSIP_OPT_REGISTER_WITH_DATE option.");
#endif
}
void sal_use_rport(Sal *ctx, bool_t use_rports){
ctx->use_rports=use_rports;
}
void sal_use_101(Sal *ctx, bool_t use_101){
ctx->use_101=use_101;
}
void sal_set_root_ca(Sal* ctx, const char* rootCa) {
if (ctx->rootCa)
ms_free(ctx->rootCa);
ctx->rootCa = ms_strdup(rootCa);
set_tls_options(ctx);
}
const char *sal_get_root_ca(Sal* ctx) {
return ctx->rootCa;
}
void sal_verify_server_certificates(Sal *ctx, bool_t verify){
ctx->verify_server_certs=verify;
#ifdef HAVE_EXOSIP_TLS_VERIFY_CERTIFICATE
eXosip_tls_verify_certificate(verify);
#endif
}
static int extract_received_rport(osip_message_t *msg, const char **received, int *rportval,SalTransport* transport){
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) {
ms_warning("extract_received_rport(): no via.");
return -1;
}
*transport = sal_transport_parse(via->protocol);
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);
*received=via->host;
}
param=NULL;
osip_via_param_get_byname(via,"received",&param);
if (param) *received=param->gvalue;
if (rport==NULL && *received==NULL){
ms_warning("extract_received_rport(): no rport and no received parameters.");
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 of type %s",h->sdp_offering ? "outgoing" : "incoming");
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;
if (h->sdp_answer){
sdp_message_free(h->sdp_answer);
}
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].rtp_port>0){
strcpy(h->result->streams[i].rtp_addr,h->base.remote_media->streams[i].rtp_addr);
strcpy(h->result->streams[i].rtcp_addr,h->base.remote_media->streams[i].rtcp_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].rtp_port=h->base.remote_media->streams[i].rtp_port;
h->result->streams[i].rtcp_port=h->base.remote_media->streams[i].rtcp_port;
if (h->result->streams[i].proto == SalProtoRtpSavp) {
h->result->streams[i].crypto[0] = h->base.remote_media->streams[i].crypto[0];
}
}
}
}
}
int sal_call_is_offerer(const SalOp *h){
return h->sdp_offering;
}
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;
if (h->base.remote_media){
/*case of an incoming call where we modify the local capabilities between the time
* the call is ringing and it is accepted (for example if you want to accept without video*/
/*reset the sdp answer so that it is computed again*/
if (h->sdp_answer){
sdp_message_free(h->sdp_answer);
h->sdp_answer=NULL;
}
}
return 0;
}
int sal_call(SalOp *h, const char *from, const char *to){
int err;
const char *route;
osip_message_t *invite=NULL;
osip_call_id_t *callid;
sal_op_set_from(h,from);
sal_op_set_to(h,to);
sal_exosip_fix_route(h);
h->terminated = FALSE;
route = sal_op_get_route(h);
err=eXosip_call_build_initial_invite(&invite,to,from,route,"Phone call");
if (err!=0){
ms_error("Could not create call. Error %d (from=%s to=%s route=%s)",
err, from, to, route);
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 ! Error code %d", err);
return -1;
}else{
callid=osip_message_get_call_id(invite);
osip_call_id_to_str(callid,(char **)(&h->base.call_id));
sal_add_call(h->base.root,h);
}
return 0;
}
int sal_call_notify_ringing(SalOp *h, bool_t early_media){
osip_message_t *msg;
/*if early media send also 180 and 183 */
if (early_media){
msg=NULL;
eXosip_lock();
eXosip_call_build_answer(h->tid,183,&msg);
if (msg){
sdp_process(h);
if (h->sdp_answer){
set_sdp(msg,h->sdp_answer);
sdp_message_free(h->sdp_answer);
h->sdp_answer=NULL;
}
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==NULL) sdp_process(h);
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_remote_media_description(SalOp *h){
return h->base.remote_media;
}
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;
}
static int send_notify_for_refer(int did, const char *sipfrag){
osip_message_t *msg;
eXosip_lock();
eXosip_call_build_notify(did,EXOSIP_SUBCRSTATE_ACTIVE,&msg);
if (msg==NULL){
eXosip_unlock();
ms_warning("Could not build NOTIFY for refer.");
return -1;
}
osip_message_set_content_type(msg,"message/sipfrag");
osip_message_set_header(msg,"Event","refer");
osip_message_set_body(msg,sipfrag,strlen(sipfrag));
eXosip_call_send_request(did,msg);
eXosip_unlock();
return 0;
}
/* currently only support to notify trying and 200Ok*/
int sal_call_notify_refer_state(SalOp *h, SalOp *newcall){
if (newcall==NULL){
/* in progress*/
send_notify_for_refer(h->did,"SIP/2.0 100 Trying\r\n");
}
else if (newcall->cid!=-1){
if (newcall->did==-1){
/* not yet established*/
if (!newcall->terminated){
/* in progress*/
send_notify_for_refer(h->did,"SIP/2.0 100 Trying\r\n");
}
}else{
if (!newcall->terminated){
if (send_notify_for_refer(h->did,"SIP/2.0 200 Ok\r\n")==-1){
/* we need previous notify transaction to complete, so buffer the request for later*/
h->sipfrag_pending="SIP/2.0 200 Ok\r\n";
}
}
}
}
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);
sal_exosip_fix_route(op);
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_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!=NULL && 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;
}
static void push_auth_to_exosip(const SalAuthInfo *info){
const char *userid;
if (info->userid==NULL || info->userid[0]=='\0') userid=info->username;
else userid=info->userid;
ms_message("Authentication info for username [%s], id[%s], realm [%s] added to eXosip", info->username,userid, info->realm);
eXosip_add_authentication_info (info->username,userid,
info->password, NULL,info->realm);
}
/*
* Just for symmetry ;-)
*/
static void pop_auth_from_exosip() {
eXosip_clear_authentication_info();
}
int sal_call_terminate(SalOp *h){
int err;
if (h == NULL) return -1;
if (h->auth_info) push_auth_to_exosip(h->auth_info);
eXosip_lock();
err=eXosip_call_terminate(h->cid,h->did);
eXosip_unlock();
if (!h->base.root->reuse_authorization) pop_auth_from_exosip();
if (err!=0){
ms_warning("Exosip could not terminate the call: cid=%i did=%i", h->cid,h->did);
}
h->terminated=TRUE;
return 0;
}
void sal_op_authenticate(SalOp *h, const SalAuthInfo *info){
if (h->terminated) return;
if (h->pending_auth){
push_auth_to_exosip(info);
/*FIXME exosip does not take into account this update register message*/
/*
if (fix_message_contact(h, h->pending_auth->request,h->pending_auth->response)) {
};
*/
update_contact_from_response(h,h->pending_auth->response);
eXosip_lock();
eXosip_default_action(h->pending_auth);
eXosip_unlock();
ms_message("eXosip_default_action() done");
if (!h->base.root->reuse_authorization) pop_auth_from_exosip();
if (h->auth_info) sal_auth_info_delete(h->auth_info); /*if already exist*/
h->auth_info=sal_auth_info_clone(info); /*store auth info for subsequent request*/
}
}
void sal_op_cancel_authentication(SalOp *h) {
if (h->rid >0) {
sal_op_get_sal(h)->callbacks.register_failure(h,SalErrorFailure, SalReasonForbidden,"Authentication failure");
} else if (h->cid >0) {
sal_op_get_sal(h)->callbacks.call_failure(h,SalErrorFailure, SalReasonForbidden,"Authentication failure",0);
} else {
ms_warning("Auth failure not handled");
}
}
static void set_network_origin(SalOp *op, osip_message_t *req){
const char *received=NULL;
int rport=5060;
char origin[64]={0};
SalTransport transport;
if (extract_received_rport(req,&received,&rport,&transport)!=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);
}
if (transport != SalTransportUDP) {
snprintf(origin,sizeof(origin)-1,"sip:%s:%i",received,rport);
} else {
snprintf(origin,sizeof(origin)-1,"sip:%s:%i;transport=%s",received,rport,sal_transport_to_string(transport));
}
__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->nid>0){
return sal_find_in_subscribe(sal,ev->nid);
}
if (ev->response) return sal_find_other(sal,ev->response);
else if (ev->request) return sal_find_other(sal,ev->request);
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);
osip_call_id_t *callid=osip_message_get_call_id(ev->request);
osip_call_id_to_str(callid,(char**)(&op->base.call_id));
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;
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);
}else {
op->sdp_offering=TRUE;
}
sal->callbacks.call_updating(op);
}
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;
}
if (op->terminated) {
ms_warning("ack for terminated call, ignoring");
return;
}
if (op->sdp_offering){
sdp=eXosip_get_sdp_info(ev->ack);
if (sdp){
if (op->base.remote_media)
sal_media_description_unref(op->base.remote_media);
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){
op->reinvite=FALSE;
}
sal->callbacks.call_ack(op);
}
static void update_contact_from_response(SalOp *op, osip_message_t *response){
const char *received;
int rport;
SalTransport transport;
if (extract_received_rport(response,&received,&rport,&transport)==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);
if (transport!=SalTransportUDP)
sal_address_set_transport(addr,transport);
tmp=sal_address_as_string(addr);
ms_message("Contact address updated to %s",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 || op->terminated==TRUE) {
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 || op->terminated==TRUE) {
ms_warning("This call has been already terminated.");
eXosip_lock();
eXosip_call_terminate(ev->cid,ev->did);
eXosip_unlock();
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);
if (msg==NULL) {
ms_warning("This call has been already terminated.");
eXosip_lock();
eXosip_call_terminate(ev->cid,ev->did);
eXosip_unlock();
return ;
}
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->callbacks.call_terminated(op,from!=NULL ? from : sal_op_get_from(op));
if (from) osip_free(from);
op->terminated=TRUE;
}
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;
}
if (!op->terminated){
/* no response received so far */
call_failure(sal,ev);
}
sal->callbacks.call_released(op);
}
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;
}else{
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 (op->pending_auth){
eXosip_event_free(op->pending_auth);
sal_remove_pending_auth(sal,op);
op->pending_auth=NULL;
}
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;
}
op->terminated=TRUE;
sal->callbacks.call_failure(op,error,sr,reason,code);
if (computedReason != NULL){
ms_free(computedReason);
}
return TRUE;
}
/* Request remote side to send us VFU */
void sal_call_send_vfu_request(SalOp *h){
osip_message_t *msg=NULL;
char info_body[] =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>"
"<media_control>"
" <vc_primitive>"
" <to_encoder>"
" <picture_fast_update></picture_fast_update>"
" </to_encoder>"
" </vc_primitive>"
"</media_control>";
char clen[10];
eXosip_lock();
eXosip_call_build_info(h->did,&msg);
if (msg){
osip_message_set_body(msg,info_body,strlen(info_body));
osip_message_set_content_type(msg,"application/media_control+xml");
snprintf(clen,sizeof(clen),"%lu",(unsigned long)strlen(info_body));
osip_message_set_content_length(msg,clen);
eXosip_call_send_request(h->did,msg);
ms_message("Sending VFU request !");
}
eXosip_unlock();
}
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_lock();
eXosip_call_build_answer(ev->tid,200,&ans);
if (ans)
eXosip_call_send_answer(ev->tid,200,ans);
eXosip_unlock();
}
}
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 process_notify(Sal *sal, eXosip_event_t *ev){
osip_header_t *h=NULL;
char *from=NULL;
SalOp *op=find_op(sal,ev);
osip_message_t *ans=NULL;
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){
osip_body_t *body=NULL;
//osip_content_type_t *ct=NULL;
osip_message_get_body(ev->request,0,&body);
//ct=osip_message_get_content_type(ev->request);
if (h->hvalue && strcasecmp(h->hvalue,"refer")==0){
/*special handling of refer events*/
if (body && body->body){
osip_message_t *msg;
osip_message_init(&msg);
if (osip_message_parse_sipfrag(msg,body->body,strlen(body->body))==0){
int code=osip_message_get_status_code(msg);
if (code==100){
sal->callbacks.notify_refer(op,SalReferTrying);
}else if (code==200){
sal->callbacks.notify_refer(op,SalReferSuccess);
}else if (code>=400){
sal->callbacks.notify_refer(op,SalReferFailed);
}
}
osip_message_free(msg);
}
}else{
/*generic handling*/
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);
}
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)){
process_notify(sal,ev);
}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=NULL;
osip_content_type_t* content_type;
osip_uri_param_t* external_body_url;
char unquoted_external_body_url [256];
int external_body_size=0;
SalMessage salmsg;
char message_id[256]={0};
content_type= osip_message_get_content_type(ev->request);
if (!content_type) {
ms_error("Could not get message because no content type");
return;
}
osip_from_to_str(ev->request->from,&from);
if (content_type->type
&& strcmp(content_type->type, "text")==0
&& content_type->subtype
&& strcmp(content_type->subtype, "plain")==0 ) {
osip_message_get_body(ev->request,0,&body);
if (body==NULL){
ms_error("Could not get text message from SIP body");
osip_free(from);
return;
}
msg=body->body;
}else if (content_type->type
&& strcmp(content_type->type, "message")==0
&& content_type->subtype
&& strcmp(content_type->subtype, "external-body")==0 ) {
osip_content_type_param_get_byname(content_type, "URL", &external_body_url);
/*remove both first and last character*/
strncpy(unquoted_external_body_url
,&external_body_url->gvalue[1]
,external_body_size=MIN(strlen(external_body_url->gvalue)-1,sizeof(unquoted_external_body_url)));
unquoted_external_body_url[external_body_size-1]='\0';
} else {
ms_warning("Unsupported content type [%s/%s]",content_type->type,content_type->subtype);
osip_free(from);
return;
}
snprintf(message_id,sizeof(message_id)-1,"%s%s",ev->request->call_id->number,ev->request->cseq->number);
salmsg.from=from;
salmsg.text=msg;
salmsg.url=external_body_size>0 ? unquoted_external_body_url : NULL;
salmsg.message_id=message_id;
sal->callbacks.text_received(sal,&salmsg);
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 (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 fix_message_contact(SalOp *op, osip_message_t *request,osip_message_t *last_answer, bool_t expire_last_contact) {
osip_contact_t *ctt=NULL;
const char *received;
int rport;
SalTransport transport;
char port[20];
if (extract_received_rport(last_answer,&received,&rport,&transport)==-1) return FALSE;
osip_message_get_contact(request,0,&ctt);
if (ctt == NULL) {
ms_warning("fix_message_contact(): no contact to update");
return FALSE;
}
if (expire_last_contact){
osip_contact_t *oldct=NULL,*prevct;
osip_generic_param_t *param=NULL;
osip_contact_clone(ctt,&oldct);
while ((prevct=(osip_contact_t*)osip_list_get(&request->contacts,1))!=NULL){
osip_contact_free(prevct);
osip_list_remove(&request->contacts,1);
}
osip_list_add(&request->contacts,oldct,1);
osip_contact_param_get_byname(oldct,"expires",&param);
if (param){
if (param->gvalue) osip_free(param->gvalue);
param->gvalue=osip_strdup("0");
}else{
osip_contact_param_add(oldct,osip_strdup("expires"),osip_strdup("0"));
}
}
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(request,received,port);
if (transport != SalTransportUDP) {
sal_address_set_param((SalAddress *)ctt, "transport", sal_transport_to_string(transport));
}
return TRUE;
}
static bool_t register_again_with_updated_contact(SalOp *op, osip_message_t *orig_request, osip_message_t *last_answer){
osip_contact_t *ctt=NULL;
SalAddress* ori_contact_address=NULL;
const char *received;
int rport;
SalTransport transport;
char* tmp;
osip_message_t *msg=NULL;
Sal* sal=op->base.root;
int i=0;
bool_t found_valid_contact=FALSE;
bool_t from_request=FALSE;
if (sal->double_reg==FALSE ) return FALSE;
if (extract_received_rport(last_answer,&received,&rport,&transport)==-1) return FALSE;
do{
ctt=NULL;
osip_message_get_contact(last_answer,i,&ctt);
if (!from_request && ctt==NULL) {
osip_message_get_contact(orig_request,0,&ctt);
from_request=TRUE;
}
if (ctt){
osip_contact_to_str(ctt,&tmp);
ori_contact_address = sal_address_new(tmp);
/*check if contact is up to date*/
if (strcmp(sal_address_get_domain(ori_contact_address),received) ==0
&& sal_address_get_port_int(ori_contact_address) == rport
&& sal_address_get_transport(ori_contact_address) == transport) {
if (!from_request){
ms_message("Register response has up to date contact, doing nothing.");
}else {
ms_warning("Register response does not have up to date contact, but last request had."
"Stupid registrar detected, giving up.");
}
found_valid_contact=TRUE;
}
osip_free(tmp);
sal_address_destroy(ori_contact_address);
}else break;
i++;
}while(!found_valid_contact);
if (!found_valid_contact)
ms_message("Contact do not match, resending register.");
else return FALSE;
eXosip_lock();
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;
}
if (fix_message_contact(op,msg,last_answer,op->base.root->expire_old_contact)) {
eXosip_register_send_register(op->rid,msg);
eXosip_unlock();
ms_message("Resending new register with updated contact");
update_contact_from_response(op,last_answer);
return TRUE;
} else {
ms_warning("Fail to send updated register.");
eXosip_unlock();
return FALSE;
}
eXosip_unlock();
return FALSE;
}
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 423: /*interval too brief*/
{/*retry with greater interval */
osip_header_t *h=NULL;
osip_message_t *msg=NULL;
osip_message_header_get_byname(ev->response,"min-expires",0,&h);
if (h && h->hvalue && h->hvalue[0]!='\0'){
int val=atoi(h->hvalue);
if (val>op->expires)
op->expires=val;
}else op->expires*=2;
eXosip_lock();
eXosip_register_build_register(op->rid,op->expires,&msg);
eXosip_register_send_register(op->rid,msg);
eXosip_unlock();
}
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){
ms_message("Processing reponse status [%i] for method [%s]",ev->response->status_code,osip_message_get_method(ev->request));
update_contact_from_response(op,ev->response);
if (ev->request && strcmp(osip_message_get_method(ev->request),"OPTIONS")==0)
sal->callbacks.ping_reply(op);
}
if (ev->request && strcmp(osip_message_get_method(ev->request),"MESSAGE")==0) {
/*out of call message acknolegment*/
SalTextDeliveryStatus status=SalTextDeliveryFailed;
if (ev->response){
if (ev->response->status_code<200){
status=SalTextDeliveryInProgress;
}else if (ev->response->status_code<300 && ev->response->status_code>=200){
status=SalTextDeliveryDone;
}
}
sal->callbacks.text_delivery_update(op,status);
}
}
static void process_in_call_reply(Sal *sal, eXosip_event_t *ev){
SalOp *op=find_op(sal,ev);
if (ev->response){
if (ev->request && strcmp(osip_message_get_method(ev->request),"NOTIFY")==0){
if (op->sipfrag_pending){
send_notify_for_refer(op->did,op->sipfrag_pending);
op->sipfrag_pending=NULL;
}
}
}
}
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);
authentication_ok(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->response &&
(ev->response->status_code==407 || ev->response->status_code==401)){
return process_authentication(sal,ev);
}
break;
case EXOSIP_CALL_MESSAGE_ANSWERED:
ms_message("EXOSIP_CALL_MESSAGE_ANSWERED ");
process_in_call_reply(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:
case EXOSIP_NOTIFICATION_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);
}
#ifdef HAVE_EXOSIP_TRYLOCK
if (eXosip_trylock()==0){
eXosip_automatic_refresh();
eXosip_unlock();
}else{
ms_warning("eXosip_trylock busy.");
}
#else
eXosip_lock();
eXosip_automatic_refresh();
eXosip_unlock();
#endif
return 0;
}
static void register_set_contact(osip_message_t *msg, const char *contact){
osip_uri_param_t *param = NULL;
osip_contact_t *ct=NULL;
char *line=NULL;
/*we get the line parameter choosed by exosip, and add it to our own contact*/
osip_message_get_contact(msg,0,&ct);
if (ct!=NULL){
osip_uri_uparam_get_byname(ct->url, "line", &param);
if (param && param->gvalue)
line=osip_strdup(param->gvalue);
}
_osip_list_set_empty(&msg->contacts,(void (*)(void*))osip_contact_free);
osip_message_set_contact(msg,contact);
osip_message_get_contact(msg,0,&ct);
osip_uri_uparam_add(ct->url,osip_strdup("line"),line);
}
static void sal_register_add_route(osip_message_t *msg, const char *proxy){
osip_route_t *route;
osip_list_special_free(&msg->routes,(void (*)(void*))osip_route_free);
osip_route_init(&route);
if (osip_route_parse(route,proxy)==0){
osip_uri_param_t *lr_param = NULL;
osip_uri_uparam_get_byname(route->url, "lr", &lr_param);
if (lr_param == NULL){
osip_uri_uparam_add(route->url,osip_strdup("lr"),NULL);
}
osip_list_add(&msg->routes,route,0);
return;
}
osip_route_free(route);
}
int sal_register(SalOp *h, const char *proxy, const char *from, int expires){
osip_message_t *msg;
const char *contact=sal_op_get_contact(h);
sal_op_set_route(h,proxy);
if (h->rid==-1){
SalAddress *from_parsed=sal_address_new(from);
char domain[256];
char *uri, *domain_ptr = NULL;
if (from_parsed==NULL) {
ms_warning("sal_register() bad from %s",from);
return -1;
}
/* Get domain using sal_address_as_string_uri_only() and stripping the username part instead of
using sal_address_get_domain() because to have a properly formatted domain with IPv6 proxy addresses. */
uri = sal_address_as_string_uri_only(from_parsed);
if (uri) domain_ptr = strchr(uri, '@');
if (domain_ptr) {
snprintf(domain,sizeof(domain),"sip:%s",domain_ptr+1);
} else {
snprintf(domain,sizeof(domain),"sip:%s",sal_address_get_domain(from_parsed));
}
if (uri) ms_free(uri);
sal_address_destroy(from_parsed);
eXosip_lock();
h->rid=eXosip_register_build_initial_register(from,domain,NULL,expires,&msg);
if (msg){
if (contact) register_set_contact(msg,contact);
sal_register_add_route(msg,proxy);
sal_add_register(h->base.root,h);
}else{
ms_error("Could not build initial register.");
eXosip_unlock();
return -1;
}
}else{
eXosip_lock();
eXosip_register_build_register(h->rid,expires,&msg);
sal_register_add_route(msg,proxy);
}
if (msg){
eXosip_register_send_register(h->rid,msg);
}
eXosip_unlock();
h->expires=expires;
return (msg != NULL) ? 0 : -1;
}
int sal_register_refresh(SalOp *op, int expires){
osip_message_t *msg=NULL;
const char *contact=sal_op_get_contact(op);
if (op->rid==-1){
ms_error("Unexistant registration context, not possible to refresh.");
return -1;
}
#ifdef HAVE_EXOSIP_TRYLOCK
{
int tries=0;
/*iOS hack: in the keep alive handler, we have no more than 10 seconds to refresh registers, otherwise the application is suspended forever.
* In order to prevent this case that can occur when the exosip thread is busy with DNS while network isn't in a good shape, we try to take
* the exosip lock in a non blocking way, and give up if it takes too long*/
while (eXosip_trylock()!=0){
ms_usleep(100000);
if (tries>30) {/*after 3 seconds, give up*/
ms_warning("Could not obtain exosip lock in a reasonable time, giving up.");
return -1;
}
}
}
#else
eXosip_lock();
#endif
eXosip_register_build_register(op->rid,expires,&msg);
if (msg!=NULL){
if (contact) register_set_contact(msg,contact);
sal_register_add_route(msg,sal_op_get_route(op));
eXosip_register_send_register(op->rid,msg);
}else ms_error("Could not build REGISTER refresh message.");
eXosip_unlock();
return (msg != NULL) ? 0 : -1;
}
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);
// Remove front spaces
while (uri[0]==' ') {
uri++;
}
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_set_param(SalAddress *u,const char* name,const char* value) {
osip_uri_param_t *param=NULL;
osip_uri_uparam_get_byname(((osip_from_t*)u)->url,(char*)name,&param);
if (param == NULL){
osip_uri_uparam_add (((osip_from_t*)u)->url,ms_strdup(name),value ? ms_strdup(value) : NULL);
} else {
osip_free(param->gvalue);
param->gvalue=value ? osip_strdup(value) : NULL;
}
}
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);
}
unsigned int sal_get_keepalive_period(Sal *ctx) {
return ctx->keepalive_period;
}
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;
}
}
SalTransport sal_address_get_transport(const SalAddress* addr) {
const osip_from_t *u=(const osip_from_t*)addr;
osip_uri_param_t *transport_param=NULL;
osip_uri_uparam_get_byname(u->url,"transport",&transport_param);
if (transport_param == NULL){
return SalTransportUDP;
} else {
return sal_transport_parse(transport_param->gvalue);
}
}
void sal_address_set_transport(SalAddress* addr,SalTransport transport) {
sal_address_set_param(addr, "transport", sal_transport_to_string(transport));
}
/* sends a reinvite. Local media description may have changed by application since call establishment*/
int sal_call_update(SalOp *h, const char *subject){
int err=0;
osip_message_t *reinvite=NULL;
eXosip_lock();
if(eXosip_call_build_request(h->did,"INVITE",&reinvite) != 0 || reinvite==NULL){
eXosip_unlock();
return -1;
}
eXosip_unlock();
osip_message_set_subject(reinvite,subject);
osip_message_set_allow(reinvite, "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY, MESSAGE, SUBSCRIBE, INFO");
if (h->base.contact){
_osip_list_set_empty(&reinvite->contacts,(void (*)(void*))osip_contact_free);
osip_message_set_contact(reinvite,h->base.contact);
}
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;
}
void sal_reuse_authorization(Sal *ctx, bool_t value) {
ctx->reuse_authorization=value;
}