]> sjero.net Git - linphone/commitdiff
Improve uPnP behaviour (Firewall policy change, local ip change, ...)
authorYann Diorcet <yann.diorcet@belledonne-communications.com>
Fri, 15 Feb 2013 15:25:47 +0000 (16:25 +0100)
committerYann Diorcet <yann.diorcet@belledonne-communications.com>
Mon, 18 Feb 2013 10:59:37 +0000 (11:59 +0100)
Hide uPnP firewall setting if uPnP is not available

configure.ac
coreapi/linphonecore.c
coreapi/proxy.c
coreapi/upnp.c
coreapi/upnp.h
gtk/propertybox.c

index 1372c5af32bcad8fc5e6262d9dba7011ae148e9d..43efbff30198fdf33b0d9d468fca786c578d989a 100644 (file)
@@ -782,6 +782,7 @@ printf "* %-30s %s\n" "Account assistant"   $build_wizard
 printf "* %-30s %s\n" "Console interface"      $console_ui
 printf "* %-30s %s\n" "Tools"                  $build_tools
 printf "* %-30s %s\n" "zRTP encryption (GPLv3)"        $zrtp
+printf "* %-30s %s\n" "uPnP support"           $build_upnp
 
 if test "$enable_tunnel" = "true" ; then
        printf "* Tunnel support\t\ttrue\n"
index ad1bb5667e8ba4f5d3960260e456b91d8576cc9c..608d1c6e55f2541c00181c6fcd1abe0f8dff159f 100644 (file)
@@ -1304,9 +1304,6 @@ static void linphone_core_init (LinphoneCore * lc, const LinphoneCoreVTable *vta
        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;
@@ -4211,6 +4208,17 @@ void linphone_core_set_firewall_policy(LinphoneCore *lc, LinphoneFirewallPolicy
                ms_warning("UPNP is not available, reset firewall policy to no firewall");
                pol = LinphonePolicyNoFirewall;
        }
+#else //BUILD_UPNP
+       if(pol == LinphonePolicyUseUpnp) {
+               if(lc->upnp == NULL) {
+                       lc->upnp = linphone_upnp_context_new(lc);
+               }
+       } else {
+               if(lc->upnp != NULL) {
+                       linphone_upnp_context_destroy(lc->upnp);
+                       lc->upnp = NULL;
+               }
+       }       
 #endif //BUILD_UPNP
        lc->net_conf.firewall_policy=pol;
        if (lc->sip_conf.contact) update_primary_contact(lc);
@@ -5170,8 +5178,10 @@ static void linphone_core_uninit(LinphoneCore *lc)
        }
 
 #ifdef BUILD_UPNP
-       linphone_upnp_context_destroy(lc->upnp);
-       lc->upnp = NULL;
+       if(lc->upnp != NULL) {
+               linphone_upnp_context_destroy(lc->upnp);
+               lc->upnp = NULL;
+       }
 #endif //BUILD_UPNP
 
        if (lc->friends)
@@ -5506,7 +5516,7 @@ void linphone_core_remove_iterate_hook(LinphoneCore *lc, LinphoneCoreIterateHook
        for(elem=lc->hooks;elem!=NULL;elem=elem->next){
                Hook *h=(Hook*)elem->data;
                if (h->fun==hook && h->data==hook_data){
-                       ms_list_remove_link(lc->hooks,elem);
+                       lc->hooks = ms_list_remove_link(lc->hooks,elem);
                        ms_free(h);
                        return;
                }
index 4aae6d3b937e51f3b605706c597fc6d1b096b647..559547727bb348bf365cd60fae4997d946d41895 100644 (file)
@@ -1097,9 +1097,18 @@ void linphone_proxy_config_update(LinphoneProxyConfig *cfg){
                if (cfg->type && cfg->ssctx==NULL){
                        linphone_proxy_config_activate_sip_setup(cfg);
                }
-               if ((!lc->sip_conf.register_only_when_network_is_up || lc->network_reachable) &&
-                       (!lc->sip_conf.register_only_when_upnp_is_ok || linphone_core_get_upnp_state(lc) == LinphoneUpnpStateOk))
-                       linphone_proxy_config_register(cfg);
+               switch(linphone_core_get_firewall_policy(lc)) {
+                       case LinphonePolicyUseUpnp:
+#ifdef BUILD_UPNP
+                       if(lc->upnp != NULL && !linphone_upnp_context_is_ready_for_register(lc->upnp)) {
+                               break;
+                       }
+#endif         
+                       default:
+                       if ((!lc->sip_conf.register_only_when_network_is_up || lc->network_reachable)) {
+                               linphone_proxy_config_register(cfg);
+                       }
+               }       
                if (cfg->publish && cfg->publish_op==NULL){
                        linphone_proxy_config_send_publish(cfg,lc->presence_mode);
                }
index 105f4a8ddab2bcc79b6d504011edbfa4d136e99d..0713a1d52854a91e4494cbc4ad8d9517cce41356 100644 (file)
@@ -24,6 +24,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 #define UPNP_ADD_MAX_RETRY 4
 #define UPNP_REMOVE_MAX_RETRY 4
 #define UPNP_SECTION_NAME "uPnP"
+#define UPNP_CORE_RETRY_DELAY 4
+#define UPNP_CALL_RETRY_DELAY 1
 
 /*
  * uPnP Definitions
@@ -41,6 +43,7 @@ typedef struct _UpnpPortBinding {
        int ref;
        bool_t to_remove;
        bool_t to_add;
+       time_t last_update;
 } UpnpPortBinding;
 
 typedef struct _UpnpStream {
@@ -77,19 +80,24 @@ 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_new_with_parameters(upnp_igd_ip_protocol protocol, int local_port, int external_port);
+UpnpPortBinding *linphone_upnp_port_binding_new_or_collect(MSList *list, upnp_igd_ip_protocol protocol, int local_port, int external_port); 
 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_update_port_binding(UpnpContext *lupnp, UpnpPortBinding **port_mapping, upnp_igd_ip_protocol protocol, int port, int retry_delay); 
 void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port);
 void linphone_upnp_port_binding_release(UpnpPortBinding *port);
 
+// Configuration
 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 
+int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry);
+int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry);
 
 
 /**
@@ -172,7 +180,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) {
                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) {
+               if(linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, TRUE) != 0) {
                        linphone_upnp_port_binding_log(ORTP_ERROR, "Can't add port binding", port_mapping);
                }
 
@@ -190,7 +198,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) {
        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) {
+               if(linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE) != 0) {
                        linphone_upnp_port_binding_log(ORTP_ERROR, "Can't remove port binding", port_mapping);
                        linphone_upnp_config_remove_port_binding(lupnp, port_mapping);
                }
@@ -208,7 +216,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) {
                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);
+                               linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, FALSE);
                        } else if(port_mapping->state == LinphoneUpnpStateKo) {
                                port_mapping->to_remove = FALSE;
                        }
@@ -216,7 +224,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) {
                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);
+                               linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, FALSE);
                        }
                }
 
@@ -239,9 +247,7 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) {
  */
 
 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);
@@ -253,31 +259,10 @@ UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) {
        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;
-       }
+       // Init ports
+       lupnp->sip_udp = NULL;
+       lupnp->sip_tcp = NULL;
+       lupnp->sip_tls = NULL;
 
        linphone_core_add_iterate_hook(lc, linphone_core_upnp_hook, lupnp);
 
@@ -289,17 +274,6 @@ UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) {
                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;
        upnp_igd_start(lupnp->upnp_igd_ctxt);
 
@@ -307,22 +281,19 @@ UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) {
 }
 
 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);
-        */
+       linphone_core_remove_iterate_hook(lupnp->lc, linphone_core_upnp_hook, lupnp);
 
        ms_mutex_lock(&lupnp->mutex);
        
        /* Send port binding removes */
        if(lupnp->sip_udp != NULL) {
-               linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_udp);
+               linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_udp, TRUE);
        }
        if(lupnp->sip_tcp != NULL) {
-               linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tcp);
+               linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tcp, TRUE);
        }
        if(lupnp->sip_tls != NULL) {
-               linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tls);
+               linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tls, TRUE);
        }
 
        /* Wait all pending bindings are done */
@@ -376,31 +347,72 @@ LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *lupnp) {
        return state;
 }
 
+bool_t linphone_upnp_context_is_ready_for_register(UpnpContext *lupnp) {
+       bool_t ready = TRUE;
+       ms_mutex_lock(&lupnp->mutex);
+
+       // 1 Check global uPnP state
+       ready = (lupnp->state == LinphoneUpnpStateOk);
+       
+       // 2 Check external ip address
+       if(ready && upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt) == NULL) {
+               ready = FALSE;
+       }
+       
+       // 3 Check sip ports bindings
+       if(ready) {
+               if(lupnp->sip_udp != NULL) {
+                       if(lupnp->sip_udp->state != LinphoneUpnpStateOk) {
+                               ready = FALSE;
+                       }
+               } else if(lupnp->sip_tcp != NULL) {
+                       if(lupnp->sip_tcp->state != LinphoneUpnpStateOk) {
+                               ready = FALSE;
+                       }
+               } else if(lupnp->sip_tls != NULL) {
+                       if(lupnp->sip_tls->state != LinphoneUpnpStateOk) {
+                               ready = FALSE;
+                       }
+               } else {
+                       ready = FALSE;
+               }
+       }
+
+       ms_mutex_unlock(&lupnp->mutex);
+       return ready;
+}
+
 int linphone_upnp_context_get_external_port(UpnpContext *lupnp) {
        int port = -1;
        ms_mutex_lock(&lupnp->mutex);
        
-       /* Send port binding removes */
        if(lupnp->sip_udp != NULL) {
-               if(lupnp->sip_udp->state == LinphoneUpnpStateOk)
+               if(lupnp->sip_udp->state == LinphoneUpnpStateOk) {
                        port = lupnp->sip_udp->external_port;
+               }
        } else if(lupnp->sip_tcp != NULL) {
-               if(lupnp->sip_tcp->state == LinphoneUpnpStateOk)
+               if(lupnp->sip_tcp->state == LinphoneUpnpStateOk) {
                        port = lupnp->sip_tcp->external_port;
+               }
        } else if(lupnp->sip_tls != NULL) {
-               if(lupnp->sip_tls->state == LinphoneUpnpStateOk)
+               if(lupnp->sip_tls->state == LinphoneUpnpStateOk) {
                        port = lupnp->sip_tls->external_port;
+               }
        }
        
        ms_mutex_unlock(&lupnp->mutex);
        return port;
 }
 
-const char* linphone_upnp_context_get_external_ipaddress(UpnpContext *ctx) {
-       return upnp_igd_get_external_ipaddress(ctx->upnp_igd_ctxt);
+const char* linphone_upnp_context_get_external_ipaddress(UpnpContext *lupnp) {
+       const char* addr = NULL;
+       ms_mutex_lock(&lupnp->mutex);
+       addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
+       ms_mutex_unlock(&lupnp->mutex);
+       return addr;
 }
 
-int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port) {
+int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry) {
        upnp_igd_port_mapping mapping;
        char description[128];
        int ret;
@@ -428,6 +440,11 @@ int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBind
                                return 0;
                }
        }
+       
+       // No retry if specified
+       if(port->retry != 0 && !retry) {
+               return -1;
+       }
 
        if(port->retry >= UPNP_ADD_MAX_RETRY) {
                ret = -1;
@@ -459,7 +476,7 @@ int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBind
        return ret;
 }
 
-int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port) {
+int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry) {
        upnp_igd_port_mapping mapping;
        int ret;
        
@@ -485,6 +502,11 @@ int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortB
                                return 0;
                }
        }
+       
+       // No retry if specified
+       if(port->retry != 0 && !retry) {
+               return 1;
+       }
 
        if(port->retry >= UPNP_REMOVE_MAX_RETRY) {
                ret = -1;
@@ -513,7 +535,6 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool
        LinphoneCore *lc = call->core;
        UpnpContext *lupnp = lc->upnp;
        int ret = -1;
-       const char *local_addr, *external_addr;
 
        if(lupnp == NULL) {
                return ret;
@@ -523,58 +544,24 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool
        // 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);
-               }
+               linphone_upnp_update_port_binding(lupnp, &call->upnp_session->audio->rtp, 
+                       UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->audio_port:0, UPNP_CALL_RETRY_DELAY);
 
+               linphone_upnp_update_port_binding(lupnp, &call->upnp_session->audio->rtcp, 
+                       UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->audio_port+1:0, UPNP_CALL_RETRY_DELAY);
+               
                /*
                 * 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);
-               }
+               linphone_upnp_update_port_binding(lupnp, &call->upnp_session->video->rtp, 
+                       UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->video_port:0, UPNP_CALL_RETRY_DELAY);
+
+               linphone_upnp_update_port_binding(lupnp, &call->upnp_session->video->rtcp, 
+                       UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->video_port+1:0, UPNP_CALL_RETRY_DELAY);
        }
 
        ms_mutex_unlock(&lupnp->mutex);
@@ -588,6 +575,7 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool
 }
 
 
+
 int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md) {
        bool_t audio = FALSE;
        bool_t video = FALSE;
@@ -615,6 +603,23 @@ void linphone_core_update_upnp_state_in_call_stats(LinphoneCall *call) {
        call->stats[LINPHONE_CALL_STATS_VIDEO].upnp_state = call->upnp_session->video->state;
 }
 
+void linphone_upnp_update_stream_state(UpnpStream *stream) {
+       if((stream->rtp == NULL || stream->rtp->state == LinphoneUpnpStateOk || stream->rtp->state == LinphoneUpnpStateIdle) &&
+          (stream->rtcp == NULL || stream->rtcp->state == LinphoneUpnpStateOk || stream->rtcp->state == LinphoneUpnpStateIdle)) {
+               stream->state = LinphoneUpnpStateOk;
+       } else if((stream->rtp != NULL && 
+                     (stream->rtp->state == LinphoneUpnpStateAdding || stream->rtp->state == LinphoneUpnpStateRemoving)) ||
+                 (stream->rtcp != NULL && 
+                    (stream->rtcp->state == LinphoneUpnpStateAdding || stream->rtcp->state == LinphoneUpnpStateRemoving))) {
+               stream->state = LinphoneUpnpStatePending;
+       } else if((stream->rtp != NULL && stream->rtp->state == LinphoneUpnpStateKo) ||
+                       (stream->rtcp != NULL && stream->rtcp->state == LinphoneUpnpStateKo)) {
+               stream->state = LinphoneUpnpStateKo;
+       } else {
+               ms_error("Invalid stream %p state", stream);            
+       }
+}
+
 int linphone_upnp_call_process(LinphoneCall *call) {
        LinphoneCore *lc = call->core;
        UpnpContext *lupnp = lc->upnp;
@@ -634,38 +639,12 @@ int linphone_upnp_call_process(LinphoneCall *call) {
                /*
                 * 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;
-               }
+               linphone_upnp_update_stream_state(call->upnp_session->audio);
 
                /*
                 * 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;
-               }
+               linphone_upnp_update_stream_state(call->upnp_session->video);
 
                /*
                 * Update session state
@@ -733,7 +712,6 @@ void linphone_core_upnp_refresh(UpnpContext *lupnp) {
 
        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);
        }
@@ -744,26 +722,32 @@ void linphone_core_upnp_refresh(UpnpContext *lupnp) {
                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);
+                       if(call->upnp_session->audio->rtp != NULL) {
+                               global_list = ms_list_append(global_list, call->upnp_session->audio->rtp);
+                       }
+                       if(call->upnp_session->audio->rtcp != NULL) {
+                               global_list = ms_list_append(global_list, call->upnp_session->audio->rtcp);
+                       }
+                       if(call->upnp_session->video->rtp != NULL) {
+                               global_list = ms_list_append(global_list, call->upnp_session->video->rtp);
+                       }
+                       if(call->upnp_session->video->rtcp != NULL) {
+                               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);
+                               linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE);
                        } else if(port_mapping2->state == LinphoneUpnpStateIdle){
                                /* Force to remove */
                                port_mapping2->state = LinphoneUpnpStateOk;
@@ -777,20 +761,76 @@ void linphone_core_upnp_refresh(UpnpContext *lupnp) {
        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);
+               linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE);
+               linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, TRUE);
                list = list->next;
        }
        global_list = ms_list_free(global_list);
 }
 
+void linphone_upnp_update_port_binding(UpnpContext *lupnp, UpnpPortBinding **port_mapping, upnp_igd_ip_protocol protocol, int port, int retry_delay) {
+       const char *local_addr, *external_addr;
+       time_t now = time(NULL);
+       if(port != 0) {
+               if(*port_mapping != NULL) {
+                       if(port != (*port_mapping)->local_port) {
+                               linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
+                               *port_mapping = NULL;
+                       }
+               }
+               if(*port_mapping == NULL) {
+                       *port_mapping = linphone_upnp_port_binding_new_or_collect(lupnp->pending_bindings, protocol, port, port);
+               }
+               
+               // Get addresses
+               local_addr = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt);
+               external_addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
+               
+               // Force binding update on local address change
+               if(local_addr != NULL) {
+                       if(strncmp((*port_mapping)->local_addr, local_addr, sizeof((*port_mapping)->local_addr))) {
+                               linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
+                               strncpy((*port_mapping)->local_addr, local_addr, sizeof((*port_mapping)->local_addr));
+                       }
+               } else {
+                       ms_warning("uPnP IGD: can't get local address");
+               }
+               if(external_addr != NULL) {
+                       strncpy((*port_mapping)->external_addr, external_addr, sizeof((*port_mapping)->external_addr));
+               } else {
+                       ms_warning("uPnP IGD: can't get external address");
+               }
+
+               // Add (if not already done) the binding
+               if(now - (*port_mapping)->last_update >= retry_delay) {
+                       (*port_mapping)->last_update = now;
+                       linphone_upnp_context_send_add_port_binding(lupnp, *port_mapping, FALSE);
+               }
+       } else {
+               if(*port_mapping != NULL) {
+                       linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
+                       *port_mapping = NULL;
+               }
+       }
+}
+
 bool_t linphone_core_upnp_hook(void *data) {
        char key[64];
+       LCSipTransports transport;
        MSList *item;
        UpnpPortBinding *port_mapping;
        UpnpContext *lupnp = (UpnpContext *)data;
+
        ms_mutex_lock(&lupnp->mutex);
 
+       /* Update ports */
+       if(lupnp->state == LinphoneUpnpStateOk) {
+               linphone_core_get_sip_transports(lupnp->lc, &transport);
+               linphone_upnp_update_port_binding(lupnp, &lupnp->sip_udp, UPNP_IGD_IP_PROTOCOL_UDP, transport.udp_port, UPNP_CORE_RETRY_DELAY);
+               linphone_upnp_update_port_binding(lupnp, &lupnp->sip_tcp, UPNP_IGD_IP_PROTOCOL_TCP, transport.tcp_port, UPNP_CORE_RETRY_DELAY);
+               linphone_upnp_update_port_binding(lupnp, &lupnp->sip_tls, UPNP_IGD_IP_PROTOCOL_TCP, transport.tls_port, UPNP_CORE_RETRY_DELAY);
+       }
+
        /* Add configs */
        for(item = lupnp->adding_configs;item!=NULL;item=item->next) {
                port_mapping = (UpnpPortBinding *)item->data;
@@ -835,11 +875,11 @@ int linphone_core_update_local_media_description_from_upnp(SalMediaDescription *
                        upnpStream = session->video;
                }
                if(upnpStream != NULL) {
-                       if(upnpStream->rtp->state == LinphoneUpnpStateOk) {
+                       if(upnpStream->rtp != NULL && 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) {
+                       if(upnpStream->rtcp != NULL && upnpStream->rtcp->state == LinphoneUpnpStateOk) {
                                strncpy(stream->rtcp_addr, upnpStream->rtcp->external_addr, LINPHONE_IPADDR_SIZE);
                                stream->rtcp_port = upnpStream->rtcp->external_port;
                        }
@@ -858,6 +898,7 @@ UpnpPortBinding *linphone_upnp_port_binding_new() {
        port = ms_new0(UpnpPortBinding,1);
        ms_mutex_init(&port->mutex, NULL);
        port->state = LinphoneUpnpStateIdle;
+       port->protocol = UPNP_IGD_IP_PROTOCOL_UDP;      
        port->local_addr[0] = '\0';
        port->local_port = -1;
        port->external_addr[0] = '\0';
@@ -865,9 +906,30 @@ UpnpPortBinding *linphone_upnp_port_binding_new() {
        port->to_remove = FALSE;
        port->to_add = FALSE;
        port->ref = 1;
+       port->last_update = 0;
        return port;
 }
 
+UpnpPortBinding *linphone_upnp_port_binding_new_with_parameters(upnp_igd_ip_protocol protocol, int local_port, int external_port) {
+       UpnpPortBinding *port_binding = linphone_upnp_port_binding_new();
+       port_binding->protocol = protocol;
+       port_binding->local_port = local_port;
+       port_binding->external_port = external_port;
+       return port_binding;
+}
+
+UpnpPortBinding *linphone_upnp_port_binding_new_or_collect(MSList *list, upnp_igd_ip_protocol protocol, int local_port, int external_port) {
+       UpnpPortBinding *tmp_binding;
+       UpnpPortBinding *end_binding;
+       end_binding = linphone_upnp_port_binding_new_with_parameters(protocol, local_port, external_port);
+       tmp_binding = linphone_upnp_port_binding_equivalent_in_list(list, end_binding);
+       if(tmp_binding != NULL) {
+               linphone_upnp_port_binding_release(end_binding);
+               end_binding = tmp_binding;
+       }
+       return end_binding;     
+} 
+
 UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port) {
        UpnpPortBinding *new_port = NULL;
        new_port = ms_new0(UpnpPortBinding,1);
@@ -939,18 +1001,20 @@ void linphone_upnp_port_binding_release(UpnpPortBinding *port) {
 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;
+       stream->rtp = NULL; 
+       stream->rtcp = NULL;
        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;
+       if(stream->rtp != NULL) {
+               linphone_upnp_port_binding_release(stream->rtp);
+               stream->rtp = NULL;
+       }
+       if(stream->rtcp != NULL) {
+               linphone_upnp_port_binding_release(stream->rtcp);
+               stream->rtcp = NULL;
+       }
        ms_free(stream);
 }
 
@@ -973,10 +1037,18 @@ void linphone_upnp_session_destroy(UpnpSession *session) {
 
        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);
+               if(session->audio->rtp != NULL) {
+                       linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtp, TRUE);
+               }
+               if(session->audio->rtcp != NULL) {
+                       linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtcp, TRUE);
+               }
+               if(session->video->rtp != NULL) {
+                       linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtp, TRUE);
+               }
+               if(session->video->rtcp != NULL) {
+                       linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtcp, TRUE);
+               }
        }
 
        linphone_upnp_stream_destroy(session->audio);
index 08483588e1ccc1bf112f5ecc62c1f0fd0be1cadc..fe403a7726c63114d17ebdcc7993275e88c7d932 100644 (file)
@@ -41,6 +41,7 @@ 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);
 int linphone_upnp_context_get_external_port(UpnpContext *ctx);
+bool_t linphone_upnp_context_is_ready_for_register(UpnpContext *ctx);
 void linphone_core_update_upnp_state_in_call_stats(LinphoneCall *call);
 
 #endif //LINPHONE_UPNP_H
index c2258ea57c175a8878b91b798e8285caca266012..5aee3f4300e92b025c02544c782bad10da923951 100644 (file)
@@ -1083,6 +1083,10 @@ void linphone_gtk_show_parameters(void){
                        gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(linphone_gtk_get_widget(pb,"use_upnp")),TRUE);
                break;
        }
+       if(!linphone_core_upnp_available(lc)) {
+               gtk_widget_hide(linphone_gtk_get_widget(pb,"use_upnp"));
+       }
+
        mtu=linphone_core_get_mtu(lc);
        if (mtu<=0){
                gtk_widget_set_sensitive(linphone_gtk_get_widget(pb,"mtu"),FALSE);