*) AC_MSG_ERROR(bad value ${enableval} for --enable-tools) ;;
esac],[build_tools=check])
+dnl check for installed version of libupnp
+AC_ARG_ENABLE(upnp,
+ [AS_HELP_STRING([--disable-upnp], [Disable uPnP support])],
+ [case "${enableval}" in
+ yes) build_upnp=true ;;
+ no) build_upnp=false ;;
+ *) AC_MSG_ERROR(bad value ${enableval} for --disable-upnp) ;;
+ esac],[build_upnp=auto])
+
+if test "$build_upnp" != "false" ; then
+PKG_CHECK_MODULES([LIBUPNP], [libupnp], [build_upnp=true],
+ [
+ if test "$build_upnp" == "true" ; then
+ AC_MSG_ERROR([libupnp not found.])
+ else
+ build_upnp=false
+ fi
+ ])
+
+fi
+
+AM_CONDITIONAL(BUILD_UPNP, test x$build_upnp != xfalse)
+if test "$build_upnp" != "false" ; then
+ AC_DEFINE(BUILD_UPNP, 1, [Define if upnp enabled])
+fi
+
dnl check libxml2 (needed for tools)
if test "$build_tools" != "false" ; then
PKG_CHECK_MODULES(LIBXML2, [libxml-2.0],[],
{ "autoanswer", lpc_cmd_autoanswer, "Show/set auto-answer mode",
"'autoanswer' \t: show current autoanswer mode\n"
"'autoanswer enable'\t: enable autoanswer mode\n"
- "'autoanswer disable'\t: disable autoanswer mode \n"},
+ "'autoanswer disable'\t: disable autoanswer mode��\n"},
{ "proxy", lpc_cmd_proxy, "Manage proxies",
"'proxy list' : list all proxy setups.\n"
"'proxy add' : add a new proxy setup.\n"
case LinphonePolicyUseIce:
linphonec_out("Using ice with stun server %s to discover firewall address\n", setting ? setting : linphone_core_get_stun_server(lc));
break;
+ case LinphonePolicyUseUpnp:
+ linphonec_out("Using uPnP IGD protocol\n");
+ break;
}
return 1;
}
lsd.c linphonecore_utils.h \
ec-calibrator.c \
conference.c \
- linphone_tunnel.cc \
$(GITVERSION_FILE)
+
+if BUILD_UPNP
+liblinphone_la_SOURCES+=upnp.c upnp.h
+endif
if BUILD_WIZARD
liblinphone_la_SOURCES+=sipwizard.c
endif
+liblinphone_la_SOURCES+=linphone_tunnel_config.c
if BUILD_TUNNEL
-liblinphone_la_SOURCES+=TunnelManager.cc TunnelManager.hh
+liblinphone_la_SOURCES+=linphone_tunnel.cc TunnelManager.cc TunnelManager.hh
+else
+liblinphone_la_SOURCES+=linphone_tunnel_stubs.c
endif
ms_message("Defer ringing to gather ICE candidates");
return;
}
+#ifdef BUILD_UPNP
+ if ((linphone_core_get_firewall_policy(lc) == LinphonePolicyUseUpnp) && (call->upnp_session != NULL)) {
+ /* Defer ringing until the end of the ICE candidates gathering process. */
+ ms_message("Defer ringing to gather uPnP candidates");
+ return;
+ }
+#endif //BUILD_UPNP
linphone_core_notify_incoming_call(lc,call);
}
if (call->ice_session != NULL) {
linphone_core_update_ice_from_remote_media_description(call, sal_call_get_remote_media_description(op));
}
+#ifdef BUILD_UPNP
+ if (call->upnp_session != NULL) {
+ linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(op));
+ }
+#endif //BUILD_UPNP
md=sal_call_get_final_media_description(op);
call->params.has_video &= linphone_core_media_description_contains_video_stream(md);
linphone_core_update_ice_from_remote_media_description(call,rmd);
linphone_core_update_local_media_description_from_ice(call->localdesc,call->ice_session);
}
+#ifdef BUILD_UPNP
+ if(call->upnp_session != NULL) {
+ linphone_core_update_upnp_from_remote_media_description(call, rmd);
+ linphone_core_update_local_media_description_from_upnp(call->localdesc,call->upnp_session);
+ }
+#endif //BUILD_UPNP
sal_call_accept(call->op);
md=sal_call_get_final_media_description(call->op);
if (md && !sal_media_description_empty(md))
if (lc->vtable.display_status!=NULL)
lc->vtable.display_status(lc,_("Call terminated."));
+#ifdef BUILD_UPNP
+ linphone_call_delete_upnp_session(call);
+#endif //BUILD_UPNP
+
linphone_call_set_state(call, LinphoneCallEnd,"Call ended");
}
/*resume to the call that send us the refer automatically*/
linphone_core_resume_call(lc,call->referer);
}
+
+#ifdef BUILD_UPNP
+ linphone_call_delete_upnp_session(call);
+#endif //BUILD_UPNP
+
if (sr == SalReasonDeclined) {
call->reason=LinphoneReasonDeclined;
linphone_call_set_state(call,LinphoneCallEnd,"Call declined.");
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
-#ifdef TUNNEL_ENABLED
#include "TunnelManager.hh"
-#endif
#include "linphone_tunnel.h"
#include "linphonecore.h"
#include "private.h"
return lc->tunnel;
}
-struct _LinphoneTunnelConfig {
- char *host;
- int port;
- int remote_udp_mirror_port;
- int delay;
-};
-
-LinphoneTunnelConfig *linphone_tunnel_config_new() {
- LinphoneTunnelConfig *ltc = ms_new0(LinphoneTunnelConfig,1);
- ltc->remote_udp_mirror_port = 12345;
- ltc->delay = 1000;
- return ltc;
-}
-
-void linphone_tunnel_config_set_host(LinphoneTunnelConfig *tunnel, const char *host) {
- if(tunnel->host != NULL) {
- ms_free(tunnel->host);
- tunnel->host = NULL;
- }
- if(host != NULL && strlen(host)) {
- tunnel->host = ms_strdup(host);
- }
-}
-
-const char *linphone_tunnel_config_get_host(LinphoneTunnelConfig *tunnel) {
- return tunnel->host;
-}
-
-void linphone_tunnel_config_set_port(LinphoneTunnelConfig *tunnel, int port) {
- tunnel->port = port;
-}
-
-int linphone_tunnel_config_get_port(LinphoneTunnelConfig *tunnel) {
- return tunnel->port;
-}
-
-void linphone_tunnel_config_set_remote_udp_mirror_port(LinphoneTunnelConfig *tunnel, int remote_udp_mirror_port) {
- tunnel->remote_udp_mirror_port = remote_udp_mirror_port;
-}
-
-int linphone_tunnel_config_get_remote_udp_mirror_port(LinphoneTunnelConfig *tunnel) {
- return tunnel->remote_udp_mirror_port;
-}
-
-void linphone_tunnel_config_set_delay(LinphoneTunnelConfig *tunnel, int delay) {
- tunnel->delay = delay;
-}
-
-int linphone_tunnel_config_get_delay(LinphoneTunnelConfig *tunnel) {
- return tunnel->delay;
-}
-
-void linphone_tunnel_config_destroy(LinphoneTunnelConfig *tunnel) {
- if(tunnel->host != NULL) {
- ms_free(tunnel->host);
- }
- ms_free(tunnel);
-}
-
-#ifdef TUNNEL_ENABLED
-
struct _LinphoneTunnel {
belledonnecomm::TunnelManager *manager;
MSList *config_list;
static char *linphone_tunnel_config_to_string(const LinphoneTunnelConfig *tunnel_config) {
char *str = NULL;
- if(tunnel_config->remote_udp_mirror_port != -1) {
+ if(linphone_tunnel_config_get_remote_udp_mirror_port(tunnel_config) != -1) {
str = ms_strdup_printf("%s:%d:%d:%d",
- tunnel_config->host,
- tunnel_config->port,
- tunnel_config->remote_udp_mirror_port,
- tunnel_config->delay);
+ linphone_tunnel_config_get_host(tunnel_config),
+ linphone_tunnel_config_get_port(tunnel_config),
+ linphone_tunnel_config_get_remote_udp_mirror_port(tunnel_config),
+ linphone_tunnel_config_get_delay(tunnel_config));
} else {
str = ms_strdup_printf("%s:%d",
- tunnel_config->host,
- tunnel_config->port);
+ linphone_tunnel_config_get_host(tunnel_config),
+ linphone_tunnel_config_get_port(tunnel_config));
}
return str;
}
static void linphone_tunnel_add_server_intern(LinphoneTunnel *tunnel, LinphoneTunnelConfig *tunnel_config) {
- if(tunnel_config->remote_udp_mirror_port == -1) {
- bcTunnel(tunnel)->addServer(tunnel_config->host, tunnel_config->port);
+ if(linphone_tunnel_config_get_remote_udp_mirror_port(tunnel_config) == -1) {
+ bcTunnel(tunnel)->addServer(linphone_tunnel_config_get_host(tunnel_config),
+ linphone_tunnel_config_get_port(tunnel_config));
} else {
- bcTunnel(tunnel)->addServer(tunnel_config->host, tunnel_config->port,
- tunnel_config->remote_udp_mirror_port, tunnel_config->delay);
+ bcTunnel(tunnel)->addServer(linphone_tunnel_config_get_host(tunnel_config),
+ linphone_tunnel_config_get_port(tunnel_config),
+ linphone_tunnel_config_get_remote_udp_mirror_port(tunnel_config),
+ linphone_tunnel_config_get_delay(tunnel_config));
}
tunnel->config_list = ms_list_append(tunnel->config_list, tunnel_config);
}
linphone_tunnel_enable(tunnel, enabled);
}
-#else
-
-/*stubs to avoid to have #ifdef TUNNEL_ENABLED in upper layers*/
-
-void linphone_tunnel_destroy(LinphoneTunnel *tunnel){
-}
-
-
-void linphone_tunnel_add_server(LinphoneTunnel *tunnel, LinphoneTunnelConfig *tunnel_config){
-}
-
-void linphone_tunnel_remove_server(LinphoneTunnel *tunnel, LinphoneTunnelConfig *tunnel_config){
-}
-
-const MSList *linphone_tunnel_get_servers(LinphoneTunnel *tunnel){
- return NULL;
-}
-
-void linphone_tunnel_clean_servers(LinphoneTunnel *tunnel){
-}
-
-void linphone_tunnel_enable(LinphoneTunnel *tunnel, bool_t enabled){
-}
-
-bool_t linphone_tunnel_enabled(LinphoneTunnel *tunnel){
- return FALSE;
-}
-
-
-void linphone_tunnel_enable_logs_with_handler(LinphoneTunnel *tunnel, bool_t enabled, OrtpLogFunc logHandler){
-}
-
-void linphone_tunnel_set_http_proxy_auth_info(LinphoneTunnel *tunnel, const char* username,const char* passwd){
-}
-
-void linphone_tunnel_set_http_proxy(LinphoneTunnel*tunnel, const char *host, int port, const char* username,const char* passwd){
-}
-
-void linphone_tunnel_get_http_proxy(LinphoneTunnel*tunnel,const char **host, int *port, const char **username, const char **passwd){
-}
-
-void linphone_tunnel_reconnect(LinphoneTunnel *tunnel){
-}
-
-void linphone_tunnel_auto_detect(LinphoneTunnel *tunnel){
-}
-
-void linphone_tunnel_configure(LinphoneTunnel *tunnel){
-}
-
-
-#endif
-
-
-
-
-
*
* @param tunnel configuration object
*/
-const char *linphone_tunnel_config_get_host(LinphoneTunnelConfig *tunnel);
+const char *linphone_tunnel_config_get_host(const LinphoneTunnelConfig *tunnel);
/**
* Set tls port of server.
*
* @param tunnel configuration object
*/
-int linphone_tunnel_config_get_port(LinphoneTunnelConfig *tunnel);
+int linphone_tunnel_config_get_port(const LinphoneTunnelConfig *tunnel);
/**
* Set the remote port on the tunnel server side used to test udp reachability.
*
* @param tunnel configuration object
*/
-int linphone_tunnel_config_get_remote_udp_mirror_port(LinphoneTunnelConfig *tunnel);
+int linphone_tunnel_config_get_remote_udp_mirror_port(const LinphoneTunnelConfig *tunnel);
/**
* Set the udp packet round trip delay in ms for a tunnel configuration.
*
* @param tunnel configuration object
*/
-int linphone_tunnel_config_get_delay(LinphoneTunnelConfig *tunnel);
+int linphone_tunnel_config_get_delay(const LinphoneTunnelConfig *tunnel);
/**
* Destroy a tunnel configuration
--- /dev/null
+/***************************************************************************
+ * linphone_tunnel_config.c
+ *
+ * Copyright 2012 Belledonne Communications
+ ****************************************************************************/
+
+/*
+ * 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 "linphone_tunnel.h"
+
+struct _LinphoneTunnelConfig {
+ char *host;
+ int port;
+ int remote_udp_mirror_port;
+ int delay;
+};
+
+LinphoneTunnelConfig *linphone_tunnel_config_new() {
+ LinphoneTunnelConfig *ltc = ms_new0(LinphoneTunnelConfig,1);
+ ltc->remote_udp_mirror_port = 12345;
+ ltc->delay = 1000;
+ return ltc;
+}
+
+void linphone_tunnel_config_set_host(LinphoneTunnelConfig *tunnel, const char *host) {
+ if(tunnel->host != NULL) {
+ ms_free(tunnel->host);
+ tunnel->host = NULL;
+ }
+ if(host != NULL && strlen(host)) {
+ tunnel->host = ms_strdup(host);
+ }
+}
+
+const char *linphone_tunnel_config_get_host(const LinphoneTunnelConfig *tunnel) {
+ return tunnel->host;
+}
+
+void linphone_tunnel_config_set_port(LinphoneTunnelConfig *tunnel, int port) {
+ tunnel->port = port;
+}
+
+int linphone_tunnel_config_get_port(const LinphoneTunnelConfig *tunnel) {
+ return tunnel->port;
+}
+
+void linphone_tunnel_config_set_remote_udp_mirror_port(LinphoneTunnelConfig *tunnel, int remote_udp_mirror_port) {
+ tunnel->remote_udp_mirror_port = remote_udp_mirror_port;
+}
+
+int linphone_tunnel_config_get_remote_udp_mirror_port(const LinphoneTunnelConfig *tunnel) {
+ return tunnel->remote_udp_mirror_port;
+}
+
+void linphone_tunnel_config_set_delay(LinphoneTunnelConfig *tunnel, int delay) {
+ tunnel->delay = delay;
+}
+
+int linphone_tunnel_config_get_delay(const LinphoneTunnelConfig *tunnel) {
+ return tunnel->delay;
+}
+
+void linphone_tunnel_config_destroy(LinphoneTunnelConfig *tunnel) {
+ if(tunnel->host != NULL) {
+ ms_free(tunnel->host);
+ }
+ ms_free(tunnel);
+}
+
--- /dev/null
+/***************************************************************************
+ * linphone_tunnel.cc
+ *
+ * Fri Dec 9, 2011
+ * Copyright 2011 Belledonne Communications
+ * Author: Guillaume Beraudo
+ * Email: guillaume dot beraudo at linphone dot org
+ ****************************************************************************/
+
+/*
+ * 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 "linphone_tunnel.h"
+#include "linphonecore.h"
+#include "private.h"
+#include "lpconfig.h"
+
+
+LinphoneTunnel* linphone_core_get_tunnel(LinphoneCore *lc){
+ return lc->tunnel;
+}
+
+/*stubs to avoid to have #ifdef TUNNEL_ENABLED in upper layers*/
+
+void linphone_tunnel_destroy(LinphoneTunnel *tunnel){
+}
+
+
+void linphone_tunnel_add_server(LinphoneTunnel *tunnel, LinphoneTunnelConfig *tunnel_config){
+}
+
+void linphone_tunnel_remove_server(LinphoneTunnel *tunnel, LinphoneTunnelConfig *tunnel_config){
+}
+
+const MSList *linphone_tunnel_get_servers(LinphoneTunnel *tunnel){
+ return NULL;
+}
+
+void linphone_tunnel_clean_servers(LinphoneTunnel *tunnel){
+}
+
+void linphone_tunnel_enable(LinphoneTunnel *tunnel, bool_t enabled){
+}
+
+bool_t linphone_tunnel_enabled(LinphoneTunnel *tunnel){
+ return FALSE;
+}
+
+
+void linphone_tunnel_enable_logs_with_handler(LinphoneTunnel *tunnel, bool_t enabled, OrtpLogFunc logHandler){
+}
+
+void linphone_tunnel_set_http_proxy_auth_info(LinphoneTunnel *tunnel, const char* username,const char* passwd){
+}
+
+void linphone_tunnel_set_http_proxy(LinphoneTunnel*tunnel, const char *host, int port, const char* username,const char* passwd){
+}
+
+void linphone_tunnel_get_http_proxy(LinphoneTunnel*tunnel,const char **host, int *port, const char **username, const char **passwd){
+}
+
+void linphone_tunnel_reconnect(LinphoneTunnel *tunnel){
+}
+
+void linphone_tunnel_auto_detect(LinphoneTunnel *tunnel){
+}
+
+void linphone_tunnel_configure(LinphoneTunnel *tunnel){
+}
+
linphone_core_update_local_media_description_from_ice(md, call->ice_session);
linphone_core_update_ice_state_in_call_stats(call);
}
+#ifdef BUILD_UPNP
+ if(call->upnp_session != NULL) {
+ linphone_core_update_local_media_description_from_upnp(md, call->upnp_session);
+ }
+#endif //BUILD_UPNP
linphone_address_destroy(addr);
call->localdesc=md;
if (old_md) sal_media_description_unref(old_md);
if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseStun) {
call->ping_time=linphone_core_run_stun_tests(call->core,call);
}
+#ifdef BUILD_UPNP
+ if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) {
+ call->upnp_session = linphone_upnp_session_new(call);
+ }
+#endif //BUILD_UPNP
call->camera_active=params->has_video;
discover_mtu(lc,linphone_address_get_domain (to));
case LinphonePolicyUseStun:
call->ping_time=linphone_core_run_stun_tests(call->core,call);
/* No break to also destroy ice session in this case. */
+ break;
+ case LinphonePolicyUseUpnp:
+#ifdef BUILD_UPNP
+ call->upnp_session = linphone_upnp_session_new(call);
+ if (call->upnp_session != NULL) {
+ linphone_call_init_media_streams(call);
+ if (linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(op))<0) {
+ /* uPnP port mappings failed, proceed with the call anyway. */
+ linphone_call_delete_upnp_session(call);
+ }
+ }
+#endif //BUILD_UPNP
+ break;
default:
break;
}
static void linphone_call_destroy(LinphoneCall *obj)
{
+#ifdef BUILD_UPNP
+ linphone_call_delete_upnp_session(obj);
+#endif //BUILD_UPNP
linphone_call_delete_ice_session(obj);
if (obj->op!=NULL) {
sal_op_release(obj->op);
}
}
+#ifdef BUILD_UPNP
+void linphone_call_delete_upnp_session(LinphoneCall *call){
+ if(call->upnp_session!=NULL) {
+ linphone_upnp_session_destroy(call->upnp_session);
+ call->upnp_session=NULL;
+ }
+}
+#endif //BUILD_UPNP
+
static void linphone_call_log_fill_stats(LinphoneCallLog *log, AudioStream *st){
audio_stream_get_local_rtp_stats (st,&log->local_stats);
log->quality=audio_stream_get_average_quality_rating(st);
report_bandwidth(call,as,vs);
ms_message("Thread processing load: audio=%f\tvideo=%f",audio_load,video_load);
}
+
+#ifdef BUILD_UPNP
+ linphone_upnp_call_process(call);
+#endif //BUILD_UPNP
+
#ifdef VIDEO_ENABLED
if (call->videostream!=NULL) {
OrtpEvent *ev;
#include "enum.h"
const char *linphone_core_get_nat_address_resolved(LinphoneCore *lc);
-void linphone_core_get_local_ip(LinphoneCore *lc, const char *dest, char *result);
static void toggle_video_preview(LinphoneCore *lc, bool_t val);
/* relative path where is stored local ring*/
lc->tunnel=linphone_core_tunnel_new(lc);
if (lc->tunnel) linphone_tunnel_configure(lc->tunnel);
#endif
+#ifdef BUILD_UPNP
+ lc->upnp = linphone_upnp_context_new(lc);
+#endif //BUILD_UPNP
if (lc->vtable.display_status)
lc->vtable.display_status(lc,_("Ready"));
lc->auto_net_state_mon=lc->sip_conf.auto_net_state_mon;
strncpy(result,ip,LINPHONE_IPADDR_SIZE);
return;
}
+#ifdef BUILD_UPNP
+ else if (lc->upnp != NULL && linphone_core_get_firewall_policy(lc)==LinphonePolicyUseUpnp &&
+ linphone_upnp_context_get_state(lc->upnp) == LinphoneUpnpStateOk) {
+ ip = linphone_upnp_context_get_external_ipaddress(lc->upnp);
+ strncpy(result,ip,LINPHONE_IPADDR_SIZE);
+ return;
+ }
+#endif //BUILD_UPNP
if (linphone_core_get_local_ip_for(lc->sip_conf.ipv6_enabled ? AF_INET6 : AF_INET,dest,result)==0)
return;
/*else fallback to SAL routine that will attempt to find the most realistic interface */
linphone_call_delete_ice_session(call);
linphone_call_stop_media_streams_for_ice_gathering(call);
}
+#ifdef BUILD_UPNP
+ if (call->upnp_session != NULL) {
+ ms_warning("uPnP mapping has not finished yet, proceeded with the call without uPnP anyway.");
+ linphone_call_delete_upnp_session(call);
+ }
+#endif //BUILD_UPNP
linphone_core_start_invite(lc,call);
}
if (call->state==LinphoneCallIncomingReceived){
int linphone_core_proceed_with_invite_if_ready(LinphoneCore *lc, LinphoneCall *call, LinphoneProxyConfig *dest_proxy){
bool_t ice_ready = FALSE;
+ bool_t upnp_ready = FALSE;
bool_t ping_ready = FALSE;
if (call->ice_session != NULL) {
} else {
ice_ready = TRUE;
}
+#ifdef BUILD_UPNP
+ if (call->upnp_session != NULL) {
+ if (linphone_upnp_session_get_state(call->upnp_session) == LinphoneUpnpStateOk) upnp_ready = TRUE;
+ } else {
+ upnp_ready = TRUE;
+ }
+#endif //BUILD_UPNP
if (call->ping_op != NULL) {
if (call->ping_replied == TRUE) ping_ready = TRUE;
} else {
ping_ready = TRUE;
}
- if ((ice_ready == TRUE) && (ping_ready == TRUE)) {
+ if ((ice_ready == TRUE) && (upnp_ready == TRUE) && (ping_ready == TRUE)) {
return linphone_core_start_invite(lc, call);
}
return 0;
LinphoneAddress *parsed_url2=NULL;
char *real_url=NULL;
LinphoneCall *call;
- bool_t use_ice = FALSE;
+ bool_t defer = FALSE;
linphone_core_preempt_sound_resources(lc);
linphone_call_delete_ice_session(call);
linphone_call_stop_media_streams_for_ice_gathering(call);
} else {
- use_ice = TRUE;
+ defer = TRUE;
}
}
+ else if (linphone_core_get_firewall_policy(call->core) == LinphonePolicyUseUpnp) {
+#ifdef BUILD_UPNP
+ linphone_call_init_media_streams(call);
+ call->start_time=time(NULL);
+ if (linphone_core_update_upnp(lc,call)<0) {
+ /* uPnP port mappings failed, proceed with the call anyway. */
+ linphone_call_delete_upnp_session(call);
+ } else {
+ defer = TRUE;
+ }
+#endif //BUILD_UPNP
+ }
if (call->dest_proxy==NULL && lc->sip_conf.ping_with_options==TRUE){
/*defer the start of the call after the OPTIONS ping*/
sal_op_set_user_pointer(call->ping_op,call);
call->start_time=time(NULL);
}else{
- if (use_ice==FALSE) linphone_core_start_invite(lc,call);
+ if (defer==FALSE) linphone_core_start_invite(lc,call);
}
if (real_url!=NULL) ms_free(real_url);
int linphone_core_start_update_call(LinphoneCore *lc, LinphoneCall *call){
const char *subject;
call->camera_active=call->params.has_video;
- if (call->ice_session != NULL)
+ if (call->ice_session != NULL) {
linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session);
-
+ }
+#ifdef BUILD_UPNP
+ if(call->upnp_session != NULL) {
+ linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session);
+ }
+#endif //BUILD_UPNP
if (call->params.in_conference){
subject="Conference";
}else{
linphone_call_set_state(call,LinphoneCallUpdating,"Updating call");
#ifdef VIDEO_ENABLED
bool_t has_video = call->params.has_video;
- if ((call->ice_session != NULL) && (call->videostream != NULL) && !params->has_video) {
- ice_session_remove_check_list(call->ice_session, call->videostream->ms.ice_check_list);
- call->videostream->ms.ice_check_list = NULL;
+
+ // Video removing
+ if((call->videostream != NULL) && !params->has_video) {
+ if (call->ice_session != NULL) {
+ ice_session_remove_check_list(call->ice_session, call->videostream->ms.ice_check_list);
+ call->videostream->ms.ice_check_list = NULL;
+ }
+#ifdef BUILD_UPNP
+ if(call->upnp_session != NULL) {
+ if (linphone_core_update_upnp(lc, call)<0) {
+ /* uPnP port mappings failed, proceed with the call anyway. */
+ linphone_call_delete_upnp_session(call);
+ }
+ }
+#endif //BUILD_UPNP
}
+
call->params = *params;
linphone_call_make_local_media_description(lc, call);
- if ((call->ice_session != NULL) && !has_video && call->params.has_video) {
- /* Defer call update until the ICE candidates gathering process has finished. */
- ms_message("Defer call update to gather ICE candidates");
- linphone_call_init_video_stream(call);
- video_stream_prepare_video(call->videostream);
- if (linphone_core_gather_ice_candidates(lc,call)<0) {
- /* Ice candidates gathering failed, proceed with the call anyway. */
- linphone_call_delete_ice_session(call);
- } else return err;
+
+ // Video adding
+ if (!has_video && call->params.has_video) {
+ if (call->ice_session != NULL) {
+ /* Defer call update until the ICE candidates gathering process has finished. */
+ ms_message("Defer call update to gather ICE candidates");
+ linphone_call_init_video_stream(call);
+ video_stream_prepare_video(call->videostream);
+ if (linphone_core_gather_ice_candidates(lc,call)<0) {
+ /* Ice candidates gathering failed, proceed with the call anyway. */
+ linphone_call_delete_ice_session(call);
+ } else {
+ return err;
+ }
+ }
+#ifdef BUILD_UPNP
+ if(call->upnp_session != NULL) {
+ ms_message("Defer call update to add uPnP port mappings");
+ linphone_call_init_video_stream(call);
+ video_stream_prepare_video(call->videostream);
+ if (linphone_core_update_upnp(lc, call)<0) {
+ /* uPnP port mappings failed, proceed with the call anyway. */
+ linphone_call_delete_upnp_session(call);
+ } else {
+ return err;
+ }
+ }
+#endif //BUILD_UPNP
}
#endif
err = linphone_core_start_update_call(lc, call);
}
linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session);
}
+#ifdef BUILD_UPNP
+ if(call->upnp_session != NULL) {
+ linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session);
+ }
+#endif //BUILD_UPNP
sal_call_set_local_media_description(call->op,call->localdesc);
sal_call_accept(call->op);
md=sal_call_get_final_media_description(call->op);
} else return 0;
}
}
-#endif
+#endif //VIDEO_ENABLED
}
+
+#if BUILD_UPNP
+ if(call->upnp_session != NULL) {
+ linphone_core_update_upnp_from_remote_media_description(call, sal_call_get_remote_media_description(call->op));
+#ifdef VIDEO_ENABLED
+ if ((call->params.has_video) && (call->params.has_video != old_has_video)) {
+ linphone_call_init_video_stream(call);
+ video_stream_prepare_video(call->videostream);
+ if (linphone_core_update_upnp(lc, call)<0) {
+ /* uPnP update failed, proceed with the call anyway. */
+ linphone_call_delete_upnp_session(call);
+ } else return 0;
+ }
+#endif //VIDEO_ENABLED
+ }
+#endif //BUILD_UPNP
+
linphone_core_start_accept_call_update(lc, call);
return 0;
}
lc->ringstream=NULL;
}
linphone_call_stop_media_streams(call);
+
+#ifdef BUILD_UPNP
+ linphone_call_delete_upnp_session(call);
+#endif //BUILD_UPNP
+
if (lc->vtable.display_status!=NULL)
lc->vtable.display_status(lc,_("Call aborted") );
linphone_call_set_state(call,LinphoneCallError,error);
}
linphone_call_stop_media_streams(call);
+
+#ifdef BUILD_UPNP
+ linphone_call_delete_upnp_session(call);
+#endif //BUILD_UPNP
+
if (lc->vtable.display_status!=NULL)
lc->vtable.display_status(lc,_("Call ended") );
linphone_call_set_state(call,LinphoneCallEnd,"Call terminated");
return -1;
}
linphone_call_make_local_media_description(lc,call);
- if (call->ice_session != NULL)
+ if (call->ice_session != NULL) {
linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session);
+ }
+#ifdef BUILD_UPNP
+ if(call->upnp_session != NULL) {
+ linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session);
+ }
+#endif //BUILD_UPNP
if (sal_media_description_has_dir(call->resultdesc,SalStreamSendRecv)){
sal_media_description_set_dir(call->localdesc,SalStreamSendOnly);
subject="Call on hold";
if (call->audiostream) audio_stream_play(call->audiostream, NULL);
linphone_call_make_local_media_description(lc,the_call);
- if (call->ice_session != NULL)
+ if (call->ice_session != NULL) {
linphone_core_update_local_media_description_from_ice(call->localdesc, call->ice_session);
+ }
+#ifdef BUILD_UPNP
+ if(call->upnp_session != NULL) {
+ linphone_core_update_local_media_description_from_upnp(call->localdesc, call->upnp_session);
+ }
+#endif //BUILD_UPNP
sal_call_set_local_media_description(call->op,call->localdesc);
sal_media_description_set_dir(call->localdesc,SalStreamSendRecv);
if (call->params.in_conference && !call->current_params.in_conference) subject="Conference";
usleep(50000);
#endif
}
+
+#ifdef BUILD_UPNP
+ linphone_upnp_context_destroy(lc->upnp);
+ lc->upnp = NULL;
+#endif //BUILD_UPNP
+
if (lc->friends)
ms_list_for_each(lc->friends,(void (*)(void *))linphone_friend_close_subscriptions);
linphone_core_set_state(lc,LinphoneGlobalShutdown,"Shutting down");
LinphonePolicyNoFirewall,
LinphonePolicyUseNatAddress,
LinphonePolicyUseStun,
- LinphonePolicyUseIce
+ LinphonePolicyUseIce,
+ LinphonePolicyUseUpnp,
} LinphoneFirewallPolicy;
typedef enum _LinphoneWaitingState{
if (ifp->ifa_addr && ifp->ifa_addr->sa_family == type
&& (ifp->ifa_flags & UP_FLAG) && !(ifp->ifa_flags & IFF_LOOPBACK))
{
- getnameinfo(ifp->ifa_addr,
+ if(getnameinfo(ifp->ifa_addr,
(type == AF_INET6) ?
sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in),
- address, size, NULL, 0, NI_NUMERICHOST);
- if (strchr(address, '%') == NULL) { /*avoid ipv6 link-local addresses */
- /*ms_message("getifaddrs() found %s",address);*/
- ret++;
- break;
+ address, size, NULL, 0, NI_NUMERICHOST) == 0) {
+ if (strchr(address, '%') == NULL) { /*avoid ipv6 link-local addresses */
+ /*ms_message("getifaddrs() found %s",address);*/
+ ret++;
+ break;
+ }
}
}
}
}
int linphone_core_get_local_ip_for(int type, const char *dest, char *result){
- strcpy(result,type==AF_INET ? "127.0.0.1" : "::1");
+ strcpy(result,type==AF_INET ? "127.0.0.1" : "::1");
#ifdef HAVE_GETIFADDRS
- if (dest==NULL) {
- /*we use getifaddrs for lookup of default interface */
- int found_ifs;
-
- found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
- if (found_ifs==1){
- return 0;
- }else if (found_ifs<=0){
- /*absolutely no network on this machine */
- return -1;
- }
- }
+ if (dest==NULL) {
+ /*we use getifaddrs for lookup of default interface */
+ int found_ifs;
+
+ found_ifs=get_local_ip_with_getifaddrs(type,result,LINPHONE_IPADDR_SIZE);
+ if (found_ifs==1){
+ return 0;
+ }else if (found_ifs<=0){
+ /*absolutely no network on this machine */
+ return -1;
+ }
+ }
#endif
- /*else use connect to find the best local ip address */
- if (type==AF_INET)
- dest="87.98.157.38"; /*a public IP address*/
- else dest="2a00:1450:8002::68";
- return get_local_ip_for_with_connect(type,dest,result);
+ /*else use connect to find the best local ip address */
+ if (type==AF_INET)
+ dest="87.98.157.38"; /*a public IP address*/
+ else dest="2a00:1450:8002::68";
+ return get_local_ip_for_with_connect(type,dest,result);
}
#ifndef WIN32
extern "C" {
#endif
#include "linphonecore.h"
+#include "linphonefriend.h"
#include "linphone_tunnel.h"
#include "linphonecore_utils.h"
#include "sal.h"
+#include "sipsetup.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#include "mediastreamer2/ice.h"
#include "mediastreamer2/mediastream.h"
#include "mediastreamer2/msconference.h"
+#ifdef BUILD_UPNP
+#include "upnp.h"
+#endif //BUILD_UPNP
#ifndef LIBLINPHONE_VERSION
#define LIBLINPHONE_VERSION LINPHONE_VERSION
OrtpEvQueue *videostream_app_evq;
CallCallbackObj nextVideoFrameDecoded;
LinphoneCallStats stats[2];
+#ifdef BUILD_UPNP
+ UpnpSession *upnp_session;
+#endif //BUILD_UPNP
IceSession *ice_session;
LinphoneChatMessage* pending_message;
int ping_time;
void linphone_call_stop_video_stream(LinphoneCall *call);
void linphone_call_stop_media_streams(LinphoneCall *call);
void linphone_call_delete_ice_session(LinphoneCall *call);
+void linphone_call_delete_upnp_session(LinphoneCall *call);
void linphone_call_stop_media_streams_for_ice_gathering(LinphoneCall *call);
void linphone_call_update_crypto_parameters(LinphoneCall *call, SalMediaDescription *old_md, SalMediaDescription *new_md);
bool_t network_reachable;
bool_t use_preview_window;
- time_t network_last_check;
- bool_t network_last_status;
+ time_t network_last_check;
+ bool_t network_last_status;
bool_t ringstream_autorelease;
bool_t pad[3];
LinphoneTunnel *tunnel;
char* device_id;
MSList *last_recv_msg_ids;
+#ifdef BUILD_UPNP
+ UpnpContext *upnp;
+#endif //BUILD_UPNP
};
LinphoneTunnel *linphone_core_tunnel_new(LinphoneCore *lc);
void linphone_tunnel_destroy(LinphoneTunnel *tunnel);
void linphone_tunnel_configure(LinphoneTunnel *tunnel);
void linphone_tunnel_enable_logs_with_handler(LinphoneTunnel *tunnel, bool_t enabled, OrtpLogFunc logHandler);
-
+
bool_t linphone_core_can_we_add_call(LinphoneCore *lc);
int linphone_core_add_call( LinphoneCore *lc, LinphoneCall *call);
int linphone_core_del_call( LinphoneCore *lc, LinphoneCall *call);
int linphone_core_get_edge_bw(LinphoneCore *lc);
int linphone_core_get_edge_ptime(LinphoneCore *lc);
+int linphone_upnp_init(LinphoneCore *lc);
+void linphone_upnp_destroy(LinphoneCore *lc);
+
#ifdef __cplusplus
}
#endif
{"Congo Democratic Republic" ,"CD" , "243" , 9 , "00" },
{"Cook Islands" ,"CK" , "682" , 5 , "00" },
{"Costa Rica" ,"CR" , "506" , 8 , "00" },
- {"C\99te d'Ivoire" ,"AD" , "225" , 8 , "00" },
+ {"C�te d'Ivoire" ,"AD" , "225" , 8 , "00" },
{"Croatia" ,"HR" , "385" , 9 , "00" },
{"Cuba" ,"CU" , "53" , 8 , "119" },
{"Cyprus" ,"CY" , "357" , 8 , "00" },
{"Portugal" ,"PT" , "351" , 9 , "00" },
{"Puerto Rico" ,"PR" , "1" , 10 , "011" },
{"Qatar" ,"QA" , "974" , 8 , "00" },
- {"R\8eunion Island" ,"RE" , "262" , 9 , "011" },
+ {"R�union Island" ,"RE" , "262" , 9 , "011" },
{"Romania" ,"RO" , "40" , 9 , "00" },
{"Russian Federation" ,"RU" , "7" , 10 , "8" },
{"Rwanda" ,"RW" , "250" , 9 , "00" },
{"Saint Vincent and the Grenadines","VC" , "1" , 10 , "011" },
{"Samoa" ,"WS" , "685" , 7 , "0" },
{"San Marino" ,"SM" , "378" , 10 , "00" },
- {"S\8bo Tom\8e and Pr\92ncipe" ,"ST" , "239" , 7 , "00" },
+ {"S�o Tom� and Pr�ncipe" ,"ST" , "239" , 7 , "00" },
{"Saudi Arabia" ,"SA" , "966" , 9 , "00" },
{"Senegal" ,"SN" , "221" , 9 , "00" },
{"Serbia" ,"RS" , "381" , 9 , "00" },
--- /dev/null
+/*
+linphone
+Copyright (C) 2012 Belledonne Communications SARL
+
+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 "upnp.h"
+#include "private.h"
+
+#define UPNP_ADD_MAX_RETRY 4
+#define UPNP_REMOVE_MAX_RETRY 4
+#define UPNP_SECTION_NAME "uPnP"
+
+/* Define private types */
+typedef struct _LpItem{
+ char *key;
+ char *value;
+} LpItem;
+
+typedef struct _LpSection{
+ char *name;
+ MSList *items;
+} LpSection;
+
+typedef struct _LpConfig{
+ FILE *file;
+ char *filename;
+ MSList *sections;
+ int modified;
+ int readonly;
+} LpConfig;
+
+/* Declare private functions */
+LpSection *lp_config_find_section(LpConfig *lpconfig, const char *name);
+void lp_section_remove_item(LpSection *sec, LpItem *item);
+void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *key, const char *value);
+
+/*
+ * uPnP Definitions
+ */
+
+typedef struct _UpnpPortBinding {
+ ms_mutex_t mutex;
+ LinphoneUpnpState state;
+ upnp_igd_ip_protocol protocol;
+ char local_addr[LINPHONE_IPADDR_SIZE];
+ int local_port;
+ char external_addr[LINPHONE_IPADDR_SIZE];
+ int external_port;
+ int retry;
+ int ref;
+ bool_t to_remove;
+ bool_t to_add;
+} UpnpPortBinding;
+
+typedef struct _UpnpStream {
+ UpnpPortBinding *rtp;
+ UpnpPortBinding *rtcp;
+ LinphoneUpnpState state;
+} UpnpStream;
+
+struct _UpnpSession {
+ LinphoneCall *call;
+ UpnpStream *audio;
+ UpnpStream *video;
+ LinphoneUpnpState state;
+};
+
+struct _UpnpContext {
+ LinphoneCore *lc;
+ upnp_igd_context *upnp_igd_ctxt;
+ UpnpPortBinding *sip_tcp;
+ UpnpPortBinding *sip_tls;
+ UpnpPortBinding *sip_udp;
+ LinphoneUpnpState state;
+ MSList *removing_configs;
+ MSList *adding_configs;
+ MSList *pending_bindings;
+
+ ms_mutex_t mutex;
+ ms_cond_t empty_cond;
+
+};
+
+
+bool_t linphone_core_upnp_hook(void *data);
+void linphone_core_upnp_refresh(UpnpContext *ctx);
+
+UpnpPortBinding *linphone_upnp_port_binding_new();
+UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port);
+bool_t linphone_upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2);
+UpnpPortBinding *linphone_upnp_port_binding_equivalent_in_list(MSList *list, const UpnpPortBinding *port);
+UpnpPortBinding *linphone_upnp_port_binding_retain(UpnpPortBinding *port);
+void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port);
+void linphone_upnp_port_binding_release(UpnpPortBinding *port);
+
+MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc);
+void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port);
+void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port);
+
+int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port);
+int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port);
+
+
+/**
+ * uPnP Callbacks
+ */
+
+/* Convert uPnP IGD logs to ortp logs */
+void linphone_upnp_igd_print(void *cookie, upnp_igd_print_level level, const char *fmt, va_list list) {
+ int ortp_level = ORTP_DEBUG;
+ switch(level) {
+ case UPNP_IGD_MESSAGE:
+ ortp_level = ORTP_MESSAGE;
+ break;
+ case UPNP_IGD_WARNING:
+ ortp_level = ORTP_DEBUG; // Too verbose otherwise
+ break;
+ case UPNP_IGD_ERROR:
+ ortp_level = ORTP_DEBUG; // Too verbose otherwise
+ break;
+ default:
+ break;
+ }
+ ortp_logv(ortp_level, fmt, list);
+}
+
+void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) {
+ UpnpContext *lupnp = (UpnpContext *)cookie;
+ upnp_igd_port_mapping *mapping = NULL;
+ UpnpPortBinding *port_mapping = NULL;
+ const char *ip_address = NULL;
+ const char *connection_status = NULL;
+ bool_t nat_enabled = FALSE;
+ ms_mutex_lock(&lupnp->mutex);
+ LinphoneUpnpState old_state = lupnp->state;
+
+ switch(event) {
+ case UPNP_IGD_EXTERNAL_IPADDRESS_CHANGED:
+ case UPNP_IGD_NAT_ENABLED_CHANGED:
+ case UPNP_IGD_CONNECTION_STATUS_CHANGED:
+ ip_address = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
+ connection_status = upnp_igd_get_connection_status(lupnp->upnp_igd_ctxt);
+ nat_enabled = upnp_igd_get_nat_enabled(lupnp->upnp_igd_ctxt);
+
+ if(ip_address == NULL || connection_status == NULL) {
+ ms_message("uPnP IGD: Pending");
+ lupnp->state = LinphoneUpnpStatePending;
+ } else if(strcasecmp(connection_status, "Connected") || !nat_enabled) {
+ ms_message("uPnP IGD: Not Available");
+ lupnp->state = LinphoneUpnpStateNotAvailable;
+ } else {
+ ms_message("uPnP IGD: Connected");
+ lupnp->state = LinphoneUpnpStateOk;
+ if(old_state != LinphoneUpnpStateOk) {
+ linphone_core_upnp_refresh(lupnp);
+ }
+ }
+
+ break;
+
+ case UPNP_IGD_PORT_MAPPING_ADD_SUCCESS:
+ mapping = (upnp_igd_port_mapping *) arg;
+ port_mapping = (UpnpPortBinding*) mapping->cookie;
+ port_mapping->external_port = mapping->remote_port;
+ port_mapping->state = LinphoneUpnpStateOk;
+ linphone_upnp_port_binding_log(ORTP_MESSAGE, "Added port binding", port_mapping);
+ linphone_upnp_config_add_port_binding(lupnp, port_mapping);
+
+ break;
+
+ case UPNP_IGD_PORT_MAPPING_ADD_FAILURE:
+ mapping = (upnp_igd_port_mapping *) arg;
+ port_mapping = (UpnpPortBinding*) mapping->cookie;
+ port_mapping->external_port = -1; //Force random external port
+ if(linphone_upnp_context_send_add_port_binding(lupnp, port_mapping) != 0) {
+ linphone_upnp_port_binding_log(ORTP_ERROR, "Can't add port binding", port_mapping);
+ }
+
+ break;
+
+ case UPNP_IGD_PORT_MAPPING_REMOVE_SUCCESS:
+ mapping = (upnp_igd_port_mapping *) arg;
+ port_mapping = (UpnpPortBinding*) mapping->cookie;
+ port_mapping->state = LinphoneUpnpStateIdle;
+ linphone_upnp_port_binding_log(ORTP_MESSAGE, "Removed port binding", port_mapping);
+ linphone_upnp_config_remove_port_binding(lupnp, port_mapping);
+
+ break;
+
+ case UPNP_IGD_PORT_MAPPING_REMOVE_FAILURE:
+ mapping = (upnp_igd_port_mapping *) arg;
+ port_mapping = (UpnpPortBinding*) mapping->cookie;
+ if(linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping) != 0) {
+ linphone_upnp_port_binding_log(ORTP_ERROR, "Can't remove port binding", port_mapping);
+ linphone_upnp_config_remove_port_binding(lupnp, port_mapping);
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ if(port_mapping != NULL) {
+ /*
+ * Execute delayed actions
+ */
+ if(port_mapping->to_remove) {
+ if(port_mapping->state == LinphoneUpnpStateOk) {
+ port_mapping->to_remove = FALSE;
+ linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping);
+ } else if(port_mapping->state == LinphoneUpnpStateKo) {
+ port_mapping->to_remove = FALSE;
+ }
+ }
+ if(port_mapping->to_add) {
+ if(port_mapping->state == LinphoneUpnpStateIdle || port_mapping->state == LinphoneUpnpStateKo) {
+ port_mapping->to_add = FALSE;
+ linphone_upnp_context_send_add_port_binding(lupnp, port_mapping);
+ }
+ }
+
+ lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping);
+ linphone_upnp_port_binding_release(port_mapping);
+ }
+
+ /*
+ * If there is no pending binding emit a signal
+ */
+ if(lupnp->pending_bindings == NULL) {
+ pthread_cond_signal(&lupnp->empty_cond);
+ }
+ ms_mutex_unlock(&lupnp->mutex);
+}
+
+
+/**
+ * uPnP Context
+ */
+
+UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) {
+ LCSipTransports transport;
+ UpnpContext *lupnp = (UpnpContext *)ms_new0(UpnpContext,1);
+ const char *ip_address;
+
+ ms_mutex_init(&lupnp->mutex, NULL);
+ ms_cond_init(&lupnp->empty_cond, NULL);
+
+ lupnp->lc = lc;
+ lupnp->pending_bindings = NULL;
+ lupnp->adding_configs = NULL;
+ lupnp->removing_configs = NULL;
+ lupnp->state = LinphoneUpnpStateIdle;
+ ms_message("uPnP IGD: New %p for core %p", lupnp, lc);
+
+ linphone_core_get_sip_transports(lc, &transport);
+ if(transport.udp_port != 0) {
+ lupnp->sip_udp = linphone_upnp_port_binding_new();
+ lupnp->sip_udp->protocol = UPNP_IGD_IP_PROTOCOL_UDP;
+ lupnp->sip_udp->local_port = transport.udp_port;
+ lupnp->sip_udp->external_port = transport.udp_port;
+ } else {
+ lupnp->sip_udp = NULL;
+ }
+ if(transport.tcp_port != 0) {
+ lupnp->sip_tcp = linphone_upnp_port_binding_new();
+ lupnp->sip_tcp->protocol = UPNP_IGD_IP_PROTOCOL_TCP;
+ lupnp->sip_tcp->local_port = transport.tcp_port;
+ lupnp->sip_tcp->external_port = transport.tcp_port;
+ } else {
+ lupnp->sip_tcp = NULL;
+ }
+ if(transport.tls_port != 0) {
+ lupnp->sip_tls = linphone_upnp_port_binding_new();
+ lupnp->sip_tls->protocol = UPNP_IGD_IP_PROTOCOL_TCP;
+ lupnp->sip_tls->local_port = transport.tls_port;
+ lupnp->sip_tls->external_port = transport.tls_port;
+ } else {
+ lupnp->sip_tls = NULL;
+ }
+
+ linphone_core_add_iterate_hook(lc, linphone_core_upnp_hook, lupnp);
+
+ lupnp->upnp_igd_ctxt = NULL;
+ lupnp->upnp_igd_ctxt = upnp_igd_create(linphone_upnp_igd_callback, linphone_upnp_igd_print, lupnp);
+ if(lupnp->upnp_igd_ctxt == NULL) {
+ lupnp->state = LinphoneUpnpStateKo;
+ ms_error("Can't create uPnP IGD context");
+ return NULL;
+ }
+
+ ip_address = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt);
+ if(lupnp->sip_udp != NULL) {
+ strncpy(lupnp->sip_udp->local_addr, ip_address, sizeof(lupnp->sip_udp->local_addr));
+ }
+ if(lupnp->sip_tcp != NULL) {
+ strncpy(lupnp->sip_tcp->local_addr, ip_address, sizeof(lupnp->sip_tcp->local_addr));
+ }
+ if(lupnp->sip_tls != NULL) {
+ strncpy(lupnp->sip_tls->local_addr, ip_address, sizeof(lupnp->sip_tls->local_addr));
+ }
+
+ lupnp->state = LinphoneUpnpStatePending;
+ return lupnp;
+}
+
+void linphone_upnp_context_destroy(UpnpContext *lupnp) {
+ /*
+ * Not need, all hooks are removed before
+ * linphone_core_remove_iterate_hook(lc, linphone_core_upnp_hook, lc);
+ */
+
+ /* Send port binding removes */
+ if(lupnp->sip_udp != NULL) {
+ linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_udp);
+ lupnp->sip_udp = NULL;
+ }
+ if(lupnp->sip_tcp != NULL) {
+ linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tcp);
+ lupnp->sip_tcp = NULL;
+ }
+ if(lupnp->sip_tls != NULL) {
+ linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tls);
+ lupnp->sip_tcp = NULL;
+ }
+
+ /* Wait all pending bindings are done */
+ ms_message("uPnP IGD: Wait all pending port bindings ...");
+ ms_mutex_lock(&lupnp->mutex);
+ ms_cond_wait(&lupnp->empty_cond, &lupnp->mutex);
+ ms_mutex_unlock(&lupnp->mutex);
+
+ if(lupnp->upnp_igd_ctxt != NULL) {
+ upnp_igd_destroy(lupnp->upnp_igd_ctxt);
+ }
+
+ /* Run one time the hook for configuration update */
+ linphone_core_upnp_hook(lupnp);
+
+ /* Release port bindings */
+ if(lupnp->sip_udp != NULL) {
+ linphone_upnp_port_binding_release(lupnp->sip_udp);
+ lupnp->sip_udp = NULL;
+ }
+ if(lupnp->sip_tcp != NULL) {
+ linphone_upnp_port_binding_release(lupnp->sip_tcp);
+ lupnp->sip_tcp = NULL;
+ }
+ if(lupnp->sip_tls != NULL) {
+ linphone_upnp_port_binding_release(lupnp->sip_tls);
+ lupnp->sip_tcp = NULL;
+ }
+
+ /* Release lists */
+ ms_list_for_each(lupnp->adding_configs,(void (*)(void*))linphone_upnp_port_binding_release);
+ lupnp->adding_configs = ms_list_free(lupnp->adding_configs);
+ ms_list_for_each(lupnp->removing_configs,(void (*)(void*))linphone_upnp_port_binding_release);
+ lupnp->removing_configs = ms_list_free(lupnp->removing_configs);
+ ms_list_for_each(lupnp->pending_bindings,(void (*)(void*))linphone_upnp_port_binding_release);
+ lupnp->pending_bindings = ms_list_free(lupnp->pending_bindings);
+
+ ms_mutex_destroy(&lupnp->mutex);
+ ms_cond_destroy(&lupnp->empty_cond);
+
+ ms_message("uPnP IGD: destroy %p", lupnp);
+ ms_free(lupnp);
+}
+
+LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *ctx) {
+ return ctx->state;
+}
+
+const char* linphone_upnp_context_get_external_ipaddress(UpnpContext *ctx) {
+ return upnp_igd_get_external_ipaddress(ctx->upnp_igd_ctxt);
+}
+
+int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port) {
+ upnp_igd_port_mapping mapping;
+ char description[128];
+ int ret;
+
+ // Compute port binding state
+ if(port->state != LinphoneUpnpStateAdding) {
+ port->to_remove = FALSE;
+ switch(port->state) {
+ case LinphoneUpnpStateKo:
+ case LinphoneUpnpStateIdle: {
+ port->retry = 0;
+ port->state = LinphoneUpnpStateAdding;
+ }
+ break;
+ case LinphoneUpnpStateRemoving: {
+ port->to_add = TRUE;
+ return 0;
+ }
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ if(port->retry >= UPNP_ADD_MAX_RETRY) {
+ ret = -1;
+ } else {
+ mapping.cookie = linphone_upnp_port_binding_retain(port);
+ lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie);
+
+ mapping.local_port = port->local_port;
+ mapping.local_host = port->local_addr;
+ if(port->external_port == -1)
+ mapping.remote_port = rand()%(0xffff - 1024) + 1024;
+ else
+ mapping.remote_port = port->external_port;
+ mapping.remote_host = "";
+ snprintf(description, 128, "%s %s at %s:%d",
+ PACKAGE_NAME,
+ (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP": "UDP",
+ port->local_addr, port->local_port);
+ mapping.description = description;
+ mapping.protocol = port->protocol;
+
+ port->retry++;
+ linphone_upnp_port_binding_log(ORTP_MESSAGE, "Try to add port binding", port);
+ ret = upnp_igd_add_port_mapping(lupnp->upnp_igd_ctxt, &mapping);
+ }
+ if(ret != 0) {
+ port->state = LinphoneUpnpStateKo;
+ }
+ return ret;
+}
+
+int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port) {
+ upnp_igd_port_mapping mapping;
+ int ret;
+
+ // Compute port binding state
+ if(port->state != LinphoneUpnpStateRemoving) {
+ port->to_add = FALSE;
+ switch(port->state) {
+ case LinphoneUpnpStateOk: {
+ port->retry = 0;
+ port->state = LinphoneUpnpStateRemoving;
+ }
+ break;
+ case LinphoneUpnpStateAdding: {
+ port->to_remove = TRUE;
+ return 0;
+ }
+ break;
+ default:
+ return 0;
+ }
+ }
+
+ if(port->retry >= UPNP_REMOVE_MAX_RETRY) {
+ ret = -1;
+ } else {
+ mapping.cookie = linphone_upnp_port_binding_retain(port);
+ lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie);
+
+ mapping.remote_port = port->external_port;
+ mapping.remote_host = "";
+ mapping.protocol = port->protocol;
+ port->retry++;
+ linphone_upnp_port_binding_log(ORTP_MESSAGE, "Try to remove port binding", port);
+ ret = upnp_igd_delete_port_mapping(lupnp->upnp_igd_ctxt, &mapping);
+ }
+ if(ret != 0) {
+ port->state = LinphoneUpnpStateKo;
+ }
+ return ret;
+}
+
+/*
+ * uPnP Core interfaces
+ */
+
+int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool_t video) {
+ LinphoneCore *lc = call->core;
+ UpnpContext *lupnp = lc->upnp;
+ int ret = -1;
+ const char *local_addr, *external_addr;
+
+ if(lupnp == NULL) {
+ return ret;
+ }
+
+ ms_mutex_lock(&lupnp->mutex);
+ // Don't handle when the call
+ if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) {
+ ret = 0;
+ local_addr = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt);
+ external_addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
+
+ /*
+ * Audio part
+ */
+ strncpy(call->upnp_session->audio->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE);
+ strncpy(call->upnp_session->audio->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE);
+ call->upnp_session->audio->rtp->local_port = call->audio_port;
+ if(call->upnp_session->audio->rtp->external_port == -1) {
+ call->upnp_session->audio->rtp->external_port = call->audio_port;
+ }
+ strncpy(call->upnp_session->audio->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE);
+ strncpy(call->upnp_session->audio->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE);
+ call->upnp_session->audio->rtcp->local_port = call->audio_port+1;
+ if(call->upnp_session->audio->rtcp->external_port == -1) {
+ call->upnp_session->audio->rtcp->external_port = call->audio_port+1;
+ }
+ if(audio) {
+ // Add audio port binding
+ linphone_upnp_context_send_add_port_binding(lupnp, call->upnp_session->audio->rtp);
+ linphone_upnp_context_send_add_port_binding(lupnp, call->upnp_session->audio->rtcp);
+ } else {
+ // Remove audio port binding
+ linphone_upnp_context_send_remove_port_binding(lupnp, call->upnp_session->audio->rtp);
+ linphone_upnp_context_send_remove_port_binding(lupnp, call->upnp_session->audio->rtcp);
+ }
+
+ /*
+ * Video part
+ */
+ strncpy(call->upnp_session->video->rtp->local_addr, local_addr, LINPHONE_IPADDR_SIZE);
+ strncpy(call->upnp_session->video->rtp->external_addr, external_addr, LINPHONE_IPADDR_SIZE);
+ call->upnp_session->video->rtp->local_port = call->video_port;
+ if(call->upnp_session->video->rtp->external_port == -1) {
+ call->upnp_session->video->rtp->external_port = call->video_port;
+ }
+ strncpy(call->upnp_session->video->rtcp->local_addr, local_addr, LINPHONE_IPADDR_SIZE);
+ strncpy(call->upnp_session->video->rtcp->external_addr, external_addr, LINPHONE_IPADDR_SIZE);
+ call->upnp_session->video->rtcp->local_port = call->video_port+1;
+ if(call->upnp_session->video->rtcp->external_port == -1) {
+ call->upnp_session->video->rtcp->external_port = call->video_port+1;
+ }
+ if(video) {
+ // Add video port binding
+ linphone_upnp_context_send_add_port_binding(lupnp, call->upnp_session->video->rtp);
+ linphone_upnp_context_send_add_port_binding(lupnp, call->upnp_session->video->rtcp);
+ } else {
+ // Remove video port binding
+ linphone_upnp_context_send_remove_port_binding(lupnp, call->upnp_session->video->rtp);
+ linphone_upnp_context_send_remove_port_binding(lupnp, call->upnp_session->video->rtcp);
+ }
+ }
+
+ ms_mutex_unlock(&lupnp->mutex);
+
+ /*
+ * Update uPnP call state
+ */
+ linphone_upnp_call_process(call);
+
+ return ret;
+}
+
+
+int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md) {
+ bool_t audio = FALSE;
+ bool_t video = FALSE;
+ int i;
+ const SalStreamDescription *stream;
+
+ for (i = 0; i < md->nstreams; i++) {
+ stream = &md->streams[i];
+ if(stream->type == SalAudio) {
+ audio = TRUE;
+ } else if(stream->type == SalVideo) {
+ video = TRUE;
+ }
+ }
+
+ return linphone_core_update_upnp_audio_video(call, audio, video);
+}
+
+int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call) {
+ return linphone_core_update_upnp_audio_video(call, call->audiostream!=NULL, call->videostream!=NULL);
+}
+
+int linphone_upnp_call_process(LinphoneCall *call) {
+ LinphoneCore *lc = call->core;
+ UpnpContext *lupnp = lc->upnp;
+ int ret = -1;
+ LinphoneUpnpState oldState;
+
+ if(lupnp == NULL) {
+ return ret;
+ }
+
+ ms_mutex_lock(&lupnp->mutex);
+
+ // Don't handle when the call
+ if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) {
+ ret = 0;
+
+ /*
+ * Update Audio state
+ */
+ if((call->upnp_session->audio->rtp->state == LinphoneUpnpStateOk || call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle) &&
+ (call->upnp_session->audio->rtcp->state == LinphoneUpnpStateOk || call->upnp_session->audio->rtcp->state == LinphoneUpnpStateIdle)) {
+ call->upnp_session->audio->state = LinphoneUpnpStateOk;
+ } else if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateAdding ||
+ call->upnp_session->audio->rtp->state == LinphoneUpnpStateRemoving ||
+ call->upnp_session->audio->rtcp->state == LinphoneUpnpStateAdding ||
+ call->upnp_session->audio->rtcp->state == LinphoneUpnpStateRemoving) {
+ call->upnp_session->audio->state = LinphoneUpnpStatePending;
+ } else if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateKo ||
+ call->upnp_session->audio->rtp->state == LinphoneUpnpStateKo) {
+ call->upnp_session->audio->state = LinphoneUpnpStateKo;
+ } else {
+ call->upnp_session->audio->state = LinphoneUpnpStateIdle;
+ }
+
+ /*
+ * Update Video state
+ */
+ if((call->upnp_session->video->rtp->state == LinphoneUpnpStateOk || call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle) &&
+ (call->upnp_session->video->rtcp->state == LinphoneUpnpStateOk || call->upnp_session->video->rtcp->state == LinphoneUpnpStateIdle)) {
+ call->upnp_session->video->state = LinphoneUpnpStateOk;
+ } else if(call->upnp_session->video->rtp->state == LinphoneUpnpStateAdding ||
+ call->upnp_session->video->rtp->state == LinphoneUpnpStateRemoving ||
+ call->upnp_session->video->rtcp->state == LinphoneUpnpStateAdding ||
+ call->upnp_session->video->rtcp->state == LinphoneUpnpStateRemoving) {
+ call->upnp_session->video->state = LinphoneUpnpStatePending;
+ } else if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateKo ||
+ call->upnp_session->video->rtp->state == LinphoneUpnpStateKo) {
+ call->upnp_session->video->state = LinphoneUpnpStateKo;
+ } else {
+ call->upnp_session->video->state = LinphoneUpnpStateIdle;
+ }
+
+ /*
+ * Update session state
+ */
+ oldState = call->upnp_session->state;
+ if(call->upnp_session->audio->state == LinphoneUpnpStateOk &&
+ call->upnp_session->video->state == LinphoneUpnpStateOk) {
+ call->upnp_session->state = LinphoneUpnpStateOk;
+ } else if(call->upnp_session->audio->state == LinphoneUpnpStatePending ||
+ call->upnp_session->video->state == LinphoneUpnpStatePending) {
+ call->upnp_session->state = LinphoneUpnpStatePending;
+ } else if(call->upnp_session->audio->state == LinphoneUpnpStateKo ||
+ call->upnp_session->video->state == LinphoneUpnpStateKo) {
+ call->upnp_session->state = LinphoneUpnpStateKo;
+ } else {
+ call->upnp_session->state = LinphoneUpnpStateIdle;
+ }
+
+ /* When change is done proceed update */
+ if(oldState != LinphoneUpnpStateOk && oldState != LinphoneUpnpStateKo &&
+ (call->upnp_session->state == LinphoneUpnpStateOk || call->upnp_session->state == LinphoneUpnpStateKo)) {
+ if(call->upnp_session->state == LinphoneUpnpStateOk)
+ ms_message("uPnP IGD: uPnP for Call %p is ok", call);
+ else
+ ms_message("uPnP IGD: uPnP for Call %p is ko", call);
+
+ switch (call->state) {
+ case LinphoneCallUpdating:
+ linphone_core_start_update_call(lc, call);
+ break;
+ case LinphoneCallUpdatedByRemote:
+ linphone_core_start_accept_call_update(lc, call);
+ break;
+ case LinphoneCallOutgoingInit:
+ linphone_core_proceed_with_invite_if_ready(lc, call, NULL);
+ break;
+ case LinphoneCallIdle:
+ linphone_core_notify_incoming_call(lc, call);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ ms_mutex_unlock(&lupnp->mutex);
+ return ret;
+}
+
+void linphone_core_upnp_refresh(UpnpContext *lupnp) {
+ MSList *global_list = NULL;
+ MSList *list = NULL;
+ MSList *item;
+ LinphoneCall *call;
+ UpnpPortBinding *port_mapping, *port_mapping2;
+
+ ms_message("uPnP IGD: Refresh mappings");
+
+ /* Remove context port bindings */
+ if(lupnp->sip_udp != NULL) {
+ global_list = ms_list_append(global_list, lupnp->sip_udp);
+ }
+ if(lupnp->sip_tcp != NULL) {
+ global_list = ms_list_append(global_list, lupnp->sip_tcp);
+ }
+ if(lupnp->sip_tls != NULL) {
+ global_list = ms_list_append(global_list, lupnp->sip_tls);
+ }
+
+ /* Remove call port bindings */
+ list = lupnp->lc->calls;
+ while(list != NULL) {
+ call = (LinphoneCall *)list->data;
+ if(call->upnp_session != NULL) {
+ global_list = ms_list_append(global_list, call->upnp_session->audio->rtp);
+ global_list = ms_list_append(global_list, call->upnp_session->audio->rtcp);
+ global_list = ms_list_append(global_list, call->upnp_session->video->rtp);
+ global_list = ms_list_append(global_list, call->upnp_session->video->rtcp);
+ }
+ list = list->next;
+ }
+
+ // Remove port binding configurations
+ list = linphone_upnp_config_list_port_bindings(lupnp->lc->config);
+ for(item = list;item != NULL; item = item->next) {
+ port_mapping = (UpnpPortBinding *)item->data;
+ port_mapping2 = linphone_upnp_port_binding_equivalent_in_list(global_list, port_mapping);
+ if(port_mapping2 == NULL) {
+ linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping);
+ } else if(port_mapping2->state == LinphoneUpnpStateIdle){
+ /* Force to remove */
+ port_mapping2->state = LinphoneUpnpStateOk;
+ }
+ }
+ ms_list_for_each(list, (void (*)(void*))linphone_upnp_port_binding_release);
+ list = ms_list_free(list);
+
+
+ // (Re)Add removed port bindings
+ list = global_list;
+ while(list != NULL) {
+ port_mapping = (UpnpPortBinding *)list->data;
+ linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping);
+ linphone_upnp_context_send_add_port_binding(lupnp, port_mapping);
+ list = list->next;
+ }
+ global_list = ms_list_free(global_list);
+}
+
+bool_t linphone_core_upnp_hook(void *data) {
+ char key[64];
+ MSList *item;
+ UpnpPortBinding *port_mapping;
+ UpnpContext *lupnp = (UpnpContext *)data;
+ ms_mutex_lock(&lupnp->mutex);
+
+ /* Add configs */
+ for(item = lupnp->adding_configs;item!=NULL;item=item->next) {
+ port_mapping = (UpnpPortBinding *)item->data;
+ snprintf(key, sizeof(key), "%s-%d-%d",
+ (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
+ port_mapping->external_port,
+ port_mapping->local_port);
+ lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, "uPnP");
+ linphone_upnp_port_binding_log(ORTP_DEBUG, "Configuration: Added port binding", port_mapping);
+ }
+ ms_list_for_each(lupnp->adding_configs,(void (*)(void*))linphone_upnp_port_binding_release);
+ lupnp->adding_configs = ms_list_free(lupnp->adding_configs);
+
+ /* Remove configs */
+ for(item = lupnp->removing_configs;item!=NULL;item=item->next) {
+ port_mapping = (UpnpPortBinding *)item->data;
+ snprintf(key, sizeof(key), "%s-%d-%d",
+ (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
+ port_mapping->external_port,
+ port_mapping->local_port);
+ lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, NULL);
+ linphone_upnp_port_binding_log(ORTP_DEBUG, "Configuration: Removed port binding", port_mapping);
+ }
+ ms_list_for_each(lupnp->removing_configs,(void (*)(void*))linphone_upnp_port_binding_release);
+ lupnp->removing_configs = ms_list_free(lupnp->removing_configs);
+
+ ms_mutex_unlock(&lupnp->mutex);
+ return TRUE;
+}
+
+int linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session) {
+ int i;
+ SalStreamDescription *stream;
+ UpnpStream *upnpStream;
+
+ for (i = 0; i < desc->nstreams; i++) {
+ stream = &desc->streams[i];
+ upnpStream = NULL;
+ if(stream->type == SalAudio) {
+ upnpStream = session->audio;
+ } else if(stream->type == SalVideo) {
+ upnpStream = session->video;
+ }
+ if(upnpStream != NULL) {
+ if(upnpStream->rtp->state == LinphoneUpnpStateOk) {
+ strncpy(stream->rtp_addr, upnpStream->rtp->external_addr, LINPHONE_IPADDR_SIZE);
+ stream->rtp_port = upnpStream->rtp->external_port;
+ }
+ if(upnpStream->rtcp->state == LinphoneUpnpStateOk) {
+ strncpy(stream->rtcp_addr, upnpStream->rtcp->external_addr, LINPHONE_IPADDR_SIZE);
+ stream->rtcp_port = upnpStream->rtcp->external_port;
+ }
+ }
+ }
+ return 0;
+}
+
+
+/*
+ * uPnP Port Binding
+ */
+
+UpnpPortBinding *linphone_upnp_port_binding_new() {
+ UpnpPortBinding *port = NULL;
+ port = ms_new0(UpnpPortBinding,1);
+ ms_mutex_init(&port->mutex, NULL);
+ port->state = LinphoneUpnpStateIdle;
+ port->local_addr[0] = '\0';
+ port->local_port = -1;
+ port->external_addr[0] = '\0';
+ port->external_port = -1;
+ port->to_remove = FALSE;
+ port->to_add = FALSE;
+ port->ref = 1;
+ return port;
+}
+
+UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port) {
+ UpnpPortBinding *new_port = NULL;
+ new_port = ms_new0(UpnpPortBinding,1);
+ memcpy(new_port, port, sizeof(UpnpPortBinding));
+ ms_mutex_init(&new_port->mutex, NULL);
+ new_port->ref = 1;
+ return new_port;
+}
+
+void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port) {
+ if(strlen(port->local_addr)) {
+ ortp_log(level, "uPnP IGD: %s %s|%d->%s:%d", msg,
+ (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
+ port->external_port,
+ port->local_addr,
+ port->local_port);
+ } else {
+ ortp_log(level, "uPnP IGD: %s %s|%d->%d", msg,
+ (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
+ port->external_port,
+ port->local_port);
+ }
+}
+
+bool_t linphone_upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2) {
+ return port1->protocol == port2->protocol &&
+ port1->local_port == port2->local_port &&
+ port1->external_port == port2->external_port;
+}
+
+UpnpPortBinding *linphone_upnp_port_binding_equivalent_in_list(MSList *list, const UpnpPortBinding *port) {
+ UpnpPortBinding *port_mapping;
+ while(list != NULL) {
+ port_mapping = (UpnpPortBinding *)list->data;
+ if(linphone_upnp_port_binding_equal(port, port_mapping)) {
+ return port_mapping;
+ }
+ list = list->next;
+ }
+
+ return NULL;
+}
+
+UpnpPortBinding *linphone_upnp_port_binding_retain(UpnpPortBinding *port) {
+ ms_mutex_lock(&port->mutex);
+ port->ref++;
+ ms_mutex_unlock(&port->mutex);
+ return port;
+}
+
+void linphone_upnp_port_binding_release(UpnpPortBinding *port) {
+ ms_mutex_lock(&port->mutex);
+ if(--port->ref == 0) {
+ ms_mutex_unlock(&port->mutex);
+ ms_mutex_destroy(&port->mutex);
+ ms_free(port);
+ return;
+ }
+ ms_mutex_unlock(&port->mutex);
+}
+
+
+/*
+ * uPnP Stream
+ */
+
+UpnpStream* linphone_upnp_stream_new() {
+ UpnpStream *stream = ms_new0(UpnpStream,1);
+ stream->state = LinphoneUpnpStateIdle;
+ stream->rtp = linphone_upnp_port_binding_new();
+ stream->rtp->protocol = UPNP_IGD_IP_PROTOCOL_UDP;
+ stream->rtcp = linphone_upnp_port_binding_new();
+ stream->rtcp->protocol = UPNP_IGD_IP_PROTOCOL_UDP;
+ return stream;
+}
+
+void linphone_upnp_stream_destroy(UpnpStream* stream) {
+ linphone_upnp_port_binding_release(stream->rtp);
+ stream->rtp = NULL;
+ linphone_upnp_port_binding_release(stream->rtcp);
+ stream->rtcp = NULL;
+ ms_free(stream);
+}
+
+
+/*
+ * uPnP Session
+ */
+
+UpnpSession* linphone_upnp_session_new(LinphoneCall* call) {
+ UpnpSession *session = ms_new0(UpnpSession,1);
+ session->call = call;
+ session->state = LinphoneUpnpStateIdle;
+ session->audio = linphone_upnp_stream_new();
+ session->video = linphone_upnp_stream_new();
+ return session;
+}
+
+void linphone_upnp_session_destroy(UpnpSession *session) {
+ LinphoneCore *lc = session->call->core;
+
+ if(lc->upnp != NULL) {
+ /* Remove bindings */
+ linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtp);
+ linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtcp);
+ linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtp);
+ linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtcp);
+ }
+
+ linphone_upnp_stream_destroy(session->audio);
+ linphone_upnp_stream_destroy(session->video);
+ ms_free(session);
+}
+
+LinphoneUpnpState linphone_upnp_session_get_state(UpnpSession *session) {
+ return session->state;
+}
+
+/*
+ * uPnP Config
+ */
+
+MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc) {
+ char protocol_str[4]; // TCP or UDP
+ upnp_igd_ip_protocol protocol;
+ int external_port;
+ int local_port;
+ MSList *retList = NULL;
+ UpnpPortBinding *port;
+ bool_t valid;
+ MSList *elem;
+ LpItem *item;
+ LpSection *sec=lp_config_find_section(lpc, UPNP_SECTION_NAME);
+ if(sec == NULL)
+ return retList;
+
+ elem = sec->items;
+ while(elem != NULL) {
+ item=(LpItem*)elem->data;
+ valid = TRUE;
+ if(sscanf(item->key, "%3s-%i-%i", protocol_str, &external_port, &local_port) == 3) {
+ if(strcasecmp(protocol_str, "TCP") == 0) {
+ protocol = UPNP_IGD_IP_PROTOCOL_TCP;
+ } else if(strcasecmp(protocol_str, "UDP") == 0) {
+ protocol = UPNP_IGD_IP_PROTOCOL_UDP;
+ } else {
+ valid = FALSE;
+ }
+ if(valid) {
+ port = linphone_upnp_port_binding_new();
+ port->state = LinphoneUpnpStateOk;
+ port->protocol = protocol;
+ port->external_port = external_port;
+ port->local_port = local_port;
+ retList = ms_list_append(retList, port);
+ }
+ } else {
+ valid = FALSE;
+ }
+ elem = ms_list_next(elem);
+ if(!valid) {
+ ms_warning("uPnP configuration invalid line: %s", item->key);
+ lp_section_remove_item(sec, item);
+ }
+ }
+
+ return retList;
+}
+
+void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) {
+ MSList *list;
+ UpnpPortBinding *list_port;
+
+ list = lupnp->removing_configs;
+ while(list != NULL) {
+ list_port = (UpnpPortBinding *)list->data;
+ if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
+ lupnp->removing_configs = ms_list_remove(lupnp->removing_configs, list_port);
+ linphone_upnp_port_binding_release(list_port);
+ return;
+ }
+ list = ms_list_next(list);
+ }
+
+ list = lupnp->adding_configs;
+ while(list != NULL) {
+ list_port = (UpnpPortBinding *)list->data;
+ if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
+ return;
+ }
+ list = ms_list_next(list);
+ }
+
+ list_port = linphone_upnp_port_binding_copy(port);
+ lupnp->adding_configs = ms_list_append(lupnp->adding_configs, list_port);
+}
+
+void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) {
+ MSList *list;
+ UpnpPortBinding *list_port;
+
+ list = lupnp->adding_configs;
+ while(list != NULL) {
+ list_port = (UpnpPortBinding *)list->data;
+ if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
+ lupnp->adding_configs = ms_list_remove(lupnp->adding_configs, list_port);
+ linphone_upnp_port_binding_release(list_port);
+ return;
+ }
+ list = ms_list_next(list);
+ }
+
+ list = lupnp->removing_configs;
+ while(list != NULL) {
+ list_port = (UpnpPortBinding *)list->data;
+ if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
+ return;
+ }
+ list = ms_list_next(list);
+ }
+
+ list_port = linphone_upnp_port_binding_copy(port);
+ lupnp->removing_configs = ms_list_append(lupnp->removing_configs, list_port);
+}
--- /dev/null
+/*
+linphone
+Copyright (C) 2012 Belledonne Communications SARL
+
+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 LINPHONE_UPNP_H
+#define LINPHONE_UPNP_H
+
+#include "mediastreamer2/upnp_igd.h"
+#include "linphonecore.h"
+#include "sal.h"
+
+typedef enum {
+ LinphoneUpnpStateIdle,
+ LinphoneUpnpStatePending, // Only used by uPnP context
+ LinphoneUpnpStateAdding, // Only used by port binding
+ LinphoneUpnpStateRemoving, // Only used by port binding
+ LinphoneUpnpStateNotAvailable, // Only used by uPnP context
+ LinphoneUpnpStateOk,
+ LinphoneUpnpStateKo,
+} LinphoneUpnpState;
+
+typedef struct _UpnpSession UpnpSession;
+typedef struct _UpnpContext UpnpContext;
+
+int linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session);
+int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md);
+int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call);
+
+int linphone_upnp_call_process(LinphoneCall *call);
+UpnpSession* linphone_upnp_session_new(LinphoneCall *call);
+void linphone_upnp_session_destroy(UpnpSession* session);
+LinphoneUpnpState linphone_upnp_session_get_state(UpnpSession *session);
+
+UpnpContext *linphone_upnp_context_new(LinphoneCore *lc);
+void linphone_upnp_context_destroy(UpnpContext *ctx);
+LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *ctx);
+const char *linphone_upnp_context_get_external_ipaddress(UpnpContext *ctx);
+
+#endif //LINPHONE_UPNP_H
setupwizard.c
endif
-linphone_LDADD=$(ORTP_LIBS) \
- $(MEDIASTREAMER_LIBS) \
+linphone_LDADD=\
$(top_builddir)/coreapi/liblinphone.la \
- $(LIBGTK_LIBS) $(NOTIFY1_LIBS) $(NOTIFY4_LIBS) $(LIBGTKMAC_LIBS) $(INTLLIBS)
+ $(ORTP_LIBS) \
+ $(MEDIASTREAMER_LIBS) \
+ $(LIBGTK_LIBS) $(NOTIFY1_LIBS) $(NOTIFY4_LIBS) $(LIBGTKMAC_LIBS) $(INTLLIBS)
if BUILD_WIN32
<property name="position">3</property>
</packing>
</child>
+ <child>
+ <object class="GtkRadioButton" id="use_upnp">
+ <property name="label" translatable="yes">Behind NAT / Firewall (use uPnP)</property>
+ <property name="use_action_appearance">False</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="draw_indicator">True</property>
+ <property name="group">no_nat</property>
+ <signal name="toggled" handler="linphone_gtk_use_upnp_toggled" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
<child>
<object class="GtkHBox" id="hbox24">
<property name="visible">True</property>
linphone_core_set_firewall_policy(linphone_gtk_get_core(),LinphonePolicyUseIce);
}
+void linphone_gtk_use_upnp_toggled(GtkWidget *w){
+ if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)))
+ linphone_core_set_firewall_policy(linphone_gtk_get_core(),LinphonePolicyUseUpnp);
+}
+
void linphone_gtk_mtu_changed(GtkWidget *w){
if (GTK_WIDGET_SENSITIVE(w))
linphone_core_set_mtu(linphone_gtk_get_core(),gtk_spin_button_get_value(GTK_SPIN_BUTTON(w)));
case LinphonePolicyUseIce:
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"use_ice")),TRUE);
break;
+ case LinphonePolicyUseUpnp:
+ gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"use_upnp")),TRUE);
+ break;
}
mtu=linphone_core_get_mtu(lc);
if (mtu<=0){
-Subproject commit d5811e376759433da3c19585b2d8d0329e1c06b2
+Subproject commit 1cf3b8e36d832d763220b762c6bceef0db88c04c