diff --git a/NEWS b/NEWS index 6e15b91ef..ceab2b179 100644 --- a/NEWS +++ b/NEWS @@ -1,9 +1,18 @@ +linphone-3.3.0 -- ????????? + * liblinphone is ported to iphoneOS and Google Android + * Internal refactoring of liblinphone (code factorisation, encapsulation + of signaling) + * enhancements made to presence support (SIP/SIMPLE) + linphone-3.2.2 -- ????????? * improve bitrate usage of speex codec * allow speex to run with vbr (variable bit rate) mode * add speex/32000 (ultra wide band speex codec) * answer empty SIP INFO requests * reverse order of call logs + * optimize video display + * manual or automatic network connectivity management (so that REGISTERs + are only sent when network is up or refreshed when it comes back) linphone-3.2.1 -- October 5, 2009 * improve graphics and behaviour of mute button diff --git a/console/Makefile.am b/console/Makefile.am index 45f886580..70c2625b4 100644 --- a/console/Makefile.am +++ b/console/Makefile.am @@ -13,7 +13,7 @@ INCLUDES = \ -bin_PROGRAMS = linphonec sipomatic linphonecsh +bin_PROGRAMS = linphonec linphonecsh if BUILD_WIN32 bin_PROGRAMS += linphoned diff --git a/console/commands.c b/console/commands.c index 1b65a9cdd..82044797e 100644 --- a/console/commands.c +++ b/console/commands.c @@ -1337,7 +1337,7 @@ linphonec_proxy_use(LinphoneCore *lc, int index) static void linphonec_friend_display(LinphoneFriend *fr) { - LinphoneAddress *uri=linphone_address_clone(linphone_friend_get_uri(fr)); + LinphoneAddress *uri=linphone_address_clone(linphone_friend_get_address(fr)); char *str; linphonec_out("name: %s\n", linphone_address_get_display_name(uri)); @@ -1362,7 +1362,7 @@ linphonec_friend_list(LinphoneCore *lc, char *pat) { if ( pat ) { const char *name = linphone_address_get_display_name( - linphone_friend_get_uri((LinphoneFriend*)friend->data)); + linphone_friend_get_address((LinphoneFriend*)friend->data)); if (name && ! strstr(name, pat) ) continue; } linphonec_out("****** Friend %i *******\n",n); @@ -1384,7 +1384,7 @@ linphonec_friend_call(LinphoneCore *lc, unsigned int num) if ( n == num ) { int ret; - addr = linphone_address_as_string(linphone_friend_get_uri((LinphoneFriend*)friend->data)); + addr = linphone_address_as_string(linphone_friend_get_address((LinphoneFriend*)friend->data)); ret=lpc_cmd_call(lc, addr); ms_free(addr); return ret; @@ -1458,17 +1458,16 @@ static int lpc_cmd_register(LinphoneCore *lc, char *args){ return 1; } if (passwd[0]!='\0'){ - osip_from_t *from; + LinphoneAddress *from; LinphoneAuthInfo *info; - osip_from_init(&from); - if (osip_from_parse(from,identity)==0){ + if ((from=linphone_address_new(identity))!=NULL){ char realm[128]; - snprintf(realm,sizeof(realm)-1,"\"%s\"",from->url->host); - info=linphone_auth_info_new(from->url->username,NULL,passwd,NULL,NULL); + snprintf(realm,sizeof(realm)-1,"\"%s\"",linphone_address_get_domain(from)); + info=linphone_auth_info_new(linphone_address_get_username(from),NULL,passwd,NULL,NULL); linphone_core_add_auth_info(lc,info); + linphone_address_destroy(from); linphone_auth_info_destroy(info); } - osip_from_free(from); } elem=linphone_core_get_proxy_config_list(lc); if (elem) { @@ -1674,7 +1673,8 @@ static void linphonec_codec_list(LinphoneCore *lc){ MSList *node; for(node=config->audio_codecs;node!=NULL;node=ms_list_next(node)){ pt=(PayloadType*)(node->data); - linphonec_out("%2d: %s (%d) %s\n", index, pt->mime_type, pt->clock_rate, payload_type_enabled(pt) ? "enabled" : "disabled"); + linphonec_out("%2d: %s (%d) %s\n", index, pt->mime_type, pt->clock_rate, + linphone_core_payload_type_enabled(lc,pt) ? "enabled" : "disabled"); index++; } } diff --git a/console/linphonec.c b/console/linphonec.c index 93b5da744..ff68fbecd 100644 --- a/console/linphonec.c +++ b/console/linphonec.c @@ -119,8 +119,7 @@ static void linphonec_display_something (LinphoneCore * lc, const char *somethin static void linphonec_display_url (LinphoneCore * lc, const char *something, const char *url); static void linphonec_display_warning (LinphoneCore * lc, const char *something); static void stub () {} -static void linphonec_notify_received(LinphoneCore *lc,LinphoneFriend *fid, - const char *from, const char *status, const char *img); +static void linphonec_notify_received(LinphoneCore *lc,LinphoneFriend *fid); static void linphonec_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url); static void linphonec_bye_received(LinphoneCore *lc, const char *from); @@ -283,10 +282,11 @@ linphonec_prompt_for_auth(LinphoneCore *lc, const char *realm, const char *usern * Linphone core callback */ static void -linphonec_notify_received(LinphoneCore *lc,LinphoneFriend *fid, - const char *from, const char *status, const char *img) +linphonec_notify_received(LinphoneCore *lc,LinphoneFriend *fid) { - printf("Friend %s is %s\n", from, status); + char *tmp=linphone_address_as_string(linphone_friend_get_address(fid)); + printf("Friend %s is %s\n", tmp, linphone_online_status_to_string(linphone_friend_get_status(fid))); + ms_free(tmp); // todo: update Friend list state (unimplemented) } diff --git a/coreapi/Makefile.am b/coreapi/Makefile.am index 944f303e7..014ae308e 100644 --- a/coreapi/Makefile.am +++ b/coreapi/Makefile.am @@ -17,18 +17,22 @@ lib_LTLIBRARIES=liblinphone.la liblinphone_la_SOURCES=\ linphonecore.c linphonecore.h private.h\ - exevents.c exevents.h \ + offeranswer.c offeranswer.h\ + sal.c sal.h \ + sal_eXosip2.c sal_eXosip2.h\ + sal_eXosip2_sdp.c \ + sal_eXosip2_presence.c \ + callbacks.c \ misc.c \ address.c \ enum.c enum.h \ - sdphandler.c sdphandler.h \ presence.c \ proxy.c \ friend.c \ authentication.c \ lpconfig.c lpconfig.h \ chat.c \ - general_state.c \ + general_state.c \ sipsetup.c sipsetup.h \ siplogin.c diff --git a/coreapi/address.c b/coreapi/address.c index 9c69a2641..1d893aede 100644 --- a/coreapi/address.c +++ b/coreapi/address.c @@ -20,7 +20,6 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "linphonecore.h" #include "lpconfig.h" #include "private.h" -#include /** * @addtogroup linphone_address @@ -31,123 +30,87 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * Constructs a LinphoneAddress object by parsing the user supplied address, * given as a string. **/ -LinphoneAddress * linphone_address_new(const char *uri){ - osip_from_t *from; - osip_from_init(&from); - if (osip_from_parse(from,uri)!=0){ - osip_from_free(from); - ms_error("Cannot create LinphoneAddress, bad uri [%s]",uri); - return NULL; - } - return from; +LinphoneAddress * linphone_address_new(const char *addr){ + SalAddress *saddr=sal_address_new(addr); + if (saddr==NULL) ms_error("Cannot create LinphoneAddress, bad uri [%s]",addr); + return saddr; } /** * Clones a LinphoneAddress object. **/ -LinphoneAddress * linphone_address_clone(const LinphoneAddress *uri){ - osip_from_t *ret=NULL; - osip_from_clone(uri,&ret); - return ret; +LinphoneAddress * linphone_address_clone(const LinphoneAddress *addr){ + return sal_address_clone(addr); } -#define null_if_empty(s) (((s)!=NULL && (s)[0]!='\0') ? (s) : NULL ) - /** * Returns the address scheme, normally "sip". **/ const char *linphone_address_get_scheme(const LinphoneAddress *u){ - return null_if_empty(u->url->scheme); + return sal_address_get_scheme(u); } /** * Returns the display name. **/ const char *linphone_address_get_display_name(const LinphoneAddress* u){ - return null_if_empty(u->displayname); + return sal_address_get_display_name(u); } /** * Returns the username. **/ const char *linphone_address_get_username(const LinphoneAddress *u){ - return null_if_empty(u->url->username); + return sal_address_get_username(u); } /** * Returns the domain name. **/ const char *linphone_address_get_domain(const LinphoneAddress *u){ - return null_if_empty(u->url->host); + return sal_address_get_domain(u); } /** * Sets the display name. **/ void linphone_address_set_display_name(LinphoneAddress *u, const char *display_name){ - if (u->displayname!=NULL){ - osip_free(u->displayname); - u->displayname=NULL; - } - if (display_name!=NULL) - u->displayname=osip_strdup(display_name); + sal_address_set_display_name(u,display_name); } /** * Sets the username. **/ void linphone_address_set_username(LinphoneAddress *uri, const char *username){ - if (uri->url->username!=NULL){ - osip_free(uri->url->username); - uri->url->username=NULL; - } - if (username) - uri->url->username=osip_strdup(username); + sal_address_set_username(uri,username); } /** * Sets the domain. **/ void linphone_address_set_domain(LinphoneAddress *uri, const char *host){ - if (uri->url->host!=NULL){ - osip_free(uri->url->host); - uri->url->host=NULL; - } - if (host) - uri->url->host=osip_strdup(host); + sal_address_set_domain(uri,host); } /** * Sets the port number. **/ void linphone_address_set_port(LinphoneAddress *uri, const char *port){ - if (uri->url->port!=NULL){ - osip_free(uri->url->port); - uri->url->port=NULL; - } - if (port) - uri->url->port=osip_strdup(port); + sal_address_set_port(uri,port); } /** * Sets the port number. **/ void linphone_address_set_port_int(LinphoneAddress *uri, int port){ - char tmp[12]; - if (port==5060){ - /*this is the default, special case to leave the port field blank*/ - linphone_address_set_port(uri,NULL); - return; - } - snprintf(tmp,sizeof(tmp),"%i",port); - linphone_address_set_port(uri,tmp); + sal_address_set_port_int(uri,port); } /** * Removes address's tags and uri headers so that it is displayable to the user. **/ void linphone_address_clean(LinphoneAddress *uri){ - osip_generic_param_freelist(&uri->gen_params); + sal_address_clean(uri); } /** @@ -155,11 +118,7 @@ void linphone_address_clean(LinphoneAddress *uri){ * The returned char * must be freed by the application. Use ms_free(). **/ char *linphone_address_as_string(const LinphoneAddress *u){ - char *tmp,*ret; - osip_from_to_str(u,&tmp); - ret=ms_strdup(tmp); - osip_free(tmp); - return ret; + return sal_address_as_string(u); } /** @@ -167,18 +126,14 @@ char *linphone_address_as_string(const LinphoneAddress *u){ * The returned char * must be freed by the application. Use ms_free(). **/ char *linphone_address_as_string_uri_only(const LinphoneAddress *u){ - char *tmp=NULL,*ret; - osip_uri_to_str(u->url,&tmp); - ret=ms_strdup(tmp); - osip_free(tmp); - return ret; + return sal_address_as_string_uri_only(u); } /** * Destroys a LinphoneAddress object. **/ void linphone_address_destroy(LinphoneAddress *u){ - osip_from_free(u); + sal_address_destroy(u); } diff --git a/coreapi/authentication.c b/coreapi/authentication.c index 618504884..24bed1c43 100644 --- a/coreapi/authentication.c +++ b/coreapi/authentication.c @@ -24,12 +24,8 @@ #include "linphonecore.h" #include "private.h" -#include -#include #include "lpconfig.h" -extern LinphoneProxyConfig *linphone_core_get_proxy_config_from_rid(LinphoneCore *lc, int rid); - /** * @addtogroup authentication * @{ @@ -51,7 +47,6 @@ LinphoneAuthInfo *linphone_auth_info_new(const char *username, const char *useri if (ha1!=NULL && (strlen(ha1)>0)) obj->ha1=ms_strdup(ha1); if (realm!=NULL && (strlen(realm)>0)) obj->realm=ms_strdup(realm); obj->works=FALSE; - obj->first_time=TRUE; return obj; } @@ -63,10 +58,28 @@ static LinphoneAuthInfo *linphone_auth_info_clone(const LinphoneAuthInfo *ai){ if (ai->ha1) obj->ha1=ms_strdup(ai->ha1); if (ai->realm) obj->realm=ms_strdup(ai->realm); obj->works=FALSE; - obj->first_time=TRUE; + obj->usecount=0; return obj; } +/** + * Returns username. +**/ +const char *linphone_auth_info_get_username(const LinphoneAuthInfo *i){ + return i->username; +} + +/** + * Returns password. +**/ +const char *linphone_auth_info_get_passwd(const LinphoneAuthInfo *i){ + return i->passwd; +} + +const char *linphone_auth_info_get_userid(const LinphoneAuthInfo *i){ + return i->userid; +} + /** * Sets the password. **/ @@ -224,21 +237,6 @@ const LinphoneAuthInfo *linphone_core_find_auth_info(LinphoneCore *lc, const cha return ret; } -static void refresh_exosip_auth_info(LinphoneCore *lc){ - MSList *elem; - eXosip_lock(); - eXosip_clear_authentication_info(); - for (elem=lc->auth_info;elem!=NULL;elem=ms_list_next(elem)){ - LinphoneAuthInfo *info=(LinphoneAuthInfo*)elem->data; - char *userid; - if (info->userid==NULL || info->userid[0]=='\0') userid=info->username; - else userid=info->userid; - eXosip_add_authentication_info(info->username,userid, - info->passwd,info->ha1,info->realm); - } - eXosip_unlock(); -} - /** * Adds authentication information to the LinphoneCore. * @@ -247,6 +245,7 @@ static void refresh_exosip_auth_info(LinphoneCore *lc){ void linphone_core_add_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info) { LinphoneAuthInfo *ai; + MSList *elem; /* find if we are attempting to modify an existing auth info */ ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,info->realm,info->username); @@ -255,10 +254,23 @@ void linphone_core_add_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info) linphone_auth_info_destroy(ai); } lc->auth_info=ms_list_append(lc->auth_info,linphone_auth_info_clone(info)); - - refresh_exosip_auth_info(lc); - /* if the user was prompted, re-allow automatic_action */ - if (lc->automatic_action>0) lc->automatic_action--; + /* retry pending authentication operations */ + for(elem=sal_get_pending_auths(lc->sal);elem!=NULL;elem=elem->next){ + const char *username,*realm; + SalOp *op=(SalOp*)elem->data; + LinphoneAuthInfo *ai; + sal_op_get_auth_requested(op,&realm,&username); + ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,realm,username); + if (ai){ + SalAuthInfo sai; + sai.username=ai->username; + sai.userid=ai->userid; + sai.realm=ai->realm; + sai.password=ai->passwd; + sal_op_authenticate(op,&sai); + ai->usecount++; + } + } } @@ -267,7 +279,6 @@ void linphone_core_add_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info) * from the auth_info_requested callback of LinphoneCoreVTable. **/ void linphone_core_abort_authentication(LinphoneCore *lc, LinphoneAuthInfo *info){ - if (lc->automatic_action>0) lc->automatic_action--; } /** @@ -286,7 +297,6 @@ void linphone_core_remove_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *in linphone_auth_info_write_config(lc->config,(LinphoneAuthInfo*)elem->data,i); } linphone_auth_info_write_config(lc->config,NULL,i); - refresh_exosip_auth_info(lc); } } @@ -303,9 +313,6 @@ const MSList *linphone_core_get_auth_info_list(const LinphoneCore *lc){ void linphone_core_clear_all_auth_info(LinphoneCore *lc){ MSList *elem; int i; - eXosip_lock(); - eXosip_clear_authentication_info(); - eXosip_unlock(); for(i=0,elem=lc->auth_info;elem!=NULL;elem=ms_list_next(elem),i++){ LinphoneAuthInfo *info=(LinphoneAuthInfo*)elem->data; linphone_auth_info_destroy(info); @@ -315,84 +322,6 @@ void linphone_core_clear_all_auth_info(LinphoneCore *lc){ lc->auth_info=NULL; } -void linphone_authentication_ok(LinphoneCore *lc, eXosip_event_t *ev){ - char *prx_realm=NULL,*www_realm=NULL; - osip_proxy_authorization_t *prx_auth; - osip_authorization_t *www_auth; - osip_message_t *msg=ev->request; - char *username; - LinphoneAuthInfo *as=NULL; - - username=osip_uri_get_username(msg->from->url); - osip_message_get_proxy_authorization(msg,0,&prx_auth); - osip_message_get_authorization(msg,0,&www_auth); - if (prx_auth!=NULL) - prx_realm=osip_proxy_authorization_get_realm(prx_auth); - if (www_auth!=NULL) - www_realm=osip_authorization_get_realm(www_auth); - - if (prx_realm==NULL && www_realm==NULL){ - ms_message("No authentication info in the request, ignoring"); - return; - } - /* see if we already have this auth information , not to ask it everytime to the user */ - if (prx_realm!=NULL) - as=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,prx_realm,username); - if (www_realm!=NULL) - as=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,www_realm,username); - if (as){ - ms_message("Authentication for user=%s realm=%s is working.",username,prx_realm ? prx_realm : www_realm); - as->works=TRUE; - } -} - - -void linphone_core_find_or_ask_for_auth_info(LinphoneCore *lc,const char *username,const char* realm, int tid) -{ - LinphoneAuthInfo *as=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,realm,username); - if ( as==NULL || (as!=NULL && as->works==FALSE && as->first_time==FALSE)){ - if (lc->vtable.auth_info_requested!=NULL){ - lc->vtable.auth_info_requested(lc,realm,username); - lc->automatic_action++;/*suspends eXosip_automatic_action until the user supplies a password */ - } - } - if (as) as->first_time=FALSE; -} - -void linphone_process_authentication(LinphoneCore *lc, eXosip_event_t *ev) -{ - char *prx_realm=NULL,*www_realm=NULL; - osip_proxy_authenticate_t *prx_auth; - osip_www_authenticate_t *www_auth; - osip_message_t *resp=ev->response; - char *username; - - /* - if (strcmp(ev->request->sip_method,"REGISTER")==0) { - gstate_new_state(lc, GSTATE_REG_FAILED, "Authentication required"); - } - */ - - 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==NULL && www_realm==NULL){ - ms_warning("No realm in the server response."); - return; - } - /* see if we already have this auth information , not to ask it everytime to the user */ - if (prx_realm!=NULL) - linphone_core_find_or_ask_for_auth_info(lc,username,prx_realm,ev->tid); - if (www_realm!=NULL) - linphone_core_find_or_ask_for_auth_info(lc,username,www_realm,ev->tid); -} - - /** * @} **/ diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c new file mode 100644 index 000000000..343161492 --- /dev/null +++ b/coreapi/callbacks.c @@ -0,0 +1,457 @@ +/* +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. +*/ + + +#include "sal.h" + +#include "linphonecore.h" +#include "private.h" +#include "mediastreamer2/mediastream.h" + +static void linphone_connect_incoming(LinphoneCore *lc, LinphoneCall *call){ + if (lc->vtable.show) + lc->vtable.show(lc); + if (lc->vtable.display_status) + lc->vtable.display_status(lc,_("Connected.")); + call->state=LCStateAVRunning; + if (lc->ringstream!=NULL){ + ring_stop(lc->ringstream); + lc->ringstream=NULL; + } + linphone_core_start_media_streams(lc,call); +} + +static void call_received(SalOp *h){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h)); + char *barmesg; + LinphoneCall *call; + const char *from,*to; + char *tmp; + LinphoneAddress *from_parsed; + + /* first check if we can answer successfully to this invite */ + if (lc->presence_mode!=LINPHONE_STATUS_ONLINE){ + ms_message("Not present !! presence mode : %d\n",lc->presence_mode); + if (lc->presence_mode==LINPHONE_STATUS_BUSY) + sal_call_decline(h,SalReasonBusy,NULL); + else if (lc->presence_mode==LINPHONE_STATUS_AWAY + ||lc->presence_mode==LINPHONE_STATUS_BERIGHTBACK + ||lc->presence_mode==LINPHONE_STATUS_ONTHEPHONE + ||lc->presence_mode==LINPHONE_STATUS_OUTTOLUNCH + ||lc->presence_mode==LINPHONE_STATUS_OFFLINE) + sal_call_decline(h,SalReasonTemporarilyUnavailable,NULL); + else if (lc->presence_mode==LINPHONE_STATUS_NOT_DISTURB) + sal_call_decline(h,SalReasonTemporarilyUnavailable,NULL); + else if (lc->alt_contact!=NULL && lc->presence_mode==LINPHONE_STATUS_MOVED) + sal_call_decline(h,SalReasonRedirect,lc->alt_contact); + else + sal_call_decline(h,SalReasonBusy,NULL); + sal_op_release(h); + return; + } + if (lc->call!=NULL){/*busy*/ + sal_call_decline(h,SalReasonBusy,NULL); + sal_op_release(h); + return; + } + from=sal_op_get_from(h); + to=sal_op_get_to(h); + + call=linphone_call_new_incoming(lc,linphone_address_new(from),linphone_address_new(to),h); + lc->call=call; + sal_call_set_local_media_description(h,call->localdesc); + call->resultdesc=sal_call_get_final_media_description(h); + if (call->resultdesc) + sal_media_description_ref(call->resultdesc); + if (call->resultdesc && sal_media_description_empty(call->resultdesc)){ + sal_call_decline(h,SalReasonMedia,NULL); + linphone_call_destroy(call); + lc->call=NULL; + return; + } + + from_parsed=linphone_address_new(sal_op_get_from(h)); + linphone_address_clean(from_parsed); + tmp=linphone_address_as_string(from_parsed); + linphone_address_destroy(from_parsed); + gstate_new_state(lc, GSTATE_CALL_IN_INVITE, tmp); + barmesg=ortp_strdup_printf(_("%s is contacting you"),tmp); + if (lc->vtable.show) lc->vtable.show(lc); + if (lc->vtable.display_status) + lc->vtable.display_status(lc,barmesg); + + /* play the ring */ + if (lc->sound_conf.ring_sndcard!=NULL){ + ms_message("Starting local ring..."); + lc->ringstream=ring_start(lc->sound_conf.local_ring,2000,lc->sound_conf.ring_sndcard); + } + linphone_call_set_state(call,LCStateRinging); + sal_call_notify_ringing(h); + linphone_core_init_media_streams(lc,lc->call); + if (lc->vtable.inv_recv) lc->vtable.inv_recv(lc,tmp); + ms_free(barmesg); + ms_free(tmp); +} + +static void call_ringing(SalOp *h){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h)); + LinphoneCall *call=lc->call; + SalMediaDescription *md; + if (call==NULL) return; + if (lc->vtable.display_status) + lc->vtable.display_status(lc,_("Remote ringing.")); + md=sal_call_get_final_media_description(h); + if (md==NULL){ + if (lc->ringstream!=NULL) return; /*already ringing !*/ + if (lc->sound_conf.play_sndcard!=NULL){ + ms_message("Remote ringing..."); + lc->ringstream=ring_start(lc->sound_conf.remote_ring,2000,lc->sound_conf.play_sndcard); + } + }else{ + /*accept early media */ + if (lc->audiostream && lc->audiostream->ticker!=NULL){ + /*streams already started */ + ms_message("Early media already started."); + return; + } + sal_media_description_ref(md); + call->resultdesc=md; + if (lc->vtable.show) lc->vtable.show(lc); + if (lc->vtable.display_status) + lc->vtable.display_status(lc,_("Early media.")); + gstate_new_state(lc, GSTATE_CALL_OUT_CONNECTED, NULL); + if (lc->ringstream!=NULL){ + ring_stop(lc->ringstream); + lc->ringstream=NULL; + } + ms_message("Doing early media..."); + linphone_core_start_media_streams(lc,call); + call->media_pending=TRUE; + } + call->state=LCStateRinging; +} + +static void call_accepted(SalOp *op){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + LinphoneCall *call=lc->call; + if (call==NULL){ + ms_warning("No call to accept."); + return ; + } + if (sal_op_get_user_pointer(op)!=lc->call){ + ms_warning("call_accepted: ignoring."); + return; + } + if (call->state==LCStateAVRunning){ + return ; /*already accepted*/ + } + if (lc->audiostream->ticker!=NULL){ + /*case where we accepted early media */ + linphone_core_stop_media_streams(lc,call); + linphone_core_init_media_streams(lc,call); + } + if (call->resultdesc) + sal_media_description_unref(call->resultdesc); + call->resultdesc=sal_call_get_final_media_description(op); + if (call->resultdesc){ + sal_media_description_ref(call->resultdesc); + call->media_pending=FALSE; + } + if (call->resultdesc && !sal_media_description_empty(call->resultdesc)){ + gstate_new_state(lc, GSTATE_CALL_OUT_CONNECTED, NULL); + linphone_connect_incoming(lc,call); + }else{ + /*send a bye*/ + ms_error("Incompatible SDP offer received in 200Ok, need to abort the call"); + linphone_core_terminate_call(lc,NULL); + } +} + +static void call_ack(SalOp *op){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + LinphoneCall *call=lc->call; + if (call==NULL){ + ms_warning("No call to be ACK'd"); + return ; + } + if (sal_op_get_user_pointer(op)!=lc->call){ + ms_warning("call_ack: ignoring."); + return; + } + if (call->media_pending){ + if (lc->audiostream->ticker!=NULL){ + /*case where we accepted early media */ + linphone_core_stop_media_streams(lc,call); + linphone_core_init_media_streams(lc,call); + } + if (call->resultdesc) + sal_media_description_unref(call->resultdesc); + call->resultdesc=sal_call_get_final_media_description(op); + if (call->resultdesc) + sal_media_description_ref(call->resultdesc); + if (call->resultdesc && !sal_media_description_empty(call->resultdesc)){ + gstate_new_state(lc, GSTATE_CALL_IN_CONNECTED, NULL); + linphone_connect_incoming(lc,call); + }else{ + /*send a bye*/ + ms_error("Incompatible SDP response received in ACK, need to abort the call"); + linphone_core_terminate_call(lc,NULL); + } + call->media_pending=FALSE; + } +} + +static void call_updated(SalOp *op){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); + linphone_core_stop_media_streams(lc,call); + linphone_core_init_media_streams(lc,call); + if (call->resultdesc) + sal_media_description_unref(call->resultdesc); + call->resultdesc=sal_call_get_final_media_description(op); + if (call->resultdesc && !sal_media_description_empty(call->resultdesc)){ + linphone_connect_incoming(lc,call); + } +} + +static void call_terminated(SalOp *op, const char *from){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + if (sal_op_get_user_pointer(op)!=lc->call){ + ms_warning("call_terminated: ignoring."); + return; + } + ms_message("Current call terminated..."); + if (lc->ringstream!=NULL) { + ring_stop(lc->ringstream); + lc->ringstream=NULL; + } + linphone_core_stop_media_streams(lc,lc->call); + lc->vtable.show(lc); + lc->vtable.display_status(lc,_("Call terminated.")); + gstate_new_state(lc, GSTATE_CALL_END, NULL); + if (lc->vtable.bye_recv!=NULL){ + LinphoneAddress *addr=linphone_address_new(from); + char *tmp; + linphone_address_clean(addr); + tmp=linphone_address_as_string(addr); + lc->vtable.bye_recv(lc,tmp); + ms_free(tmp); + linphone_address_destroy(addr); + } + linphone_call_destroy(lc->call); + lc->call=NULL; +} + +static void call_failure(SalOp *op, SalError error, SalReason sr, const char *details){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + char *msg486=_("User is busy."); + char *msg480=_("User is temporarily unavailable."); + /*char *retrymsg=_("%s. Retry after %i minute(s).");*/ + char *msg600=_("User does not want to be disturbed."); + char *msg603=_("Call declined."); + LinphoneCall *call=lc->call; + + if (sal_op_get_user_pointer(op)!=lc->call){ + ms_warning("call_failure: ignoring."); + return; + } + if (lc->vtable.show) lc->vtable.show(lc); + + if (error==SalErrorNoResponse){ + if (lc->vtable.display_status) + lc->vtable.display_status(lc,_("No response.")); + }else if (error==SalErrorProtocol){ + if (lc->vtable.display_status) + lc->vtable.display_status(lc, details ? details : _("Error.")); + }else if (error==SalErrorFailure){ + switch(sr){ + case SalReasonDeclined: + if (lc->vtable.display_status) + lc->vtable.display_status(lc,msg603); + break; + case SalReasonBusy: + if (lc->vtable.display_status) + lc->vtable.display_status(lc,msg486); + break; + case SalReasonRedirect: + if (lc->vtable.display_status) + lc->vtable.display_status(lc,_("Redirected")); + break; + case SalReasonTemporarilyUnavailable: + if (lc->vtable.display_status) + lc->vtable.display_status(lc,msg480); + break; + case SalReasonNotFound: + if (lc->vtable.display_status) + lc->vtable.display_status(lc,_("Not found")); + break; + case SalReasonDoNotDisturb: + if (lc->vtable.display_status) + lc->vtable.display_status(lc,msg600); + break; + case SalReasonMedia: + if (lc->vtable.display_status) + lc->vtable.display_status(lc,_("No common codecs")); + break; + default: + if (lc->vtable.display_status) + lc->vtable.display_status(lc,_("Call failed.")); + } + } + if (lc->ringstream!=NULL) { + ring_stop(lc->ringstream); + lc->ringstream=NULL; + } + linphone_core_stop_media_streams(lc,call); + if (call!=NULL) { + linphone_call_destroy(call); + gstate_new_state(lc, GSTATE_CALL_ERROR, NULL); + lc->call=NULL; + } +} + +static void auth_requested(SalOp *h, const char *realm, const char *username){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h)); + LinphoneAuthInfo *ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,realm,username); + ms_message("auth_requested() for realm=%s, username=%s",realm,username); + if (ai && (ai->works || ai->usecount<3)){ + SalAuthInfo sai; + sai.username=ai->username; + sai.userid=ai->userid; + sai.realm=ai->realm; + sai.password=ai->passwd; + ms_message("auth_requested(): authenticating realm=%s, username=%s",realm,username); + sal_op_authenticate(h,&sai); + ai->usecount++; + }else{ + if (lc->vtable.auth_info_requested) + lc->vtable.auth_info_requested(lc,realm,username); + } +} + +static void auth_success(SalOp *h, const char *realm, const char *username){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h)); + LinphoneAuthInfo *ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,realm,username); + if (ai){ + ms_message("%s/%s authentication works.",realm,username); + ai->works=TRUE; + } +} + +static void register_success(SalOp *op, bool_t registered){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)sal_op_get_user_pointer(op); + char *msg; + gstate_new_state(lc, GSTATE_REG_OK, NULL); + cfg->registered=registered; + if (cfg->registered) msg=ms_strdup_printf(_("Registration on %s successful."),sal_op_get_proxy(op)); + else msg=ms_strdup_printf(_("Unregistration on %s done."),sal_op_get_proxy(op)); + if (lc->vtable.display_status) + lc->vtable.display_status(lc,msg); + ms_free(msg); +} + +static void register_failure(SalOp *op, SalError error, SalReason reason, const char *details){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + char *msg=ortp_strdup_printf(_("Registration on %s failed: %s"),sal_op_get_proxy(op),(details!=NULL) ? details : _("no response timeout")); + if (lc->vtable.display_status) lc->vtable.display_status(lc,msg); + gstate_new_state(lc, GSTATE_REG_FAILED, msg); + ms_free(msg); +} + +static void vfu_request(SalOp *op){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); +#ifdef VIDEO_ENABLED + if (lc->videostream) + video_stream_send_vfu(lc->videostream); +#endif +} + +static void dtmf_received(SalOp *op, char dtmf){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + if (lc->vtable.dtmf_received != NULL) + lc->vtable.dtmf_received(lc, dtmf); +} + +static void refer_received(Sal *sal, SalOp *op, const char *referto){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal); + if (lc->vtable.refer_received) + lc->vtable.refer_received(lc,referto); +} + +static void text_received(Sal *sal, const char *from, const char *msg){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal); + linphone_core_text_received(lc,from,msg); +} + +static void notify(SalOp *op, SalSubscribeState ss, SalPresenceStatus status, const char *msg){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + linphone_notify_recv(lc,op,ss,status); +} + +static void subscribe_received(SalOp *op, const char *from){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + linphone_subscription_new(lc,op,from); +} + +static void subscribe_closed(SalOp *op, const char *from){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(op)); + linphone_subscription_closed(lc,op); +} + +static void internal_message(Sal *sal, const char *msg){ + LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal); + if (lc->vtable.show) + lc->vtable.show(lc); +} + +static void ping_reply(SalOp *op){ + LinphoneCall *call=(LinphoneCall*) sal_op_get_user_pointer(op); + ms_message("ping reply !"); + if (call){ + if (call->state==LCStatePreEstablishing){ + linphone_core_start_invite(call->core,call,NULL); + } + } +} + +SalCallbacks linphone_sal_callbacks={ + call_received, + call_ringing, + call_accepted, + call_ack, + call_updated, + call_terminated, + call_failure, + auth_requested, + auth_success, + register_success, + register_failure, + vfu_request, + dtmf_received, + refer_received, + text_received, + notify, + subscribe_received, + subscribe_closed, + internal_message, + ping_reply +}; + + diff --git a/coreapi/chat.c b/coreapi/chat.c index 95526274c..c1a884964 100644 --- a/coreapi/chat.c +++ b/coreapi/chat.c @@ -24,17 +24,16 @@ #include "linphonecore.h" #include "private.h" - #include LinphoneChatRoom * linphone_core_create_chat_room(LinphoneCore *lc, const char *to){ LinphoneAddress *parsed_url=NULL; - char *route; - if (linphone_core_interpret_url(lc,to,&parsed_url,&route)){ + + if ((parsed_url=linphone_core_interpret_url(lc,to))!=NULL){ LinphoneChatRoom *cr=ms_new0(LinphoneChatRoom,1); cr->lc=lc; cr->peer=linphone_address_as_string(parsed_url); cr->peer_url=parsed_url; - cr->route=route; + cr->route=ms_strdup(linphone_core_get_route(lc)); lc->chatrooms=ms_list_append(lc->chatrooms,(void *)cr); return cr; } @@ -52,11 +51,10 @@ void linphone_chat_room_send_message(LinphoneChatRoom *cr, const char *msg){ const char *identity=linphone_core_get_identity(cr->lc); - osip_message_t *sip=NULL; - eXosip_message_build_request(&sip,"MESSAGE",cr->peer,identity,cr->route); - osip_message_set_content_type(sip,"text/plain"); - osip_message_set_body(sip,msg,strlen(msg)); - eXosip_message_send_request(sip); + SalOp *op=sal_op_new(cr->lc->sal); + + sal_op_set_route(op,cr->route); + sal_text_send(op,identity,cr->peer,msg); } bool_t linphone_chat_room_matches(LinphoneChatRoom *cr, const LinphoneAddress *from){ @@ -69,40 +67,29 @@ void linphone_chat_room_text_received(LinphoneChatRoom *cr, LinphoneCore *lc, co if (lc->vtable.text_received!=NULL) lc->vtable.text_received(lc, cr, from, msg); } -void linphone_core_text_received(LinphoneCore *lc, eXosip_event_t *ev){ +void linphone_core_text_received(LinphoneCore *lc, const char *from, const char *msg){ MSList *elem; - const char *msg; LinphoneChatRoom *cr=NULL; - char *from; - osip_from_t *from_url=ev->request->from; - osip_body_t *body=NULL; - LinphoneAddress *uri; + LinphoneAddress *addr; + char *cleanfrom; - 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(from_url,&from); - uri=linphone_address_new(from); - osip_free(from); - linphone_address_clean(uri); + addr=linphone_address_new(from); + linphone_address_clean(addr); for(elem=lc->chatrooms;elem!=NULL;elem=ms_list_next(elem)){ cr=(LinphoneChatRoom*)elem->data; - if (linphone_chat_room_matches(cr,uri)){ + if (linphone_chat_room_matches(cr,addr)){ break; } cr=NULL; } - from=linphone_address_as_string(uri); + cleanfrom=linphone_address_as_string(addr); if (cr==NULL){ /* create a new chat room */ - cr=linphone_core_create_chat_room(lc,from); + cr=linphone_core_create_chat_room(lc,cleanfrom); } - linphone_address_destroy(uri); - linphone_chat_room_text_received(cr,lc,from,msg); - ms_free(from); + linphone_address_destroy(addr); + linphone_chat_room_text_received(cr,lc,cleanfrom,msg); + ms_free(cleanfrom); } diff --git a/coreapi/exevents.c b/coreapi/exevents.c index 5265a2efe..24e830905 100644 --- a/coreapi/exevents.c +++ b/coreapi/exevents.c @@ -464,7 +464,8 @@ int linphone_set_audio_offer(sdp_context_t *ctx) elem=lc->codecs_conf.audio_codecs; while(elem!=NULL){ codec=(PayloadType*) elem->data; - if (linphone_core_check_payload_type_usability(lc,codec) && payload_type_enabled(codec)){ + if (linphone_core_check_payload_type_usability(lc,codec) && + linphone_core_payload_type_enabled(lc,codec)){ sdp_payload_init(&payload); payload.a_rtpmap=ortp_strdup_printf("%s/%i/1",codec->mime_type,codec->clock_rate); payload.pt=rtp_profile_get_payload_number_from_rtpmap(lc->local_profile,payload.a_rtpmap); @@ -539,7 +540,8 @@ int linphone_set_video_offer(sdp_context_t *ctx) for(elem=lc->codecs_conf.video_codecs;elem!=NULL;elem=ms_list_next(elem)){ codec=(PayloadType*) elem->data; - if (linphone_core_check_payload_type_usability(lc,codec) && payload_type_enabled(codec)){ + if (linphone_core_check_payload_type_usability(lc,codec) && + linphone_core_payload_type_enabled(lc,codec)){ sdp_payload_t payload; sdp_payload_init(&payload); payload.line=1; @@ -591,7 +593,7 @@ SupportLevel linphone_payload_is_supported(LinphoneCore *lc, sdp_payload_t *payl ms_warning("payload %s is not usable",rtppayload->mime_type); return Unsupported; } - if ( !payload_type_enabled(rtppayload)) { + if ( !linphone_core_payload_type_enabled(lc,rtppayload)) { ms_warning("payload %s is not enabled.",rtppayload->mime_type); return Unsupported; } diff --git a/coreapi/friend.c b/coreapi/friend.c index 08f072de3..c7d621a5e 100644 --- a/coreapi/friend.c +++ b/coreapi/friend.c @@ -31,9 +31,6 @@ const char *linphone_online_status_to_string(LinphoneOnlineStatus ss){ const char *str=NULL; switch(ss){ - case LINPHONE_STATUS_UNKNOWN: - str=_("Unknown"); - break; case LINPHONE_STATUS_ONLINE: str=_("Online"); break; @@ -67,9 +64,6 @@ const char *linphone_online_status_to_string(LinphoneOnlineStatus ss){ case LINPHONE_STATUS_PENDING: str=_("Pending"); break; - case LINPHONE_STATUS_CLOSED: - str=_("Closed"); - break; default: str=_("Unknown-bug"); } @@ -114,20 +108,20 @@ MSList *linphone_find_friend(MSList *fl, const LinphoneAddress *friend, Linphone return res; } -LinphoneFriend *linphone_find_friend_by_nid(MSList *l, int nid){ +LinphoneFriend *linphone_find_friend_by_inc_subscribe(MSList *l, SalOp *op){ MSList *elem; for (elem=l;elem!=NULL;elem=elem->next){ LinphoneFriend *lf=(LinphoneFriend*)elem->data; - if (lf->nid==nid) return lf; + if (lf->insub==op) return lf; } return NULL; } -LinphoneFriend *linphone_find_friend_by_sid(MSList *l, int sid){ +LinphoneFriend *linphone_find_friend_by_out_subscribe(MSList *l, SalOp *op){ MSList *elem; for (elem=l;elem!=NULL;elem=elem->next){ LinphoneFriend *lf=(LinphoneFriend*)elem->data; - if (lf->sid==sid) return lf; + if (lf->outsub==op) return lf; } return NULL; } @@ -136,30 +130,34 @@ void __linphone_friend_do_subscribe(LinphoneFriend *fr){ char *friend=NULL; const char *route=NULL; const char *from=NULL; - osip_message_t *msg=NULL; + LinphoneProxyConfig *cfg; + friend=linphone_address_as_string(fr->uri); - if (fr->proxy!=NULL){ - route=fr->proxy->reg_route; - from=fr->proxy->reg_identity; + cfg=linphone_core_lookup_known_proxy(fr->lc,linphone_friend_get_address(fr)); + if (cfg!=NULL){ + route=linphone_proxy_config_get_route(cfg); + from=linphone_proxy_config_get_identity(cfg); }else from=linphone_core_get_primary_contact(fr->lc); - if (fr->sid<0){ + if (fr->outsub==NULL){ /* people for which we don't have yet an answer should appear as offline */ - fr->lc->vtable.notify_recv(fr->lc,(LinphoneFriend*)fr,friend,_("Gone"),"sip-closed.png"); + fr->status=LINPHONE_STATUS_OFFLINE; + /* + if (fr->lc->vtable.notify_recv) + fr->lc->vtable.notify_recv(fr->lc,(LinphoneFriend*)fr); + */ + }else{ + sal_op_release(fr->outsub); + fr->outsub=NULL; } - eXosip_lock(); - eXosip_subscribe_build_initial_request(&msg,friend,from,route,"presence",600); - eXosip_subscribe_send_initial_request(msg); - eXosip_unlock(); + fr->outsub=sal_op_new(fr->lc->sal); + sal_op_set_route(fr->outsub,route); + sal_subscribe_presence(fr->outsub,from,friend); + fr->subscribe_active=TRUE; ms_free(friend); } - LinphoneFriend * linphone_friend_new(){ LinphoneFriend *obj=ms_new0(LinphoneFriend,1); - obj->out_did=-1; - obj->in_did=-1; - obj->nid=-1; - obj->sid=-1; obj->pol=LinphoneSPAccept; obj->status=LINPHONE_STATUS_OFFLINE; obj->subscribe=TRUE; @@ -175,6 +173,10 @@ LinphoneFriend *linphone_friend_new_with_addr(const char *addr){ return fr; } +bool_t linphone_friend_in_list(const LinphoneFriend *lf){ + return lf->lc!=NULL; +} + void linphone_core_interpret_friend_uri(LinphoneCore *lc, const char *uri, char **result){ LinphoneAddress *fr=NULL; *result=NULL; @@ -214,6 +216,7 @@ int linphone_friend_set_sip_addr(LinphoneFriend *lf, const char *addr){ ms_warning("Invalid friend sip uri: %s",addr); return -1; } + linphone_address_clean(fr); if (lf->uri!=NULL) linphone_address_destroy(lf->uri); lf->uri=fr; return 0; @@ -240,335 +243,84 @@ int linphone_friend_set_inc_subscribe_policy(LinphoneFriend *fr, LinphoneSubscri return 0; } -int linphone_friend_set_proxy(LinphoneFriend *fr, struct _LinphoneProxyConfig *cfg){ - fr->proxy=cfg; - return 0; +SalPresenceStatus linphone_online_status_to_sal(LinphoneOnlineStatus os){ + switch(os){ + case LINPHONE_STATUS_OFFLINE: + return SalPresenceOffline; + break; + case LINPHONE_STATUS_ONLINE: + return SalPresenceOnline; + break; + case LINPHONE_STATUS_BUSY: + return SalPresenceBusy; + break; + case LINPHONE_STATUS_BERIGHTBACK: + return SalPresenceBerightback; + break; + case LINPHONE_STATUS_AWAY: + return SalPresenceAway; + break; + case LINPHONE_STATUS_ONTHEPHONE: + return SalPresenceOnthephone; + break; + case LINPHONE_STATUS_OUTTOLUNCH: + return SalPresenceOuttolunch; + break; + case LINPHONE_STATUS_NOT_DISTURB: + return SalPresenceDonotdisturb; + break; + case LINPHONE_STATUS_MOVED: + return SalPresenceMoved; + break; + case LINPHONE_STATUS_ALT_SERVICE: + return SalPresenceAltService; + break; + case LINPHONE_STATUS_PENDING: + return SalPresenceOffline; + break; + default: + return SalPresenceOffline; + break; + } + return SalPresenceOffline; } -void linphone_friend_set_sid(LinphoneFriend *lf, int sid){ - lf->sid=sid; -} -void linphone_friend_set_nid(LinphoneFriend *lf, int nid){ - lf->nid=nid; - lf->inc_subscribe_pending=TRUE; -} - -void add_presence_body(osip_message_t *notify, LinphoneOnlineStatus online_status) -{ - char buf[1000]; -#ifdef SUPPORT_MSN - int atom_id = 1000; -#endif - char *contact_info; - - osip_contact_t *ct=NULL; - osip_message_get_contact(notify,0,&ct); - osip_contact_to_str(ct,&contact_info); - -#ifdef SUPPORT_MSN - - if (online_status==LINPHONE_STATUS_ONLINE) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - - } - else if (online_status==LINPHONE_STATUS_BUSY) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - - } - else if (online_status==LINPHONE_STATUS_BERIGHTBACK) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - - } - else if (online_status==LINPHONE_STATUS_AWAY) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - - } - else if (online_status==LINPHONE_STATUS_ONTHEPHONE) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - - } - else if (online_status==LINPHONE_STATUS_OUTTOLUNCH) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - - } - else - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -\n\ -
\n\ -\n\ -\n\ -
\n\ -
\n\ -
", contact_info, atom_id, contact_info); - } - - osip_message_set_body(notify, buf, strlen(buf)); - osip_message_set_content_type(notify, "application/xpidf+xml"); -#else - - if (online_status==LINPHONE_STATUS_ONLINE) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ -%s\n\ -online\n\ -\n\ -", - contact_info, contact_info); - } - else if (online_status==LINPHONE_STATUS_BUSY) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - busy\n\ -\n\ -\n\ -%s\n\ -busy\n\ -\n\ -", - contact_info, contact_info); - } - else if (online_status==LINPHONE_STATUS_BERIGHTBACK) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - in-transit\n\ -\n\ -\n\ -%s\n\ -be right back\n\ -\n\ -", - contact_info, contact_info); - } - else if (online_status==LINPHONE_STATUS_AWAY) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - away\n\ -\n\ -\n\ -%s\n\ -away\n\ -\n\ -", - contact_info, contact_info); - } - else if (online_status==LINPHONE_STATUS_ONTHEPHONE) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - on-the-phone\n\ -\n\ -\n\ -%s\n\ -on the phone\n\ -\n\ -", - contact_info, contact_info); - } - else if (online_status==LINPHONE_STATUS_OUTTOLUNCH) - { - sprintf(buf, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - meal\n\ -\n\ -\n\ -%s\n\ -out to lunch\n\ -\n\ -", - contact_info, contact_info); - } - else - { - /* */ - sprintf(buf, "\n\ -\n%s", - contact_info, -"\n\ -\n\ -closed\n\ -\n\ - permanent-absence\n\ -\n\ -\n\ -\n\ -\n\n"); - } - osip_message_set_body(notify, buf, strlen(buf)); - osip_message_set_content_type(notify, "application/pidf+xml"); - -#endif - osip_free(contact_info); -} - - -void linphone_friend_notify(LinphoneFriend *lf, int ss, LinphoneOnlineStatus os){ +void linphone_friend_notify(LinphoneFriend *lf, LinphoneOnlineStatus os){ //printf("Wish to notify %p, lf->nid=%i\n",lf,lf->nid); - if (lf->in_did!=-1){ - osip_message_t *msg=NULL; - const char *identity; - if (lf->proxy!=NULL) identity=lf->proxy->reg_identity; - else identity=linphone_core_get_primary_contact(lf->lc); - eXosip_lock(); - eXosip_insubscription_build_notify(lf->in_did,ss,0,&msg); - if (msg!=NULL){ - osip_message_set_contact(msg,identity); - add_presence_body(msg,os); - eXosip_insubscription_send_request(lf->in_did,msg); - }else ms_error("could not create notify for incoming subscription."); - eXosip_unlock(); + if (lf->insub!=NULL){ + sal_notify_presence(lf->insub,linphone_online_status_to_sal(os),NULL); } } static void linphone_friend_unsubscribe(LinphoneFriend *lf){ - if (lf->out_did!=-1) { - osip_message_t *msg=NULL; - eXosip_lock(); - eXosip_subscribe_build_refresh_request(lf->out_did,&msg); - if (msg){ - osip_message_set_expires(msg,"0"); - eXosip_subscribe_send_refresh_request(lf->out_did,msg); - }else ms_error("Could not build subscribe refresh request !"); - eXosip_unlock(); + if (lf->outsub!=NULL) { + sal_unsubscribe(lf->outsub); + sal_op_release(lf->outsub); + lf->outsub=NULL; + lf->subscribe_active=FALSE; + } +} + +void linphone_friend_close_subscriptions(LinphoneFriend *lf){ + linphone_friend_unsubscribe(lf); + if (lf->insub){ + sal_notify_close(lf->insub); + sal_op_release(lf->insub); + lf->insub=NULL; } } void linphone_friend_destroy(LinphoneFriend *lf){ - linphone_friend_notify(lf,EXOSIP_SUBCRSTATE_TERMINATED,LINPHONE_STATUS_CLOSED); - linphone_friend_unsubscribe(lf); + if (lf->uri!=NULL) linphone_address_destroy(lf->uri); if (lf->info!=NULL) buddy_info_free(lf->info); ms_free(lf); } -void linphone_friend_check_for_removed_proxy(LinphoneFriend *lf, LinphoneProxyConfig *cfg){ - if (lf->proxy==cfg){ - lf->proxy=NULL; - } -} - -const LinphoneAddress *linphone_friend_get_uri(const LinphoneFriend *lf){ +const LinphoneAddress *linphone_friend_get_address(const LinphoneFriend *lf){ return lf->uri; } - bool_t linphone_friend_get_send_subscribe(const LinphoneFriend *lf){ return lf->subscribe; } @@ -597,22 +349,22 @@ void linphone_friend_apply(LinphoneFriend *fr, LinphoneCore *lc){ if (fr->inc_subscribe_pending){ switch(fr->pol){ case LinphoneSPWait: - linphone_friend_notify(fr,EXOSIP_SUBCRSTATE_PENDING,LINPHONE_STATUS_PENDING); + linphone_friend_notify(fr,LINPHONE_STATUS_PENDING); break; case LinphoneSPAccept: if (fr->lc!=NULL) { - linphone_friend_notify(fr,EXOSIP_SUBCRSTATE_ACTIVE,fr->lc->presence_mode); + linphone_friend_notify(fr,fr->lc->presence_mode); } break; case LinphoneSPDeny: - linphone_friend_notify(fr,EXOSIP_SUBCRSTATE_TERMINATED,LINPHONE_STATUS_CLOSED); + linphone_friend_notify(fr,LINPHONE_STATUS_OFFLINE); break; } fr->inc_subscribe_pending=FALSE; } - if (fr->subscribe && fr->out_did==-1){ - + if (fr->subscribe && fr->subscribe_active==FALSE){ + ms_message("Sending a new SUBSCRIBE"); __linphone_friend_do_subscribe(fr); } ms_message("linphone_friend_apply() done."); @@ -632,6 +384,14 @@ void linphone_core_add_friend(LinphoneCore *lc, LinphoneFriend *lf) { ms_return_if_fail(lf->lc==NULL); ms_return_if_fail(lf->uri!=NULL); + if (ms_list_find(lc->friends,lf)!=NULL){ + char *tmp=NULL; + const LinphoneAddress *addr=linphone_friend_get_address(lf); + if (addr) tmp=linphone_address_as_string(addr); + ms_warning("Friend %s already in list, ignored.", tmp ? tmp : "unknown"); + if (tmp) ms_free(tmp); + return ; + } lc->friends=ms_list_append(lc->friends,lf); linphone_friend_apply(lf,lc); return ; @@ -667,7 +427,7 @@ static bool_t username_match(const char *u1, const char *u2){ return FALSE; } -LinphoneFriend *linphone_core_get_friend_by_uri(const LinphoneCore *lc, const char *uri){ +LinphoneFriend *linphone_core_get_friend_by_address(const LinphoneCore *lc, const char *uri){ LinphoneAddress *puri=linphone_address_new(uri); const MSList *elem; const char *username=linphone_address_get_username(puri); @@ -752,10 +512,6 @@ LinphoneFriend * linphone_friend_new_from_config_file(LinphoneCore *lc, int inde a=lp_config_get_int(config,item,"subscribe",0); linphone_friend_send_subscribe(lf,a); - a=lp_config_get_int(config,item,"proxy",-1); - if (a!=-1) { - linphone_friend_set_proxy(lf,__index_to_proxy(lc,a)); - } linphone_friend_set_ref_key(lf,lp_config_get_string(config,item,"refkey",NULL)); return lf; } @@ -779,7 +535,6 @@ const char *__policy_enum_to_str(LinphoneSubscribePolicy pol){ void linphone_friend_write_to_config_file(LpConfig *config, LinphoneFriend *lf, int index){ char key[50]; char *tmp; - int a; const char *refkey; sprintf(key,"friend_%i",index); @@ -798,10 +553,6 @@ void linphone_friend_write_to_config_file(LpConfig *config, LinphoneFriend *lf, } lp_config_set_string(config,key,"pol",__policy_enum_to_str(lf->pol)); lp_config_set_int(config,key,"subscribe",lf->subscribe); - if (lf->proxy!=NULL){ - a=ms_list_index(lf->lc->sip_conf.proxies,lf->proxy); - lp_config_set_int(config,key,"proxy",a); - }else lp_config_set_int(config,key,"proxy",-1); refkey=linphone_friend_get_ref_key(lf); if (refkey){ diff --git a/coreapi/general_state.c b/coreapi/general_state.c index 7cf713773..210037887 100644 --- a/coreapi/general_state.c +++ b/coreapi/general_state.c @@ -24,7 +24,7 @@ #include "linphonecore.h" - +#include "private.h" #if 0 static const char *_gstates_text[] = { "GSTATE_POWER_OFF", /* 0 */ diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 6146bdb3c..3e911cee1 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -24,11 +24,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "mediastreamer2/mediastream.h" #include "mediastreamer2/msvolume.h" #include "mediastreamer2/msequalizer.h" -#include -#include "sdphandler.h" #include -#include "exevents.h" #ifdef INET6 @@ -45,7 +42,6 @@ static void set_network_reachable(LinphoneCore* lc,bool_t isReachable); #include "enum.h" void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result); -static void apply_nat_settings(LinphoneCore *lc); static void toggle_video_preview(LinphoneCore *lc, bool_t val); /* relative path where is stored local ring*/ @@ -53,15 +49,7 @@ static void toggle_video_preview(LinphoneCore *lc, bool_t val); /* same for remote ring (ringback)*/ #define REMOTE_RING "ringback.wav" - -sdp_handler_t linphone_sdphandler={ - linphone_accept_audio_offer, /*from remote sdp */ - linphone_accept_video_offer, /*from remote sdp */ - linphone_set_audio_offer, /*to local sdp */ - linphone_set_video_offer, /*to local sdp */ - linphone_read_audio_answer, /*from incoming answer */ - linphone_read_video_answer /*from incoming answer */ -}; +extern SalCallbacks linphone_sal_callbacks; void lc_callback_obj_init(LCCallbackObj *obj,LinphoneCoreCbFunc func,void* ud) { @@ -74,7 +62,56 @@ int lc_callback_obj_invoke(LCCallbackObj *obj, LinphoneCore *lc){ return 0; } -static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){ + +static MSList *make_codec_list(LinphoneCore *lc, const MSList *codecs, bool_t only_one_codec){ + MSList *l=NULL; + const MSList *it; + for(it=codecs;it!=NULL;it=it->next){ + PayloadType *pt=(PayloadType*)it->data; + if ((pt->flags & PAYLOAD_TYPE_ENABLED) && linphone_core_check_payload_type_usability(lc,pt)){ + l=ms_list_append(l,payload_type_clone(pt)); + if (only_one_codec) break; + } + } + return l; +} + +static SalMediaDescription *create_local_media_description(LinphoneCore *lc, + const char *localip, const char *username, bool_t only_one_codec){ + MSList *l; + PayloadType *pt; + SalMediaDescription *md=sal_media_description_new(); + md->nstreams=1; + strncpy(md->addr,localip,sizeof(md->addr)); + strncpy(md->username,username,sizeof(md->username)); + /*set audio capabilities */ + strncpy(md->streams[0].addr,localip,sizeof(md->streams[0].addr)); + md->streams[0].port=linphone_core_get_audio_port(lc); + md->streams[0].proto=SalProtoRtpAvp; + md->streams[0].type=SalAudio; + md->streams[0].ptime=lc->net_conf.down_ptime; + l=make_codec_list(lc,lc->codecs_conf.audio_codecs,only_one_codec); + pt=payload_type_clone(rtp_profile_get_payload_from_mime(&av_profile,"telephone-event")); + l=ms_list_append(l,pt); + md->streams[0].payloads=l; + + if (lc->dw_audio_bw>0) + md->streams[0].bandwidth=lc->dw_audio_bw; + + if (linphone_core_video_enabled (lc)){ + md->nstreams++; + md->streams[1].port=linphone_core_get_video_port(lc); + md->streams[1].proto=SalProtoRtpAvp; + md->streams[1].type=SalVideo; + l=make_codec_list(lc,lc->codecs_conf.video_codecs,only_one_codec); + md->streams[1].payloads=l; + if (lc->dw_video_bw) + md->streams[1].bandwidth=lc->dw_video_bw; + } + return md; +} + +static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from, LinphoneAddress *to){ call->state=LCStateInit; call->start_time=time(NULL); call->media_start_time=0; @@ -82,12 +119,6 @@ static void linphone_call_init_common(LinphoneCall *call, LinphoneAddress *from linphone_core_notify_all_friends(call->core,LINPHONE_STATUS_ONTHEPHONE); if (linphone_core_get_firewall_policy(call->core)==LINPHONE_POLICY_USE_STUN) linphone_core_run_stun_tests(call->core,call); - call->profile=rtp_profile_new("Call RTP profile"); -} - -void linphone_call_init_media_params(LinphoneCall *call){ - memset(&call->audio_params,0,sizeof(call->audio_params)); - memset(&call->video_params,0,sizeof(call->video_params)); } static void discover_mtu(LinphoneCore *lc, const char *remote){ @@ -107,44 +138,47 @@ LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddr { LinphoneCall *call=ms_new0(LinphoneCall,1); call->dir=LinphoneCallOutgoing; - call->cid=-1; - call->did=-1; - call->tid=-1; + call->op=sal_op_new(lc->sal); + sal_op_set_user_pointer(call->op,call); call->core=lc; linphone_core_get_local_ip(lc,linphone_address_get_domain(to),call->localip); + call->localdesc=create_local_media_description (lc,call->localip, + linphone_address_get_username(from),FALSE); linphone_call_init_common(call,from,to); - call->sdpctx=sdp_handler_create_context(&linphone_sdphandler, - call->audio_params.natd_port>0 ? call->audio_params.natd_addr : call->localip, - linphone_address_get_username (from),NULL); - sdp_context_set_user_pointer(call->sdpctx,(void*)call); discover_mtu(lc,linphone_address_get_domain (to)); return call; } - -LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, eXosip_event_t *ev){ +LinphoneCall * linphone_call_new_incoming(LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op){ LinphoneCall *call=ms_new0(LinphoneCall,1); LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc); - osip_header_t *h=NULL; + char *to_str; + char *from_str; call->dir=LinphoneCallIncoming; - call->cid=ev->cid; - call->did=ev->did; - call->tid=ev->tid; + sal_op_set_user_pointer(op,call); + call->op=op; call->core=lc; + + if (lc->sip_conf.ping_with_options){ + /*the following sends an option request back to the caller so that + we get a chance to discover our nat'd address before answering.*/ + call->ping_op=sal_op_new(lc->sal); + to_str=linphone_address_as_string(to); + from_str=linphone_address_as_string(from); + sal_op_set_route(call->ping_op,sal_op_get_network_origin(call->op)); + sal_ping(call->ping_op,to_str,from_str); + ms_free(to_str); + ms_free(from_str); + } linphone_address_clean(from); - linphone_core_get_local_ip(lc,linphone_address_get_domain(from),call->localip); + call->localdesc=create_local_media_description (lc,call->localip, + linphone_address_get_username(me),lc->sip_conf.only_one_codec); linphone_call_init_common(call, from, to); - call->sdpctx=sdp_handler_create_context(&linphone_sdphandler, - call->audio_params.natd_port>0 ? call->audio_params.natd_addr : call->localip, - linphone_address_get_username (me),NULL); - sdp_context_set_user_pointer(call->sdpctx,(void*)call); discover_mtu(lc,linphone_address_get_domain(from)); linphone_address_destroy(me); - osip_message_header_get_byname(ev->request,"Session-expires",0,&h); - if (h) call->supports_session_timers=TRUE; return call; } @@ -153,8 +187,21 @@ void linphone_call_destroy(LinphoneCall *obj) linphone_core_notify_all_friends(obj->core,obj->core->prev_mode); linphone_call_log_completed(obj->log,obj); linphone_core_update_allocated_audio_bandwidth(obj->core); - if (obj->profile!=NULL) rtp_profile_destroy(obj->profile); - if (obj->sdpctx!=NULL) sdp_context_free(obj->sdpctx); + if (obj->op!=NULL) { + sal_op_release(obj->op); + obj->op=NULL; + } + if (obj->resultdesc!=NULL) { + sal_media_description_unref(obj->resultdesc); + obj->resultdesc=NULL; + } + if (obj->localdesc!=NULL) { + sal_media_description_unref(obj->localdesc); + obj->localdesc=NULL; + } + if (obj->ping_op) { + sal_op_release(obj->ping_op); + } ms_free(obj); } @@ -252,6 +299,7 @@ void linphone_call_log_completed(LinphoneCallLog *calllog, LinphoneCall *call){ calllog->duration=time(NULL)-call->start_time; switch(call->state){ case LCStateInit: + case LCStatePreEstablishing: calllog->status=LinphoneCallAborted; break; case LCStateRinging: @@ -388,43 +436,6 @@ const LinphoneAddress *linphone_core_get_remote_uri(LinphoneCore *lc){ return call->dir==LinphoneCallIncoming ? call->log->from : call->log->to; } -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); - } -} - /** * Enable logs in supplied FILE*. * @@ -437,7 +448,6 @@ void linphone_core_enable_logs(FILE *file){ if (file==NULL) file=stdout; ortp_set_log_file(file); ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL); - osip_trace_initialize_func (OSIP_INFO4,&_osip_trace_func); } /** @@ -451,7 +461,6 @@ void linphone_core_enable_logs(FILE *file){ **/ void linphone_core_enable_logs_with_cb(OrtpLogFunc logfunc){ ortp_set_log_level_mask(ORTP_MESSAGE|ORTP_WARNING|ORTP_ERROR|ORTP_FATAL); - osip_trace_initialize_func (OSIP_INFO4,&_osip_trace_func); ortp_set_log_handler(logfunc); } @@ -461,8 +470,6 @@ void linphone_core_enable_logs_with_cb(OrtpLogFunc logfunc){ * @ingroup misc **/ void linphone_core_disable_logs(){ - int tl; - for (tl=0;tl<=OSIP_INFO4;tl++) osip_trace_disable_level(tl); ortp_set_log_level_mask(ORTP_ERROR|ORTP_FATAL); } @@ -515,6 +522,7 @@ static void sound_config_read(LinphoneCore *lc) /*int tmp;*/ const char *tmpbuf; const char *devid; + float gain=0; #ifdef __linux /*alsadev let the user use custom alsa device within linphone*/ devid=lp_config_get_string(lc->config,"sound","alsadev",NULL); @@ -580,6 +588,9 @@ static void sound_config_read(LinphoneCore *lc) lp_config_get_int(lc->config,"sound","echolimiter",0)); linphone_core_enable_agc(lc, lp_config_get_int(lc->config,"sound","agc",0)); + + gain=lp_config_get_float(lc->config,"sound","soft_play_lev",0); + linphone_core_set_soft_play_level(lc,gain); } static void sip_config_read(LinphoneCore *lc) @@ -650,16 +661,19 @@ static void sip_config_read(LinphoneCore *lc) LinphoneAuthInfo *ai=linphone_auth_info_new_from_config_file(lc->config,i); if (ai!=NULL){ linphone_core_add_auth_info(lc,ai); + linphone_auth_info_destroy(ai); }else{ break; } } - /*for test*/ + /*for tuning or test*/ lc->sip_conf.sdp_200_ack=lp_config_get_int(lc->config,"sip","sdp_200_ack",0); lc->sip_conf.only_one_codec=lp_config_get_int(lc->config,"sip","only_one_codec",0); lc->sip_conf.register_only_when_network_is_up= lp_config_get_int(lc->config,"sip","register_only_when_network_is_up",1); + lc->sip_conf.ping_with_options=lp_config_get_int(lc->config,"sip","ping_with_options",1); + lc->sip_conf.auto_net_state_mon=lp_config_get_int(lc->config,"sip","auto_net_state_mon",1); } static void rtp_config_read(LinphoneCore *lc) @@ -681,28 +695,100 @@ static void rtp_config_read(LinphoneCore *lc) linphone_core_set_nortp_timeout(lc,nortp_timeout); } +static PayloadType * find_payload(RtpProfile *prof, const char *mime_type, int clock_rate, const char *recv_fmtp){ + PayloadType *candidate=NULL; + int i; + PayloadType *it; + for(i=0;i<127;++i){ + it=rtp_profile_get_payload(prof,i); + if (it!=NULL && strcasecmp(mime_type,it->mime_type)==0 + && (clock_rate==it->clock_rate || clock_rate<=0) ){ + if ( (recv_fmtp && it->recv_fmtp && strcasecmp(recv_fmtp,it->recv_fmtp)==0) || + (recv_fmtp==NULL && it->recv_fmtp==NULL) ){ + /*exact match*/ + return it; + }else candidate=it; + } + } + return candidate; +} -static PayloadType * get_codec(LpConfig *config, char* type,int index){ +static bool_t get_codec(LpConfig *config, char* type, int index, PayloadType **ret){ char codeckey[50]; const char *mime,*fmtp; int rate,enabled; PayloadType *pt; + *ret=NULL; snprintf(codeckey,50,"%s_%i",type,index); mime=lp_config_get_string(config,codeckey,"mime",NULL); - if (mime==NULL || strlen(mime)==0 ) return NULL; - - pt=payload_type_new(); - pt->mime_type=ms_strdup(mime); + if (mime==NULL || strlen(mime)==0 ) return FALSE; rate=lp_config_get_int(config,codeckey,"rate",8000); - pt->clock_rate=rate; fmtp=lp_config_get_string(config,codeckey,"recv_fmtp",NULL); - if (fmtp) pt->recv_fmtp=ms_strdup(fmtp); enabled=lp_config_get_int(config,codeckey,"enabled",1); - if (enabled ) pt->flags|=PAYLOAD_TYPE_ENABLED; + pt=find_payload(&av_profile,mime,rate,fmtp); + if (pt && enabled ) pt->flags|=PAYLOAD_TYPE_ENABLED; //ms_message("Found codec %s/%i",pt->mime_type,pt->clock_rate); - return pt; + if (pt==NULL) ms_warning("Ignoring codec config %s/%i with fmtp=%s because unsupported", + mime,rate,fmtp ? fmtp : ""); + *ret=pt; + return TRUE; +} + +static const char *codec_pref_order[]={ + "speex", + "gsm", + "pcmu", + "pcma", + "H264", + "MP4V-ES", + "theora", + "H263-1998", + "H263", + NULL, +}; + +static int find_codec_rank(const char *mime){ + int i; + for(i=0;codec_pref_order[i]!=NULL;++i){ + if (strcasecmp(codec_pref_order[i],mime)==0) + break; + } + return i; +} + +static int codec_compare(const PayloadType *a, const PayloadType *b){ + int ra,rb; + ra=find_codec_rank(a->mime_type); + rb=find_codec_rank(b->mime_type); + if (ra>rb) return 1; + if (ratype!=PAYLOAD_VIDEO) + pt=NULL; + else if (mtype==SalAudio && (pt->type!=PAYLOAD_AUDIO_PACKETIZED + && pt->type!=PAYLOAD_AUDIO_CONTINUOUS)){ + pt=NULL; + } + if (pt && ms_filter_codec_supported(pt->mime_type)){ + if (ms_list_find(l,pt)==NULL){ + payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED); + ms_message("Adding new codec %s/%i with fmtp %s", + pt->mime_type,pt->clock_rate,pt->recv_fmtp ? pt->recv_fmtp : ""); + l=ms_list_insert_sorted(l,pt,(int (*)(const void *, const void *))codec_compare); + } + } + } + } + return l; } static void codecs_config_read(LinphoneCore *lc) @@ -711,23 +797,28 @@ static void codecs_config_read(LinphoneCore *lc) PayloadType *pt; MSList *audio_codecs=NULL; MSList *video_codecs=NULL; - for (i=0;;i++){ - pt=get_codec(lc->config,"audio_codec",i); - if (pt==NULL) break; - audio_codecs=ms_list_append(audio_codecs,(void *)pt); + for (i=0;get_codec(lc->config,"audio_codec",i,&pt);i++){ + if (pt){ + if (!ms_filter_codec_supported(pt->mime_type)){ + ms_warning("Codec %s is not supported by mediastreamer2, removed.",pt->mime_type); + }else audio_codecs=ms_list_append(audio_codecs,pt); + } } - for (i=0;;i++){ - pt=get_codec(lc->config,"video_codec",i); - if (pt==NULL) break; - video_codecs=ms_list_append(video_codecs,(void *)pt); + audio_codecs=add_missing_codecs(SalAudio,audio_codecs); + for (i=0;get_codec(lc->config,"video_codec",i,&pt);i++){ + if (pt){ + if (!ms_filter_codec_supported(pt->mime_type)){ + ms_warning("Codec %s is not supported by mediastreamer2, removed.",pt->mime_type); + }else video_codecs=ms_list_append(video_codecs,(void *)pt); + } } + video_codecs=add_missing_codecs(SalVideo,video_codecs); linphone_core_set_audio_codecs(lc,audio_codecs); linphone_core_set_video_codecs(lc,video_codecs); - linphone_core_setup_local_rtp_profile(lc); + linphone_core_update_allocated_audio_bandwidth(lc); } -static void video_config_read(LinphoneCore *lc) -{ +static void video_config_read(LinphoneCore *lc){ int capture, display, self_view; int enabled; const char *str; @@ -861,13 +952,12 @@ int linphone_core_get_upload_bandwidth(const LinphoneCore *lc){ /** * set audio packetization time linphone expect to received from peer */ -void linphone_core_set_download_ptime(LinphoneCore *lc, int ptime); - void linphone_core_set_download_ptime(LinphoneCore *lc, int ptime) { - lc->down_ptime=ptime; + lc->net_conf.down_ptime=ptime; } + int linphone_core_get_download_ptime(LinphoneCore *lc) { - return lc->down_ptime; + return lc->net_conf.down_ptime; } @@ -887,6 +977,7 @@ static MSList *linphone_payload_types=NULL; static void linphone_core_assign_payload_type(PayloadType *const_pt, int number, const char *recv_fmtp){ PayloadType *pt; pt=payload_type_clone(const_pt); + payload_type_set_number(pt,number); if (recv_fmtp!=NULL) payload_type_set_recv_fmtp(pt,recv_fmtp); rtp_profile_set_payload(&av_profile,number,pt); linphone_payload_types=ms_list_append(linphone_payload_types,pt); @@ -910,11 +1001,14 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta gstate_new_state(lc, GSTATE_POWER_STARTUP, NULL); ortp_init(); + linphone_core_assign_payload_type(&payload_type_pcmu8000,0,NULL); + linphone_core_assign_payload_type(&payload_type_gsm,3,NULL); + linphone_core_assign_payload_type(&payload_type_pcma8000,8,NULL); linphone_core_assign_payload_type(&payload_type_lpc1015,115,NULL); linphone_core_assign_payload_type(&payload_type_speex_nb,110,"vbr=on"); linphone_core_assign_payload_type(&payload_type_speex_wb,111,"vbr=on"); linphone_core_assign_payload_type(&payload_type_speex_uwb,112,"vbr=on"); - linphone_core_assign_payload_type(&payload_type_telephone_event,101,NULL); + linphone_core_assign_payload_type(&payload_type_telephone_event,101,"0-11"); linphone_core_assign_payload_type(&payload_type_ilbc,113,"mode=30"); #ifdef ENABLE_NONSTANDARD_GSM @@ -947,11 +1041,12 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta if (factory_config_path) lp_config_read_file(lc->config,factory_config_path); -#ifdef VINCENT_MAURY_RSVP - /* default qos parameters : rsvp on, rpc off */ - lc->rsvp_enable = 1; - lc->rpc_enable = 0; -#endif + lc->sal=sal_init(); + sal_set_user_pointer(lc->sal,lc); + sal_set_callbacks(lc->sal,&linphone_sal_callbacks); + if (lp_config_get_int(lc->config,"sip","use_session_timers",0)==1){ + sal_use_session_timers(lc->sal,200); + } sip_setup_register_all(); sound_config_read(lc); net_config_read(lc); @@ -964,10 +1059,9 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta lc->presence_mode=LINPHONE_STATUS_ONLINE; lc->max_call_logs=15; ui_config_read(lc); - ms_mutex_init(&lc->lock,NULL); lc->vtable.display_status(lc,_("Ready")); gstate_new_state(lc, GSTATE_POWER_ON, NULL); - lc->auto_net_state_mon=TRUE; + lc->auto_net_state_mon=lc->sip_conf.auto_net_state_mon; lc->ready=TRUE; } @@ -1032,11 +1126,10 @@ const MSList *linphone_core_get_video_codecs(const LinphoneCore *lc) **/ int linphone_core_set_primary_contact(LinphoneCore *lc, const char *contact) { - osip_from_t *ctt=NULL; - osip_from_init(&ctt); - if (osip_from_parse(ctt,contact)!=0){ + LinphoneAddress *ctt; + + if ((ctt=linphone_address_new(contact))==0) { ms_error("Bad contact url: %s",contact); - osip_from_free(ctt); return -1; } if (lc->sip_conf.contact!=NULL) ms_free(lc->sip_conf.contact); @@ -1045,17 +1138,13 @@ int linphone_core_set_primary_contact(LinphoneCore *lc, const char *contact) ms_free(lc->sip_conf.guessed_contact); lc->sip_conf.guessed_contact=NULL; } - osip_from_free(ctt); + linphone_address_destroy(ctt); return 0; } /*result must be an array of chars at least LINPHONE_IPADDR_SIZE */ void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result){ - if (lc->apply_nat_settings){ - apply_nat_settings(lc); - lc->apply_nat_settings=FALSE; - } if (linphone_core_get_firewall_policy(lc)==LINPHONE_POLICY_USE_NAT_ADDRESS){ strncpy(result,linphone_core_get_nat_address(lc),LINPHONE_IPADDR_SIZE); return; @@ -1063,12 +1152,34 @@ void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result if (dest==NULL) dest="87.98.157.38"; /*a public IP address*/ if (linphone_core_get_local_ip_for(dest,result)==0) return; - /*else fallback to exosip routine that will attempt to find the most realistic interface */ - if (eXosip_guess_localip(lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,result,LINPHONE_IPADDR_SIZE)<0){ - /*default to something */ - strncpy(result,lc->sip_conf.ipv6_enabled ? "::1" : "127.0.0.1",LINPHONE_IPADDR_SIZE); - ms_error("Could not find default routable ip address !"); + /*else fallback to SAL routine that will attempt to find the most realistic interface */ + sal_get_default_local_ip(lc->sal,lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,result,LINPHONE_IPADDR_SIZE); +} + +static void update_primary_contact(LinphoneCore *lc){ + char *guessed=NULL; + char tmp[LINPHONE_IPADDR_SIZE]; + + LinphoneAddress *url; + if (lc->sip_conf.guessed_contact!=NULL){ + ms_free(lc->sip_conf.guessed_contact); + lc->sip_conf.guessed_contact=NULL; } + url=linphone_address_new(lc->sip_conf.contact); + if (!url){ + ms_error("Could not parse identity contact !"); + url=linphone_address_new("sip:unknown@unkwownhost"); + } + linphone_core_get_local_ip(lc, NULL, tmp); + if (strcmp(tmp,"127.0.0.1")==0 || strcmp(tmp,"::1")==0 ){ + ms_warning("Local loopback network only !"); + lc->sip_conf.loopback_only=TRUE; + }else lc->sip_conf.loopback_only=FALSE; + linphone_address_set_domain(url,tmp); + linphone_address_set_port_int(url,lc->sip_conf.sip_port); + guessed=linphone_address_as_string(url); + lc->sip_conf.guessed_contact=guessed; + linphone_address_destroy(url); } /** @@ -1076,42 +1187,12 @@ void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result * * @ingroup proxies **/ -const char *linphone_core_get_primary_contact(LinphoneCore *lc) -{ +const char *linphone_core_get_primary_contact(LinphoneCore *lc){ char *identity; - char tmp[LINPHONE_IPADDR_SIZE]; + if (lc->sip_conf.guess_hostname){ if (lc->sip_conf.guessed_contact==NULL || lc->sip_conf.loopback_only){ - char *guessed=NULL; - osip_from_t *url; - if (lc->sip_conf.guessed_contact!=NULL){ - ms_free(lc->sip_conf.guessed_contact); - lc->sip_conf.guessed_contact=NULL; - } - - osip_from_init(&url); - if (osip_from_parse(url,lc->sip_conf.contact)==0){ - - }else ms_error("Could not parse identity contact !"); - linphone_core_get_local_ip(lc, NULL, tmp); - if (strcmp(tmp,"127.0.0.1")==0 || strcmp(tmp,"::1")==0 ){ - ms_warning("Local loopback network only !"); - lc->sip_conf.loopback_only=TRUE; - }else lc->sip_conf.loopback_only=FALSE; - osip_free(url->url->host); - url->url->host=osip_strdup(tmp); - if (url->url->port!=NULL){ - osip_free(url->url->port); - url->url->port=NULL; - } - if (lc->sip_conf.sip_port!=5060){ - url->url->port=ortp_strdup_printf("%i",lc->sip_conf.sip_port); - } - osip_from_to_str(url,&guessed); - lc->sip_conf.guessed_contact=guessed; - - osip_from_free(url); - + update_primary_contact(lc); } identity=lc->sip_conf.guessed_contact; }else{ @@ -1313,20 +1394,23 @@ int linphone_core_get_sip_port(LinphoneCore *lc) return lc->sip_conf.sip_port; } -static bool_t exosip_running=FALSE; static char _ua_name[64]="Linphone"; static char _ua_version[64]=LINPHONE_VERSION; -static void apply_user_agent(void){ +#ifdef HAVE_EXOSIP_GET_VERSION +extern const char *eXosip_get_version(); +#endif + +static void apply_user_agent(LinphoneCore *lc){ char ua_string[256]; - snprintf(ua_string,sizeof(ua_string),"%s/%s (eXosip2/%s)",_ua_name,_ua_version, + snprintf(ua_string,sizeof(ua_string)-1,"%s/%s (eXosip2/%s)",_ua_name,_ua_version, #ifdef HAVE_EXOSIP_GET_VERSION eXosip_get_version() #else "unknown" #endif ); - eXosip_set_user_agent(ua_string); + if (lc->sal) sal_set_user_agent(lc->sal,ua_string); } /** @@ -1350,18 +1434,14 @@ void linphone_core_set_sip_port(LinphoneCore *lc,int port) int err=0; if (port==lc->sip_conf.sip_port) return; lc->sip_conf.sip_port=port; - if (exosip_running) eXosip_quit(); - eXosip_init(); - 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*/ - eXosip_enable_ipv6(lc->sip_conf.ipv6_enabled); + + if (lc->sal==NULL) return; + if (lc->sip_conf.ipv6_enabled) anyaddr="::0"; else anyaddr="0.0.0.0"; - err=eXosip_listen_addr (IPPROTO_UDP, anyaddr, port, - lc->sip_conf.ipv6_enabled ? PF_INET6 : PF_INET, 0); + err=sal_listen_port (lc->sal,anyaddr,port, SalTransportDatagram,FALSE); if (err<0){ char *msg=ortp_strdup_printf("UDP port %i seems already in use ! Cannot initialize.",port); ms_warning(msg); @@ -1369,13 +1449,7 @@ void linphone_core_set_sip_port(LinphoneCore *lc,int port) ms_free(msg); return; } -#ifdef VINCENT_MAURY_RSVP - /* tell exosip the qos settings according to default linphone parameters */ - eXosip_set_rsvp_mode (lc->rsvp_enable); - eXosip_set_rpc_mode (lc->rpc_enable); -#endif - apply_user_agent(); - exosip_running=TRUE; + apply_user_agent(lc); } /** @@ -1400,7 +1474,7 @@ bool_t linphone_core_ipv6_enabled(LinphoneCore *lc){ void linphone_core_enable_ipv6(LinphoneCore *lc, bool_t val){ if (lc->sip_conf.ipv6_enabled!=val){ lc->sip_conf.ipv6_enabled=val; - if (exosip_running){ + if (lc->sal){ /* we need to restart eXosip */ linphone_core_set_sip_port(lc, lc->sip_conf.sip_port); } @@ -1428,11 +1502,12 @@ static void monitor_network_state(LinphoneCore *lc, time_t curtime){ /* only do the network up checking every five seconds */ if (last_check==0 || (curtime-last_check)>=5){ - if (eXosip_guess_localip(lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,result,LINPHONE_IPADDR_SIZE)==0){ - if (strcmp(result,"::1")!=0 && strcmp(result,"127.0.0.1")!=0){ - new_status=TRUE; - }else new_status=FALSE; - } + sal_get_default_local_ip(lc->sal, + lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET, + result,LINPHONE_IPADDR_SIZE); + if (strcmp(result,"::1")!=0 && strcmp(result,"127.0.0.1")!=0){ + new_status=TRUE; + }else new_status=FALSE; last_check=curtime; if (new_status!=last_status) { set_network_reachable(lc,new_status); @@ -1446,7 +1521,7 @@ static void proxy_update(LinphoneCore *lc){ } static void assign_buddy_info(LinphoneCore *lc, BuddyInfo *info){ - LinphoneFriend *lf=linphone_core_get_friend_by_uri(lc,info->sip_uri); + LinphoneFriend *lf=linphone_core_get_friend_by_address(lc,info->sip_uri); if (lf!=NULL){ lf->info=info; ms_message("%s has a BuddyInfo assigned with image %p",info->sip_uri, info->image_data); @@ -1530,14 +1605,12 @@ static void linphone_core_do_plugin_tasks(LinphoneCore *lc){ * other liblinphone methods. In not the case make sure all liblinphone calls are * serialized with a mutex. **/ -void linphone_core_iterate(LinphoneCore *lc) -{ - eXosip_event_t *ev; - bool_t disconnected=FALSE; +void linphone_core_iterate(LinphoneCore *lc){ int disconnect_timeout = linphone_core_get_nortp_timeout(lc); time_t curtime=time(NULL); int elapsed; bool_t one_second_elapsed=FALSE; + bool_t disconnected=FALSE; if (curtime-lc->prevtime>=1){ lc->prevtime=curtime; @@ -1551,24 +1624,17 @@ void linphone_core_iterate(LinphoneCore *lc) lc_callback_obj_invoke(&lc->preview_finished_cb,lc); } - if (exosip_running){ - while((ev=eXosip_event_wait(0,0))!=NULL){ - linphone_core_process_event(lc,ev); - } - if (lc->automatic_action==0) { - eXosip_lock(); - eXosip_automatic_action(); - eXosip_unlock(); - } - } - + sal_iterate(lc->sal); if (lc->auto_net_state_mon) monitor_network_state(lc,curtime); proxy_update(lc); if (lc->call!=NULL){ LinphoneCall *call=lc->call; - + if (call->state==LCStatePreEstablishing && (curtime-call->start_time>=2)){ + /*start the call even if the OPTIONS reply did not arrive*/ + linphone_core_start_invite(lc,call,NULL); + } if (call->dir==LinphoneCallIncoming && call->state==LCStateRinging){ elapsed=curtime-call->start_time; ms_message("incoming call ringing for %i seconds",elapsed); @@ -1614,63 +1680,25 @@ void linphone_core_iterate(LinphoneCore *lc) } -bool_t linphone_core_is_in_main_thread(LinphoneCore *lc){ - return TRUE; -} - -static char *guess_route_if_any(LinphoneCore *lc, osip_to_t *parsed_url){ - const MSList *elem=linphone_core_get_proxy_config_list(lc); - for(;elem!=NULL;elem=elem->next){ - LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data; - char prx[256]; - if (cfg->ssctx && sip_setup_context_get_proxy(cfg->ssctx,parsed_url->url->host,prx,sizeof(prx))==0){ - ms_message("We have a proxy for domain %s",parsed_url->url->host); - if (strcmp(parsed_url->url->host,prx)!=0){ - char *route=NULL; - osip_route_t *rt; - osip_route_init(&rt); - if (osip_route_parse(rt,prx)==0){ - char *rtstr; - osip_uri_uparam_add(rt->url,osip_strdup("lr"),NULL); - osip_route_to_str(rt,&rtstr); - route=ms_strdup(rtstr); - osip_free(rtstr); - } - osip_route_free(rt); - ms_message("Adding a route: %s",route); - return route; - } - } - } - return NULL; -} - -bool_t linphone_core_interpret_url(LinphoneCore *lc, const char *url, LinphoneAddress **real_parsed_url, char **route){ +LinphoneAddress * linphone_core_interpret_url(LinphoneCore *lc, const char *url){ enum_lookup_res_t *enumres=NULL; - osip_to_t *parsed_url=NULL; char *enum_domain=NULL; LinphoneProxyConfig *proxy=lc->default_proxy;; char *tmpurl; - const char *tmproute; LinphoneAddress *uri; - if (real_parsed_url!=NULL) *real_parsed_url=NULL; - *route=NULL; - tmproute=linphone_core_get_route(lc); - if (is_enum(url,&enum_domain)){ lc->vtable.display_status(lc,_("Looking for telephone number destination...")); if (enum_lookup(enum_domain,&enumres)<0){ lc->vtable.display_status(lc,_("Could not resolve this number.")); ms_free(enum_domain); - return FALSE; + return NULL; } ms_free(enum_domain); tmpurl=enumres->sip_address[0]; - if (real_parsed_url!=NULL) *real_parsed_url=linphone_address_new(tmpurl); + uri=linphone_address_new(tmpurl); enum_lookup_res_free(enumres); - if (tmproute) *route=ms_strdup(tmproute); - return TRUE; + return uri; } /* check if we have a "sip:" */ if (strstr(url,"sip:")==NULL){ @@ -1681,8 +1709,7 @@ bool_t linphone_core_interpret_url(LinphoneCore *lc, const char *url, LinphoneAd uri=linphone_address_new(tmpurl); ms_free(tmpurl); if (uri){ - if (real_parsed_url!=NULL) *real_parsed_url=uri; - return TRUE; + return uri; } } @@ -1692,51 +1719,24 @@ bool_t linphone_core_interpret_url(LinphoneCore *lc, const char *url, LinphoneAd char normalized_username[128]; uri=linphone_address_new(identity); if (uri==NULL){ - return FALSE; + return NULL; } linphone_address_set_display_name(uri,NULL); linphone_proxy_config_normalize_number(proxy,url,normalized_username, sizeof(normalized_username)); linphone_address_set_username(uri,normalized_username); - - if (real_parsed_url!=NULL) *real_parsed_url=uri; -#if 0 - /*if the prompted uri was auto-suffixed with proxy domain, - then automatically set a route so that the request goes - through the proxy*/ - if (tmproute==NULL){ - osip_route_t *rt=NULL; - char *rtstr=NULL; - osip_route_init(&rt); - if (osip_route_parse(rt,linphone_proxy_config_get_addr(proxy))==0){ - osip_uri_uparam_add(rt->url,osip_strdup("lr"),NULL); - osip_route_to_str(rt,&rtstr); - *route=ms_strdup(rtstr); - osip_free(rtstr); - } - ms_message("setting automatically a route to %s",*route); - } - else *route=ms_strdup(tmproute); -#else - if (tmproute==NULL) *route=guess_route_if_any(lc,*real_parsed_url); - if (tmproute) *route=ms_strdup(tmproute); -#endif - return TRUE; - }else return FALSE; + return uri; + }else return NULL; } - parsed_url=linphone_address_new(url); - if (parsed_url!=NULL){ - if (real_parsed_url!=NULL) *real_parsed_url=parsed_url; - else linphone_address_destroy(parsed_url); - if (tmproute) *route=ms_strdup(tmproute); - else *route=guess_route_if_any(lc,*real_parsed_url); - return TRUE; + uri=linphone_address_new(url); + if (uri!=NULL){ + return uri; } /* else we could not do anything with url given by user, so display an error */ if (lc->vtable.display_warning!=NULL){ lc->vtable.display_warning(lc,_("Could not parse given sip address. A sip url usually looks like sip:user@domain")); } - return FALSE; + return NULL; } /** @@ -1769,15 +1769,6 @@ const char * linphone_core_get_route(LinphoneCore *lc){ return route; } -void linphone_set_sdp(osip_message_t *sip, const char *sdpmesg){ - int sdplen=strlen(sdpmesg); - char clen[10]; - snprintf(clen,sizeof(clen),"%i",sdplen); - osip_message_set_body(sip,sdpmesg,sdplen); - osip_message_set_content_type(sip,"application/sdp"); - osip_message_set_content_length(sip,clen); -} - LinphoneProxyConfig * linphone_core_lookup_known_proxy(LinphoneCore *lc, const LinphoneAddress *uri){ const MSList *elem; LinphoneProxyConfig *found_cfg=NULL; @@ -1792,37 +1783,93 @@ LinphoneProxyConfig * linphone_core_lookup_known_proxy(LinphoneCore *lc, const L return found_cfg; } -static void fix_contact(LinphoneCore *lc, osip_message_t *msg, const char *localip, LinphoneProxyConfig *dest_proxy){ - osip_contact_t *ctt=NULL; - const char *ip=NULL; - int port=5060; +static char *get_fixed_contact(LinphoneCore *lc, LinphoneCall *call , LinphoneProxyConfig *dest_proxy){ + LinphoneAddress *ctt; + const char *localip=call->localip; - osip_message_get_contact(msg,0,&ctt); - if (ctt!=NULL){ - if (dest_proxy!=NULL){ - /* if we know the request will go to a known proxy for which we are registered, - we can use the same contact address as in the register */ - linphone_proxy_config_get_contact(dest_proxy,&ip,&port); - }else{ - ip=localip; - port=linphone_core_get_sip_port(lc); - } - if (ip!=NULL){ - osip_free(ctt->url->host); - ctt->url->host=osip_strdup(ip); - } - if (port!=0){ - char tmp[10]={0}; - char *str; - snprintf(tmp,sizeof(tmp)-1,"%i",port); - if (ctt->url->port!=NULL) - osip_free(ctt->url->port); - ctt->url->port=osip_strdup(tmp); - osip_contact_to_str(ctt,&str); - ms_message("Contact has been fixed to %s",str); - osip_free(str); + /* first use user's supplied ip address if asked*/ + if (linphone_core_get_firewall_policy(lc)==LINPHONE_POLICY_USE_NAT_ADDRESS){ + ctt=linphone_core_get_primary_contact_parsed(lc); + return ms_strdup_printf("sip:%s@%s",linphone_address_get_username(ctt), + linphone_core_get_nat_address(lc)); + } + + /* if already choosed, don't change it */ + if (call->op && sal_op_get_contact(call->op)!=NULL){ + return NULL; + } + /*if using a proxy, use the contact address as guessed with the REGISTERs*/ + if (dest_proxy && dest_proxy->op){ + const char *fixed_contact=sal_op_get_contact(dest_proxy->op); + if (fixed_contact) { + ms_message("Contact has been fixed using proxy to %s",fixed_contact); + return ms_strdup(fixed_contact); } } + /* if the ping OPTIONS request succeeded use the contact guessed from the + received, rport*/ + if (call->ping_op){ + const char *guessed=sal_op_get_contact(call->ping_op); + if (guessed){ + ms_message("Contact has been fixed using OPTIONS to %s",guessed); + return ms_strdup(guessed); + } + } + + ctt=linphone_core_get_primary_contact_parsed(lc); + + if (ctt!=NULL){ + char *ret; + /*otherwise use supllied localip*/ + linphone_address_set_domain(ctt,localip); + linphone_address_set_port_int(ctt,linphone_core_get_sip_port(lc)); + ret=linphone_address_as_string_uri_only(ctt); + linphone_address_destroy(ctt); + ms_message("Contact has been fixed using local ip to %s",ret); + return ret; + } + return NULL; +} + +int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy){ + int err; + char *contact; + char *real_url,*barmsg; + char *from; + /*try to be best-effort in giving real local or routable contact address */ + contact=get_fixed_contact(lc,call,dest_proxy); + if (contact){ + sal_op_set_contact(call->op, contact); + ms_free(contact); + } + call->state=LCStateInit; + linphone_core_init_media_streams(lc,lc->call); + if (!lc->sip_conf.sdp_200_ack){ + call->media_pending=TRUE; + sal_call_set_local_media_description(call->op,call->localdesc); + } + real_url=linphone_address_as_string(call->log->to); + from=linphone_address_as_string(call->log->from); + err=sal_call(call->op,from,real_url); + + if (lc->sip_conf.sdp_200_ack){ + call->media_pending=TRUE; + sal_call_set_local_media_description(call->op,call->localdesc); + } + barmsg=ortp_strdup_printf("%s %s", _("Contacting"), real_url); + lc->vtable.display_status(lc,barmsg); + ms_free(barmsg); + + if (err<0){ + ms_warning("Could not initiate call."); + lc->vtable.display_status(lc,_("could not call")); + linphone_core_stop_media_streams(lc,call); + linphone_call_destroy(call); + lc->call=NULL; + }else gstate_new_state(lc, GSTATE_CALL_OUT_INVITE, real_url); + ms_free(real_url); + ms_free(from); + return err; } /** @@ -1830,32 +1877,44 @@ static void fix_contact(LinphoneCore *lc, osip_message_t *msg, const char *local * * @ingroup call_control * @param lc the LinphoneCore object + * @param url the destination of the call (sip address, or phone number). +**/ +int linphone_core_invite(LinphoneCore *lc, const char *url){ + LinphoneAddress *addr=linphone_core_interpret_url(lc,url); + if (addr){ + int err=linphone_core_invite_address(lc,addr); + linphone_address_destroy(addr); + return err; + } + return -1; +} + +/** + * Initiates an outgoing call given a destination LinphoneAddress + * + * @ingroup call_control + * @param lc the LinphoneCore object * @param url the destination of the call (sip address). **/ -int linphone_core_invite(LinphoneCore *lc, const char *url) +int linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddress *real_parsed_url) { - char *barmsg; int err=0; - char *sdpmesg=NULL; - char *route=NULL; + const char *route=NULL; const char *from=NULL; - osip_message_t *invite=NULL; - sdp_context_t *ctx=NULL; LinphoneProxyConfig *proxy=NULL; LinphoneAddress *parsed_url2=NULL; - LinphoneAddress *real_parsed_url=NULL; char *real_url=NULL; LinphoneProxyConfig *dest_proxy=NULL; - + LinphoneCall *call; + if (lc->call!=NULL){ lc->vtable.display_warning(lc,_("Sorry, having multiple simultaneous calls is not supported yet !")); return -1; } linphone_core_get_default_proxy(lc,&proxy); - if (!linphone_core_interpret_url(lc,url,&real_parsed_url,&route)){ - return -1; - } + route=linphone_core_get_route(lc); + real_url=linphone_address_as_string(real_parsed_url); dest_proxy=linphone_core_lookup_known_proxy(lc,real_parsed_url); @@ -1872,67 +1931,36 @@ int linphone_core_invite(LinphoneCore *lc, const char *url) /* if no proxy or no identity defined for this proxy, default to primary contact*/ if (from==NULL) from=linphone_core_get_primary_contact(lc); - err=eXosip_call_build_initial_invite(&invite,real_url,from, - route,"Phone call"); - if (err<0){ - ms_warning("Could not build initial invite cause [%i]",err); - goto end; - } - if (lp_config_get_int(lc->config,"sip","use_session_timers",0)==1){ - osip_message_set_header(invite, "Session-expires", "200"); - osip_message_set_supported(invite, "timer"); - } - /* make sdp message */ - parsed_url2=linphone_address_new(from); - lc->call=linphone_call_new_outgoing(lc,parsed_url2,real_parsed_url); - /*try to be best-effort in giving real local or routable contact address, - except when the user choosed to override the ipaddress */ - if (linphone_core_get_firewall_policy(lc)!=LINPHONE_POLICY_USE_NAT_ADDRESS) - fix_contact(lc,invite,lc->call->localip,dest_proxy); - - barmsg=ortp_strdup_printf("%s %s", _("Contacting"), real_url); - lc->vtable.display_status(lc,barmsg); - ms_free(barmsg); - if (!lc->sip_conf.sdp_200_ack){ - ctx=lc->call->sdpctx; - sdpmesg=sdp_context_get_offer(ctx); - linphone_set_sdp(invite,sdpmesg); - linphone_core_init_media_streams(lc); - } - eXosip_lock(); - err=eXosip_call_send_initial_invite(invite); - lc->call->cid=err; - eXosip_unlock(); + call=linphone_call_new_outgoing(lc,parsed_url2,linphone_address_clone(real_parsed_url)); + sal_op_set_route(call->op,route); - if (err<0){ - ms_warning("Could not initiate call."); - lc->vtable.display_status(lc,_("could not call")); - linphone_call_destroy(lc->call); - lc->call=NULL; - linphone_core_stop_media_streams(lc); - }else gstate_new_state(lc, GSTATE_CALL_OUT_INVITE, url); - - goto end; - end: - if (real_url!=NULL) ms_free(real_url); - if (route!=NULL) ms_free(route); - return (err<0) ? -1 : 0; + lc->call=call; + if (dest_proxy!=NULL || lc->sip_conf.ping_with_options==FALSE){ + err=linphone_core_start_invite(lc,call,dest_proxy); + }else{ + /*defer the start of the call after the OPTIONS ping*/ + call->state=LCStatePreEstablishing; + call->ping_op=sal_op_new(lc->sal); + sal_ping(call->ping_op,from,real_url); + call->start_time=time(NULL); + } + + if (real_url!=NULL) ms_free(real_url); + return err; } int linphone_core_refer(LinphoneCore *lc, const char *url) { char *real_url=NULL; - LinphoneAddress *real_parsed_url=NULL; + LinphoneAddress *real_parsed_url=linphone_core_interpret_url(lc,url); LinphoneCall *call; - osip_message_t *msg=NULL; - char *route; - if (!linphone_core_interpret_url(lc,url,&real_parsed_url, &route)){ + + if (!real_parsed_url){ /* bad url */ return -1; } - if (route!=NULL) ms_free(route); call=lc->call; if (call==NULL){ ms_warning("No established call to refer."); @@ -1940,11 +1968,8 @@ int linphone_core_refer(LinphoneCore *lc, const char *url) } lc->call=NULL; real_url=linphone_address_as_string (real_parsed_url); - eXosip_call_build_refer(call->did, real_url, &msg); + sal_refer(call->op,real_url); ms_free(real_url); - eXosip_lock(); - eXosip_call_send_request(call->did, msg); - eXosip_unlock(); return 0; } @@ -1960,81 +1985,9 @@ bool_t linphone_core_inc_invite_pending(LinphoneCore*lc){ return FALSE; } -#ifdef VINCENT_MAURY_RSVP -/* on=1 for RPC_ENABLE=1...*/ -int linphone_core_set_rpc_mode(LinphoneCore *lc, int on) -{ - if (on==1) - printf("RPC_ENABLE set on\n"); - else - printf("RPC_ENABLE set off\n"); - lc->rpc_enable = (on==1); - /* need to tell eXosip the new setting */ - if (eXosip_set_rpc_mode (lc->rpc_enable)!=0) - return -1; - return 0; -} - -/* on=1 for RSVP_ENABLE=1...*/ -int linphone_core_set_rsvp_mode(LinphoneCore *lc, int on) -{ - if (on==1) - printf("RSVP_ENABLE set on\n"); - else - printf("RSVP_ENABLE set off\n"); - lc->rsvp_enable = (on==1); - /* need to tell eXosip the new setting */ - if (eXosip_set_rsvp_mode (lc->rsvp_enable)!=0) - return -1; - return 0; -} - -/* answer : 1 for yes, 0 for no */ -int linphone_core_change_qos(LinphoneCore *lc, int answer) -{ - char *sdpmesg; - if (lc->call==NULL){ - return -1; - } - - if (lc->rsvp_enable && answer==1) - { - /* answer is yes, local setting is with qos, so - * the user chose to continue with no qos ! */ - /* so switch in normal mode : ring and 180 */ - lc->rsvp_enable = 0; /* no more rsvp */ - eXosip_set_rsvp_mode (lc->rsvp_enable); - /* send 180 */ - eXosip_lock(); - eXosip_answer_call(lc->call->did,180,NULL); - eXosip_unlock(); - /* play the ring */ - ms_message("Starting local ring..."); - lc->ringstream=ring_start(lc->sound_conf.local_ring, - 2000,ms_snd_card_manager_get_card(ms_snd_card_manager_get(),lc->sound_conf.ring_sndcard)); - } - else if (!lc->rsvp_enable && answer==1) - { - /* switch to QoS mode on : answer 183 session progress */ - lc->rsvp_enable = 1; - eXosip_set_rsvp_mode (lc->rsvp_enable); - /* take the sdp already computed, see osipuacb.c */ - sdpmesg=lc->call->sdpctx->answerstr; - eXosip_lock(); - eXosip_answer_call_with_body(lc->call->did,183,"application/sdp",sdpmesg); - eXosip_unlock(); - } - else - { - /* decline offer (603) */ - linphone_core_terminate_call(lc, NULL); - } - return 0; -} -#endif - -void linphone_core_init_media_streams(LinphoneCore *lc){ - lc->audiostream=audio_stream_new(linphone_core_get_audio_port(lc),linphone_core_ipv6_enabled(lc)); +void linphone_core_init_media_streams(LinphoneCore *lc, LinphoneCall *call){ + SalMediaDescription *md=call->localdesc; + lc->audiostream=audio_stream_new(md->streams[0].port,linphone_core_ipv6_enabled(lc)); if (linphone_core_echo_limiter_enabled(lc)){ const char *type=lp_config_get_string(lc->config,"sound","el_type","mic"); if (strcasecmp(type,"mic")==0) @@ -2059,17 +2012,23 @@ void linphone_core_init_media_streams(LinphoneCore *lc){ rtp_session_set_transports(lc->audiostream->session,lc->a_rtp,lc->a_rtcp); #ifdef VIDEO_ENABLED - if (lc->video_conf.display || lc->video_conf.capture) - lc->videostream=video_stream_new(linphone_core_get_video_port(lc),linphone_core_ipv6_enabled(lc)); + if ((lc->video_conf.display || lc->video_conf.capture) && md->streams[1].port>0) + lc->videostream=video_stream_new(md->streams[1].port,linphone_core_ipv6_enabled(lc)); #else lc->videostream=NULL; #endif } +static int dtmf_tab[16]={'0','1','2','3','4','5','6','7','8','9','*','#','A','B','C','D'}; + static void linphone_core_dtmf_received(RtpSession* s, int dtmf, void* user_data){ LinphoneCore* lc = (LinphoneCore*)user_data; + if (dtmf<0 || dtmf>15){ + ms_warning("Bad dtmf value %i",dtmf); + return; + } if (lc->vtable.dtmf_received != NULL) - lc->vtable.dtmf_received(lc, dtmf); + lc->vtable.dtmf_received(lc, dtmf_tab[dtmf]); } static void parametrize_equalizer(LinphoneCore *lc, AudioStream *st){ @@ -2099,6 +2058,10 @@ static void post_configure_audio_streams(LinphoneCore *lc){ float gain=lp_config_get_float(lc->config,"sound","mic_gain",-1); if (gain!=-1) audio_stream_set_mic_gain(st,gain); + float recv_gain = lc->sound_conf.soft_play_lev; + if (recv_gain != 0) { + linphone_core_set_soft_play_level(lc,recv_gain); + } if (linphone_core_echo_limiter_enabled(lc)){ float speed=lp_config_get_float(lc->config,"sound","el_speed",-1); float thres=lp_config_get_float(lc->config,"sound","el_thres",-1); @@ -2139,99 +2102,133 @@ static void post_configure_audio_streams(LinphoneCore *lc){ } } +static RtpProfile *make_profile(LinphoneCore *lc, const SalStreamDescription *desc, int *used_pt){ + int bw; + const MSList *elem; + RtpProfile *prof=rtp_profile_new("Call profile"); + bool_t first=TRUE; + + for(elem=desc->payloads;elem!=NULL;elem=elem->next){ + PayloadType *pt=(PayloadType*)elem->data; + + if (first) { + if (desc->type==SalAudio){ + linphone_core_update_allocated_audio_bandwidth_in_call(lc,pt); + } + *used_pt=payload_type_get_number(pt); + first=FALSE; + } + if (desc->type==SalAudio){ + bw=get_min_bandwidth(lc->up_audio_bw,desc->bandwidth); + }else bw=get_min_bandwidth(lc->up_video_bw,desc->bandwidth); + if (bw>0) pt->normal_bitrate=bw*1000; + else if (desc->type==SalAudio){ + pt->normal_bitrate=-1; + } + if (desc->ptime>0){ + char tmp[40]; + snprintf(tmp,sizeof(tmp),"ptime=%i",desc->ptime); + payload_type_append_send_fmtp(pt,tmp); + } + rtp_profile_set_payload(prof,payload_type_get_number(pt),pt); + } + return prof; +} + void linphone_core_start_media_streams(LinphoneCore *lc, LinphoneCall *call){ LinphoneAddress *me=linphone_core_get_primary_contact_parsed(lc); const char *tool="linphone-" LINPHONE_VERSION; char *cname; + int used_pt=-1; /* adjust rtp jitter compensation. It must be at least the latency of the sound card */ int jitt_comp=MAX(lc->sound_conf.latency,lc->rtp_conf.audio_jitt_comp); if (call->media_start_time==0) call->media_start_time=time(NULL); - cname=ortp_strdup_printf("%s@%s",me->url->username,me->url->host); + cname=linphone_address_as_string_uri_only(me); { - StreamParams *audio_params=&call->audio_params; - if (!lc->use_files){ - MSSndCard *playcard=lc->sound_conf.play_sndcard; - MSSndCard *captcard=lc->sound_conf.capt_sndcard; - if (playcard==NULL) { - ms_warning("No card defined for playback !"); - goto end; + const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc, + SalProtoRtpAvp,SalAudio); + if (stream){ + call->audio_profile=make_profile(lc,stream,&used_pt); + if (!lc->use_files){ + MSSndCard *playcard=lc->sound_conf.play_sndcard; + MSSndCard *captcard=lc->sound_conf.capt_sndcard; + if (playcard==NULL) { + ms_warning("No card defined for playback !"); + goto end; + } + if (captcard==NULL) { + ms_warning("No card defined for capture !"); + goto end; + } + audio_stream_start_now( + lc->audiostream, + call->audio_profile, + stream->addr[0]!='\0' ? stream->addr : call->resultdesc->addr, + stream->port, + stream->port+1, + used_pt, + jitt_comp, + playcard, + captcard, + linphone_core_echo_cancellation_enabled(lc)); + }else{ + audio_stream_start_with_files( + lc->audiostream, + call->audio_profile, + stream->addr[0]!='\0' ? stream->addr : call->resultdesc->addr, + stream->port, + stream->port+1, + used_pt, + 100, + lc->play_file, + lc->rec_file); } - if (captcard==NULL) { - ms_warning("No card defined for capture !"); - goto end; - } - if (audio_params->relay_session_id!=NULL) - audio_stream_set_relay_session_id(lc->audiostream,audio_params->relay_session_id); - audio_stream_start_now( - lc->audiostream, - call->profile, - audio_params->remoteaddr, - audio_params->remoteport, - audio_params->remotertcpport, - audio_params->pt, - jitt_comp, - playcard, - captcard, - linphone_core_echo_cancellation_enabled(lc)); - }else{ - audio_stream_start_with_files( - lc->audiostream, - call->profile, - audio_params->remoteaddr, - audio_params->remoteport, - audio_params->remotertcpport, - audio_params->pt, - 100, - lc->play_file, - lc->rec_file); - } - post_configure_audio_streams(lc); - audio_stream_set_rtcp_information(lc->audiostream, cname, tool); + post_configure_audio_streams(lc); + audio_stream_set_rtcp_information(lc->audiostream, cname, tool); + }else ms_warning("No audio stream defined ?"); } #ifdef VIDEO_ENABLED { + const SalStreamDescription *stream=sal_media_description_find_stream(call->resultdesc, + SalProtoRtpAvp,SalVideo); /* shutdown preview */ if (lc->previewstream!=NULL) { video_preview_stop(lc->previewstream); lc->previewstream=NULL; } - if (lc->video_conf.display || lc->video_conf.capture) { - StreamParams *video_params=&call->video_params; - - if (video_params->remoteport>0){ - if (video_params->relay_session_id!=NULL) - video_stream_set_relay_session_id(lc->videostream,video_params->relay_session_id); - video_stream_set_sent_video_size(lc->videostream,linphone_core_get_preferred_video_size(lc)); - video_stream_enable_self_view(lc->videostream,lc->video_conf.selfview); - if (lc->video_conf.display && lc->video_conf.capture) - video_stream_start(lc->videostream, - call->profile, video_params->remoteaddr, video_params->remoteport, - video_params->remotertcpport, - video_params->pt, jitt_comp, lc->video_conf.device); - else if (lc->video_conf.display) - video_stream_recv_only_start(lc->videostream, - call->profile, video_params->remoteaddr, video_params->remoteport, - video_params->pt, jitt_comp); - else if (lc->video_conf.capture) - video_stream_send_only_start(lc->videostream, - call->profile, video_params->remoteaddr, video_params->remoteport, - video_params->remotertcpport, - video_params->pt, jitt_comp, lc->video_conf.device); - video_stream_set_rtcp_information(lc->videostream, cname,tool); - } + if (stream && (lc->video_conf.display || lc->video_conf.capture)) { + const char *addr=stream->addr[0]!='\0' ? stream->addr : call->resultdesc->addr; + call->video_profile=make_profile(lc,stream,&used_pt); + video_stream_set_sent_video_size(lc->videostream,linphone_core_get_preferred_video_size(lc)); + video_stream_enable_self_view(lc->videostream,lc->video_conf.selfview); + if (lc->video_conf.display && lc->video_conf.capture) + video_stream_start(lc->videostream, + call->video_profile, addr, stream->port, + stream->port+1, + used_pt, jitt_comp, lc->video_conf.device); + else if (lc->video_conf.display) + video_stream_recv_only_start(lc->videostream, + call->video_profile, addr, stream->port, + used_pt, jitt_comp); + else if (lc->video_conf.capture) + video_stream_send_only_start(lc->videostream, + call->video_profile, addr, stream->port, + stream->port+1, + used_pt, jitt_comp, lc->video_conf.device); + video_stream_set_rtcp_information(lc->videostream, cname,tool); } } #endif goto end; end: - ms_free(cname); - linphone_address_destroy(me); - lc->call->state=LCStateAVRunning; + ms_free(cname); + linphone_address_destroy(me); + lc->call->state=LCStateAVRunning; } -void linphone_core_stop_media_streams(LinphoneCore *lc){ +void linphone_core_stop_media_streams(LinphoneCore *lc, LinphoneCall *call){ if (lc->audiostream!=NULL) { audio_stream_stop(lc->audiostream); lc->audiostream=NULL; @@ -2252,6 +2249,16 @@ void linphone_core_stop_media_streams(LinphoneCore *lc){ } } #endif + if (call->audio_profile){ + rtp_profile_clear_all(call->audio_profile); + rtp_profile_destroy(call->audio_profile); + call->audio_profile=NULL; + } + if (call->video_profile){ + rtp_profile_clear_all(call->video_profile); + rtp_profile_destroy(call->video_profile); + call->video_profile=NULL; + } } /** @@ -2270,17 +2277,14 @@ void linphone_core_stop_media_streams(LinphoneCore *lc){ **/ int linphone_core_accept_call(LinphoneCore *lc, const char *url) { - char *sdpmesg; - osip_message_t *msg=NULL; LinphoneCall *call=lc->call; - int err; - bool_t offering=FALSE; - + const char *contact=NULL; + if (call==NULL){ return -1; } - if (lc->call->state==LCStateAVRunning){ + if (call->state==LCStateAVRunning){ /*call already accepted*/ return -1; } @@ -2292,42 +2296,20 @@ int linphone_core_accept_call(LinphoneCore *lc, const char *url) ms_message("ring stopped"); lc->ringstream=NULL; } - /* sends a 200 OK */ - err=eXosip_call_build_answer(call->tid,200,&msg); - if (err<0 || msg==NULL){ - ms_error("Fail to build answer for call: err=%i",err); - return -1; - } - if (lp_config_get_int(lc->config,"sip","use_session_timers",0)==1){ - if (call->supports_session_timers) osip_message_set_supported(msg, "timer"); - } - /*try to be best-effort in giving real local or routable contact address, - except when the user choosed to override the ipaddress */ - if (linphone_core_get_firewall_policy(lc)!=LINPHONE_POLICY_USE_NAT_ADDRESS) - fix_contact(lc,msg,call->localip,NULL); - /*if a sdp answer is computed, send it, else send an offer */ - sdpmesg=call->sdpctx->answerstr; - if (sdpmesg==NULL){ - offering=TRUE; - ms_message("generating sdp offer"); - sdpmesg=sdp_context_get_offer(call->sdpctx); - - if (sdpmesg==NULL){ - ms_error("fail to generate sdp offer !"); - return -1; - } - linphone_set_sdp(msg,sdpmesg); - linphone_core_init_media_streams(lc); - }else{ - linphone_set_sdp(msg,sdpmesg); - } - eXosip_lock(); - eXosip_call_send_answer(call->tid,200,msg); - eXosip_unlock(); + + /*try to be best-effort in giving real local or routable contact address*/ + contact=get_fixed_contact(lc,call,NULL); + if (contact) + sal_op_set_contact(call->op,contact); + + sal_call_accept(call->op); lc->vtable.display_status(lc,_("Connected.")); gstate_new_state(lc, GSTATE_CALL_IN_CONNECTED, NULL); - - if (!offering) linphone_core_start_media_streams(lc, lc->call); + call->resultdesc=sal_call_get_final_media_description(call->op); + if (call->resultdesc){ + sal_media_description_ref(call->resultdesc); + linphone_core_start_media_streams(lc, call); + }else call->media_pending=TRUE; ms_message("call answered."); return 0; } @@ -2347,17 +2329,14 @@ int linphone_core_terminate_call(LinphoneCore *lc, const char *url) return -1; } lc->call=NULL; - - eXosip_lock(); - eXosip_call_terminate(call->cid,call->did); - eXosip_unlock(); + sal_call_terminate(call->op); /*stop ringing*/ if (lc->ringstream!=NULL) { ring_stop(lc->ringstream); lc->ringstream=NULL; } - linphone_core_stop_media_streams(lc); + linphone_core_stop_media_streams(lc,call); lc->vtable.display_status(lc,_("Call ended") ); gstate_new_state(lc, GSTATE_CALL_END, NULL); linphone_call_destroy(call); @@ -2409,22 +2388,13 @@ void linphone_core_set_presence_info(LinphoneCore *lc,int minutes_away, const char *contact, LinphoneOnlineStatus presence_mode) { - int contactok=-1; if (minutes_away>0) lc->minutes_away=minutes_away; - if (contact!=NULL) { - osip_from_t *url; - osip_from_init(&url); - contactok=osip_from_parse(url,contact); - if (contactok>=0) { - ms_message("contact url is correct."); - } - osip_from_free(url); - - } - if (contactok>=0){ - if (lc->alt_contact!=NULL) ms_free(lc->alt_contact); - lc->alt_contact=ms_strdup(contact); + + if (lc->alt_contact!=NULL) { + ms_free(lc->alt_contact); + lc->alt_contact=NULL; } + if (contact) lc->alt_contact=ms_strdup(contact); if (lc->presence_mode!=presence_mode){ linphone_core_notify_all_friends(lc,presence_mode); /* @@ -2436,7 +2406,6 @@ void linphone_core_set_presence_info(LinphoneCore *lc,int minutes_away, } lc->prev_mode=lc->presence_mode; lc->presence_mode=presence_mode; - } LinphoneOnlineStatus linphone_core_get_presence_info(const LinphoneCore *lc){ @@ -2484,6 +2453,27 @@ void linphone_core_set_ring_level(LinphoneCore *lc, int level){ if (sndcard) ms_snd_card_set_level(sndcard,MS_SND_CARD_PLAYBACK,level); } + +void linphone_core_set_soft_play_level(LinphoneCore *lc, float level){ + float gain=level; + lc->sound_conf.soft_play_lev=level; + AudioStream *st=lc->audiostream; + if (!st) return; /*just return*/ + + if (st->volrecv){ + ms_filter_call_method(st->volrecv,MS_VOLUME_SET_DB_GAIN,&gain); + }else ms_warning("Could not apply gain: gain control wasn't activated."); +} +float linphone_core_get_soft_play_level(LinphoneCore *lc) { + float gain=0; + AudioStream *st=lc->audiostream; + if (st->volrecv){ + ms_filter_call_method(st->volrecv,MS_VOLUME_GET,&gain); + }else ms_warning("Could not get gain: gain control wasn't activated."); + + return gain; +} + /** * Set sound playback level in 0-100 scale * @@ -2791,6 +2781,15 @@ void linphone_core_mute_mic(LinphoneCore *lc, bool_t val){ } } +bool_t linphone_core_is_mic_muted(LinphoneCore *lc) { + float gain=1.0; + if (lc->audiostream && lc->audiostream->volsend){ + ms_filter_call_method(lc->audiostream->volsend,MS_VOLUME_GET_GAIN,&gain); + }else ms_warning("Could not get gain: gain control wasn't activated. "); + + return gain==0; +} + void linphone_core_enable_agc(LinphoneCore *lc, bool_t val){ lc->sound_conf.agc=val; } @@ -2808,7 +2807,7 @@ bool_t linphone_core_agc_enabled(const LinphoneCore *lc){ * @param dtmf The dtmf name specified as a char, such as '0', '#' etc... * **/ -void linphone_core_send_dtmf(LinphoneCore *lc,char dtmf) +void linphone_core_send_dtmf(LinphoneCore *lc, char dtmf) { /*By default we send DTMF RFC2833 if we do not have enabled SIP_INFO but we can also send RFC2833 and SIP_INFO*/ if (linphone_core_get_use_rfc2833_for_dtmf(lc)!=0 || linphone_core_get_use_info_for_dtmf(lc)==0) @@ -2822,26 +2821,13 @@ void linphone_core_send_dtmf(LinphoneCore *lc,char dtmf) ms_error("we cannot send RFC2833 dtmf when we are not in communication"); } } - if (linphone_core_get_use_info_for_dtmf(lc)!=0) - { - char dtmf_body[1000]; - char clen[10]; - osip_message_t *msg=NULL; + if (linphone_core_get_use_info_for_dtmf(lc)!=0){ /* Out of Band DTMF (use INFO method) */ LinphoneCall *call=lc->call; if (call==NULL){ return; } - eXosip_call_build_info(call->did,&msg); - snprintf(dtmf_body, 999, "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_lock(); - eXosip_call_send_request(call->did,msg); - eXosip_unlock(); + sal_call_send_dtmf(call->op,dtmf); } } @@ -2851,7 +2837,6 @@ void linphone_core_set_stun_server(LinphoneCore *lc, const char *server){ if (server) lc->net_conf.stun_server=ms_strdup(server); else lc->net_conf.stun_server=NULL; - lc->apply_nat_settings=TRUE; } const char * linphone_core_get_stun_server(const LinphoneCore *lc){ @@ -2873,75 +2858,6 @@ int linphone_core_set_relay_addr(LinphoneCore *lc, const char *addr){ return 0; } -static void apply_nat_settings(LinphoneCore *lc){ - char *wmsg; - char *tmp=NULL; - int err; - struct addrinfo hints,*res; - const char *addr=lc->net_conf.nat_address; - - if (lc->net_conf.firewall_policy==LINPHONE_POLICY_USE_NAT_ADDRESS){ - if (addr==NULL || strlen(addr)==0){ - lc->vtable.display_warning(lc,_("No nat/firewall address supplied !")); - linphone_core_set_firewall_policy(lc,LINPHONE_POLICY_NO_FIREWALL); - } - /*check the ip address given */ - memset(&hints,0,sizeof(struct addrinfo)); - if (lc->sip_conf.ipv6_enabled) - hints.ai_family=AF_INET6; - else - hints.ai_family=AF_INET; - hints.ai_socktype = SOCK_DGRAM; - err=getaddrinfo(addr,NULL,&hints,&res); - if (err!=0){ - wmsg=ortp_strdup_printf(_("Invalid nat address '%s' : %s"), - addr, gai_strerror(err)); - ms_warning(wmsg); // what is this for ? - lc->vtable.display_warning(lc, wmsg); - ms_free(wmsg); - linphone_core_set_firewall_policy(lc,LINPHONE_POLICY_NO_FIREWALL); - return; - } - /*now get it as an numeric ip address */ - tmp=ms_malloc0(50); - err=getnameinfo(res->ai_addr,res->ai_addrlen,tmp,50,NULL,0,NI_NUMERICHOST); - if (err!=0){ - wmsg=ortp_strdup_printf(_("Invalid nat address '%s' : %s"), - addr, gai_strerror(err)); - ms_warning(wmsg); // what is this for ? - lc->vtable.display_warning(lc, wmsg); - ms_free(wmsg); - ms_free(tmp); - freeaddrinfo(res); - linphone_core_set_firewall_policy(lc,LINPHONE_POLICY_NO_FIREWALL); - return; - } - freeaddrinfo(res); - } - - if (lc->net_conf.firewall_policy==LINPHONE_POLICY_USE_NAT_ADDRESS){ - if (tmp!=NULL){ - if (!lc->net_conf.nat_sdp_only){ - eXosip_set_option(EXOSIP_OPT_SET_IPV4_FOR_GATEWAY,tmp); - /* the following does not work in all cases */ - /* - eXosip_masquerade_contact(tmp,lc->sip_conf.sip_port); - */ - } - ms_free(tmp); - } - else{ - eXosip_set_option(EXOSIP_OPT_SET_IPV4_FOR_GATEWAY,NULL); - eXosip_masquerade_contact("",0); - } - } - else { - eXosip_set_option(EXOSIP_OPT_SET_IPV4_FOR_GATEWAY,NULL); - eXosip_masquerade_contact("",0); - } -} - - void linphone_core_set_nat_address(LinphoneCore *lc, const char *addr) { if (lc->net_conf.nat_address!=NULL){ @@ -2949,7 +2865,7 @@ void linphone_core_set_nat_address(LinphoneCore *lc, const char *addr) } if (addr!=NULL) lc->net_conf.nat_address=ms_strdup(addr); else lc->net_conf.nat_address=NULL; - lc->apply_nat_settings=TRUE; + if (lc->sip_conf.contact) update_primary_contact(lc); } const char *linphone_core_get_nat_address(const LinphoneCore *lc) @@ -2959,7 +2875,7 @@ const char *linphone_core_get_nat_address(const LinphoneCore *lc) void linphone_core_set_firewall_policy(LinphoneCore *lc, LinphoneFirewallPolicy pol){ lc->net_conf.firewall_policy=pol; - lc->apply_nat_settings=TRUE; + if (lc->sip_conf.contact) update_primary_contact(lc); } LinphoneFirewallPolicy linphone_core_get_firewall_policy(const LinphoneCore *lc){ @@ -3274,7 +3190,7 @@ void linphone_core_set_play_file(LinphoneCore *lc, const char *file){ } if (file!=NULL) { lc->play_file=ms_strdup(file); - if (lc->audiostream) + if (lc->audiostream->ticker) audio_stream_play(lc->audiostream,file); } } @@ -3356,14 +3272,16 @@ void net_config_uninit(LinphoneCore *lc) lp_config_set_int(lc->config,"net","download_bw",config->download_bw); lp_config_set_int(lc->config,"net","upload_bw",config->upload_bw); - if (config->stun_server!=NULL) + if (config->stun_server!=NULL){ lp_config_set_string(lc->config,"net","stun_server",config->stun_server); - if (config->nat_address!=NULL) - lp_config_set_string(lc->config,"net","nat_address",config->nat_address); - lp_config_set_int(lc->config,"net","firewall_policy",config->firewall_policy); - lp_config_set_int(lc->config,"net","mtu",config->mtu); - if (lc->net_conf.stun_server!=NULL) ms_free(lc->net_conf.stun_server); + } + if (config->nat_address!=NULL){ + lp_config_set_string(lc->config,"net","nat_address",config->nat_address); + ms_free(lc->net_conf.nat_address); + } + lp_config_set_int(lc->config,"net","firewall_policy",config->firewall_policy); + lp_config_set_int(lc->config,"net","mtu",config->mtu); } @@ -3380,30 +3298,31 @@ void sip_config_uninit(LinphoneCore *lc) lp_config_set_int(lc->config,"sip","use_rfc2833",config->use_rfc2833); lp_config_set_int(lc->config,"sip","use_ipv6",config->ipv6_enabled); lp_config_set_int(lc->config,"sip","register_only_when_network_is_up",config->register_only_when_network_is_up); + + lp_config_set_int(lc->config,"sip","default_proxy",linphone_core_get_default_proxy(lc,NULL)); + for(elem=config->proxies,i=0;elem!=NULL;elem=ms_list_next(elem),i++){ LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)(elem->data); linphone_proxy_config_write_to_config_file(lc->config,cfg,i); linphone_proxy_config_edit(cfg); /* to unregister */ } - if (exosip_running) - { + if (lc->sal){ int i; - for (i=0;i<20;i++) - { - eXosip_event_t *ev; - while((ev=eXosip_event_wait(0,0))!=NULL){ - linphone_core_process_event(lc,ev); - } - eXosip_automatic_action(); + for (i=0;i<20;i++){ + sal_iterate(lc->sal); #ifndef WIN32 - usleep(100000); + usleep(100000); #else - Sleep(100); + Sleep(100); #endif - } - } + } + } + ms_list_for_each(config->proxies,(void (*)(void*)) linphone_proxy_config_destroy); + ms_list_free(config->proxies); + config->proxies=NULL; + linphone_proxy_config_write_to_config_file(lc->config,NULL,i); /*mark the end */ for(elem=lc->auth_info,i=0;elem!=NULL;elem=ms_list_next(elem),i++){ @@ -3411,6 +3330,18 @@ void sip_config_uninit(LinphoneCore *lc) linphone_auth_info_write_config(lc->config,ai,i); } linphone_auth_info_write_config(lc->config,NULL,i); /* mark the end */ + ms_list_for_each(lc->auth_info,(void (*)(void*))linphone_auth_info_destroy); + ms_list_free(lc->auth_info); + lc->auth_info=NULL; + + sal_uninit(lc->sal); + lc->sal=NULL; + + if (lc->sip_conf.guessed_contact) + ms_free(lc->sip_conf.guessed_contact); + if (config->contact) + ms_free(config->contact); + } void rtp_config_uninit(LinphoneCore *lc) @@ -3443,6 +3374,8 @@ void video_config_uninit(LinphoneCore *lc) lp_config_set_int(lc->config,"video","capture",lc->video_conf.capture); lp_config_set_int(lc->config,"video","show_local",linphone_core_video_preview_enabled(lc)); lp_config_set_int(lc->config,"video","self_view",linphone_core_self_view_enabled(lc)); + if (lc->video_conf.cams) + ms_free(lc->video_conf.cams); } void codecs_config_uninit(LinphoneCore *lc) @@ -3458,7 +3391,7 @@ void codecs_config_uninit(LinphoneCore *lc) sprintf(key,"audio_codec_%i",index); lp_config_set_string(lc->config,key,"mime",pt->mime_type); lp_config_set_int(lc->config,key,"rate",pt->clock_rate); - lp_config_set_int(lc->config,key,"enabled",payload_type_enabled(pt)); + lp_config_set_int(lc->config,key,"enabled",linphone_core_payload_type_enabled(lc,pt)); index++; } index=0; @@ -3467,14 +3400,12 @@ void codecs_config_uninit(LinphoneCore *lc) sprintf(key,"video_codec_%i",index); lp_config_set_string(lc->config,key,"mime",pt->mime_type); lp_config_set_int(lc->config,key,"rate",pt->clock_rate); - lp_config_set_int(lc->config,key,"enabled",payload_type_enabled(pt)); + lp_config_set_int(lc->config,key,"enabled",linphone_core_payload_type_enabled(lc,pt)); lp_config_set_string(lc->config,key,"recv_fmtp",pt->recv_fmtp); index++; } - if (lc->local_profile){ - rtp_profile_destroy(lc->local_profile); - lc->local_profile=NULL; - } + ms_list_free(lc->codecs_conf.audio_codecs); + ms_list_free(lc->codecs_conf.video_codecs); } void ui_config_uninit(LinphoneCore* lc) @@ -3512,6 +3443,8 @@ static void linphone_core_uninit(LinphoneCore *lc) linphone_core_iterate(lc); } } + if (lc->friends) + ms_list_for_each(lc->friends,(void (*)(void *))linphone_friend_close_subscriptions); gstate_new_state(lc, GSTATE_POWER_SHUTDOWN, NULL); #ifdef VIDEO_ENABLED if (lc->previewstream!=NULL){ @@ -3522,7 +3455,6 @@ static void linphone_core_uninit(LinphoneCore *lc) /* save all config */ net_config_uninit(lc); sip_config_uninit(lc); - lp_config_set_int(lc->config,"sip","default_proxy",linphone_core_get_default_proxy(lc,NULL)); rtp_config_uninit(lc); sound_config_uninit(lc); video_config_uninit(lc); @@ -3533,11 +3465,12 @@ static void linphone_core_uninit(LinphoneCore *lc) lc->config = NULL; /* Mark the config as NULL to block further calls */ sip_setup_unregister_all(); + ms_list_for_each(lc->call_logs,(void (*)(void*))linphone_call_log_destroy); + lc->call_logs=ms_list_free(lc->call_logs); + linphone_core_free_payload_types(); ortp_exit(); - eXosip_quit(); - exosip_running=FALSE; gstate_new_state(lc, GSTATE_POWER_OFF, NULL); } diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index aa9d04484..e53022daf 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -39,109 +39,10 @@ extern "C" { struct _MSSndCard; struct _LinphoneCore; - -bool_t payload_type_enabled(struct _PayloadType *pt); -void payload_type_set_enable(struct _PayloadType *pt,int value); -const char *payload_type_get_description(struct _PayloadType *pt); +struct SalOp; struct _LpConfig; -typedef struct sip_config -{ - char *contact; - char *guessed_contact; - int sip_port; - MSList *proxies; - MSList *deleted_proxies; - int inc_timeout; /*timeout after an un-answered incoming call is rejected*/ - bool_t use_info; - bool_t use_rfc2833; /*force RFC2833 to be sent*/ - bool_t guess_hostname; - bool_t loopback_only; - bool_t ipv6_enabled; - bool_t sdp_200_ack; - bool_t only_one_codec; /*in SDP answers*/ - bool_t register_only_when_network_is_up; -} sip_config_t; - -typedef struct rtp_config -{ - int audio_rtp_port; - int video_rtp_port; - int audio_jitt_comp; /*jitter compensation*/ - int video_jitt_comp; /*jitter compensation*/ - int nortp_timeout; -}rtp_config_t; - - - -typedef struct net_config -{ - char *nat_address; - char *stun_server; - char *relay; - int download_bw; - int upload_bw; - int firewall_policy; - int mtu; - bool_t nat_sdp_only; -}net_config_t; - - -typedef struct sound_config -{ - struct _MSSndCard * ring_sndcard; /* the playback sndcard currently used */ - struct _MSSndCard * play_sndcard; /* the playback sndcard currently used */ - struct _MSSndCard * capt_sndcard; /* the capture sndcard currently used */ - const char **cards; - int latency; /* latency in samples of the current used sound device */ - char rec_lev; - char play_lev; - char ring_lev; - char source; - char *local_ring; - char *remote_ring; - bool_t ec; - bool_t ea; - bool_t agc; -} sound_config_t; - -typedef struct codecs_config -{ - MSList *audio_codecs; /* list of audio codecs in order of preference*/ - MSList *video_codecs; /* for later use*/ -}codecs_config_t; - -typedef struct video_config{ - struct _MSWebCam *device; - const char **cams; - MSVideoSize vsize; - bool_t capture; - bool_t show_local; - bool_t display; - bool_t selfview; /*during calls*/ -}video_config_t; - -typedef struct ui_config -{ - int is_daemon; - int is_applet; - unsigned int timer_id; /* the timer id for registration */ -}ui_config_t; - - - -typedef struct autoreplier_config -{ - int enabled; - int after_seconds; /* accept the call after x seconds*/ - int max_users; /* maximum number of user that can call simultaneously */ - int max_rec_time; /* the max time of incoming voice recorded */ - int max_rec_msg; /* maximum number of recorded messages */ - const char *message; /* the path of the file to be played */ -}autoreplier_config_t; - -struct osip_from; /** * Object that represents a SIP address. @@ -157,7 +58,7 @@ struct osip_from; * @ingroup linphone_address * @var LinphoneAddress */ -typedef struct osip_from LinphoneAddress; +typedef struct SalAddress LinphoneAddress; LinphoneAddress * linphone_address_new(const char *uri); LinphoneAddress * linphone_address_clone(const LinphoneAddress *uri); @@ -176,8 +77,6 @@ char *linphone_address_as_string(const LinphoneAddress *u); char *linphone_address_as_string_uri_only(const LinphoneAddress *u); void linphone_address_destroy(LinphoneAddress *u); -struct _LinphoneCore; -struct _sdp_context; struct _SipSetupContext; struct _LinphoneCall; @@ -240,7 +139,7 @@ typedef enum{ }LinphoneSubscribePolicy; typedef enum _LinphoneOnlineStatus{ - LINPHONE_STATUS_UNKNOWN, + LINPHONE_STATUS_OFFLINE, LINPHONE_STATUS_ONLINE, LINPHONE_STATUS_BUSY, LINPHONE_STATUS_BERIGHTBACK, @@ -250,29 +149,15 @@ typedef enum _LinphoneOnlineStatus{ LINPHONE_STATUS_NOT_DISTURB, LINPHONE_STATUS_MOVED, LINPHONE_STATUS_ALT_SERVICE, - LINPHONE_STATUS_OFFLINE, LINPHONE_STATUS_PENDING, - LINPHONE_STATUS_CLOSED, LINPHONE_STATUS_END }LinphoneOnlineStatus; const char *linphone_online_status_to_string(LinphoneOnlineStatus ss); -typedef struct _LinphoneFriend{ - LinphoneAddress *uri; - int in_did; - int out_did; - int sid; - int nid; - LinphoneSubscribePolicy pol; - LinphoneOnlineStatus status; - struct _LinphoneProxyConfig *proxy; - struct _LinphoneCore *lc; - BuddyInfo *info; - char *refkey; - bool_t subscribe; - bool_t inc_subscribe_pending; -}LinphoneFriend; +struct _LinphoneFriend; + +typedef struct _LinphoneFriend LinphoneFriend; LinphoneFriend * linphone_friend_new(); LinphoneFriend *linphone_friend_new_with_addr(const char *addr); @@ -280,21 +165,21 @@ int linphone_friend_set_sip_addr(LinphoneFriend *fr, const char *uri); int linphone_friend_set_name(LinphoneFriend *fr, const char *name); int linphone_friend_send_subscribe(LinphoneFriend *fr, bool_t val); int linphone_friend_set_inc_subscribe_policy(LinphoneFriend *fr, LinphoneSubscribePolicy pol); -int linphone_friend_set_proxy(LinphoneFriend *fr, struct _LinphoneProxyConfig *cfg); void linphone_friend_edit(LinphoneFriend *fr); void linphone_friend_done(LinphoneFriend *fr); void linphone_friend_destroy(LinphoneFriend *lf); -const LinphoneAddress *linphone_friend_get_uri(const LinphoneFriend *lf); +const LinphoneAddress *linphone_friend_get_address(const LinphoneFriend *lf); bool_t linphone_friend_get_send_subscribe(const LinphoneFriend *lf); LinphoneSubscribePolicy linphone_friend_get_inc_subscribe_policy(const LinphoneFriend *lf); LinphoneOnlineStatus linphone_friend_get_status(const LinphoneFriend *lf); BuddyInfo * linphone_friend_get_info(const LinphoneFriend *lf); void linphone_friend_set_ref_key(LinphoneFriend *lf, const char *key); const char *linphone_friend_get_ref_key(const LinphoneFriend *lf); -#define linphone_friend_in_list(lf) ((lf)->lc!=NULL) +bool_t linphone_friend_in_list(const LinphoneFriend *lf); #define linphone_friend_url(lf) ((lf)->url) + /** * @addtogroup proxies * @{ @@ -315,33 +200,12 @@ const char *linphone_friend_get_ref_key(const LinphoneFriend *lf); * The default proxy (see linphone_core_set_default_proxy() ) is the one of the list * that is used by default for calls. **/ -typedef struct _LinphoneProxyConfig -{ - struct _LinphoneCore *lc; - char *reg_proxy; - char *reg_identity; - char *reg_route; - char *realm; - int expires; - int reg_time; - int rid; - char *type; - struct _SipSetupContext *ssctx; - int auth_failures; - char *contact_addr; /* our IP address as seen by the proxy, read from via 's received= parameter*/ - int contact_port; /*our IP port as seen by the proxy, read from via's rport= parameter */ - char *dial_prefix; - bool_t commit; - bool_t reg_sendregister; - bool_t registered; - bool_t publish; - bool_t dial_escape_plus; -} LinphoneProxyConfig; +typedef struct _LinphoneProxyConfig LinphoneProxyConfig; LinphoneProxyConfig *linphone_proxy_config_new(void); int linphone_proxy_config_set_server_addr(LinphoneProxyConfig *obj, const char *server_addr); -void linphone_proxy_config_set_identity(LinphoneProxyConfig *obj, const char *identity); -void linphone_proxy_config_set_route(LinphoneProxyConfig *obj, const char *route); +int linphone_proxy_config_set_identity(LinphoneProxyConfig *obj, const char *identity); +int linphone_proxy_config_set_route(LinphoneProxyConfig *obj, const char *route); void linphone_proxy_config_expires(LinphoneProxyConfig *obj, int expires); void linphone_proxy_config_enable_register(LinphoneProxyConfig *obj, bool_t val); #define linphone_proxy_config_enableregister linphone_proxy_config_enable_register @@ -353,18 +217,15 @@ void linphone_proxy_config_set_dial_prefix(LinphoneProxyConfig *cfg, const char bool_t linphone_proxy_config_is_registered(const LinphoneProxyConfig *obj); const char *linphone_proxy_config_get_domain(const LinphoneProxyConfig *cfg); -/** Returns the proxy configured identity as a const char * */ -#define linphone_proxy_config_get_route(obj) ((obj)->reg_route) -/** Returns the proxy configured identity as a const char * */ -#define linphone_proxy_config_get_identity(obj) ((obj)->reg_identity) -#define linphone_proxy_config_publish_enabled(obj) ((obj)->publish) -/** Returns the proxy sip address as const char * */ -#define linphone_proxy_config_get_addr(obj) ((obj)->reg_proxy) -/** Returns the 'expire' time of the registration */ -#define linphone_proxy_config_get_expires(obj) ((obj)->expires) -/** Returns TRUE if registration is enabled, FALSE otherwise */ -#define linphone_proxy_config_register_enabled(obj) ((obj)->reg_sendregister) -#define linphone_proxy_config_get_core(obj) ((obj)->lc) + +const char *linphone_proxy_config_get_route(const LinphoneProxyConfig *obj); +const char *linphone_proxy_config_get_identity(const LinphoneProxyConfig *obj); +bool_t linphone_proxy_config_publish_enabled(const LinphoneProxyConfig *obj); +const char *linphone_proxy_config_get_addr(const LinphoneProxyConfig *obj); +int linphone_proxy_config_get_expires(const LinphoneProxyConfig *obj); +bool_t linphone_proxy_config_register_enabled(const LinphoneProxyConfig *obj); +struct _LinphoneCore * linphone_proxy_config_get_core(const LinphoneProxyConfig *obj); + bool_t linphone_proxy_config_get_dial_escape_plus(const LinphoneProxyConfig *cfg); const char * linphone_proxy_config_get_dial_prefix(const LinphoneProxyConfig *cfg); @@ -377,6 +238,14 @@ SipSetup *linphone_proxy_config_get_sip_setup(LinphoneProxyConfig *cfg); * normalize a human readable phone number into a basic string. 888-444-222 becomes 888444222 */ int linphone_proxy_config_normalize_number(LinphoneProxyConfig *proxy, const char *username, char *result, size_t result_len); +/* + * attached a user data to a proxy config + */ +void linphone_proxy_config_set_user_data(LinphoneProxyConfig *cr, void * ud); +/* + * get user data to a proxy config. return null if any + */ +void * linphone_proxy_config_get_user_data(LinphoneProxyConfig *cr); /** * @} @@ -401,6 +270,8 @@ int linphone_account_creator_test_existence(LinphoneAccountCreator *obj); LinphoneProxyConfig * linphone_account_creator_validate(LinphoneAccountCreator *obj); void linphone_account_creator_destroy(LinphoneAccountCreator *obj); +struct _LinphoneAuthInfo; + /** * @ingroup authentication * Object holding authentication information. @@ -425,33 +296,23 @@ void linphone_account_creator_destroy(LinphoneAccountCreator *obj); * transactions and retry them with authentication headers. * **/ -typedef struct _LinphoneAuthInfo -{ - char *username; - char *realm; - char *userid; - char *passwd; - char *ha1; - bool_t works; - bool_t first_time; -}LinphoneAuthInfo; +typedef struct _LinphoneAuthInfo LinphoneAuthInfo; LinphoneAuthInfo *linphone_auth_info_new(const char *username, const char *userid, const char *passwd, const char *ha1,const char *realm); void linphone_auth_info_set_passwd(LinphoneAuthInfo *info, const char *passwd); void linphone_auth_info_set_username(LinphoneAuthInfo *info, const char *username); void linphone_auth_info_set_userid(LinphoneAuthInfo *info, const char *userid); + +const char *linphone_auth_info_get_username(const LinphoneAuthInfo *i); +const char *linphone_auth_info_get_passwd(const LinphoneAuthInfo *i); +const char *linphone_auth_info_get_userid(const LinphoneAuthInfo *i); + /* you don't need those function*/ void linphone_auth_info_destroy(LinphoneAuthInfo *info); LinphoneAuthInfo * linphone_auth_info_new_from_config_file(struct _LpConfig *config, int pos); -struct _LinphoneChatRoom{ - struct _LinphoneCore *lc; - char *peer; - char *route; - LinphoneAddress *peer_url; - void * user_data; -}; +struct _LinphoneChatRoom; typedef struct _LinphoneChatRoom LinphoneChatRoom; LinphoneChatRoom * linphone_core_create_chat_room(struct _LinphoneCore *lc, const char *to); @@ -523,7 +384,7 @@ typedef void (*DisplayQuestionCb)(struct _LinphoneCore *lc, const char *message) /** Callback prototype */ typedef void (*LinphoneCoreCbFunc)(struct _LinphoneCore *lc,void * user_data); /** Callback prototype */ -typedef void (*NotifyReceivedCb)(struct _LinphoneCore *lc, LinphoneFriend * fid, const char *url, const char *status, const char *img); +typedef void (*NotifyReceivedCb)(struct _LinphoneCore *lc, LinphoneFriend * fid); /** Callback prototype */ typedef void (*NewUnknownSubscriberCb)(struct _LinphoneCore *lc, LinphoneFriend *lf, const char *url); /** Callback prototype */ @@ -555,10 +416,6 @@ typedef struct _LinphoneVTable AuthInfoRequested auth_info_requested; /**< Ask the application some authentication information */ DisplayStatusCb display_status; /**< Callback that notifies various events with human readable text.*/ DisplayMessageCb display_message;/**< Callback to display a message to the user */ -#ifdef VINCENT_MAURY_RSVP - /* the yes/no dialog box */ - DisplayMessageCb display_yes_no; -#endif DisplayMessageCb display_warning;/** Callback to display a warning to the user */ DisplayUrlCb display_url; DisplayQuestionCb display_question; @@ -595,77 +452,13 @@ typedef enum _LinphoneWaitingState{ } LinphoneWaitingState; typedef void * (*LinphoneWaitingCallback)(struct _LinphoneCore *lc, void *context, LinphoneWaitingState ws, const char *purpose, float progress); - -typedef struct _LinphoneCore -{ - LinphoneCoreVTable vtable; - struct _LpConfig *config; - net_config_t net_conf; - sip_config_t sip_conf; - rtp_config_t rtp_conf; - sound_config_t sound_conf; - video_config_t video_conf; - codecs_config_t codecs_conf; - ui_config_t ui_conf; - autoreplier_config_t autoreplier_conf; - LinphoneProxyConfig *default_proxy; - MSList *friends; - MSList *auth_info; - struct _RingStream *ringstream; - LCCallbackObj preview_finished_cb; - bool_t preview_finished; - struct _LinphoneCall *call; /* the current call, in the future it will be a list of calls (conferencing)*/ - int rid; /*registration id*/ - MSList *queued_calls; /* used by the autoreplier */ - MSList *call_logs; - MSList *chatrooms; - int max_call_logs; - int missed_calls; - struct _AudioStream *audiostream; /**/ - struct _VideoStream *videostream; - struct _VideoStream *previewstream; - RtpTransport *a_rtp,*a_rtcp; - struct _RtpProfile *local_profile; - MSList *bl_reqs; - MSList *subscribers; /* unknown subscribers */ - int minutes_away; - LinphoneOnlineStatus presence_mode; - LinphoneOnlineStatus prev_mode; - char *alt_contact; - void *data; - ms_mutex_t lock; - char *play_file; - char *rec_file; - time_t prevtime; - int dw_audio_bw; - int up_audio_bw; - int dw_video_bw; - int up_video_bw; - int audio_bw; - int automatic_action; - gstate_t gstate_power; - gstate_t gstate_reg; - gstate_t gstate_call; - LinphoneWaitingCallback wait_cb; - void *wait_ctx; - bool_t use_files; - bool_t apply_nat_settings; - bool_t ready; - bool_t bl_refresh; -#ifdef VINCENT_MAURY_RSVP - /* QoS parameters*/ - int rsvp_enable; - int rpc_enable; -#endif +typedef struct _LinphoneCore LinphoneCore; /*set this field to false if application manage network connection state * In case of false, network state must be communicate to linphone core with method linphone_core_ */ bool_t auto_net_state_mon; bool_t network_reachable; int down_ptime; -} LinphoneCore; - - /* THE main API */ @@ -682,8 +475,12 @@ LinphoneCore *linphone_core_new(const LinphoneCoreVTable *vtable, /* function to be periodically called in a main loop */ void linphone_core_iterate(LinphoneCore *lc); +LinphoneAddress * linphone_core_interpret_url(LinphoneCore *lc, const char *url); + int linphone_core_invite(LinphoneCore *lc, const char *url); +int linphone_core_invite_address(LinphoneCore *lc, const LinphoneAddress *addr); + int linphone_core_refer(LinphoneCore *lc, const char *url); bool_t linphone_core_inc_invite_pending(LinphoneCore*lc); @@ -742,6 +539,12 @@ const MSList *linphone_core_get_video_codecs(const LinphoneCore *lc); int linphone_core_set_video_codecs(LinphoneCore *lc, MSList *codecs); +bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, PayloadType *pt); + +int linphone_core_enable_payload_type(LinphoneCore *lc, PayloadType *pt, bool_t enable); + +const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt); + bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, PayloadType *pt); int linphone_core_add_proxy_config(LinphoneCore *lc, LinphoneProxyConfig *config); @@ -828,6 +631,18 @@ int linphone_core_get_play_level(LinphoneCore *lc); int linphone_core_get_rec_level(LinphoneCore *lc); void linphone_core_set_ring_level(LinphoneCore *lc, int level); void linphone_core_set_play_level(LinphoneCore *lc, int level); +/** + * Allow to control play level before entering sound card: level in db + * + * @ingroup media_parameters +**/ +void linphone_core_set_soft_play_level(LinphoneCore *lc, float level); +/** + * get play level before entering sound card: level in db + * + * @ingroup media_parameters +**/ +float linphone_core_get_soft_play_level(LinphoneCore *lc); void linphone_core_set_rec_level(LinphoneCore *lc, int level); const char * linphone_core_get_ringer_device(LinphoneCore *lc); const char * linphone_core_get_playback_device(LinphoneCore *lc); @@ -852,6 +667,12 @@ void linphone_core_enable_agc(LinphoneCore *lc, bool_t val); bool_t linphone_core_agc_enabled(const LinphoneCore *lc); void linphone_core_mute_mic(LinphoneCore *lc, bool_t muted); +/** + * return mic state. + * + * @ingroup media_parameters +**/ +bool_t linphone_core_is_mic_muted(LinphoneCore *lc); void linphone_core_set_presence_info(LinphoneCore *lc,int minutes_away,const char *contact,LinphoneOnlineStatus os); @@ -865,7 +686,7 @@ void linphone_core_reject_subscriber(LinphoneCore *lc, LinphoneFriend *lf); const MSList * linphone_core_get_friend_list(const LinphoneCore *lc); /* notify all friends that have subscribed */ void linphone_core_notify_all_friends(LinphoneCore *lc, LinphoneOnlineStatus os); -LinphoneFriend *linphone_core_get_friend_by_uri(const LinphoneCore *lc, const char *uri); +LinphoneFriend *linphone_core_get_friend_by_address(const LinphoneCore *lc, const char *addr); LinphoneFriend *linphone_core_get_friend_by_ref_key(const LinphoneCore *lc, const char *key); /* returns a list of LinphoneCallLog */ @@ -927,8 +748,6 @@ void linphone_core_set_mtu(LinphoneCore *lc, int mtu); void linphone_core_set_network_reachable(LinphoneCore* lc,bool_t value); -bool_t linphone_core_is_in_main_thread(LinphoneCore *lc); - void *linphone_core_get_user_data(LinphoneCore *lc); /* returns LpConfig object to read/write to the config file: usefull if you wish to extend @@ -954,21 +773,6 @@ void linphone_core_destroy(LinphoneCore *lc); /*for advanced users:*/ void linphone_core_set_audio_transports(LinphoneCore *lc, RtpTransport *rtp, RtpTransport *rtcp); -/* end of lecacy api */ - -/*internal use only */ -#define linphone_core_lock(lc) ms_mutex_lock(&(lc)->lock) -#define linphone_core_unlock(lc) ms_mutex_unlock((&lc)->lock) -void linphone_core_start_media_streams(LinphoneCore *lc, struct _LinphoneCall *call); -void linphone_core_stop_media_streams(LinphoneCore *lc); -const char * linphone_core_get_identity(LinphoneCore *lc); -const char * linphone_core_get_route(LinphoneCore *lc); -bool_t linphone_core_interpret_url(LinphoneCore *lc, const char *url, LinphoneAddress **real_parsed_url, char **route); -void linphone_core_start_waiting(LinphoneCore *lc, const char *purpose); -void linphone_core_update_progress(LinphoneCore *lc, const char *purpose, float progresses); -void linphone_core_stop_waiting(LinphoneCore *lc); - - #ifdef __cplusplus } #endif diff --git a/coreapi/linphonecore_jni.cc b/coreapi/linphonecore_jni.cc index 1c14757ca..e65193e3b 100644 --- a/coreapi/linphonecore_jni.cc +++ b/coreapi/linphonecore_jni.cc @@ -42,8 +42,7 @@ static void linphone_android_log_handler(OrtpLogLevel lev, const char *fmt, va_l JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *ajvm, void *reserved) { - #ifdef ANDROID - linphone_core_enable_logs_with_cb(linphone_android_log_handler); +#ifdef ANDROID ms_andsnd_register_card(ajvm); #endif /*ANDROID*/ jvm=ajvm; @@ -51,7 +50,16 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *ajvm, void *reserved) } - +//LinphoneFactory +extern "C" void Java_org_linphone_core_LinphoneCoreFactory_setDebugMode(JNIEnv* env + ,jobject thiz + ,jboolean isDebug) { + if (isDebug) { + linphone_core_enable_logs_with_cb(linphone_android_log_handler); + } else { + linphone_core_disable_logs(); + } +} // LinphoneCore class LinphoneCoreData { @@ -150,6 +158,9 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_newLinphoneCore(JNIEnv* const char* userConfig = env->GetStringUTFChars(juserConfig, NULL); const char* factoryConfig = env->GetStringUTFChars(jfactoryConfig, NULL); LinphoneCoreData* ldata = new LinphoneCoreData(env,thiz,jlistener,juserdata); +#ifdef ANDROID + ms_andsnd_register_card(jvm); +#endif /*ANDROID*/ jlong nativePtr = (jlong)linphone_core_new( &ldata->vTable ,userConfig ,factoryConfig @@ -191,8 +202,12 @@ extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_getDefaultProxyConfig( extern "C" int Java_org_linphone_core_LinphoneCoreImpl_addProxyConfig( JNIEnv* env ,jobject thiz + ,jobject jproxyCfg ,jlong lc ,jlong pc) { + LinphoneProxyConfig* proxy = (LinphoneProxyConfig*)pc; + linphone_proxy_config_set_user_data(proxy, env->NewGlobalRef(jproxyCfg)); + return linphone_core_add_proxy_config((LinphoneCore*)lc,(LinphoneProxyConfig*)pc); } @@ -219,6 +234,12 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_invite( JNIEnv* env linphone_core_invite((LinphoneCore*)lc,uri); env->ReleaseStringUTFChars(juri, uri); } +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_inviteAddress( JNIEnv* env + ,jobject thiz + ,jlong lc + ,jlong to) { + linphone_core_invite_address((LinphoneCore*)lc,(LinphoneAddress*)to); +} extern "C" void Java_org_linphone_core_LinphoneCoreImpl_terminateCall( JNIEnv* env ,jobject thiz @@ -268,11 +289,59 @@ extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setNetworkStateReachable linphone_core_set_network_reachable((LinphoneCore*)lc,isReachable); } +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_setSoftPlayLevel( JNIEnv* env + ,jobject thiz + ,jlong lc + ,jfloat gain) { + linphone_core_set_soft_play_level((LinphoneCore*)lc,gain); +} + +extern "C" float Java_org_linphone_core_LinphoneCoreImpl_getSoftPlayLevel( JNIEnv* env + ,jobject thiz + ,jlong lc) { + return linphone_core_get_soft_play_level((LinphoneCore*)lc); +} + +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_muteMic( JNIEnv* env + ,jobject thiz + ,jlong lc + ,jboolean isMuted) { + linphone_core_mute_mic((LinphoneCore*)lc,isMuted); +} + +extern "C" jlong Java_org_linphone_core_LinphoneCoreImpl_interpretUrl( JNIEnv* env + ,jobject thiz + ,jlong lc + ,jstring jurl) { + const char* url = env->GetStringUTFChars(jurl, NULL); + jlong result = (jlong)linphone_core_interpret_url((LinphoneCore*)lc,url); + env->ReleaseStringUTFChars(jurl, url); + return result; +} +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_sendDtmf( JNIEnv* env + ,jobject thiz + ,jlong lc + ,jchar dtmf) { + linphone_core_send_dtmf((LinphoneCore*)lc,dtmf); +} +extern "C" void Java_org_linphone_core_LinphoneCoreImpl_clearCallLogs(JNIEnv* env + ,jobject thiz + ,jlong lc) { + linphone_core_clear_call_logs((LinphoneCore*)lc); +} +extern "C" jboolean Java_org_linphone_core_LinphoneCoreImpl_isMicMuted( JNIEnv* env + ,jobject thiz + ,jlong lc) { + return linphone_core_is_mic_muted((LinphoneCore*)lc); +} + + //ProxyConfig extern "C" jlong Java_org_linphone_core_LinphoneProxyConfigImpl_newLinphoneProxyConfig(JNIEnv* env,jobject thiz) { - return (jlong) linphone_proxy_config_new(); + LinphoneProxyConfig* proxy = linphone_proxy_config_new(); + return (jlong) proxy; } extern "C" void Java_org_linphone_core_LinphoneProxyConfigImpl_delete(JNIEnv* env,jobject thiz,jlong ptr) { @@ -417,7 +486,7 @@ extern "C" jstring Java_org_linphone_core_LinphoneAddressImpl_getDomain(JNIEnv* } } -extern "C" jstring Java_org_linphone_core_LinphoneAddressImpl_toUri(JNIEnv* env +extern "C" jstring Java_org_linphone_core_LinphoneAddressImpl_toString(JNIEnv* env ,jobject thiz ,jlong ptr) { char* uri = linphone_address_as_string((LinphoneAddress*)ptr); @@ -425,6 +494,23 @@ extern "C" jstring Java_org_linphone_core_LinphoneAddressImpl_toUri(JNIEnv* env ms_free(uri); return juri; } +extern "C" jstring Java_org_linphone_core_LinphoneAddressImpl_toUri(JNIEnv* env + ,jobject thiz + ,jlong ptr) { + char* uri = linphone_address_as_string_uri_only((LinphoneAddress*)ptr); + jstring juri =env->NewStringUTF(uri); + ms_free(uri); + return juri; +} +extern "C" void Java_org_linphone_core_LinphoneAddressImpl_setDisplayName(JNIEnv* env + ,jobject thiz + ,jlong address + ,jstring jdisplayName) { + const char* displayName = env->GetStringUTFChars(jdisplayName, NULL); + linphone_address_set_display_name((LinphoneAddress*)address,displayName); + env->ReleaseStringUTFChars(jdisplayName, displayName); +} + //CallLog extern "C" jlong Java_org_linphone_core_LinphoneCallLogImpl_getFrom(JNIEnv* env diff --git a/coreapi/misc.c b/coreapi/misc.c index db2c93932..c6e673547 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -170,21 +170,42 @@ void check_sound_device(LinphoneCore *lc) #define RTP_HDR_SZ 12 #define IP4_HDR_SZ 20 /*20 is the minimum, but there may be some options*/ -const char *payload_type_get_description(PayloadType *pt){ - return _((const char *)pt->user_data); -} - -void payload_type_set_enable(PayloadType *pt,int value) +static void payload_type_set_enable(PayloadType *pt,int value) { if ((value)!=0) payload_type_set_flag(pt,PAYLOAD_TYPE_ENABLED); \ else payload_type_unset_flag(pt,PAYLOAD_TYPE_ENABLED); } - -bool_t payload_type_enabled(PayloadType *pt) { +static bool_t payload_type_enabled(PayloadType *pt) { return (((pt)->flags & PAYLOAD_TYPE_ENABLED)!=0); } +bool_t linphone_core_payload_type_enabled(LinphoneCore *lc, PayloadType *pt){ + if (ms_list_find(lc->codecs_conf.audio_codecs,pt) || ms_list_find(lc->codecs_conf.video_codecs,pt)){ + return payload_type_enabled(pt); + } + ms_error("Getting enablement status of codec not in audio or video list of PayloadType !"); + return FALSE; +} + +int linphone_core_enable_payload_type(LinphoneCore *lc, PayloadType *pt, bool_t enabled){ + if (ms_list_find(lc->codecs_conf.audio_codecs,pt) || ms_list_find(lc->codecs_conf.video_codecs,pt)){ + payload_type_set_enable(pt,enabled); + return 0; + } + ms_error("Enabling codec not in audio or video list of PayloadType !"); + return -1; +} + +const char *linphone_core_get_payload_type_description(LinphoneCore *lc, PayloadType *pt){ + if (ms_filter_codec_supported(pt->mime_type)){ + MSFilterDesc *desc=ms_filter_get_encoder(pt->mime_type); + return _(desc->text); + } + return NULL; +} + + /*this function makes a special case for speex/8000. This codec is variable bitrate. The 8kbit/s mode is interesting when having a low upload bandwidth, but its quality is not very good. We 'd better use its 15kbt/s mode when we have enough bandwidth*/ @@ -255,6 +276,12 @@ bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, PayloadType case PAYLOAD_AUDIO_PACKETIZED: codec_band=get_audio_payload_bandwidth(lc,pt); ret=bandwidth_is_greater(min_audio_bw*1000,codec_band); + /*hack to avoid using uwb codecs when having low bitrate and video*/ + if (bandwidth_is_greater(199,min_audio_bw)){ + if (linphone_core_video_enabled(lc) && pt->clock_rate>16000){ + ret=FALSE; + } + } //ms_message("Payload %s: %g",pt->mime_type,codec_band); break; case PAYLOAD_VIDEO: @@ -274,152 +301,6 @@ bool_t linphone_core_check_payload_type_usability(LinphoneCore *lc, PayloadType return ret; } -static PayloadType * find_payload(RtpProfile *prof, PayloadType *pt /*from config*/){ - PayloadType *candidate=NULL; - int i; - PayloadType *it; - for(i=0;i<127;++i){ - it=rtp_profile_get_payload(prof,i); - if (it!=NULL && strcasecmp(pt->mime_type,it->mime_type)==0 - && (pt->clock_rate==it->clock_rate || pt->clock_rate<=0) - && payload_type_get_user_data(it)==NULL ){ - if ( (pt->recv_fmtp && it->recv_fmtp && strcasecmp(pt->recv_fmtp,it->recv_fmtp)==0) || - (pt->recv_fmtp==NULL && it->recv_fmtp==NULL) ){ - /*exact match*/ - return it; - }else candidate=it; - } - } - return candidate; -} - -static bool_t check_h264_packmode(PayloadType *payload, MSFilterDesc *desc){ - if (payload->recv_fmtp==NULL || strstr(payload->recv_fmtp,"packetization-mode")==0){ - /*this is packetization-mode=0 H264, we only support it with a multislicing - enabled version of x264*/ - if (strstr(desc->text,"x264") && strstr(desc->text,"multislicing")==0){ - /*this is x264 without multisclicing*/ - ms_message("Disabling packetization-mode=0 H264 codec because " - "of lack of multislicing support"); - return FALSE; - } - } - return TRUE; -} - -static MSList *fix_codec_list(RtpProfile *prof, MSList *conflist) -{ - MSList *elem; - MSList *newlist=NULL; - PayloadType *payload,*confpayload; - - for (elem=conflist;elem!=NULL;elem=ms_list_next(elem)) - { - confpayload=(PayloadType*)elem->data; - payload=find_payload(prof,confpayload); - if (payload!=NULL){ - if (ms_filter_codec_supported(confpayload->mime_type)){ - MSFilterDesc *desc=ms_filter_get_encoder(confpayload->mime_type); - if (strcasecmp(confpayload->mime_type,"H264")==0){ - if (!check_h264_packmode(confpayload,desc)){ - continue; - } - } - payload_type_set_user_data(payload,(void*)desc->text); - payload_type_set_enable(payload,payload_type_enabled(confpayload)); - newlist=ms_list_append(newlist,payload); - } - } - else{ - ms_warning("Cannot support %s/%i: does not exist.",confpayload->mime_type, - confpayload->clock_rate); - } - } - return newlist; -} - - -void linphone_core_setup_local_rtp_profile(LinphoneCore *lc) -{ - int i; - MSList *audiopt,*videopt; - PayloadType *payload; - bool_t prepend; - lc->local_profile=rtp_profile_clone_full(&av_profile); - /* first look at the list given by configuration file to see if - it is correct */ - audiopt=fix_codec_list(lc->local_profile,lc->codecs_conf.audio_codecs); - videopt=fix_codec_list(lc->local_profile,lc->codecs_conf.video_codecs); - /* now find and add payloads that are not listed in the configuration - codec list */ - for (i=0;i<127;i++) - { - payload=rtp_profile_get_payload(lc->local_profile,i); - if (payload!=NULL){ - if (payload_type_get_user_data(payload)!=NULL) continue; - /* find a mediastreamer codec for this payload type */ - if (ms_filter_codec_supported(payload->mime_type)){ - MSFilterDesc *desc=ms_filter_get_encoder(payload->mime_type); - ms_message("Adding new codec %s/%i",payload->mime_type,payload->clock_rate); - payload_type_set_enable(payload,1); - payload_type_set_user_data(payload,(void *)desc->text); - prepend=FALSE; - /* by default, put speex, mpeg4, or h264 on top of list*/ - if (strcmp(payload->mime_type,"speex")==0) - prepend=TRUE; - else if (strcmp(payload->mime_type,"MP4V-ES")==0) - prepend=TRUE; - else if (strcasecmp(payload->mime_type,"H264")==0){ - if (check_h264_packmode(payload,desc)) - prepend=TRUE; - else continue; - } - switch (payload->type){ - case PAYLOAD_AUDIO_CONTINUOUS: - case PAYLOAD_AUDIO_PACKETIZED: - if (prepend) - audiopt=ms_list_prepend(audiopt,(void *)payload); - else - audiopt=ms_list_append(audiopt,(void *)payload); - break; - case PAYLOAD_VIDEO: - if (prepend) - videopt=ms_list_prepend(videopt,(void *)payload); - else - videopt=ms_list_append(videopt,(void *)payload); - break; - default: - ms_error("Unsupported rtp media type."); - } - } - } - } - ms_list_for_each(lc->codecs_conf.audio_codecs,(void (*)(void*))payload_type_destroy); - ms_list_for_each(lc->codecs_conf.video_codecs,(void (*)(void *))payload_type_destroy); - ms_list_free(lc->codecs_conf.audio_codecs); - ms_list_free(lc->codecs_conf.video_codecs); - /* set the fixed lists instead:*/ - lc->codecs_conf.audio_codecs=audiopt; - lc->codecs_conf.video_codecs=videopt; - linphone_core_update_allocated_audio_bandwidth(lc); -} - -int from_2char_without_params(osip_from_t *from,char **str) -{ - osip_from_t *tmpfrom=NULL; - osip_from_clone(from,&tmpfrom); - if (tmpfrom!=NULL){ - while(!osip_list_eol(&tmpfrom->gen_params,0)){ - osip_generic_param_t *param=(osip_generic_param_t*)osip_list_get(&tmpfrom->gen_params,0); - osip_generic_param_free(param); - osip_list_remove(&tmpfrom->gen_params,0); - } - }else return -1; - osip_from_to_str(tmpfrom,str); - osip_from_free(tmpfrom); - return 0; -} - bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret){ #if !defined(_WIN32_WCE) FILE *f=popen(command,"r"); @@ -598,6 +479,11 @@ void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ bool_t got_audio,got_video; bool_t cone_audio=FALSE,cone_video=FALSE; struct timeval init,cur; + SalEndpointCandidate *ac,*vc; + + ac=&call->localdesc->streams[0].candidates[0]; + vc=&call->localdesc->streams[1].candidates[0]; + if (parse_stun_server_addr(server,&ss,&ss_len)<0){ ms_error("Fail to parser stun server address: %s",server); return; @@ -630,20 +516,20 @@ void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ usleep(10000); #endif - if (recvStunResponse(sock1,call->audio_params.natd_addr, - &call->audio_params.natd_port,&id)>0){ + if (recvStunResponse(sock1,ac->addr, + &ac->port,&id)>0){ ms_message("STUN test result: local audio port maps to %s:%i", - call->audio_params.natd_addr, - call->audio_params.natd_port); + ac->addr, + ac->port); if (id==11) cone_audio=TRUE; got_audio=TRUE; } - if (recvStunResponse(sock2,call->video_params.natd_addr, - &call->video_params.natd_port,&id)>0){ + if (recvStunResponse(sock2,vc->addr, + &vc->port,&id)>0){ ms_message("STUN test result: local video port maps to %s:%i", - call->video_params.natd_addr, - call->video_params.natd_port); + vc->addr, + vc->port); if (id==22) cone_video=TRUE; got_video=TRUE; @@ -657,7 +543,8 @@ void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ }else{ if (!cone_audio) { ms_warning("NAT is symmetric for audio port"); - call->audio_params.natd_port=0; + ac->addr[0]='\0'; + ac->port=0; } } if (sock2>=0){ @@ -666,7 +553,8 @@ void linphone_core_run_stun_tests(LinphoneCore *lc, LinphoneCall *call){ }else{ if (!cone_video) { ms_warning("NAT is symmetric for video port."); - call->video_params.natd_port=0; + vc->addr[0]='\0'; + vc->port=0; } } } diff --git a/coreapi/offeranswer.c b/coreapi/offeranswer.c new file mode 100644 index 000000000..2abf7174d --- /dev/null +++ b/coreapi/offeranswer.c @@ -0,0 +1,164 @@ +/* +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. +*/ + +#include "sal.h" +#include "offeranswer.h" + + +static PayloadType * find_payload_type_best_match(const MSList *l, const PayloadType *refpt){ + PayloadType *pt; + char value[10]; + const MSList *elem; + PayloadType *candidate=NULL; + + for (elem=l;elem!=NULL;elem=elem->next){ + pt=(PayloadType*)elem->data; + if (strcasecmp(pt->mime_type,refpt->mime_type)==0 && pt->clock_rate==refpt->clock_rate){ + candidate=pt; + /*good candidate, check fmtp for H264 */ + if (strcasecmp(pt->mime_type,"H264")==0){ + if (pt->recv_fmtp!=NULL && refpt->recv_fmtp!=NULL){ + int mode1=0,mode2=0; + if (fmtp_get_value(pt->recv_fmtp,"packetization-mode",value,sizeof(value))){ + mode1=atoi(value); + } + if (fmtp_get_value(refpt->recv_fmtp,"packetization-mode",value,sizeof(value))){ + mode2=atoi(value); + } + if (mode1==mode2) + break; /*exact match */ + } + }else break; + } + } + return candidate; +} + +static MSList *match_payloads(const MSList *local, const MSList *remote){ + const MSList *e2; + MSList *res=NULL; + PayloadType *matched; + for(e2=remote;e2!=NULL;e2=e2->next){ + PayloadType *p2=(PayloadType*)e2->data; + matched=find_payload_type_best_match(local,p2); + if (matched){ + matched=payload_type_clone(matched); + if (p2->recv_fmtp) + payload_type_set_send_fmtp(matched,p2->recv_fmtp); + res=ms_list_append(res,matched); + payload_type_set_number(matched,payload_type_get_number(p2)); + }else{ + ms_message("No match for %s/%i",p2->mime_type,p2->clock_rate); + } + } + return res; +} + +static bool_t only_telephone_event(const MSList *l){ + PayloadType *p=(PayloadType*)l->data; + if (strcasecmp(p->mime_type,"telephone-event")!=0){ + return FALSE; + } + return TRUE; +} + +static void initiate_outgoing(const SalStreamDescription *local_offer, + const SalStreamDescription *remote_answer, + SalStreamDescription *result){ + if (remote_answer->port!=0) + result->payloads=match_payloads(local_offer->payloads,remote_answer->payloads); + result->proto=local_offer->proto; + result->type=local_offer->type; + if (result->payloads && !only_telephone_event(result->payloads)){ + strcpy(result->addr,remote_answer->addr); + result->port=remote_answer->port; + result->bandwidth=remote_answer->bandwidth; + result->ptime=remote_answer->ptime; + }else{ + result->port=0; + } +} + + +static void initiate_incoming(const SalStreamDescription *local_cap, + const SalStreamDescription *remote_offer, + SalStreamDescription *result){ + result->payloads=match_payloads(local_cap->payloads,remote_offer->payloads); + result->proto=local_cap->proto; + result->type=local_cap->type; + if (result->payloads && !only_telephone_event(result->payloads)){ + strcpy(result->addr,local_cap->addr); + result->port=local_cap->port; + result->bandwidth=local_cap->bandwidth; + result->ptime=local_cap->ptime; + }else{ + result->port=0; + } +} + +/** + * Returns a media description to run the streams with, based on a local offer + * and the returned response (remote). +**/ +int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer, + const SalMediaDescription *remote_answer, + SalMediaDescription *result){ + int i,j; + const SalStreamDescription *ls,*rs; + for(i=0,j=0;instreams;++i){ + ms_message("Processing for stream %i",i); + ls=&local_offer->streams[i]; + rs=sal_media_description_find_stream(remote_answer,ls->proto,ls->type); + if (rs) { + initiate_outgoing(ls,rs,&result->streams[j]); + ++j; + } + else ms_warning("No matching stream for %i",i); + } + result->nstreams=j; + strcpy(result->addr,remote_answer->addr); + return 0; +} + +/** + * Returns a media description to run the streams with, based on the local capabilities and + * and the received offer. + * The returned media description is an answer and should be sent to the offerer. +**/ +int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities, + const SalMediaDescription *remote_offer, + SalMediaDescription *result){ + int i,j; + const SalStreamDescription *ls,*rs; + + for(i=0,j=0;instreams;++i){ + rs=&remote_offer->streams[i]; + ms_message("Processing for stream %i",i); + ls=sal_media_description_find_stream(local_capabilities,rs->proto,rs->type); + if (ls){ + initiate_incoming(ls,rs,&result->streams[j]); + ++j; + } + } + result->nstreams=j; + strcpy(result->username, local_capabilities->username); + strcpy(result->addr,local_capabilities->addr); + return 0; +} + diff --git a/coreapi/offeranswer.h b/coreapi/offeranswer.h new file mode 100644 index 000000000..079f41c96 --- /dev/null +++ b/coreapi/offeranswer.h @@ -0,0 +1,47 @@ +/* +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. +*/ + +#ifndef offeranswer_h +#define offeranswer_h + +/** + This header files defines the SDP offer answer API. + It can be used by implementations of SAL directly. +**/ + + +/** + * Returns a media description to run the streams with, based on a local offer + * and the returned response (remote). +**/ +int offer_answer_initiate_outgoing(const SalMediaDescription *local_offer, + const SalMediaDescription *remote_answer, + SalMediaDescription *result); + +/** + * Returns a media description to run the streams with, based on the local capabilities and + * and the received offer. + * The returned media description is an answer and should be sent to the offerer. +**/ +int offer_answer_initiate_incoming(const SalMediaDescription *local_capabilities, + const SalMediaDescription *remote_offer, + SalMediaDescription *result); + +#endif + diff --git a/coreapi/presence.c b/coreapi/presence.c index fc8f46b74..4367130d5 100644 --- a/coreapi/presence.c +++ b/coreapi/presence.c @@ -18,26 +18,23 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "linphonecore.h" -#include -#include #include "private.h" extern const char *__policy_enum_to_str(LinphoneSubscribePolicy pol); -void linphone_core_add_subscriber(LinphoneCore *lc, const char *subscriber, int did, int nid){ +void linphone_core_add_subscriber(LinphoneCore *lc, const char *subscriber, SalOp *op){ LinphoneFriend *fl=linphone_friend_new_with_addr(subscriber); if (fl==NULL) return ; - fl->in_did=did; - linphone_friend_set_nid(fl,nid); + fl->insub=op; linphone_friend_set_inc_subscribe_policy(fl,LinphoneSPAccept); fl->inc_subscribe_pending=TRUE; lc->subscribers=ms_list_append(lc->subscribers,(void *)fl); if (lc->vtable.new_unknown_subscriber!=NULL) { - char *subscriber=linphone_address_as_string(fl->uri); - lc->vtable.new_unknown_subscriber(lc,fl,subscriber); - ms_free(subscriber); + char *tmp=linphone_address_as_string(fl->uri); + lc->vtable.new_unknown_subscriber(lc,fl,tmp); + ms_free(tmp); } } @@ -45,167 +42,115 @@ void linphone_core_reject_subscriber(LinphoneCore *lc, LinphoneFriend *lf){ linphone_friend_set_inc_subscribe_policy(lf,LinphoneSPDeny); } -static void __do_notify(void * data, void * user_data){ - int *tab=(int*)user_data; - LinphoneFriend *lf=(LinphoneFriend*)data; - linphone_friend_notify(lf,tab[0],tab[1]); -} - -void __linphone_core_notify_all_friends(LinphoneCore *lc, int ss, int os){ - int tab[2]; - tab[0]=ss; - tab[1]=os; - ms_list_for_each2(lc->friends,__do_notify,(void *)tab); -} - void linphone_core_notify_all_friends(LinphoneCore *lc, LinphoneOnlineStatus os){ + MSList *elem; ms_message("Notifying all friends that we are in status %i",os); - __linphone_core_notify_all_friends(lc,EXOSIP_SUBCRSTATE_ACTIVE,os); + for(elem=lc->friends;elem!=NULL;elem=elem->next){ + LinphoneFriend *lf=(LinphoneFriend *)elem->data; + if (lf->insub){ + linphone_friend_notify(lf,os); + } + } } -/* check presence state before answering to call; returns TRUE if we can proceed, else answer the appropriate answer -to close the dialog*/ -bool_t linphone_core_check_presence(LinphoneCore *lc){ - return TRUE; -} - -void linphone_subscription_new(LinphoneCore *lc, eXosip_event_t *ev){ +void linphone_subscription_new(LinphoneCore *lc, SalOp *op, const char *from){ LinphoneFriend *lf=NULL; - osip_from_t *from=ev->request->from; char *tmp; - osip_message_t *msg=NULL; LinphoneAddress *uri; - osip_from_to_str(ev->request->from,&tmp); - uri=linphone_address_new(tmp); - ms_message("Receiving new subscription from %s.",tmp); + + uri=linphone_address_new(from); + linphone_address_clean(uri); + tmp=linphone_address_as_string(uri); + ms_message("Receiving new subscription from %s.",from); /* check if we answer to this subscription */ if (linphone_find_friend(lc->friends,uri,&lf)!=NULL){ - lf->in_did=ev->did; - linphone_friend_set_nid(lf,ev->nid); - eXosip_insubscription_build_answer(ev->tid,202,&msg); - eXosip_insubscription_send_answer(ev->tid,202,msg); - __eXosip_wakeup_event(); + lf->insub=op; + lf->inc_subscribe_pending=TRUE; + sal_subscribe_accept(op); linphone_friend_done(lf); /*this will do all necessary actions */ }else{ /* check if this subscriber is in our black list */ if (linphone_find_friend(lc->subscribers,uri,&lf)){ if (lf->pol==LinphoneSPDeny){ ms_message("Rejecting %s because we already rejected it once.",from); - eXosip_insubscription_send_answer(ev->tid,401,NULL); + sal_subscribe_decline(op); } else { /* else it is in wait for approval state, because otherwise it is in the friend list.*/ ms_message("New subscriber found in friend list, in %s state.",__policy_enum_to_str(lf->pol)); } }else { - eXosip_insubscription_build_answer(ev->tid,202,&msg); - eXosip_insubscription_send_answer(ev->tid,202,msg); - linphone_core_add_subscriber(lc,tmp,ev->did,ev->nid); + sal_subscribe_accept(op); + linphone_core_add_subscriber(lc,tmp,op); } } - osip_free(tmp); + linphone_address_destroy(uri); + ms_free(tmp); } -void linphone_notify_recv(LinphoneCore *lc, eXosip_event_t *ev) -{ - const char *status=_("Gone"); - const char *img="sip-closed.png"; +void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeState ss, SalPresenceStatus sal_status){ char *tmp; LinphoneFriend *lf; LinphoneAddress *friend=NULL; - osip_from_t *from=NULL; - osip_body_t *body=NULL; - LinphoneOnlineStatus estatus=LINPHONE_STATUS_UNKNOWN; - ms_message("Receiving notify with sid=%i,nid=%i",ev->sid,ev->nid); - if (ev->request!=NULL){ - from=ev->request->from; - osip_message_get_body(ev->request,0,&body); - if (body==NULL){ - ms_error("No body in NOTIFY"); - return; - } - if (strstr(body->body,"pending")!=NULL){ - status=_("Waiting for Approval"); - img="sip-wfa.png"; - estatus=LINPHONE_STATUS_PENDING; - }else if ((strstr(body->body,"online")!=NULL) || (strstr(body->body,"open")!=NULL)) { - status=_("Online"); - img="sip-online.png"; - estatus=LINPHONE_STATUS_ONLINE; - }else if (strstr(body->body,"busy")!=NULL){ - status=_("Busy"); - img="sip-busy.png"; - estatus=LINPHONE_STATUS_BUSY; - }else if (strstr(body->body,"berightback")!=NULL - || strstr(body->body,"in-transit")!=NULL ){ - status=_("Be Right Back"); - img="sip-bifm.png"; - estatus=LINPHONE_STATUS_BERIGHTBACK; - }else if (strstr(body->body,"away")!=NULL){ - status=_("Away"); - img="sip-away.png"; - estatus=LINPHONE_STATUS_AWAY; - }else if (strstr(body->body,"onthephone")!=NULL - || strstr(body->body,"on-the-phone")!=NULL){ - status=_("On The Phone"); - img="sip-otp.png"; - estatus=LINPHONE_STATUS_ONTHEPHONE; - }else if (strstr(body->body,"outtolunch")!=NULL - || strstr(body->body,"meal")!=NULL){ - status=_("Out To Lunch"); - img="sip-otl.png"; - estatus=LINPHONE_STATUS_OUTTOLUNCH; - }else if (strstr(body->body,"closed")!=NULL){ - status=_("Closed"); - img="sip-away.png"; - estatus=LINPHONE_STATUS_CLOSED; - }else{ - status=_("Gone"); - img="sip-closed.png"; + LinphoneOnlineStatus estatus=LINPHONE_STATUS_OFFLINE; + + switch(sal_status){ + case SalPresenceOffline: estatus=LINPHONE_STATUS_OFFLINE; - } - ms_message("We are notified that sip:%s@%s has online status %s",from->url->username,from->url->host,status); + break; + case SalPresenceOnline: + estatus=LINPHONE_STATUS_ONLINE; + break; + case SalPresenceBusy: + estatus=LINPHONE_STATUS_BUSY; + break; + case SalPresenceBerightback: + estatus=LINPHONE_STATUS_AWAY; + break; + case SalPresenceAway: + estatus=LINPHONE_STATUS_AWAY; + break; + case SalPresenceOnthephone: + estatus=LINPHONE_STATUS_ONTHEPHONE; + break; + case SalPresenceOuttolunch: + estatus=LINPHONE_STATUS_OUTTOLUNCH; + break; + case SalPresenceDonotdisturb: + estatus=LINPHONE_STATUS_BUSY; + break; + case SalPresenceMoved: + case SalPresenceAltService: + estatus=LINPHONE_STATUS_AWAY; + break; } - lf=linphone_find_friend_by_sid(lc->friends,ev->sid); + lf=linphone_find_friend_by_out_subscribe(lc->friends,op); if (lf!=NULL){ friend=lf->uri; tmp=linphone_address_as_string(friend); lf->status=estatus; - lc->vtable.notify_recv(lc,(LinphoneFriend*)lf,tmp,status,img); + lf->subscribe_active=TRUE; + lc->vtable.notify_recv(lc,(LinphoneFriend*)lf); ms_free(tmp); - if (ev->ss_status==EXOSIP_SUBCRSTATE_TERMINATED) { - lf->sid=-1; - lf->out_did=-1; - ms_message("Outgoing subscription terminated by remote."); - } }else{ ms_message("But this person is not part of our friend list, so we don't care."); } + if (ss==SalSubscribeTerminated){ + sal_op_release(op); + if (lf){ + lf->outsub=NULL; + lf->subscribe_active=FALSE; + } + } } -void linphone_subscription_answered(LinphoneCore *lc, eXosip_event_t *ev){ +void linphone_subscription_closed(LinphoneCore *lc, SalOp *op){ LinphoneFriend *lf; - osip_from_t *from=ev->response->to; - char *tmp; - osip_from_to_str(from,&tmp); - LinphoneAddress *uri=linphone_address_new(tmp); - linphone_find_friend(lc->friends,uri,&lf); + lf=linphone_find_friend_by_inc_subscribe(lc->friends,op); + sal_op_release(op); if (lf!=NULL){ - lf->out_did=ev->did; - linphone_friend_set_sid(lf,ev->sid); + lf->insub=NULL; }else{ - ms_warning("Receiving answer for unknown subscribe sip:%s@%s", from->url->username,from->url->host); - } - ms_free(tmp); -} -void linphone_subscription_closed(LinphoneCore *lc,eXosip_event_t *ev){ - LinphoneFriend *lf; - osip_from_t *from=ev->request->from; - lf=linphone_find_friend_by_nid(lc->friends,ev->nid); - if (lf!=NULL){ - lf->in_did=-1; - linphone_friend_set_nid(lf,-1); - }else{ - ms_warning("Receiving unsuscribe for unknown in-subscribtion from sip:%s@%s", from->url->username, from->url->host); + ms_warning("Receiving unsuscribe for unknown in-subscribtion from %s", sal_op_get_from(op)); } } diff --git a/coreapi/private.h b/coreapi/private.h index 93b1c77be..50de9c9c6 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -26,7 +26,7 @@ #define _PRIVATE_H #include "linphonecore.h" -#include +#include "sal.h" #ifdef HAVE_CONFIG_H #include "config.h" @@ -54,23 +54,9 @@ #endif #endif - -typedef struct _StreamParams -{ - int initialized; - int line; - int localport; - int remoteport; - int remotertcpport; - int pt; - char *relay_session_id; - int natd_port; - char remoteaddr[LINPHONE_HOSTNAME_SIZE]; - char natd_addr[LINPHONE_HOSTNAME_SIZE]; -} StreamParams; - typedef enum _LCState{ LCStateInit, + LCStatePreEstablishing, LCStateRinging, LCStateAVRunning }LCState; @@ -79,25 +65,23 @@ typedef enum _LCState{ typedef struct _LinphoneCall { struct _LinphoneCore *core; - StreamParams audio_params; - StreamParams video_params; + SalMediaDescription *localdesc; + SalMediaDescription *resultdesc; LinphoneCallDir dir; - struct _RtpProfile *profile; /*points to the local_profile or to the remote "guessed" profile*/ + struct _RtpProfile *audio_profile; + struct _RtpProfile *video_profile; struct _LinphoneCallLog *log; - int cid; /*call id */ - int did; /*dialog id */ - int tid; /*last transaction id*/ + SalOp *op; + SalOp *ping_op; char localip[LINPHONE_IPADDR_SIZE]; /* our best guess for local ipaddress for this call */ - struct _sdp_context *sdpctx; time_t start_time; /*time at which the call was initiated*/ time_t media_start_time; /*time at which it was accepted, media streams established*/ LCState state; - bool_t auth_pending; - bool_t supports_session_timers; + bool_t media_pending; } LinphoneCall; LinphoneCall * linphone_call_new_outgoing(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to); -LinphoneCall * linphone_call_new_incoming(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, eXosip_event_t *ev); +LinphoneCall * linphone_call_new_incoming(struct _LinphoneCore *lc, LinphoneAddress *from, LinphoneAddress *to, SalOp *op); #define linphone_call_set_state(lcall,st) (lcall)->state=(st) void linphone_call_destroy(struct _LinphoneCall *obj); @@ -107,7 +91,7 @@ void linphone_call_log_completed(LinphoneCallLog *calllog, LinphoneCall *call); void linphone_call_log_destroy(LinphoneCallLog *cl); -void linphone_core_init_media_streams(LinphoneCore *lc); +void linphone_core_init_media_streams(LinphoneCore *lc, LinphoneCall *call); void linphone_auth_info_write_config(struct _LpConfig *config, LinphoneAuthInfo *obj, int pos); @@ -117,27 +101,26 @@ void linphone_core_refresh_subscribes(LinphoneCore *lc); int linphone_proxy_config_send_publish(LinphoneProxyConfig *cfg, LinphoneOnlineStatus os); int linphone_online_status_to_eXosip(LinphoneOnlineStatus os); +void linphone_friend_close_subscriptions(LinphoneFriend *lf); +void linphone_friend_notify(LinphoneFriend *lf, LinphoneOnlineStatus os); +LinphoneFriend *linphone_find_friend_by_inc_subscribe(MSList *l, SalOp *op); +LinphoneFriend *linphone_find_friend_by_out_subscribe(MSList *l, SalOp *op); -void linphone_friend_set_sid(LinphoneFriend *lf, int sid); -void linphone_friend_set_nid(LinphoneFriend *lf, int nid); -void linphone_friend_notify(LinphoneFriend *lf, int ss, LinphoneOnlineStatus os); int set_lock_file(); int get_lock_file(); int remove_lock_file(); int do_registration(LinphoneCore *lc, bool_t doit); void check_for_registration(LinphoneCore *lc); -char *int2str(int number); -int from_2char_without_params(osip_from_t *from,char **str); void check_sound_device(LinphoneCore *lc); -void linphone_core_setup_local_rtp_profile(LinphoneCore *lc); +void linphone_core_verify_codecs(LinphoneCore *lc); void linphone_core_get_local_ip(LinphoneCore *lc, const char *to, char *result); bool_t host_has_ipv6_network(); bool_t lp_spawn_command_line_sync(const char *command, char **result,int *command_ret); static inline int get_min_bandwidth(int dbw, int ubw){ - if (dbw<0) return ubw; - if (ubw<0) return dbw; + if (dbw<=0) return ubw; + if (ubw<=0) return dbw; return MIN(dbw,ubw); } @@ -157,24 +140,18 @@ static inline void set_string(char **dest, const char *src){ } #define PAYLOAD_TYPE_ENABLED PAYLOAD_TYPE_USER_FLAG_0 -bool_t linphone_proxy_config_register_again_with_updated_contact(LinphoneProxyConfig *obj, osip_message_t *orig_request, osip_message_t *last_answer); -void linphone_process_authentication(LinphoneCore* lc, eXosip_event_t *ev); -void linphone_authentication_ok(LinphoneCore *lc, eXosip_event_t *ev); -void linphone_subscription_new(LinphoneCore *lc, eXosip_event_t *ev); -void linphone_notify_recv(LinphoneCore *lc,eXosip_event_t *ev); -LinphoneProxyConfig *linphone_core_get_proxy_config_from_rid(LinphoneCore *lc, int rid); -void linphone_proxy_config_process_authentication_failure(LinphoneCore *lc, int code, eXosip_event_t *ev); -void linphone_subscription_answered(LinphoneCore *lc, eXosip_event_t *ev); -void linphone_subscription_closed(LinphoneCore *lc, eXosip_event_t *ev); +SalPresenceStatus linphone_online_status_to_sal(LinphoneOnlineStatus os); +void linphone_process_authentication(LinphoneCore* lc, SalOp *op); +void linphone_authentication_ok(LinphoneCore *lc, SalOp *op); +void linphone_subscription_new(LinphoneCore *lc, SalOp *op, const char *from); +void linphone_notify_recv(LinphoneCore *lc, SalOp *op, SalSubscribeState ss, SalPresenceStatus status); +void linphone_proxy_config_process_authentication_failure(LinphoneCore *lc, SalOp *op); -void linphone_call_init_media_params(LinphoneCall *call); - -void linphone_set_sdp(osip_message_t *sip, const char *sdp); +void linphone_subscription_answered(LinphoneCore *lc, SalOp *op); +void linphone_subscription_closed(LinphoneCore *lc, SalOp *op); MSList *linphone_find_friend(MSList *fl, const LinphoneAddress *fri, LinphoneFriend **lf); -LinphoneFriend *linphone_find_friend_by_nid(MSList *l, int nid); -LinphoneFriend *linphone_find_friend_by_sid(MSList *l, int sid); void linphone_core_update_allocated_audio_bandwidth(LinphoneCore *lc); void linphone_core_update_allocated_audio_bandwidth_in_call(LinphoneCore *lc, const PayloadType *pt); @@ -194,4 +171,231 @@ void linphone_proxy_config_write_to_config_file(struct _LpConfig* config,Linphon int linphone_proxy_config_normalize_number(LinphoneProxyConfig *cfg, const char *username, char *result, size_t result_len); +void linphone_core_text_received(LinphoneCore *lc, const char *from, const char *msg); + +void linphone_core_start_media_streams(LinphoneCore *lc, struct _LinphoneCall *call); +void linphone_core_stop_media_streams(LinphoneCore *lc, struct _LinphoneCall *call); +const char * linphone_core_get_identity(LinphoneCore *lc); +const char * linphone_core_get_route(LinphoneCore *lc); +void linphone_core_start_waiting(LinphoneCore *lc, const char *purpose); +void linphone_core_update_progress(LinphoneCore *lc, const char *purpose, float progresses); +void linphone_core_stop_waiting(LinphoneCore *lc); + +int linphone_core_start_invite(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy); + +extern SalCallbacks linphone_sal_callbacks; + + +struct _LinphoneProxyConfig +{ + struct _LinphoneCore *lc; + char *reg_proxy; + char *reg_identity; + char *reg_route; + char *realm; + int expires; + int reg_time; + SalOp *op; + char *type; + struct _SipSetupContext *ssctx; + int auth_failures; + char *dial_prefix; + bool_t commit; + bool_t reg_sendregister; + bool_t registered; + bool_t publish; + bool_t dial_escape_plus; + void* user_data; +}; + +struct _LinphoneAuthInfo +{ + char *username; + char *realm; + char *userid; + char *passwd; + char *ha1; + int usecount; + bool_t works; +}; + +struct _LinphoneChatRoom{ + struct _LinphoneCore *lc; + char *peer; + char *route; + LinphoneAddress *peer_url; + void * user_data; +}; + +struct _LinphoneFriend{ + LinphoneAddress *uri; + SalOp *insub; + SalOp *outsub; + LinphoneSubscribePolicy pol; + LinphoneOnlineStatus status; + struct _LinphoneCore *lc; + BuddyInfo *info; + char *refkey; + bool_t subscribe; + bool_t subscribe_active; + bool_t inc_subscribe_pending; +}; + +typedef struct sip_config +{ + char *contact; + char *guessed_contact; + int sip_port; + MSList *proxies; + MSList *deleted_proxies; + int inc_timeout; /*timeout after an un-answered incoming call is rejected*/ + bool_t use_info; + bool_t use_rfc2833; /*force RFC2833 to be sent*/ + bool_t guess_hostname; + bool_t loopback_only; + bool_t ipv6_enabled; + bool_t sdp_200_ack; + bool_t only_one_codec; /*in SDP answers*/ + bool_t register_only_when_network_is_up; + bool_t ping_with_options; + bool_t auto_net_state_mon; +} sip_config_t; + +typedef struct rtp_config +{ + int audio_rtp_port; + int video_rtp_port; + int audio_jitt_comp; /*jitter compensation*/ + int video_jitt_comp; /*jitter compensation*/ + int nortp_timeout; +}rtp_config_t; + + + +typedef struct net_config +{ + char *nat_address; + char *stun_server; + char *relay; + int download_bw; + int upload_bw; + int firewall_policy; + int mtu; + int down_ptime; + bool_t nat_sdp_only; +}net_config_t; + + +typedef struct sound_config +{ + struct _MSSndCard * ring_sndcard; /* the playback sndcard currently used */ + struct _MSSndCard * play_sndcard; /* the playback sndcard currently used */ + struct _MSSndCard * capt_sndcard; /* the capture sndcard currently used */ + const char **cards; + int latency; /* latency in samples of the current used sound device */ + char rec_lev; + char play_lev; + char ring_lev; + char soft_play_lev; + char source; + char *local_ring; + char *remote_ring; + bool_t ec; + bool_t ea; + bool_t agc; +} sound_config_t; + +typedef struct codecs_config +{ + MSList *audio_codecs; /* list of audio codecs in order of preference*/ + MSList *video_codecs; /* for later use*/ +}codecs_config_t; + +typedef struct video_config{ + struct _MSWebCam *device; + const char **cams; + MSVideoSize vsize; + bool_t capture; + bool_t show_local; + bool_t display; + bool_t selfview; /*during calls*/ +}video_config_t; + +typedef struct ui_config +{ + int is_daemon; + int is_applet; + unsigned int timer_id; /* the timer id for registration */ +}ui_config_t; + + + +typedef struct autoreplier_config +{ + int enabled; + int after_seconds; /* accept the call after x seconds*/ + int max_users; /* maximum number of user that can call simultaneously */ + int max_rec_time; /* the max time of incoming voice recorded */ + int max_rec_msg; /* maximum number of recorded messages */ + const char *message; /* the path of the file to be played */ +}autoreplier_config_t; + + +struct _LinphoneCore +{ + LinphoneCoreVTable vtable; + Sal *sal; + struct _LpConfig *config; + net_config_t net_conf; + sip_config_t sip_conf; + rtp_config_t rtp_conf; + sound_config_t sound_conf; + video_config_t video_conf; + codecs_config_t codecs_conf; + ui_config_t ui_conf; + autoreplier_config_t autoreplier_conf; + LinphoneProxyConfig *default_proxy; + MSList *friends; + MSList *auth_info; + struct _RingStream *ringstream; + LCCallbackObj preview_finished_cb; + struct _LinphoneCall *call; /* the current call, in the future it will be a list of calls (conferencing)*/ + MSList *queued_calls; /* used by the autoreplier */ + MSList *call_logs; + MSList *chatrooms; + int max_call_logs; + int missed_calls; + struct _AudioStream *audiostream; /**/ + struct _VideoStream *videostream; + struct _VideoStream *previewstream; + RtpTransport *a_rtp,*a_rtcp; + MSList *bl_reqs; + MSList *subscribers; /* unknown subscribers */ + int minutes_away; + LinphoneOnlineStatus presence_mode; + LinphoneOnlineStatus prev_mode; + char *alt_contact; + void *data; + char *play_file; + char *rec_file; + time_t prevtime; + int dw_audio_bw; + int up_audio_bw; + int dw_video_bw; + int up_video_bw; + int audio_bw; + gstate_t gstate_power; + gstate_t gstate_reg; + gstate_t gstate_call; + LinphoneWaitingCallback wait_cb; + void *wait_ctx; + bool_t use_files; + bool_t apply_nat_settings; + bool_t ready; + bool_t bl_refresh; + bool_t preview_finished; + bool_t auto_net_state_mon; + bool_t network_reachable; +}; + #endif /* _PRIVATE_H */ diff --git a/coreapi/proxy.c b/coreapi/proxy.c index 637400cbb..a1bf54a07 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -20,8 +20,6 @@ Copyright (C) 2000 Simon MORLAT (simon.morlat@linphone.org) #include "linphonecore.h" #include "sipsetup.h" -#include -#include #include "lpconfig.h" #include "private.h" @@ -40,7 +38,6 @@ void linphone_proxy_config_write_all_to_config_file(LinphoneCore *lc){ void linphone_proxy_config_init(LinphoneProxyConfig *obj){ memset(obj,0,sizeof(LinphoneProxyConfig)); - obj->rid=-1; obj->expires=3600; } @@ -72,8 +69,8 @@ void linphone_proxy_config_destroy(LinphoneProxyConfig *obj){ if (obj->ssctx!=NULL) sip_setup_context_free(obj->ssctx); if (obj->realm!=NULL) ms_free(obj->realm); if (obj->type!=NULL) ms_free(obj->type); - if (obj->contact_addr!=NULL) ms_free(obj->contact_addr); if (obj->dial_prefix!=NULL) ms_free(obj->dial_prefix); + if (obj->op) sal_op_release(obj->op); } /** @@ -83,79 +80,6 @@ bool_t linphone_proxy_config_is_registered(const LinphoneProxyConfig *obj){ return obj->registered; } -void linphone_proxy_config_get_contact(LinphoneProxyConfig *cfg, const char **ip, int *port){ - if (cfg->registered){ - *ip=cfg->contact_addr; - *port=cfg->contact_port; - }else{ - *ip=NULL; - *port=0; - } -} - -static void update_contact(LinphoneProxyConfig *cfg, const char *ip, const char *port){ - if (cfg->contact_addr){ - ms_free(cfg->contact_addr); - } - cfg->contact_addr=ms_strdup(ip); - if (port!=NULL) - cfg->contact_port=atoi(port); - else cfg->contact_port=5060; -} - -bool_t linphone_proxy_config_register_again_with_updated_contact(LinphoneProxyConfig *obj, osip_message_t *orig_request, osip_message_t *last_answer){ - osip_message_t *msg; - const char *rport,*received; - osip_via_t *via=NULL; - osip_generic_param_t *param=NULL; - osip_contact_t *ctt=NULL; - osip_message_get_via(last_answer,0,&via); - if (!via) return FALSE; - osip_via_param_get_byname(via,"rport",¶m); - if (param) rport=param->gvalue; - else return FALSE; - param=NULL; - osip_via_param_get_byname(via,"received",¶m); - if (param) received=param->gvalue; - else 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; - const char *via_rport=rport; - if (via_rport==NULL || strlen(via_rport)>0) - via_rport="5060"; - if (contact_port==NULL || strlen(contact_port)>0) - contact_port="5060"; - if (strcmp(contact_port,via_rport)==0){ - 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 <> %s)", contact_port,via_rport); - } - eXosip_lock(); - msg=NULL; - eXosip_register_build_register(obj->rid,obj->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); - } - ctt->url->port=osip_strdup(rport); - eXosip_register_send_register(obj->rid,msg); - eXosip_unlock(); - update_contact(obj,received,rport); - ms_message("Resending new register with updated contact %s:%s",received,rport); - return TRUE; -} - /** * Sets the proxy address * @@ -165,19 +89,29 @@ bool_t linphone_proxy_config_register_again_with_updated_contact(LinphoneProxyCo * - hostnames : sip:sip.example.net **/ int linphone_proxy_config_set_server_addr(LinphoneProxyConfig *obj, const char *server_addr){ - int err; - osip_from_t *url; + LinphoneAddress *addr; + char *try=NULL; + if (obj->reg_proxy!=NULL) ms_free(obj->reg_proxy); obj->reg_proxy=NULL; + if (server_addr!=NULL && strlen(server_addr)>0){ - osip_from_init(&url); - err=osip_from_parse(url,server_addr); - if (err==0 && url->url->host!=NULL){ - obj->reg_proxy=ms_strdup(server_addr); + addr=linphone_address_new(server_addr); + if (!addr){ + /*try to prepend 'sip:' */ + if (strstr(server_addr,"sip:")==NULL){ + try=ms_strdup_printf("sip:%s",server_addr); + addr=linphone_address_new(try); + ms_free(try); + } + } + if (addr){ + obj->reg_proxy=linphone_address_as_string_uri_only(addr); + linphone_address_destroy(addr); }else{ ms_warning("Could not parse %s",server_addr); + return -1; } - osip_from_free(url); } return 0; } @@ -191,30 +125,30 @@ int linphone_proxy_config_set_server_addr(LinphoneProxyConfig *obj, const char * * The REGISTER messages will have from and to set to this identity. * **/ -void linphone_proxy_config_set_identity(LinphoneProxyConfig *obj, const char *identity){ - int err=0; - osip_from_t *url=NULL; +int linphone_proxy_config_set_identity(LinphoneProxyConfig *obj, const char *identity){ + LinphoneAddress *addr; if (identity!=NULL && strlen(identity)>0){ - osip_from_init(&url); - err=osip_from_parse(url,identity); - if (err<0 || url->url->host==NULL || url->url->username==NULL){ - ms_warning("Could not parse %s",identity); - osip_from_free(url); - return; + addr=linphone_address_new(identity); + if (!addr || linphone_address_get_username(addr)==NULL){ + ms_warning("Invalid sip identity: %s",identity); + if (addr) + linphone_address_destroy(addr); + return -1; + }else{ + if (obj->reg_identity!=NULL) { + ms_free(obj->reg_identity); + obj->reg_identity=NULL; + } + obj->reg_identity=ms_strdup(identity); + if (obj->realm){ + ms_free(obj->realm); + } + obj->realm=ms_strdup(linphone_address_get_domain(addr)); + linphone_address_destroy(addr); + return 0; } - } else err=-2; - if (obj->reg_identity!=NULL) { - ms_free(obj->reg_identity); - obj->reg_identity=NULL; } - if (err==-2) obj->reg_identity=NULL; - else { - obj->reg_identity=ms_strdup(identity); - if (obj->realm) - ms_free(obj->realm); - obj->realm=ms_strdup(url->url->host); - } - if (url) osip_from_free(url); + return -1; } const char *linphone_proxy_config_get_domain(const LinphoneProxyConfig *cfg){ @@ -226,37 +160,14 @@ const char *linphone_proxy_config_get_domain(const LinphoneProxyConfig *cfg){ * When a route is set, all outgoing calls will go to the route's destination if this proxy * is the default one (see linphone_core_set_default_proxy() ). **/ -void linphone_proxy_config_set_route(LinphoneProxyConfig *obj, const char *route) +int linphone_proxy_config_set_route(LinphoneProxyConfig *obj, const char *route) { - int err; - osip_uri_param_t *lr_param=NULL; - osip_route_t *rt=NULL; - char *tmproute=NULL; - if (route!=NULL && strlen(route)>0){ - osip_route_init(&rt); - err=osip_route_parse(rt,route); - if (err<0){ - ms_warning("Could not parse %s",route); - osip_route_free(rt); - return ; - } - if (obj->reg_route!=NULL) { - ms_free(obj->reg_route); - obj->reg_route=NULL; - } - - /* 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){ - osip_uri_uparam_add(rt->url,osip_strdup("lr"),NULL); - osip_route_to_str(rt,&tmproute); - obj->reg_route=ms_strdup(tmproute); - osip_free(tmproute); - }else obj->reg_route=ms_strdup(route); - }else{ - if (obj->reg_route!=NULL) ms_free(obj->reg_route); + if (obj->reg_route!=NULL){ + ms_free(obj->reg_route); obj->reg_route=NULL; } + obj->reg_route=ms_strdup(route); + return 0; } bool_t linphone_proxy_config_check(LinphoneCore *lc, LinphoneProxyConfig *obj){ @@ -304,15 +215,10 @@ void linphone_proxy_config_enable_publish(LinphoneProxyConfig *obj, bool_t val){ * linphone_proxy_config_done() to commit the changes. **/ void linphone_proxy_config_edit(LinphoneProxyConfig *obj){ - obj->auth_failures=0; if (obj->reg_sendregister){ /* unregister */ if (obj->registered) { - osip_message_t *msg; - eXosip_lock(); - eXosip_register_build_register(obj->rid,0,&msg); - eXosip_register_send_register(obj->rid,msg); - eXosip_unlock(); + sal_unregister(obj->op); obj->registered=FALSE; } } @@ -329,13 +235,11 @@ static void linphone_proxy_config_register(LinphoneProxyConfig *obj){ if (obj->reg_identity!=NULL) id_str=obj->reg_identity; else id_str=linphone_core_get_primary_contact(obj->lc); if (obj->reg_sendregister){ - char *ct=NULL; - osip_message_t *msg=NULL; - eXosip_lock(); - obj->rid=eXosip_register_build_initial_register(id_str,obj->reg_proxy,NULL,obj->expires,&msg); - eXosip_register_send_register(obj->rid,msg); - eXosip_unlock(); - if (ct!=NULL) osip_free(ct); + if (obj->op) + sal_op_release(obj->op); + obj->op=sal_op_new(obj->lc->sal); + sal_op_set_user_pointer(obj->op,obj); + sal_register(obj->op,obj->reg_proxy,obj->reg_identity,obj->expires); } } @@ -350,7 +254,7 @@ void linphone_proxy_config_set_dial_prefix(LinphoneProxyConfig *cfg, const char ms_free(cfg->dial_prefix); cfg->dial_prefix=NULL; } - if (prefix) cfg->dial_prefix=ms_strdup(prefix); + if (prefix && prefix[0]!='\0') cfg->dial_prefix=ms_strdup(prefix); } /** @@ -484,172 +388,62 @@ void linphone_proxy_config_set_realm(LinphoneProxyConfig *cfg, const char *realm } int linphone_proxy_config_send_publish(LinphoneProxyConfig *proxy, - LinphoneOnlineStatus presence_mode) -{ - osip_message_t *pub; - int i; - const char *from=NULL; - char buf[5000]; - - if (proxy->publish==FALSE) return 0; - - if (proxy!=NULL) { - from=linphone_proxy_config_get_identity(proxy); - } - if (from==NULL) from=linphone_core_get_primary_contact(proxy->lc); - - if (presence_mode==LINPHONE_STATUS_ONLINE) - { - snprintf(buf, 5000, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ -%s\n\ -online\n\ -\n\ -", - from, from); - } - else if (presence_mode==LINPHONE_STATUS_BUSY - ||presence_mode==LINPHONE_STATUS_NOT_DISTURB) - { - snprintf(buf, 5000, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - busy\n\ -\n\ -\n\ -%s\n\ -busy\n\ -\n\ -", - from, from); - } - else if (presence_mode==LINPHONE_STATUS_BERIGHTBACK) - { - snprintf(buf, 5000, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - in-transit\n\ -\n\ -\n\ -%s\n\ -be right back\n\ -\n\ -", - from,from); - } - else if (presence_mode==LINPHONE_STATUS_AWAY - ||presence_mode==LINPHONE_STATUS_MOVED - ||presence_mode==LINPHONE_STATUS_ALT_SERVICE) - { - snprintf(buf, 5000, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - away\n\ -\n\ -\n\ -%s\n\ -away\n\ -\n\ -", - from, from); - } - else if (presence_mode==LINPHONE_STATUS_ONTHEPHONE) - { - snprintf(buf, 5000, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - on-the-phone\n\ -\n\ -\n\ -%s\n\ -on the phone\n\ -\n\ -", - from, from); - } - else if (presence_mode==LINPHONE_STATUS_OUTTOLUNCH) - { - snprintf(buf, 5000, "\n\ -\n\ -\n\ -\n\ -open\n\ -\n\ - meal\n\ -\n\ -\n\ -%s\n\ -out to lunch\n\ -\n\ -", - from, from); - } - else if (presence_mode==LINPHONE_STATUS_OFFLINE) - { - /* */ - snprintf(buf, 5000, "\n\ -\n%s", - from, -"\n\ -\n\ -closed\n\ -\n\ - permanent-absence\n\ -\n\ -\n\ -\n\ -\n\n"); - } - - i = eXosip_build_publish(&pub, (char *)from, (char *)from, NULL, "presence", "1800", "application/pidf+xml", buf); - - if (i<0) - { - ms_message("Failed to build publish request."); - return -1; - } - - eXosip_lock(); - i = eXosip_publish(pub, from); /* should update the sip-if-match parameter - from sip-etag from last 200ok of PUBLISH */ - eXosip_unlock(); - if (i<0) - { - ms_message("Failed to send publish request."); - return -1; - } - return 0; + LinphoneOnlineStatus presence_mode){ + int err; + SalOp *op=sal_op_new(proxy->lc->sal); + err=sal_publish(op,linphone_proxy_config_get_identity(proxy), + linphone_proxy_config_get_identity(proxy),linphone_online_status_to_sal(presence_mode)); + sal_op_release(op); + return err; } +/** + * Returns the route set for this proxy configuration. +**/ +const char *linphone_proxy_config_get_route(const LinphoneProxyConfig *obj){ + return obj->reg_route; +} + +/** + * Returns the SIP identity that belongs to this proxy configuration. + * + * The SIP identity is a SIP address (Display Name ) +**/ +const char *linphone_proxy_config_get_identity(const LinphoneProxyConfig *obj){ + return obj->reg_identity; +} + +/** + * Returns TRUE if PUBLISH request is enabled for this proxy. +**/ +bool_t linphone_proxy_config_publish_enabled(const LinphoneProxyConfig *obj){ + return obj->publish; +} + +/** + * Returns the proxy's SIP address. +**/ +const char *linphone_proxy_config_get_addr(const LinphoneProxyConfig *obj){ + return obj->reg_proxy; +} + +/** + * Returns the duration of registration. +**/ +int linphone_proxy_config_get_expires(const LinphoneProxyConfig *obj){ + return obj->expires; +} + +/** + * Returns TRUE if registration to the proxy is enabled. +**/ +bool_t linphone_proxy_config_register_enabled(const LinphoneProxyConfig *obj){ + return obj->reg_sendregister; +} + +struct _LinphoneCore * linphone_proxy_config_get_core(const LinphoneProxyConfig *obj){ + return obj->lc; +} /** * Add a proxy configuration. @@ -657,13 +451,15 @@ entity=\"%s\">\n%s", **/ int linphone_core_add_proxy_config(LinphoneCore *lc, LinphoneProxyConfig *cfg){ if (!linphone_proxy_config_check(lc,cfg)) return -1; + if (ms_list_find(lc->sip_conf.proxies,cfg)!=NULL){ + ms_warning("ProxyConfig already entered, ignored."); + return 0; + } lc->sip_conf.proxies=ms_list_append(lc->sip_conf.proxies,(void *)cfg); linphone_proxy_config_apply(cfg,lc); return 0; } -extern void linphone_friend_check_for_removed_proxy(LinphoneFriend *lf, LinphoneProxyConfig *cfg); - /** * Removes a proxy configuration. * @@ -671,7 +467,6 @@ extern void linphone_friend_check_for_removed_proxy(LinphoneFriend *lf, Linphone * on a deleted list. For that reason, a removed proxy does NOT need to be freed. **/ void linphone_core_remove_proxy_config(LinphoneCore *lc, LinphoneProxyConfig *cfg){ - MSList *elem; lc->sip_conf.proxies=ms_list_remove(lc->sip_conf.proxies,(void *)cfg); /* add to the list of destroyed proxies, so that the possible unREGISTER request can succeed authentication */ lc->sip_conf.deleted_proxies=ms_list_append(lc->sip_conf.deleted_proxies,(void *)cfg); @@ -680,11 +475,6 @@ void linphone_core_remove_proxy_config(LinphoneCore *lc, LinphoneProxyConfig *cf if (lc->default_proxy==cfg){ lc->default_proxy=NULL; } - /* invalidate all references to this proxy in our friend list */ - for (elem=lc->friends;elem!=NULL;elem=ms_list_next(elem)){ - linphone_friend_check_for_removed_proxy((LinphoneFriend*)elem->data,cfg); - } - } /** * Erase all proxies from config. @@ -735,23 +525,6 @@ int linphone_core_get_default_proxy(LinphoneCore *lc, LinphoneProxyConfig **conf return pos; } -static int rid_compare(const void *pcfg,const void *prid){ - const LinphoneProxyConfig *cfg=(const LinphoneProxyConfig*)pcfg; - const int *rid=(const int*)prid; - ms_message("cfg= %s, cfg->rid=%i, rid=%i",cfg->reg_proxy, cfg->rid, *rid); - return cfg->rid-(*rid); -} - -LinphoneProxyConfig *linphone_core_get_proxy_config_from_rid(LinphoneCore *lc, int rid){ - MSList *elem=ms_list_find_custom(lc->sip_conf.proxies,rid_compare, &rid); - if (elem==NULL){ - ms_message("linphone_core_get_proxy_config_from_rid: searching in deleted proxies..."); - elem=ms_list_find_custom(lc->sip_conf.deleted_proxies,rid_compare, &rid); - } - if (elem==NULL) return NULL; - else return (LinphoneProxyConfig*)elem->data; -} - /** * Returns an unmodifiable list of entered proxy configurations. **/ @@ -759,47 +532,6 @@ const MSList *linphone_core_get_proxy_config_list(const LinphoneCore *lc){ return lc->sip_conf.proxies; } - -void linphone_proxy_config_process_authentication_failure(LinphoneCore *lc, int code, eXosip_event_t *ev){ - if (code==403) { - LinphoneProxyConfig *cfg=linphone_core_get_proxy_config_from_rid(lc, ev->rid); - if (cfg){ - cfg->auth_failures++; - /*restart a new register so that the user gets a chance to be prompted for a password*/ - if (cfg->auth_failures==1){ - linphone_proxy_config_register(cfg); - } - } - } else { - //unknown error (possibly timeout) - char *prx_realm=NULL,*www_realm=NULL; - osip_proxy_authenticate_t *prx_auth; - osip_www_authenticate_t *www_auth; - osip_message_t *req=ev->request; - char *username; - username=osip_uri_get_username(req->from->url); - prx_auth=(osip_proxy_authenticate_t*)osip_list_get(&req->proxy_authenticates,0); - www_auth=(osip_proxy_authenticate_t*)osip_list_get(&req->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==NULL && www_realm==NULL){ - ms_warning("No realm in the client request."); - return; - } - LinphoneAuthInfo *as=NULL; - /* see if we already have this auth information , not to ask it everytime to the user */ - if (prx_realm!=NULL) - as=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,prx_realm,username); - if (www_realm!=NULL) - as=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,www_realm,username); - - if (as) as->first_time=TRUE; - } -} - void linphone_proxy_config_write_to_config_file(LpConfig *config, LinphoneProxyConfig *obj, int index) { char key[50]; @@ -1016,5 +748,13 @@ void linphone_account_creator_destroy(LinphoneAccountCreator *obj){ } } +void linphone_proxy_config_set_user_data(LinphoneProxyConfig *cr, void * ud) { + cr->user_data=ud; +} + +void * linphone_proxy_config_get_user_data(LinphoneProxyConfig *cr) { + return cr->user_data; +} + diff --git a/coreapi/sal.c b/coreapi/sal.c new file mode 100644 index 000000000..750555016 --- /dev/null +++ b/coreapi/sal.c @@ -0,0 +1,172 @@ +/* +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. +*/ + +/** + This header files defines the Signaling Abstraction Layer. + The purpose of this layer is too allow experiment different call signaling + protocols and implementations under linphone, for example SIP, JINGLE... +**/ + +#include "sal.h" + +SalMediaDescription *sal_media_description_new(){ + SalMediaDescription *md=ms_new0(SalMediaDescription,1); + md->refcount=1; + return md; +} + +static void sal_media_description_destroy(SalMediaDescription *md){ + int i; + for(i=0;istreams[i].payloads,(void (*)(void *))payload_type_destroy); + ms_list_free(md->streams[i].payloads); + md->streams[i].payloads=NULL; + } + ms_free(md); +} + +void sal_media_description_ref(SalMediaDescription *md){ + md->refcount++; +} + +void sal_media_description_unref(SalMediaDescription *md){ + md->refcount--; + if (md->refcount==0){ + sal_media_description_destroy (md); + } +} + +const SalStreamDescription *sal_media_description_find_stream(const SalMediaDescription *md, + SalMediaProto proto, SalStreamType type){ + int i; + for(i=0;instreams;++i){ + const SalStreamDescription *ss=&md->streams[i]; + if (ss->proto==proto && ss->type==type) return ss; + } + return NULL; +} + +bool_t sal_media_description_empty(SalMediaDescription *md){ + int i; + for(i=0;instreams;++i){ + SalStreamDescription *ss=&md->streams[i]; + if (ss->port!=0) return FALSE; + } + return TRUE; +} + +static void assign_string(char **str, const char *arg){ + if (*str){ + ms_free(*str); + *str=NULL; + } + if (arg) + *str=ms_strdup(arg); +} + +void sal_op_set_contact(SalOp *op, const char *contact){ + assign_string(&((SalOpBase*)op)->contact,contact); +} + +void sal_op_set_route(SalOp *op, const char *route){ + assign_string(&((SalOpBase*)op)->route,route); +} + +void sal_op_set_from(SalOp *op, const char *from){ + assign_string(&((SalOpBase*)op)->from,from); +} + +void sal_op_set_to(SalOp *op, const char *to){ + assign_string(&((SalOpBase*)op)->to,to); +} + +void sal_op_set_user_pointer(SalOp *op, void *up){ + ((SalOpBase*)op)->user_pointer=up; +} + +Sal *sal_op_get_sal(const SalOp *op){ + return ((SalOpBase*)op)->root; +} + +const char *sal_op_get_from(const SalOp *op){ + return ((SalOpBase*)op)->from; +} + +const char *sal_op_get_to(const SalOp *op){ + return ((SalOpBase*)op)->to; +} + +const char *sal_op_get_contact(const SalOp *op){ + return ((SalOpBase*)op)->contact; +} + +const char *sal_op_get_route(const SalOp *op){ + return ((SalOpBase*)op)->route; +} + +void *sal_op_get_user_pointer(const SalOp *op){ + return ((SalOpBase*)op)->user_pointer; +} + +const char *sal_op_get_proxy(const SalOp *op){ + return ((SalOpBase*)op)->route; +} + +const char *sal_op_get_network_origin(const SalOp *op){ + return ((SalOpBase*)op)->origin; +} + +void __sal_op_init(SalOp *b, Sal *sal){ + memset(b,0,sizeof(SalOpBase)); + ((SalOpBase*)b)->root=sal; +} + +void __sal_op_set_network_origin(SalOp *op, const char *origin){ + assign_string(&((SalOpBase*)op)->origin,origin); +} + + +void __sal_op_free(SalOp *op){ + SalOpBase *b=(SalOpBase *)op; + if (b->from) { + ms_free(b->from); + b->from=NULL; + } + if (b->to) { + ms_free(b->to); + b->to=NULL; + } + if (b->route) { + ms_free(b->route); + b->route=NULL; + } + if (b->contact) { + ms_free(b->contact); + b->contact=NULL; + } + if (b->origin){ + ms_free(b->origin); + b->origin=NULL; + } + if (b->local_media) + sal_media_description_unref(b->local_media); + if (b->remote_media) + sal_media_description_unref(b->remote_media); + ms_free(op); +} diff --git a/coreapi/sal.h b/coreapi/sal.h new file mode 100644 index 000000000..e672216c9 --- /dev/null +++ b/coreapi/sal.h @@ -0,0 +1,297 @@ +/* +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. +*/ + +/** + This header files defines the Signaling Abstraction Layer. + The purpose of this layer is too allow experiment different call signaling + protocols and implementations under linphone, for example SIP, JINGLE... +**/ + +#ifndef sal_h +#define sal_h + +#include "mediastreamer2/mscommon.h" + +struct Sal; + +typedef struct Sal Sal; + +struct SalOp; + +typedef struct SalOp SalOp; + +struct SalAddress; + +typedef struct SalAddress SalAddress; + +/* Address manipulation API*/ +SalAddress * sal_address_new(const char *uri); +SalAddress * sal_address_clone(const SalAddress *addr); +const char *sal_address_get_scheme(const SalAddress *addr); +const char *sal_address_get_display_name(const SalAddress* addr); +const char *sal_address_get_username(const SalAddress *addr); +const char *sal_address_get_domain(const SalAddress *addr); +void sal_address_set_display_name(SalAddress *addr, const char *display_name); +void sal_address_set_username(SalAddress *addr, const char *username); +void sal_address_set_domain(SalAddress *addr, const char *host); +void sal_address_set_port(SalAddress *addr, const char *port); +void sal_address_set_port_int(SalAddress *uri, int port); +void sal_address_clean(SalAddress *addr); +char *sal_address_as_string(const SalAddress *u); +char *sal_address_as_string_uri_only(const SalAddress *u); +void sal_address_destroy(SalAddress *u); + + + + +Sal * sal_init(); +void sal_uninit(Sal* sal); +void sal_set_user_pointer(Sal *sal, void *user_data); +void *sal_get_user_pointer(const Sal *sal); + +typedef enum { + SalTransportDatagram, + SalTransportStream +}SalTransport; + +typedef enum { + SalAudio, + SalVideo, + SalOther +} SalStreamType; + +typedef enum{ + SalProtoUnknown, + SalProtoRtpAvp, + SalProtoRtpSavp +}SalMediaProto; + +typedef struct SalEndpointCandidate{ + char addr[64]; + int port; +}SalEndpointCandidate; + +#define SAL_ENDPOINT_CANDIDATE_MAX 2 + +typedef struct SalStreamDescription{ + SalMediaProto proto; + SalStreamType type; + char addr[64]; + int port; + MSList *payloads; //user_data=(void*)((long)n); +#define payload_type_get_number(pt) ((int)(long)(pt)->user_data) + +/*misc*/ +void sal_get_default_local_ip(Sal *sal, int address_family, char *ip, size_t iplen); + + +/*internal API */ +void __sal_op_init(SalOp *b, Sal *sal); +void __sal_op_set_network_origin(SalOp *op, const char *origin /*a sip uri*/); +void __sal_op_free(SalOp *b); + + +#endif diff --git a/coreapi/sal_eXosip2.c b/coreapi/sal_eXosip2.c new file mode 100644 index 000000000..d4ce598a3 --- /dev/null +++ b/coreapi/sal_eXosip2.c @@ -0,0 +1,1559 @@ +/* +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" +/*this function is not declared in some versions of eXosip*/ +extern void *eXosip_call_get_reference(int cid); + +static 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_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; +} + +static void sal_add_other(Sal *sal, SalOp *op, osip_message_t *request){ + osip_call_id_t *callid=osip_message_get_call_id(request); + if (callid==NULL) { + ms_error("There is no call id in the request !"); + return; + } + osip_call_id_clone(callid,&op->call_id); + sal->other_transactions=ms_list_append(sal->other_transactions,op); +} + +static void sal_remove_other(Sal *sal, SalOp *op){ + sal->other_transactions=ms_list_remove(sal->other_transactions,op); +} + + +static void sal_add_pending_auth(Sal *sal, SalOp *op){ + sal->pending_auths=ms_list_append(sal->pending_auths,op); +} + + +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->masquerade_via=FALSE; + return op; +} + +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); + eXosip_call_set_reference(op->cid,NULL); + } + 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); + } + __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; + if (firsttime){ + osip_trace_initialize_func (OSIP_INFO4,&_osip_trace_func); + firsttime=FALSE; + } + eXosip_init(); + return ms_new0(Sal,1); +} + +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_updated==NULL) + ctx->callbacks.call_updated=(SalOnCallUpdated)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.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_listen_port(Sal *ctx, const char *addr, int port, SalTransport tr, int is_secure){ + int err; + bool_t ipv6; + int proto=IPPROTO_UDP; + + if (ctx->running) eXosip_quit(); + eXosip_init(); + 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 (tr!=SalTransportDatagram || is_secure){ + ms_fatal("SIP over TCP or TLS or DTLS is not supported yet."); + return -1; + } + + err=eXosip_listen_addr(proto, addr, port, ipv6 ? PF_INET6 : PF_INET, 0); + return err; +} + +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; +} + +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; + + osip_message_get_via(msg,0,&via); + if (!via) return -1; + osip_via_param_get_byname(via,"rport",¶m); + if (param) { + rport=param->gvalue; + if (rport && rport[0]!='\0') *rportval=atoi(rport); + else *rportval=5060; + } + param=NULL; + osip_via_param_get_byname(via,"received",¶m); + if (param) *received=param->gvalue; + else 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->sdp_answer=media_description_to_sdp(h->result); + strcpy(h->result->addr,h->base.remote_media->addr); + for(i=0;iresult->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; + } + 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; + 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{ + eXosip_call_set_reference(h->cid,h); + } + return 0; +} + +int sal_call_notify_ringing(SalOp *h){ + 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); + } + }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_ping(SalOp *op, const char *from, const char *to){ + osip_message_t *options=NULL; + + sal_op_set_from(op,from); + sal_op_set_to(op,to); + eXosip_options_build_request (&options, sal_op_get_to(op), + sal_op_get_from(op),sal_op_get_route(op)); + if (options){ + if (op->base.root->session_expires!=0){ + osip_message_set_header(options, "Session-expires", "200"); + osip_message_set_supported(options, "timer"); + } + sal_add_other(sal_op_get_sal(op),op,options); + return eXosip_options_send_request(options); + } + return -1; +} + +int sal_refer(SalOp *h, const char *refer_to){ + osip_message_t *msg=NULL; + int err=0; + 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_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){ + eXosip_lock(); + eXosip_call_terminate(h->cid,h->did); + eXosip_call_set_reference(h->cid,NULL); + eXosip_unlock(); + 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 SalOp *find_op(Sal *sal, eXosip_event_t *ev){ + if (ev->cid>0){ +#ifdef HAVE_EXOSIP_GET_REF + return (SalOp*)eXosip_call_get_ref(ev->cid); +#else + return (SalOp*)eXosip_call_get_reference(ev->cid); +#endif + } + if (ev->rid>0){ + return sal_find_register(sal,ev->rid); + } + 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; + char *tmp; + sdp_message_t *sdp=eXosip_get_sdp_info(ev->request); + + set_network_origin(op,ev->request); + + if (sdp){ + op->sdp_offering=FALSE; + op->base.remote_media=sal_media_description_new(); + 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); + + op->tid=ev->tid; + op->cid=ev->cid; + op->did=ev->did; + + eXosip_call_set_reference(op->cid,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; + } + eXosip_lock(); + eXosip_call_build_answer(ev->tid,200,&msg); + eXosip_unlock(); + if (msg==NULL) return; + if (op->base.root->session_expires!=0){ + if (op->supports_session_timers) osip_message_set_supported(msg, "timer"); + } + if (op->base.contact){ + _osip_list_set_empty(&msg->contacts,(void (*)(void*))osip_contact_free); + osip_message_set_contact(msg,op->base.contact); + } + 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); + sdp_process(op); + set_sdp(msg,op->sdp_answer); + }else { + op->sdp_offering=TRUE; + set_sdp_from_desc(msg,op->base.local_media); + } + eXosip_lock(); + 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){ + sal->callbacks.call_updated(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); + 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; + } + 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; + if (call_proceeding(sal, ev)==-1) return; + op=(SalOp*)ev->external_reference; + 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; + } + 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); + eXosip_call_send_ack(ev->did,msg); + sal->callbacks.call_accepted(op); +} + +static void call_terminated(Sal *sal, eXosip_event_t *ev){ + char *from; + SalOp *op=find_op(sal,ev); + if (op==NULL){ + ms_warning("Call terminated for already closed call ?"); + return; + } + osip_from_to_str(ev->request->from,&from); + eXosip_call_set_reference(ev->cid,NULL); + op->cid=-1; + sal->callbacks.call_terminated(op,from); + osip_free(from); +} + +static void call_released(Sal *sal, eXosip_event_t *ev){ + SalOp *op=find_op(sal,ev); + if (op==NULL){ + return; + } + op->cid=-1; + if (op->did==-1) + sal->callbacks.call_failure(op,SalErrorNoResponse,SalReasonUnknown,NULL); +} + +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; + const char *reason=NULL; + SalError error=SalErrorUnknown; + SalReason sr=SalReasonUnknown; + + op=(SalOp*)ev->external_reference; + + 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); + } + 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); + return TRUE; +} + + +static void process_media_control_xml(Sal *sal, eXosip_event_t *ev){ + SalOp *op=(SalOp*)ev->external_reference; + 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); + } + } +} + +static void process_dtmf_relay(Sal *sal, eXosip_event_t *ev){ + SalOp *op=(SalOp*)ev->external_reference; + 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 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 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",¶m); + 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); + osip_message_set_allow(options,"INVITE, ACK, BYE, CANCEL, OPTIONS, MESSAGE, SUBSCRIBE, NOTIFY, INFO"); + osip_message_set_accept(options,"application/sdp"); + 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)) { + osip_header_t *h=NULL; + osip_message_header_get_byname(ev->request,"Refer-To",0,&h); + eXosip_message_send_answer(ev->tid,200,NULL); + if (h){ + sal->callbacks.refer_received(sal,NULL,h->hvalue); + } + }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 registered=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); + }else return TRUE; + 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 (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); + sal->callbacks.ping_reply(op); + } +} + +static bool_t process_event(Sal *sal, eXosip_event_t *ev){ + 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_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_CALL_RELEASED: + ms_message("CALL_RELEASED\n"); + call_released(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 && (ev->response->status_code == 407 || ev->response->status_code == 401)){ + return process_authentication(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; + } + 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) + 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_to_str((osip_from_t*)u,&tmp); + 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_destroy(SalAddress *u){ + osip_from_free((osip_from_t*)u); +} + diff --git a/coreapi/sal_eXosip2.h b/coreapi/sal_eXosip2.h new file mode 100644 index 000000000..8c4bd2438 --- /dev/null +++ b/coreapi/sal_eXosip2.h @@ -0,0 +1,76 @@ +/* +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. +*/ + +#ifndef sal_exosip2_h +#define sal_exosip2_h + +#include "sal.h" +#include + + + +sdp_message_t *media_description_to_sdp(const SalMediaDescription *sal); +int sdp_to_media_description(sdp_message_t *sdp, SalMediaDescription *desc); + +struct Sal{ + SalCallbacks callbacks; + MSList *registers;/*MSList of SalOp */ + MSList *out_subscribes;/*MSList of SalOp */ + MSList *in_subscribes;/*MSList of SalOp */ + MSList *pending_auths;/*MSList of SalOp */ + MSList *other_transactions; /*MSList of SalOp */ + int running; + int session_expires; + void *up; +}; + +struct SalOp{ + SalOpBase base; + int cid; + int did; + int tid; + int rid; + int sid; + int nid; + int expires; + SalMediaDescription *result; + sdp_message_t *sdp_answer; + eXosip_event_t *pending_auth; + osip_call_id_t *call_id; /*used for out of calls transaction in order + to retrieve the operation when receiving a response*/ + bool_t supports_session_timers; + bool_t sdp_offering; + bool_t reinvite; + bool_t masquerade_via; +}; + +void sal_remove_out_subscribe(Sal *sal, SalOp *op); +void sal_remove_in_subscribe(Sal *sal, SalOp *op); + +void sal_exosip_subscription_recv(Sal *sal, eXosip_event_t *ev); +void sal_exosip_subscription_answered(Sal *sal,eXosip_event_t *ev); +void sal_exosip_notify_recv(Sal *sal,eXosip_event_t *ev); +void sal_exosip_subscription_closed(Sal *sal,eXosip_event_t *ev); + +void sal_exosip_in_subscription_closed(Sal *sal, eXosip_event_t *ev); + +void sal_exosip_fix_route(SalOp *op); + + +#endif diff --git a/coreapi/sal_eXosip2_presence.c b/coreapi/sal_eXosip2_presence.c new file mode 100644 index 000000000..9d5641024 --- /dev/null +++ b/coreapi/sal_eXosip2_presence.c @@ -0,0 +1,732 @@ +/* +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. +*/ + + +#include "sal_eXosip2.h" + + +static SalOp * sal_find_out_subscribe(Sal *sal, int sid){ + const MSList *elem; + SalOp *op; + for(elem=sal->out_subscribes;elem!=NULL;elem=elem->next){ + op=(SalOp*)elem->data; + if (op->sid==sid) return op; + } + return NULL; +} + +static void sal_add_out_subscribe(Sal *sal, SalOp *op){ + sal->out_subscribes=ms_list_append(sal->out_subscribes,op); +} + +void sal_remove_out_subscribe(Sal *sal, SalOp *op){ + sal->out_subscribes=ms_list_remove(sal->out_subscribes,op); +} + +static SalOp * sal_find_in_subscribe(Sal *sal, int nid){ + const MSList *elem; + SalOp *op; + for(elem=sal->in_subscribes;elem!=NULL;elem=elem->next){ + op=(SalOp*)elem->data; + if (op->nid==nid) return op; + } + return NULL; +} + +static SalOp * sal_find_in_subscribe_by_call_id(Sal *sal, osip_call_id_t *call_id){ + const MSList *elem; + SalOp *op; + for(elem=sal->in_subscribes;elem!=NULL;elem=elem->next){ + op=(SalOp*)elem->data; + if (op->call_id && osip_call_id_match(op->call_id,call_id)==0) + return op; + } + return NULL; +} + +static void sal_add_in_subscribe(Sal *sal, SalOp *op, osip_message_t *subs){ + osip_call_id_clone(subs->call_id,&op->call_id); + sal->in_subscribes=ms_list_append(sal->in_subscribes,op); +} + +void sal_remove_in_subscribe(Sal *sal, SalOp *op){ + sal->in_subscribes=ms_list_remove(sal->in_subscribes,op); +} + +int sal_text_send(SalOp *op, const char *from, const char *to, const char *msg){ + osip_message_t *sip=NULL; + if (from) + sal_op_set_from(op,from); + if (to) + sal_op_set_to(op,to); + + eXosip_lock(); + eXosip_message_build_request(&sip,"MESSAGE",sal_op_get_to(op), + sal_op_get_from(op),sal_op_get_route(op)); + osip_message_set_content_type(sip,"text/plain"); + osip_message_set_body(sip,msg,strlen(msg)); + eXosip_message_send_request(sip); + eXosip_unlock(); + return 0; +} + +/*presence Subscribe/notify*/ +int sal_subscribe_presence(SalOp *op, const char *from, const char *to){ + osip_message_t *msg; + if (from) + sal_op_set_from(op,from); + if (to) + sal_op_set_to(op,to); + sal_exosip_fix_route(op); + eXosip_lock(); + eXosip_subscribe_build_initial_request(&msg,sal_op_get_to(op),sal_op_get_from(op), + sal_op_get_route(op),"presence",600); + op->sid=eXosip_subscribe_send_initial_request(msg); + eXosip_unlock(); + if (op->sid==-1){ + osip_message_free(msg); + return -1; + } + sal_add_out_subscribe(op->base.root,op); + return 0; +} + +int sal_unsubscribe(SalOp *op){ + osip_message_t *msg=NULL; + if (op->did==-1){ + ms_error("cannot unsubscribe, no dialog !"); + return -1; + } + eXosip_lock(); + eXosip_subscribe_build_refresh_request(op->did,&msg); + if (msg){ + osip_message_set_expires(msg,"0"); + eXosip_subscribe_send_refresh_request(op->did,msg); + }else ms_error("Could not build subscribe refresh request ! op->sid=%i, op->did=%i", + op->sid,op->did); + eXosip_unlock(); + return 0; +} + +int sal_subscribe_accept(SalOp *op){ + osip_message_t *msg; + eXosip_lock(); + eXosip_insubscription_build_answer(op->tid,202,&msg); + eXosip_insubscription_send_answer(op->tid,202,msg); + eXosip_unlock(); + return 0; +} + +int sal_subscribe_decline(SalOp *op){ + eXosip_lock(); + eXosip_insubscription_send_answer(op->tid,401,NULL); + eXosip_unlock(); + return 0; +} + +static void add_presence_body(osip_message_t *notify, SalPresenceStatus online_status) +{ + char buf[1000]; +#ifdef SUPPORT_MSN + int atom_id = 1000; +#endif + char *contact_info; + + osip_contact_t *ct=NULL; + osip_message_get_contact(notify,0,&ct); + osip_contact_to_str(ct,&contact_info); + +#ifdef SUPPORT_MSN + + if (online_status==SalPresenceOnline) + { + sprintf(buf, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status==SalPresenceBusy) + { + sprintf(buf, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status==SalPresenceBerightback) + { + sprintf(buf, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status==SalPresenceAway) + { + sprintf(buf, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status==SalPresenceOnthephone) + { + sprintf(buf, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else if (online_status==SalPresenceOuttolunch) + { + sprintf(buf, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + + } + else + { + sprintf(buf, "\n\ +\n\ +\n\ +\n\ +\n\ +
\n\ +\n\ +\n\ +
\n\ +
\n\ +
", contact_info, atom_id, contact_info); + } + + osip_message_set_body(notify, buf, strlen(buf)); + osip_message_set_content_type(notify, "application/xpidf+xml"); +#else + + if (online_status==SalPresenceOnline) + { + sprintf(buf, "\n\ +\n\ +\n\ +\n\ +open\n\ +\n\ +%s\n\ +online\n\ +\n\ +", + contact_info, contact_info); + } + else if (online_status==SalPresenceBusy) + { + sprintf(buf, "\n\ +\n\ +\n\ +\n\ +open\n\ +\n\ + busy\n\ +\n\ +\n\ +%s\n\ +busy\n\ +\n\ +", + contact_info, contact_info); + } + else if (online_status==SalPresenceBerightback) + { + sprintf(buf, "\n\ +\n\ +\n\ +\n\ +open\n\ +\n\ + in-transit\n\ +\n\ +\n\ +%s\n\ +be right back\n\ +\n\ +", + contact_info, contact_info); + } + else if (online_status==SalPresenceAway) + { + sprintf(buf, "\n\ +\n\ +\n\ +\n\ +open\n\ +\n\ + away\n\ +\n\ +\n\ +%s\n\ +away\n\ +\n\ +", + contact_info, contact_info); + } + else if (online_status==SalPresenceOnthephone) + { + sprintf(buf, "\n\ +\n\ +\n\ +\n\ +open\n\ +\n\ + on-the-phone\n\ +\n\ +\n\ +%s\n\ +on the phone\n\ +\n\ +", + contact_info, contact_info); + } + else if (online_status==SalPresenceOuttolunch) + { + sprintf(buf, "\n\ +\n\ +\n\ +\n\ +open\n\ +\n\ + meal\n\ +\n\ +\n\ +%s\n\ +out to lunch\n\ +\n\ +", + contact_info, contact_info); + } + else + { + /* */ + sprintf(buf, "\n\ +\n%s", + contact_info, +"\n\ +\n\ +closed\n\ +\n\ + permanent-absence\n\ +\n\ +\n\ +\n\ +\n\n"); + } + osip_message_set_body(notify, buf, strlen(buf)); + osip_message_set_content_type(notify, "application/pidf+xml"); + +#endif + osip_free(contact_info); +} + + +int sal_notify_presence(SalOp *op, SalPresenceStatus status, const char *status_message){ + osip_message_t *msg; + eXosip_ss_t ss=EXOSIP_SUBCRSTATE_ACTIVE; + if (op->nid==-1){ + ms_warning("Cannot notify, subscription was closed."); + return -1; + } + + eXosip_lock(); + eXosip_insubscription_build_notify(op->did,ss,DEACTIVATED,&msg); + if (msg!=NULL){ + const char *identity=sal_op_get_contact(op); + if (identity==NULL) identity=sal_op_get_to(op); + osip_message_set_contact(msg,identity); + add_presence_body(msg,status); + eXosip_insubscription_send_request(op->did,msg); + }else ms_error("could not create notify for incoming subscription."); + eXosip_unlock(); + return 0; +} + +int sal_notify_close(SalOp *op){ + osip_message_t *msg=NULL; + eXosip_lock(); + eXosip_insubscription_build_notify(op->did,EXOSIP_SUBCRSTATE_TERMINATED,DEACTIVATED,&msg); + if (msg!=NULL){ + const char *identity=sal_op_get_contact(op); + if (identity==NULL) identity=sal_op_get_to(op); + osip_message_set_contact(msg,identity); + add_presence_body(msg,SalPresenceOffline); + eXosip_insubscription_send_request(op->did,msg); + }else ms_error("sal_notify_close(): could not create notify for incoming subscription" + " did=%i, nid=%i",op->did,op->nid); + eXosip_unlock(); + return 0; +} + +int sal_publish(SalOp *op, const char *from, const char *to, SalPresenceStatus presence_mode){ + osip_message_t *pub; + int i; + char buf[1024]; + + if (presence_mode==SalPresenceOnline) + { + snprintf(buf, sizeof(buf), "\n\ + \n\ + \n\ + \n\ + open\n\ + \n\ + %s\n\ + online\n\ + \n\ + ", + from, from); + } + else if (presence_mode==SalPresenceBusy + ||presence_mode==SalPresenceDonotdisturb) + { + snprintf(buf, sizeof(buf), "\n\ + \n\ + \n\ + \n\ + open\n\ + \n\ + busy\n\ + \n\ + \n\ + %s\n\ + busy\n\ + \n\ + ", + from, from); + } + else if (presence_mode==SalPresenceBerightback) + { + snprintf(buf, sizeof(buf), "\n\ + \n\ + \n\ + \n\ + open\n\ + \n\ + in-transit\n\ + \n\ + \n\ + %s\n\ + be right back\n\ + \n\ + ", + from,from); + } + else if (presence_mode==SalPresenceAway + ||presence_mode==SalPresenceMoved) + { + snprintf(buf, sizeof(buf), "\n\ + \n\ + \n\ + \n\ + open\n\ + \n\ + away\n\ + \n\ + \n\ + %s\n\ + away\n\ + \n\ + ", + from, from); + } + else if (presence_mode==SalPresenceOnthephone) + { + snprintf(buf, sizeof(buf), "\n\ + \n\ + \n\ + \n\ + open\n\ + \n\ + on-the-phone\n\ + \n\ + \n\ + %s\n\ + on the phone\n\ + \n\ + ", + from, from); + } + else if (presence_mode==SalPresenceOuttolunch) + { + snprintf(buf, sizeof(buf), "\n\ + \n\ + \n\ + \n\ + open\n\ + \n\ + meal\n\ + \n\ + \n\ + %s\n\ + out to lunch\n\ + \n\ + ", + from, from); + } + else{ + /* offline */ + snprintf(buf, sizeof(buf), "\n\ + \n%s", + from, + "\n\ + \n\ + closed\n\ + \n\ + permanent-absence\n\ + \n\ + \n\ + \n\ + \n\n"); + } + + i = eXosip_build_publish(&pub,from, to, NULL, "presence", "1800", "application/pidf+xml", buf); + if (i<0){ + ms_warning("Failed to build publish request."); + return -1; + } + + eXosip_lock(); + i = eXosip_publish(pub, to); /* should update the sip-if-match parameter + from sip-etag from last 200ok of PUBLISH */ + eXosip_unlock(); + if (i<0){ + ms_message("Failed to send publish request."); + return -1; + } + return 0; +} + +static void _sal_exosip_subscription_recv(Sal *sal, eXosip_event_t *ev){ + SalOp *op=sal_op_new(sal); + char *tmp; + op->did=ev->did; + op->tid=ev->tid; + op->nid=ev->nid; + osip_from_to_str(ev->request->from,&tmp); + sal_op_set_from(op,tmp); + ms_free(tmp); + osip_from_to_str(ev->request->to,&tmp); + sal_op_set_to(op,tmp); + ms_free(tmp); + sal_add_in_subscribe(sal,op,ev->request); + sal->callbacks.subscribe_received(op,sal_op_get_from(op)); +} + +void sal_exosip_subscription_recv(Sal *sal, eXosip_event_t *ev){ + /*workaround a bug in eXosip: incoming SUBSCRIBES within dialog with expires: 0 are + recognized as new incoming subscribes*/ + SalOp *op=sal_find_in_subscribe_by_call_id(sal,ev->request->call_id); + if (op){ + osip_header_t *h; + osip_message_header_get_byname(ev->request,"expires",0,&h); + if (h && h->hvalue && atoi(h->hvalue)==0){ + ms_warning("This susbscribe is not a new one but terminates an old one."); + ev->did=op->did; + ev->nid=op->nid; + sal_exosip_subscription_closed(sal,ev); + }else { + osip_message_t *msg=NULL; + ms_warning("Probably a refresh subscribe"); + eXosip_lock(); + eXosip_insubscription_build_answer(ev->tid,202,&msg); + eXosip_insubscription_send_answer(ev->tid,202,msg); + eXosip_unlock(); + } + }else _sal_exosip_subscription_recv(sal,ev); +} + +void sal_exosip_notify_recv(Sal *sal, eXosip_event_t *ev){ + SalOp *op=sal_find_out_subscribe(sal,ev->sid); + char *tmp; + osip_from_t *from=NULL; + osip_body_t *body=NULL; + SalPresenceStatus estatus=SalPresenceOffline; + + ms_message("Receiving notify with sid=%i,nid=%i",ev->sid,ev->nid); + + if (op==NULL){ + ms_error("No operation related to this notify !"); + return; + } + if (ev->request==NULL) return; + + from=ev->request->from; + osip_message_get_body(ev->request,0,&body); + if (body==NULL){ + ms_error("No body in NOTIFY"); + return; + } + osip_from_to_str(from,&tmp); + if (strstr(body->body,"pending")!=NULL){ + estatus=SalPresenceOffline; + }else if (strstr(body->body,"busy")!=NULL){ + estatus=SalPresenceBusy; + }else if (strstr(body->body,"berightback")!=NULL + || strstr(body->body,"in-transit")!=NULL ){ + estatus=SalPresenceBerightback; + }else if (strstr(body->body,"away")!=NULL){ + estatus=SalPresenceAway; + }else if (strstr(body->body,"onthephone")!=NULL + || strstr(body->body,"on-the-phone")!=NULL){ + estatus=SalPresenceOnthephone; + }else if (strstr(body->body,"outtolunch")!=NULL + || strstr(body->body,"meal")!=NULL){ + estatus=SalPresenceOuttolunch; + }else if (strstr(body->body,"closed")!=NULL){ + estatus=SalPresenceOffline; + }else if ((strstr(body->body,"online")!=NULL) || (strstr(body->body,"open")!=NULL)) { + estatus=SalPresenceOnline; + }else{ + estatus=SalPresenceOffline; + } + ms_message("We are notified that %s has online status %i",tmp,estatus); + if (ev->ss_status==EXOSIP_SUBCRSTATE_TERMINATED) { + sal_remove_out_subscribe(sal,op); + op->sid=-1; + op->did=-1; + ms_message("And outgoing subscription terminated by remote."); + } + sal->callbacks.notify(op,op->sid!=-1 ? SalSubscribeActive : SalSubscribeTerminated, estatus,NULL); + osip_free(tmp); +} + +void sal_exosip_subscription_answered(Sal *sal,eXosip_event_t *ev){ + SalOp *op=sal_find_out_subscribe(sal,ev->sid); + if (op==NULL){ + ms_error("Subscription answered but no associated op !"); + return; + } + op->did=ev->did; +} + +void sal_exosip_in_subscription_closed(Sal *sal, eXosip_event_t *ev){ + SalOp *op=sal_find_in_subscribe(sal,ev->nid); + char *tmp; + if (op==NULL){ + ms_error("Incoming subscription closed but no associated op !"); + return; + } + + + sal_remove_in_subscribe(sal,op); + op->nid=-1; + op->did=-1; + if (ev->request){ + osip_from_to_str(ev->request->from,&tmp); + sal->callbacks.subscribe_closed(op,tmp); + osip_free(tmp); + } +} + +void sal_exosip_subscription_closed(Sal *sal,eXosip_event_t *ev){ + SalOp *op=sal_find_out_subscribe(sal,ev->sid); + if (op==NULL){ + ms_error("Subscription closed but no associated op !"); + return; + } + sal_remove_out_subscribe(sal,op); + op->sid=-1; + op->did=-1; + sal->callbacks.notify(op,SalSubscribeTerminated, SalPresenceOffline,NULL); +} + + diff --git a/coreapi/sal_eXosip2_sdp.c b/coreapi/sal_eXosip2_sdp.c new file mode 100644 index 000000000..580428765 --- /dev/null +++ b/coreapi/sal_eXosip2_sdp.c @@ -0,0 +1,288 @@ +/* +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. +*/ + + +#include "ortp/b64.h" +#include "sal.h" +#include + +#define keywordcmp(key,b) strncmp(key,b,sizeof(key)) + +#ifdef FOR_LATER + +static char *make_relay_session_id(const char *username, const char *relay){ + /*ideally this should be a hash of the parameters with a random part*/ + char tmp[128]; + int s1=(int)random(); + int s2=(int)random(); + long long int res=((long long int)s1)<<32 | (long long int) s2; + void *src=&res; + b64_encode(src, sizeof(long long int), tmp, sizeof(tmp)); + return osip_strdup(tmp); +} + + +static void add_relay_info(sdp_message_t *sdp, int mline, const char *relay, const char *relay_session_id){ + + if (relay) sdp_message_a_attribute_add(sdp, mline, + osip_strdup ("relay-addr"),osip_strdup(relay)); + if (relay_session_id) sdp_message_a_attribute_add(sdp, mline, + osip_strdup ("relay-session-id"), osip_strdup(relay_session_id)); +} + +#endif + +static char * int_2char(int a){ + char *p=osip_malloc(16); + snprintf(p,16,"%i",a); + return p; +} + +/* return the value of attr "field" for payload pt at line pos (field=rtpmap,fmtp...)*/ +static const char *sdp_message_a_attr_value_get_with_pt(sdp_message_t *sdp,int pos,int pt,const char *field) +{ + int i,tmppt=0,scanned=0; + char *tmp; + sdp_attribute_t *attr; + for (i=0;(attr=sdp_message_attribute_get(sdp,pos,i))!=NULL;i++){ + if (keywordcmp(field,attr->a_att_field)==0 && attr->a_att_value!=NULL){ + int nb = sscanf(attr->a_att_value,"%i %n",&tmppt,&scanned); + /* the return value may depend on how %n is interpreted by the libc: see manpage*/ + if (nb == 1 || nb==2 ){ + if (pt==tmppt){ + tmp=attr->a_att_value+scanned; + if (strlen(tmp)>0) + return tmp; + } + }else ms_warning("sdp has a strange a= line (%s) nb=%i",attr->a_att_value,nb); + } + } + return NULL; +} + +#ifdef FOR_LATER +/* return the value of attr "field" */ +static const char *sdp_message_a_attr_value_get(sdp_message_t *sdp,int pos,const char *field) +{ + int i; + sdp_attribute_t *attr; + for (i=0;(attr=sdp_message_attribute_get(sdp,pos,i))!=NULL;i++){ + if (keywordcmp(field,attr->a_att_field)==0 && attr->a_att_value!=NULL){ + return attr->a_att_value; + } + } + return NULL; +} +#endif + +static int _sdp_message_get_a_ptime(sdp_message_t *sdp, int mline){ + int i,ret; + sdp_attribute_t *attr; + for (i=0;(attr=sdp_message_attribute_get(sdp,mline,i))!=NULL;i++){ + if (keywordcmp("ptime",attr->a_att_field)==0){ + int nb = sscanf(attr->a_att_value,"%i",&ret); + /* the return value may depend on how %n is interpreted by the libc: see manpage*/ + if (nb == 1){ + return ret; + }else ms_warning("sdp has a strange a=ptime line (%s) ",attr->a_att_value); + } + } + return 0; +} + +static sdp_message_t *create_generic_sdp(const SalMediaDescription *desc) +{ + sdp_message_t *local; + int inet6; + + sdp_message_init (&local); + if (strchr(desc->addr,':')!=NULL){ + inet6=1; + }else inet6=0; + sdp_message_v_version_set (local, osip_strdup ("0")); + sdp_message_o_origin_set (local, osip_strdup (desc->username), + osip_strdup ("123456"), osip_strdup ("654321"), + osip_strdup ("IN"), inet6 ? osip_strdup("IP6") : osip_strdup ("IP4"), + osip_strdup (desc->addr)); + sdp_message_s_name_set (local, osip_strdup ("A conversation")); + sdp_message_c_connection_add (local, -1, + osip_strdup ("IN"), inet6 ? osip_strdup ("IP6") : osip_strdup ("IP4"), + osip_strdup (desc->addr), NULL, NULL); + sdp_message_t_time_descr_add (local, osip_strdup ("0"), osip_strdup ("0")); + return local; +} + + + +static void add_payload(sdp_message_t *msg, int line, const PayloadType *pt) +{ + char attr[256]; + sdp_message_m_payload_add (msg,line, int_2char (payload_type_get_number(pt))); + if (pt->channels>0) + snprintf (attr,sizeof(attr),"%i %s/%i/%i", payload_type_get_number(pt), + pt->mime_type, pt->clock_rate,pt->channels); + else + snprintf (attr,sizeof(attr),"%i %s/%i", payload_type_get_number(pt), + pt->mime_type, pt->clock_rate); + sdp_message_a_attribute_add (msg, line, + osip_strdup ("rtpmap"), osip_strdup(attr)); + + if (pt->recv_fmtp != NULL) + { + snprintf (attr,sizeof(attr),"%i %s", payload_type_get_number(pt),pt->recv_fmtp); + sdp_message_a_attribute_add (msg, line, osip_strdup ("fmtp"), + osip_strdup(attr)); + } +} + + +static void add_line(sdp_message_t *msg, int lineno, const SalStreamDescription *desc){ + const char *mt=desc->type==SalAudio ? "audio" : "video"; + const MSList *elem; + const char *addr; + int port; + if (desc->candidates[0].addr[0]!='\0'){ + addr=desc->candidates[0].addr; + port=desc->candidates[0].port; + }else{ + addr=desc->addr; + port=desc->port; + } + /*only add a c= line within the stream description if address are differents*/ + if (strcmp(addr,sdp_message_c_addr_get(msg, -1, 0))!=0){ + bool_t inet6; + if (strchr(addr,':')!=NULL){ + inet6=TRUE; + }else inet6=FALSE; + sdp_message_c_connection_add (msg, lineno, + osip_strdup ("IN"), inet6 ? osip_strdup ("IP6") : osip_strdup ("IP4"), + osip_strdup (addr), NULL, NULL); + } + sdp_message_m_media_add (msg, osip_strdup (mt), + int_2char (port), NULL, + osip_strdup ("RTP/AVP")); + if (desc->bandwidth>0) sdp_message_b_bandwidth_add (msg, lineno, osip_strdup ("AS"), + int_2char(desc->bandwidth)); + if (desc->ptime>0) sdp_message_a_attribute_add(msg,lineno,osip_strdup("ptime"), + int_2char(desc->ptime)); + for(elem=desc->payloads;elem!=NULL;elem=elem->next){ + add_payload(msg, lineno, (PayloadType*)elem->data); + } +} + +sdp_message_t *media_description_to_sdp(const SalMediaDescription *desc){ + int i; + sdp_message_t *msg=create_generic_sdp(desc); + for(i=0;instreams;++i){ + add_line(msg,i,&desc->streams[i]); + } + return msg; +} + +static int payload_type_fill_from_rtpmap(PayloadType *pt, const char *rtpmap){ + if (rtpmap==NULL){ + PayloadType *refpt=rtp_profile_get_payload(&av_profile,payload_type_get_number(pt)); + if (refpt){ + pt->mime_type=ms_strdup(refpt->mime_type); + pt->clock_rate=refpt->clock_rate; + }else{ + ms_error("payload number %i has no rtpmap and is unknown in AV Profile, ignored.", + payload_type_get_number(pt)); + return -1; + } + }else{ + char *mime=ms_strdup(rtpmap); + char *p=strchr(mime,'/'); + if (p){ + char *chans; + *p='\0'; + p++; + chans=strchr(p,'/'); + if (chans){ + *chans='\0'; + chans++; + pt->channels=atoi(chans); + }else pt->channels=1; + pt->clock_rate=atoi(p); + } + pt->mime_type=mime; + } + return 0; +} + +int sdp_to_media_description(sdp_message_t *msg, SalMediaDescription *desc){ + int i,j; + const char *mtype,*proto,*port,*addr,*number; + sdp_bandwidth_t *sbw=NULL; + + addr=sdp_message_c_addr_get (msg, -1, 0); + if (addr) + strncpy(desc->addr,addr,sizeof(desc->addr)); + /* for each m= line */ + for (i=0; !sdp_message_endof_media (msg, i) && istreams[i]; + + memset(stream,0,sizeof(*stream)); + mtype = sdp_message_m_media_get(msg, i); + proto = sdp_message_m_proto_get (msg, i); + port = sdp_message_m_port_get(msg, i); + stream->proto=SalProtoUnknown; + if (proto){ + if (strcasecmp(proto,"RTP/AVP")==0) + stream->proto=SalProtoRtpAvp; + else if (strcasecmp(proto,"RTP/SAVP")==0){ + stream->proto=SalProtoRtpSavp; + } + } + addr = sdp_message_c_addr_get (msg, i, 0); + if (addr != NULL) + strncpy(stream->addr,addr,sizeof(stream->addr)); + if (port) + stream->port=atoi(port); + + stream->ptime=_sdp_message_get_a_ptime(msg,i); + if (strcasecmp("audio", mtype) == 0){ + stream->type=SalAudio; + }else if (strcasecmp("video", mtype) == 0){ + stream->type=SalVideo; + }else stream->type=SalOther; + for(j=0;(sbw=sdp_message_bandwidth_get(msg,i,j))!=NULL;++j){ + if (strcasecmp(sbw->b_bwtype,"AS")==0) stream->bandwidth=atoi(sbw->b_bandwidth); + } + /* for each payload type */ + for (j=0;((number=sdp_message_m_payload_get (msg, i,j)) != NULL); j++){ + const char *rtpmap,*fmtp; + int ptn=atoi(number); + PayloadType *pt=payload_type_new(); + payload_type_set_number(pt,ptn); + /* get the rtpmap associated to this codec, if any */ + rtpmap=sdp_message_a_attr_value_get_with_pt(msg, i,ptn,"rtpmap"); + payload_type_fill_from_rtpmap(pt,rtpmap); + /* get the fmtp, if any */ + fmtp=sdp_message_a_attr_value_get_with_pt(msg, i, ptn,"fmtp"); + payload_type_set_send_fmtp(pt,fmtp); + stream->payloads=ms_list_append(stream->payloads,pt); + ms_message("Found payload %s/%i fmtp=%s",pt->mime_type,pt->clock_rate, + pt->send_fmtp ? pt->send_fmtp : ""); + } + } + desc->nstreams=i; + return 0; +} diff --git a/coreapi/siplogin.c b/coreapi/siplogin.c index 528e3f328..0037fa1d6 100644 --- a/coreapi/siplogin.c +++ b/coreapi/siplogin.c @@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #endif #include "linphonecore.h" +#include "private.h" #include static void sip_login_init_instance(SipSetupContext *ctx){ diff --git a/gtk-glade/friendlist.c b/gtk-glade/friendlist.c index 035a1079d..25173488d 100644 --- a/gtk-glade/friendlist.c +++ b/gtk-glade/friendlist.c @@ -39,7 +39,6 @@ typedef struct _status_picture_tab_t{ } status_picture_tab_t; status_picture_tab_t status_picture_tab[]={ - { LINPHONE_STATUS_UNKNOWN, "sip-closed.png" }, { LINPHONE_STATUS_ONLINE, "sip-online.png" }, { LINPHONE_STATUS_BUSY, "sip-busy.png" }, { LINPHONE_STATUS_BERIGHTBACK, "sip-bifm.png" }, @@ -51,7 +50,6 @@ status_picture_tab_t status_picture_tab[]={ { LINPHONE_STATUS_ALT_SERVICE, "sip-closed.png" }, { LINPHONE_STATUS_OFFLINE, "sip-away.png" }, { LINPHONE_STATUS_PENDING, "sip-wfa.png" }, - { LINPHONE_STATUS_CLOSED, "sip-closed.png" }, { LINPHONE_STATUS_END, NULL }, }; @@ -104,7 +102,7 @@ static void linphone_gtk_set_selection_to_uri_bar(GtkTreeView *treeview){ if (gtk_tree_selection_get_selected (select, &model, &iter)) { gtk_tree_model_get (model, &iter,FRIEND_ID , &lf, -1); - friend=linphone_address_as_string(linphone_friend_get_uri(lf)); + friend=linphone_address_as_string(linphone_friend_get_address(lf)); gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(linphone_gtk_get_main_window(),"uribar")),friend); ms_free(friend); } @@ -134,8 +132,7 @@ static GtkWidget * create_presence_menu(){ GdkPixbuf *pbuf; status_picture_tab_t *t; for(t=status_picture_tab;t->img!=NULL;++t){ - if (t->status==LINPHONE_STATUS_UNKNOWN || - t->status==LINPHONE_STATUS_PENDING){ + if (t->status==LINPHONE_STATUS_PENDING){ continue; } menu_item=gtk_image_menu_item_new_with_label(linphone_online_status_to_string(t->status)); @@ -309,7 +306,7 @@ void linphone_gtk_show_friends(void){ for(itf=linphone_core_get_friend_list(core);itf!=NULL;itf=ms_list_next(itf)){ LinphoneFriend *lf=(LinphoneFriend*)itf->data; - const LinphoneAddress *f_uri=linphone_friend_get_uri(lf); + const LinphoneAddress *f_uri=linphone_friend_get_address(lf); char *uri=linphone_address_as_string(f_uri); const char *name=linphone_address_get_display_name(f_uri); const char *display=name; @@ -376,7 +373,7 @@ void linphone_gtk_show_contact(LinphoneFriend *lf){ GtkWidget *w=linphone_gtk_create_window("contact"); char *uri; const char *name; - const LinphoneAddress *f_uri=linphone_friend_get_uri(lf); + const LinphoneAddress *f_uri=linphone_friend_get_address(lf); uri=linphone_address_as_string_uri_only(f_uri); name=linphone_address_get_display_name(f_uri); if (uri) { @@ -419,7 +416,7 @@ void linphone_gtk_chat_selected(GtkWidget *item){ { char *uri; gtk_tree_model_get (model, &iter,FRIEND_ID , &lf, -1); - uri=linphone_address_as_string(linphone_friend_get_uri(lf)); + uri=linphone_address_as_string(linphone_friend_get_address(lf)); linphone_gtk_create_chatroom(uri); ms_free(uri); } diff --git a/gtk-glade/logging.c b/gtk-glade/logging.c index 5864375f2..9bdf1dc0e 100644 --- a/gtk-glade/logging.c +++ b/gtk-glade/logging.c @@ -163,7 +163,7 @@ static void linphone_gtk_log_file(OrtpLogLevel lev, const char *msg) /* lc->config will turn NULL at exit, close the file to flush and return to stop logging */ - if (lc->config == NULL) { + if (linphone_core_get_config(lc) == NULL) { linphone_gtk_log_uninit(); return; } diff --git a/gtk-glade/loginframe.c b/gtk-glade/loginframe.c index c6f310b4e..5b9d7f5c4 100644 --- a/gtk-glade/loginframe.c +++ b/gtk-glade/loginframe.c @@ -60,6 +60,7 @@ void linphone_gtk_show_login_frame(LinphoneProxyConfig *cfg){ LinphoneAddress *from; LinphoneCore *lc=linphone_gtk_get_core(); int nettype; + const char *passwd=NULL; if (linphone_core_get_download_bandwidth(lc)==512 && @@ -91,8 +92,9 @@ void linphone_gtk_show_login_frame(LinphoneProxyConfig *cfg){ if (linphone_address_get_username(from)[0]!='?') gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(mw,"login_username")), linphone_address_get_username(from)); + if (ai) passwd=linphone_auth_info_get_passwd(ai); gtk_entry_set_text(GTK_ENTRY(linphone_gtk_get_widget(mw,"login_password")), - ai!=NULL ? ai->passwd : ""); + passwd!=NULL ? passwd : ""); linphone_address_destroy(from); } diff --git a/gtk-glade/main.c b/gtk-glade/main.c index 918be5545..64927abac 100644 --- a/gtk-glade/main.c +++ b/gtk-glade/main.c @@ -42,7 +42,7 @@ static GtkWidget *the_ui=NULL; static void linphone_gtk_show(LinphoneCore *lc); static void linphone_gtk_inv_recv(LinphoneCore *lc, const char *from); static void linphone_gtk_bye_recv(LinphoneCore *lc, const char *from); -static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid, const char *url, const char *status, const char *img); +static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid); static void linphone_gtk_new_unknown_subscriber(LinphoneCore *lc, LinphoneFriend *lf, const char *url); static void linphone_gtk_auth_info_requested(LinphoneCore *lc, const char *realm, const char *username); static void linphone_gtk_display_status(LinphoneCore *lc, const char *status); @@ -208,7 +208,7 @@ static const char *linphone_gtk_get_factory_config_file(){ } static void linphone_gtk_init_liblinphone(const char *config_file, - const char *factory_config_file) { + const char *factory_config_file) { linphone_core_set_user_agent("Linphone", LINPHONE_VERSION); the_core=linphone_core_new(&vtable,config_file,factory_config_file,NULL); linphone_core_set_waiting_callback(the_core,linphone_gtk_wait,NULL); @@ -720,7 +720,8 @@ static void linphone_gtk_bye_recv(LinphoneCore *lc, const char *from){ } -static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid, const char *url, const char *status, const char *img){ +static void linphone_gtk_notify_recv(LinphoneCore *lc, LinphoneFriend * fid){ + linphone_gtk_show_friends(); } static void linphone_gtk_new_subscriber_response(GtkWidget *dialog, guint response_id, LinphoneFriend *lf){ diff --git a/gtk-glade/propertybox.c b/gtk-glade/propertybox.c index eb264bc7f..4ed3f2f0e 100644 --- a/gtk-glade/propertybox.c +++ b/gtk-glade/propertybox.c @@ -295,7 +295,7 @@ static void linphone_gtk_show_codecs(GtkTreeView *listview, const MSList *codecl gchar *color; const char *params=""; struct _PayloadType *pt=(struct _PayloadType *)elem->data; - if (payload_type_enabled(pt)) status=_("Enabled"); + if (linphone_core_payload_type_enabled(linphone_gtk_get_core(),pt)) status=_("Enabled"); else status=_("Disabled"); if (linphone_core_check_payload_type_usability(linphone_gtk_get_core(),pt)) color="blue"; else color="red"; @@ -311,7 +311,7 @@ static void linphone_gtk_show_codecs(GtkTreeView *listview, const MSList *codecl CODEC_PARAMS,params, CODEC_PRIVDATA,(gpointer)pt, CODEC_COLOR,(gpointer)color, - CODEC_INFO,(gpointer)payload_type_get_description(pt), + CODEC_INFO,(gpointer)linphone_core_get_payload_type_description(linphone_gtk_get_core(),pt), -1); } @@ -433,7 +433,7 @@ static void linphone_gtk_codec_set_enable(GtkWidget *button, gboolean enabled){ if (gtk_tree_selection_get_selected(sel,&mod,&iter)){ store=GTK_LIST_STORE(mod); gtk_tree_model_get(mod,&iter,CODEC_PRIVDATA,&pt,-1); - payload_type_set_enable(pt,enabled); + linphone_core_enable_payload_type(linphone_gtk_get_core(),pt,enabled); gtk_list_store_set(store,&iter,CODEC_STATUS, enabled ? _("Enabled") : _("Disabled"), -1); } } diff --git a/m4/exosip.m4 b/m4/exosip.m4 index d3f89ee30..ae89236c8 100644 --- a/m4/exosip.m4 +++ b/m4/exosip.m4 @@ -24,6 +24,10 @@ AC_CHECK_LIB([eXosip2],[eXosip_get_version], [AC_DEFINE([HAVE_EXOSIP_GET_VERSION],[1],[Defined when eXosip_get_version is available])], [], [-losipparser2 -losip2 ]) +AC_CHECK_LIB([eXosip2],[eXosip_call_get_reference], + [], + [AC_MSG_ERROR([Could not find eXosip_call_get_reference() in eXosip2 !])], + [-losipparser2 -losip2 ]) dnl AC_CHECK_LIB([eXosip2],[eXosip_get_naptr], dnl [AC_DEFINE([HAVE_EXOSIP_NAPTR_SUPPORT],[1],[Defined when eXosip_get_naptr is available])], dnl [],