]> sjero.net Git - linphone/blobdiff - coreapi/upnp.c
Fix upnp forgotten retain
[linphone] / coreapi / upnp.c
index 3a68cbcbedfd92c030884aaea4b1db9aebc99773..0922dad59eb7aa7c66d8f7517da589715f2c1422 100644 (file)
@@ -19,51 +19,154 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
 #include "upnp.h"
 #include "private.h"
+#include "lpconfig.h"
+#include <ctype.h>
 
-#define UPNP_ADD_MAX_RETRY 4
+#define UPNP_STRINGIFY(x)     #x
+#define UPNP_TOSTRING(x)      UPNP_STRINGIFY(x)
+
+#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);
+#define UPNP_SECTION_NAME     "uPnP"
+#define UPNP_CORE_READY_CHECK 1 
+#define UPNP_CORE_RETRY_DELAY 10
+#define UPNP_CALL_RETRY_DELAY 3
+#define UPNP_UUID_LEN         128
+#define UPNP_UUID_LEN_STR     UPNP_TOSTRING(UPNP_UUID_LEN)
+/*
+ * uPnP Definitions
+ */
 
-bool_t linphone_core_upnp_hook(void *data);
+typedef struct _UpnpPortBinding {
+       ms_mutex_t mutex;
+       LinphoneUpnpState state;
+       upnp_igd_ip_protocol protocol;
+       char *device_id;
+       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;
+       time_t last_update;
+} 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;
+       
+       time_t last_ready_check;
+       LinphoneUpnpState last_ready_state;
+};
 
-UpnpPortBinding *upnp_port_binding_new();
-UpnpPortBinding *upnp_port_binding_copy(const UpnpPortBinding *port);
-bool_t upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2);
-UpnpPortBinding *upnp_port_binding_retain(UpnpPortBinding *port);
-void upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port);
-void upnp_port_binding_release(UpnpPortBinding *port);
 
-MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc);
-void upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port);
-void upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *port);
+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);
+void linphone_upnp_port_binding_set_device_id(UpnpPortBinding *port, const char * device_id);
+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);
+void linphone_upnp_update_config(UpnpContext *lupnp);
+void linphone_upnp_update_proxy(UpnpContext *lupnp, bool_t force);
+
+// Configuration
+MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc, const char *device_id);
+void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port);
+void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const 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);
+
+static int linphone_upnp_strncmpi(const char *str1, const char *str2, int len) {
+       int i = 0;
+       char char1, char2;
+       while(i < len) {
+               char1 = toupper(*str1);
+               char2 = toupper(*str2);
+               if(char1 == '\0' || char1 != char2) {
+                       return char1 - char2;
+               }
+               str1++;
+               str2++;
+               i++;
+       }
+       return 0;
+}
 
-int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *port);
-int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port);
+static int linphone_upnp_str_min(const char *str1, const char *str2) {
+       int len1 = strlen(str1);
+       int len2 = strlen(str2);
+       if(len1 > len2) {
+               return len2;
+       }
+       return len1;
+}
 
+char * linphone_upnp_format_device_id(const char *device_id) {
+       char *ret = NULL;
+       char *tmp;
+       char tchar;
+       bool_t copy;    
+       if(device_id == NULL) {
+               return ret;
+       }
+       ret = ms_new(char, UPNP_UUID_LEN + 1);
+       tmp = ret;
+       if(linphone_upnp_strncmpi(device_id, "uuid:", linphone_upnp_str_min(device_id, "uuid:")) == 0) {
+               device_id += strlen("uuid:");
+       }
+       while(*device_id != '\0' && tmp - ret < UPNP_UUID_LEN) {
+               copy = FALSE;
+               tchar = *device_id;
+               if(tchar >= '0' && tchar <= '9')
+                       copy = TRUE;
+               if(!copy && tchar >= 'A' && tchar <= 'Z')
+                       copy = TRUE;
+               if(!copy && tchar >= 'a' && tchar <= 'z')
+                       copy = TRUE;
+               if(copy) {
+                       *tmp = *device_id;
+                       tmp++;
+               }
+               device_id++;
+       }
+       *tmp = '\0';
+       return ret;
+}
 
 /**
  * uPnP Callbacks
@@ -89,16 +192,25 @@ void linphone_upnp_igd_print(void *cookie, upnp_igd_print_level level, const cha
 }
 
 void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) {
-       LinphoneCore *lc = (LinphoneCore *)cookie;
-       UpnpContext *lupnp = &lc->upnp;
+       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;
+       LinphoneUpnpState old_state;
+
+       if(lupnp == NULL || lupnp->upnp_igd_ctxt == NULL) {
+               ms_error("uPnP IGD: Invalid context in callback");
+               return;
+       }
+
        ms_mutex_lock(&lupnp->mutex);
+       old_state = lupnp->state;
 
        switch(event) {
+       case UPNP_IGD_DEVICE_ADDED:
+       case UPNP_IGD_DEVICE_REMOVED:
        case UPNP_IGD_EXTERNAL_IPADDRESS_CHANGED:
        case UPNP_IGD_NAT_ENABLED_CHANGED:
        case UPNP_IGD_CONNECTION_STATUS_CHANGED:
@@ -114,10 +226,10 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) {
                        lupnp->state = LinphoneUpnpStateNotAvailable;
                } else {
                        ms_message("uPnP IGD: Connected");
-                       if(lupnp->state != LinphoneUpnpStateOk) {
-                               lupnp->clean = TRUE; // Remove saved port mapping configurations
-                       }
                        lupnp->state = LinphoneUpnpStateOk;
+                       if(old_state != LinphoneUpnpStateOk) {
+                               linphone_core_upnp_refresh(lupnp);
+                       }
                }
 
                break;
@@ -127,57 +239,72 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) {
                port_mapping = (UpnpPortBinding*) mapping->cookie;
                port_mapping->external_port = mapping->remote_port;
                port_mapping->state = LinphoneUpnpStateOk;
-               upnp_port_binding_log(ORTP_MESSAGE, "Added port binding", port_mapping);
-               upnp_config_add_port_binding(lc, port_mapping);
+               linphone_upnp_port_binding_log(ORTP_MESSAGE, "Added port binding", port_mapping);
+               linphone_upnp_config_add_port_binding(lupnp, port_mapping);
 
-               lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping);
-               upnp_port_binding_release(port_mapping);
                break;
 
        case UPNP_IGD_PORT_MAPPING_ADD_FAILURE:
                mapping = (upnp_igd_port_mapping *) arg;
                port_mapping = (UpnpPortBinding*) mapping->cookie;
-               if(upnp_context_send_add_port_binding(lc, port_mapping) != 0) {
-                       upnp_port_binding_log(ORTP_ERROR, "Can't add port binding", port_mapping);
+               port_mapping->external_port = -1; //Force random external port
+               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);
                }
 
-               lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping);
-               upnp_port_binding_release(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;
-               upnp_port_binding_log(ORTP_MESSAGE, "Removed port binding", port_mapping);
-               upnp_config_remove_port_binding(lc, port_mapping);
+               linphone_upnp_port_binding_log(ORTP_MESSAGE, "Removed port binding", port_mapping);
+               linphone_upnp_config_remove_port_binding(lupnp, port_mapping);
 
-               lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping);
-               upnp_port_binding_release(port_mapping);
                break;
 
        case UPNP_IGD_PORT_MAPPING_REMOVE_FAILURE:
                mapping = (upnp_igd_port_mapping *) arg;
                port_mapping = (UpnpPortBinding*) mapping->cookie;
-               if(upnp_context_send_remove_port_binding(lc, port_mapping) != 0) {
-                       upnp_port_binding_log(ORTP_ERROR, "Can't remove port binding", port_mapping);
-                       upnp_config_remove_port_binding(lc, port_mapping);
+               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);
                }
 
-               lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping);
-               upnp_port_binding_release(port_mapping);
                break;
 
        default:
                break;
        }
 
-       if(lupnp->pending_bindings == NULL) {
-               if(lupnp->cleaning == TRUE) {
-                       lupnp->emit = TRUE; // Emit port bindings
-                       lupnp->cleaning = FALSE;
+       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, FALSE);
+                       } 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, FALSE);
+                       }
                }
-               pthread_cond_signal(&lupnp->cond);
+
+               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) {
+               ms_cond_signal(&lupnp->empty_cond);
        }
        ms_mutex_unlock(&lupnp->mutex);
 }
@@ -187,166 +314,257 @@ void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) {
  * uPnP Context
  */
 
-int upnp_context_init(LinphoneCore *lc) {
-       LCSipTransports transport;
-       UpnpContext *lupnp = &lc->upnp;
-       const char *ip_address;
+UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) {
+       UpnpContext *lupnp = (UpnpContext *)ms_new0(UpnpContext,1);
 
        ms_mutex_init(&lupnp->mutex, NULL);
-       ms_cond_init(&lupnp->cond, NULL);
+       ms_cond_init(&lupnp->empty_cond, NULL);
 
+       lupnp->last_ready_check = 0;
+       lupnp->last_ready_state = LinphoneUpnpStateIdle;
+
+       lupnp->lc = lc;
        lupnp->pending_bindings = NULL;
        lupnp->adding_configs = NULL;
        lupnp->removing_configs = NULL;
-       lupnp->clean = FALSE;
-       lupnp->cleaning = FALSE;
-       lupnp->emit = FALSE;
        lupnp->state = LinphoneUpnpStateIdle;
-       ms_message("uPnP IGD: Init");
-
-       linphone_core_get_sip_transports(lc, &transport);
-       if(transport.udp_port != 0) {
-               lupnp->sip_udp = 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 = 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 = 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;
-       }
+       ms_message("uPnP IGD: New %p for core %p", lupnp, lc);
+
+       // Init ports
+       lupnp->sip_udp = NULL;
+       lupnp->sip_tcp = NULL;
+       lupnp->sip_tls = NULL;
 
-       linphone_core_add_iterate_hook(lc, linphone_core_upnp_hook, lc);
+       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, lc);
+       lupnp->upnp_igd_ctxt = upnp_igd_create(linphone_upnp_igd_callback, linphone_upnp_igd_print, NULL, lupnp);
        if(lupnp->upnp_igd_ctxt == NULL) {
                lupnp->state = LinphoneUpnpStateKo;
                ms_error("Can't create uPnP IGD context");
-               return -1;
-       }
-
-       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));
+               return NULL;
        }
 
        lupnp->state = LinphoneUpnpStatePending;
-       return 0;
-}
+       upnp_igd_start(lupnp->upnp_igd_ctxt);
 
-void upnp_context_uninit(LinphoneCore *lc) {
-       UpnpContext *lupnp = &lc->upnp;
+       return lupnp;
+}
 
-       /*
-        * Not need, all hooks are removed before
-        * linphone_core_remove_iterate_hook(lc, linphone_core_upnp_hook, lc);
-     */
+void linphone_upnp_context_destroy(UpnpContext *lupnp) {
+       linphone_core_remove_iterate_hook(lupnp->lc, linphone_core_upnp_hook, lupnp);
 
-       /* Send port binding removes */
-       if(lupnp->sip_udp != NULL) {
-               upnp_context_send_remove_port_binding(lc, lupnp->sip_udp);
-               lupnp->sip_udp = NULL;
-       }
-       if(lupnp->sip_tcp != NULL) {
-               upnp_context_send_remove_port_binding(lc, lupnp->sip_tcp);
-               lupnp->sip_tcp = NULL;
-       }
-       if(lupnp->sip_tls != NULL) {
-               upnp_context_send_remove_port_binding(lc, lupnp->sip_tls);
-               lupnp->sip_tcp = NULL;
+       ms_mutex_lock(&lupnp->mutex);
+       
+       if(lupnp->lc->network_reachable) {
+               /* Send port binding removes */
+               if(lupnp->sip_udp != NULL) {
+                       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, TRUE);
+               }
+               if(lupnp->sip_tls != NULL) {
+                       linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tls, TRUE);
+               }
        }
 
        /* Wait all pending bindings are done */
-       ms_message("uPnP IGD: Wait all pending port bindings ...");
-       ms_mutex_lock(&lupnp->mutex);
-       ms_cond_wait(&lupnp->cond, &lupnp->mutex);
+       if(lupnp->pending_bindings != NULL) {
+               ms_message("uPnP IGD: Wait all pending port bindings ...");
+               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);
+               lupnp->upnp_igd_ctxt = NULL;
        }
+       
+       /* No more multi threading here */
 
-       /* Run one time the hook for configuration update */
-       linphone_core_upnp_hook(lc);
+       /* Run one more time configuration update and proxy */
+       linphone_upnp_update_config(lupnp);
+       linphone_upnp_update_proxy(lupnp, TRUE);
 
        /* Release port bindings */
        if(lupnp->sip_udp != NULL) {
-               upnp_port_binding_release(lupnp->sip_udp);
+               linphone_upnp_port_binding_release(lupnp->sip_udp);
                lupnp->sip_udp = NULL;
        }
        if(lupnp->sip_tcp != NULL) {
-               upnp_port_binding_release(lupnp->sip_tcp);
+               linphone_upnp_port_binding_release(lupnp->sip_tcp);
                lupnp->sip_tcp = NULL;
        }
        if(lupnp->sip_tls != NULL) {
-               upnp_port_binding_release(lupnp->sip_tls);
+               linphone_upnp_port_binding_release(lupnp->sip_tls);
                lupnp->sip_tcp = NULL;
        }
 
        /* Release lists */
-       ms_list_for_each(lupnp->adding_configs,(void (*)(void*))upnp_port_binding_release);
+       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*))upnp_port_binding_release);
+       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*))upnp_port_binding_release);
+       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->cond);
+       ms_cond_destroy(&lupnp->empty_cond);
+
+       ms_message("uPnP IGD: destroy %p", lupnp);
+       ms_free(lupnp);
+}
 
-       ms_message("uPnP IGD: Uninit");
+LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *lupnp) {
+       LinphoneUpnpState state = LinphoneUpnpStateKo;
+       if(lupnp != NULL) {
+               ms_mutex_lock(&lupnp->mutex);
+               state = lupnp->state;
+               ms_mutex_unlock(&lupnp->mutex);
+       }
+       return state;
 }
 
-int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port) {
-       UpnpContext *lupnp = &lc->upnp;
+bool_t _linphone_upnp_context_is_ready_for_register(UpnpContext *lupnp) {
+       bool_t ready = TRUE;
+       
+       // 1 Check global uPnP state
+       ready = (lupnp->state == LinphoneUpnpStateOk);
+       
+       // 2 Check external ip address
+       if(ready) {
+               if (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;
+               }
+       }
+       
+       return ready;
+}
+
+bool_t linphone_upnp_context_is_ready_for_register(UpnpContext *lupnp) {
+       bool_t ready = FALSE;
+       if(lupnp != NULL) {
+               ms_mutex_lock(&lupnp->mutex);
+               ready = _linphone_upnp_context_is_ready_for_register(lupnp);
+               ms_mutex_unlock(&lupnp->mutex);
+       }
+       return ready;
+}
+
+int linphone_upnp_context_get_external_port(UpnpContext *lupnp) {
+       int port = -1;
+       if(lupnp != NULL) {
+               ms_mutex_lock(&lupnp->mutex);
+               
+               if(lupnp->sip_udp != NULL) {
+                       if(lupnp->sip_udp->state == LinphoneUpnpStateOk) {
+                               port = lupnp->sip_udp->external_port;
+                       }
+               } else if(lupnp->sip_tcp != NULL) {
+                       if(lupnp->sip_tcp->state == LinphoneUpnpStateOk) {
+                               port = lupnp->sip_tcp->external_port;
+                       }
+               } else if(lupnp->sip_tls != NULL) {
+                       if(lupnp->sip_tls->state == LinphoneUpnpStateOk) {
+                               port = lupnp->sip_tls->external_port;
+                       }
+               }
+               
+               ms_mutex_unlock(&lupnp->mutex);
+       }
+       return port;
+}
+
+void linphone_upnp_refresh(UpnpContext * lupnp) {
+       upnp_igd_refresh(lupnp->upnp_igd_ctxt);
+}
+
+const char* linphone_upnp_context_get_external_ipaddress(UpnpContext *lupnp) {
+       const char* addr = NULL;
+       if(lupnp != 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, bool_t retry) {
        upnp_igd_port_mapping mapping;
+       char description[128];
        int ret;
-       if(port->state == LinphoneUpnpStateIdle) {
-               port->retry = 0;
-               port->state = LinphoneUpnpStateAdding;
-       } else if(port->state != LinphoneUpnpStateAdding) {
-               ms_error("uPnP: try to add a port binding in wrong state: %d", port->state);
+       
+       if(lupnp->state != LinphoneUpnpStateOk) {
                return -2;
        }
 
+       // 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;
+               }
+       }
+       
+       // No retry if specified
+       if(port->retry != 0 && !retry) {
+               return -1;
+       }
+
        if(port->retry >= UPNP_ADD_MAX_RETRY) {
                ret = -1;
        } else {
-               mapping.cookie = upnp_port_binding_retain(port);
+               linphone_upnp_port_binding_set_device_id(port, upnp_igd_get_device_id(lupnp->upnp_igd_ctxt));
+               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; // TODO: use better method
-               else
-                       mapping.remote_port = port->external_port;
+                       port->external_port = rand()%(0xffff - 1024) + 1024;
+               mapping.remote_port = port->external_port;
                mapping.remote_host = "";
-               mapping.description = PACKAGE_NAME;
+               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) {
@@ -355,28 +573,50 @@ int upnp_context_send_add_port_binding(LinphoneCore *lc, UpnpPortBinding *port)
        return ret;
 }
 
-int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *port) {
-       UpnpContext *lupnp = &lc->upnp;
+int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry) {
        upnp_igd_port_mapping mapping;
        int ret;
-       if(port->state == LinphoneUpnpStateOk) {
-               port->retry = 0;
-               port->state = LinphoneUpnpStateRemoving;
-       } else if(port->state != LinphoneUpnpStateRemoving) {
-               ms_error("uPnP: try to remove a port binding in wrong state: %d", port->state);
+       
+       if(lupnp->state != LinphoneUpnpStateOk) {
                return -2;
        }
 
+       // 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;
+               }
+       }
+       
+       // No retry if specified
+       if(port->retry != 0 && !retry) {
+               return 1;
+       }
+
        if(port->retry >= UPNP_REMOVE_MAX_RETRY) {
                ret = -1;
        } else {
-               mapping.cookie = upnp_port_binding_retain(port);
+               linphone_upnp_port_binding_set_device_id(port, upnp_igd_get_device_id(lupnp->upnp_igd_ctxt));
+               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) {
@@ -391,76 +631,36 @@ int upnp_context_send_remove_port_binding(LinphoneCore *lc, UpnpPortBinding *por
 
 int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool_t video) {
        LinphoneCore *lc = call->core;
-       UpnpContext *lupnp = &lc->upnp;
+       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(call->upnp_session->audio->rtp->state == LinphoneUpnpStateIdle && audio) {
-                       // Add audio port binding
-                       upnp_context_send_add_port_binding(lc, call->upnp_session->audio->rtp);
-               } else if(call->upnp_session->audio->rtp->state == LinphoneUpnpStateOk && !audio) {
-                       // Remove audio port binding
-                       upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtp);
-               }
-               if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateIdle && audio) {
-                       // Add audio port binding
-                       upnp_context_send_add_port_binding(lc, call->upnp_session->audio->rtcp);
-               } else if(call->upnp_session->audio->rtcp->state == LinphoneUpnpStateOk && !audio) {
-                       // Remove audio port binding
-                       upnp_context_send_remove_port_binding(lc, 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(call->upnp_session->video->rtp->state == LinphoneUpnpStateIdle && video) {
-                       // Add video port binding
-                       upnp_context_send_add_port_binding(lc, call->upnp_session->video->rtp);
-               } else if(call->upnp_session->video->rtp->state == LinphoneUpnpStateOk && !video) {
-                       // Remove video port binding
-                       upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtp);
-               }
-               if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateIdle && video) {
-                       // Add video port binding
-                       upnp_context_send_add_port_binding(lc, call->upnp_session->video->rtcp);
-               } else if(call->upnp_session->video->rtcp->state == LinphoneUpnpStateOk && !video) {
-                       // Remove video port binding
-                       upnp_context_send_remove_port_binding(lc, 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);
@@ -468,19 +668,20 @@ int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool
        /*
         * Update uPnP call state
         */
-       upnp_call_process(call);
+       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++) {
+       for (i = 0; i < md->n_total_streams; i++) {
                stream = &md->streams[i];
                if(stream->type == SalAudio) {
                        audio = TRUE;
@@ -496,11 +697,37 @@ int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call) {
        return linphone_core_update_upnp_audio_video(call, call->audiostream!=NULL, call->videostream!=NULL);
 }
 
-int upnp_call_process(LinphoneCall *call) {
+void linphone_core_update_upnp_state_in_call_stats(LinphoneCall *call) {
+       call->stats[LINPHONE_CALL_STATS_AUDIO].upnp_state = call->upnp_session->audio->state;
+       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;
+       UpnpContext *lupnp = lc->upnp;
        int ret = -1;
-       UpnpState oldState;
+       LinphoneUpnpState oldState = 0, newState = 0;
+
+       if(lupnp == NULL) {
+               return ret;
+       }
 
        ms_mutex_lock(&lupnp->mutex);
 
@@ -511,39 +738,18 @@ int 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 stat
+                */
+               linphone_core_update_upnp_state_in_call_stats(call);
+               
                /*
                 * Update session state
                 */
@@ -560,129 +766,238 @@ int upnp_call_process(LinphoneCall *call) {
                } else {
                        call->upnp_session->state = LinphoneUpnpStateIdle;
                }
+               newState = call->upnp_session->state;
+       }
 
-               /* 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);
+       
+       /* When change is done proceed update */
+       if(oldState != LinphoneUpnpStateOk && oldState != LinphoneUpnpStateKo &&
+                       (newState == LinphoneUpnpStateOk || newState == 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;
 }
 
-bool_t linphone_core_upnp_hook(void *data) {
-       char key[64];
+void linphone_core_upnp_refresh(UpnpContext *lupnp) {
+       MSList *global_list = NULL;
        MSList *list = NULL;
        MSList *item;
-       UpnpPortBinding *port_mapping;
-       LinphoneCore *lc = (LinphoneCore *)data;
        LinphoneCall *call;
-       UpnpContext *lupnp = &lc->upnp;
-       ms_mutex_lock(&lupnp->mutex);
+       UpnpPortBinding *port_mapping, *port_mapping2;
 
-       if(lupnp->clean && !lupnp->cleaning) {
-               lupnp->clean = FALSE;
-               // Remove old mapping
-               list = upnp_config_list_port_bindings(lc->config);
-               if(list == NULL) {
-                       lupnp->emit = TRUE;
-               } else {
-                       lupnp->cleaning = TRUE;
-                       for(item = list;item != NULL; item = item->next) {
-                               port_mapping = (UpnpPortBinding *)item->data;
-                               upnp_context_send_remove_port_binding(lc, port_mapping);
+       ms_message("uPnP IGD: Refresh mappings");
+
+       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);
+       }
+
+       list = lupnp->lc->calls;
+       while(list != NULL) {
+               call = (LinphoneCall *)list->data;
+               if(call->upnp_session != NULL) {
+                       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);
                        }
-                       ms_list_for_each(list,(void (*)(void*))upnp_port_binding_release);
-                       list = ms_list_free(list);
                }
+               list = list->next;
+       }
+
+       list = linphone_upnp_config_list_port_bindings(lupnp->lc->config, upnp_igd_get_device_id(lupnp->upnp_igd_ctxt));
+       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, TRUE);
+                       } 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);
 
-       if(lupnp->emit) {
-               lupnp->emit = FALSE;
 
-               /* Force port bindings */
-               if(lupnp->sip_udp != NULL) {
-                       lupnp->sip_udp->state = LinphoneUpnpStateIdle;
-                       upnp_context_send_add_port_binding(lc, lupnp->sip_udp);
+       // (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, 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(lupnp->sip_tcp != NULL) {
-                       lupnp->sip_udp->state = LinphoneUpnpStateIdle;
-                       upnp_context_send_add_port_binding(lc, lupnp->sip_tcp);
+               if(*port_mapping == NULL) {
+                       *port_mapping = linphone_upnp_port_binding_new_or_collect(lupnp->pending_bindings, protocol, port, port);
                }
-               if(lupnp->sip_tls != NULL) {
-                       lupnp->sip_udp->state = LinphoneUpnpStateIdle;
-                       upnp_context_send_add_port_binding(lc, lupnp->sip_tls);
+               
+               // 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));
+                       }
+               }
+               if(external_addr != NULL) {
+                       strncpy((*port_mapping)->external_addr, external_addr, sizeof((*port_mapping)->external_addr));
+               }
+
+               // 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);
                }
-               list = lc->calls;
-               while(list != NULL) {
-                       call = (LinphoneCall *)list->data;
-                       call->upnp_session->audio->rtp->state = LinphoneUpnpStateIdle;
-                       call->upnp_session->audio->rtcp->state = LinphoneUpnpStateIdle;
-                       call->upnp_session->video->rtp->state = LinphoneUpnpStateIdle;
-                       call->upnp_session->video->rtcp->state = LinphoneUpnpStateIdle;
-                       linphone_core_update_upnp_audio_video(call, call->audiostream!=NULL, call->videostream!=NULL);
-                       list = list->next;
+       } else {
+               if(*port_mapping != NULL) {
+                       linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
+                       *port_mapping = NULL;
                }
        }
+}
 
+void linphone_upnp_update_config(UpnpContext* lupnp) {
+       char key[64];
+       const MSList *item;
+       UpnpPortBinding *port_mapping;
+       
        /* Add configs */
        for(item = lupnp->adding_configs;item!=NULL;item=item->next) {
                port_mapping = (UpnpPortBinding *)item->data;
-               snprintf(key, sizeof(key), "%s-%d-%d",
+               snprintf(key, sizeof(key), "%s-%s-%d-%d",
+                                       port_mapping->device_id,
                                        (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
-                                                       port_mapping->external_port,
-                                                       port_mapping->local_port);
-               lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, "uPnP");
-               upnp_port_binding_log(ORTP_DEBUG, "Configuration: Added port binding", port_mapping);
+                                       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*))upnp_port_binding_release);
+       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",
+               snprintf(key, sizeof(key), "%s-%s-%d-%d",
+                                       port_mapping->device_id,
                                        (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
-                                                       port_mapping->external_port,
-                                                       port_mapping->local_port);
-               lp_config_set_string(lc->config, UPNP_SECTION_NAME, key, NULL);
-               upnp_port_binding_log(ORTP_DEBUG, "Configuration: Removed port binding", port_mapping);
+                                       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*))upnp_port_binding_release);
+       ms_list_for_each(lupnp->removing_configs,(void (*)(void*))linphone_upnp_port_binding_release);
        lupnp->removing_configs = ms_list_free(lupnp->removing_configs);
+}
+
+void linphone_upnp_update_proxy(UpnpContext* lupnp, bool_t force) {
+       LinphoneUpnpState ready_state;
+       const MSList *item;
+       time_t now = (force)? (lupnp->last_ready_check + UPNP_CORE_READY_CHECK) : time(NULL);
+       
+       /* Refresh registers if we are ready */
+       if(now - lupnp->last_ready_check >= UPNP_CORE_READY_CHECK) {
+               lupnp->last_ready_check = now;
+               ready_state = (_linphone_upnp_context_is_ready_for_register(lupnp))? LinphoneUpnpStateOk: LinphoneUpnpStateKo;
+               if(ready_state != lupnp->last_ready_state) {
+                       for(item=linphone_core_get_proxy_config_list(lupnp->lc);item!=NULL;item=item->next) {
+                               LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)item->data;
+                               if (linphone_proxy_config_register_enabled(cfg)) {
+                                       if (ready_state != LinphoneUpnpStateOk) {
+                                               // Only reset ithe registration if we require that upnp should be ok
+                                               if(lupnp->lc->sip_conf.register_only_when_upnp_is_ok) {
+                                                       linphone_proxy_config_set_state(cfg, LinphoneRegistrationNone, "Registration impossible (uPnP not ready)");
+                                               } else {
+                                                       cfg->commit=TRUE;
+                                               }
+                                       } else {
+                                               cfg->commit=TRUE;
+                                       }
+                               }
+                       }
+                       lupnp->last_ready_state = ready_state;
+               }
+       }
+}
+
+bool_t linphone_core_upnp_hook(void *data) {
+       LCSipTransports transport;
+       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);
+       }
+
+       linphone_upnp_update_proxy(lupnp, FALSE);       
+       linphone_upnp_update_config(lupnp);
 
        ms_mutex_unlock(&lupnp->mutex);
        return TRUE;
 }
 
-void linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session) {
+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++) {
+       for (i = 0; i < desc->n_active_streams; i++) {
                stream = &desc->streams[i];
                upnpStream = NULL;
                if(stream->type == SalAudio) {
@@ -691,16 +1006,17 @@ void 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;
                        }
                }
        }
+       return 0;
 }
 
 
@@ -708,59 +1024,125 @@ void linphone_core_update_local_media_description_from_upnp(SalMediaDescription
  * uPnP Port Binding
  */
 
-UpnpPortBinding *upnp_port_binding_new() {
+UpnpPortBinding *linphone_upnp_port_binding_new() {
        UpnpPortBinding *port = NULL;
        port = ms_new0(UpnpPortBinding,1);
        ms_mutex_init(&port->mutex, NULL);
        port->state = LinphoneUpnpStateIdle;
+       port->protocol = UPNP_IGD_IP_PROTOCOL_UDP;      
+       port->device_id = NULL;
        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;
+       port->last_update = 0;
        return port;
 }
 
-UpnpPortBinding *upnp_port_binding_copy(const UpnpPortBinding *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;
+       
+       // Seek an binding with same protocol and local port
+       end_binding = linphone_upnp_port_binding_new_with_parameters(protocol, local_port, -1);
+       tmp_binding = linphone_upnp_port_binding_equivalent_in_list(list, end_binding);
+       
+       // Must be not attached to any struct 
+       if(tmp_binding != NULL && tmp_binding->ref == 1) {
+               linphone_upnp_port_binding_release(end_binding);
+               end_binding = linphone_upnp_port_binding_retain(tmp_binding);
+       } else {
+               end_binding->external_port = external_port;
+       }
+       return end_binding;     
+} 
+
+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));
+       new_port->device_id = NULL;
+       linphone_upnp_port_binding_set_device_id(new_port, port->device_id);
        ms_mutex_init(&new_port->mutex, NULL);
        new_port->ref = 1;
        return new_port;
 }
 
-void upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port) {
+void linphone_upnp_port_binding_set_device_id(UpnpPortBinding *port, const char *device_id) {
+       char *formated_device_id = linphone_upnp_format_device_id(device_id);
+       if(formated_device_id != NULL && port->device_id != NULL) {
+               if(strcmp(formated_device_id, port->device_id) == 0) {
+                       ms_free(formated_device_id);
+                       return;
+               }
+       }
+       if(port->device_id != NULL) {
+               ms_free(port->device_id);
+       }
+       port->device_id = formated_device_id;
+}
+
+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,
+               ortp_log(level, "uPnP IGD: %s %s|%d->%s:%d (retry %d)", msg,
                                                        (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
                                                                        port->external_port,
                                                                        port->local_addr,
-                                                                       port->local_port);
+                                                                       port->local_port,
+                                                                       port->retry - 1);
        } else {
-               ortp_log(level, "uPnP IGD: %s %s|%d->%d", msg,
+               ortp_log(level, "uPnP IGD: %s %s|%d->%d (retry %d)", msg,
                                                        (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
                                                                        port->external_port,
-                                                                       port->local_port);
+                                                                       port->local_port,
+                                                                       port->retry - 1);
        }
 }
 
-bool_t upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2) {
+// Return true if the binding are equivalent. (Note external_port == -1 means "don't care")
+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;
+              port1->local_port == port2->local_port &&
+              (port1->external_port == -1 || port2->external_port == -1 || port1->external_port == port2->external_port);
 }
 
-UpnpPortBinding *upnp_port_binding_retain(UpnpPortBinding *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 upnp_port_binding_release(UpnpPortBinding *port) {
+void linphone_upnp_port_binding_release(UpnpPortBinding *port) {
        ms_mutex_lock(&port->mutex);
        if(--port->ref == 0) {
+               if(port->device_id != NULL) {
+                       ms_free(port->device_id);
+               }
                ms_mutex_unlock(&port->mutex);
                ms_mutex_destroy(&port->mutex);
                ms_free(port);
@@ -774,21 +1156,23 @@ void upnp_port_binding_release(UpnpPortBinding *port) {
  * uPnP Stream
  */
 
-UpnpStream* upnp_stream_new() {
+UpnpStream* linphone_upnp_stream_new() {
        UpnpStream *stream = ms_new0(UpnpStream,1);
        stream->state = LinphoneUpnpStateIdle;
-       stream->rtp = upnp_port_binding_new();
-       stream->rtp->protocol = UPNP_IGD_IP_PROTOCOL_UDP;
-       stream->rtcp = upnp_port_binding_new();
-       stream->rtcp->protocol = UPNP_IGD_IP_PROTOCOL_UDP;
+       stream->rtp = NULL; 
+       stream->rtcp = NULL;
        return stream;
 }
 
-void upnp_stream_destroy(UpnpStream* stream) {
-       upnp_port_binding_release(stream->rtp);
-       stream->rtp = NULL;
-       upnp_port_binding_release(stream->rtcp);
-       stream->rtcp = NULL;
+void linphone_upnp_stream_destroy(UpnpStream* stream) {
+       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);
 }
 
@@ -797,35 +1181,44 @@ void upnp_stream_destroy(UpnpStream* stream) {
  * uPnP Session
  */
 
-UpnpSession* upnp_session_new() {
+UpnpSession* linphone_upnp_session_new(LinphoneCall* call) {
        UpnpSession *session = ms_new0(UpnpSession,1);
+       session->call = call;
        session->state = LinphoneUpnpStateIdle;
-       session->audio = upnp_stream_new();
-       session->video = upnp_stream_new();
+       session->audio = linphone_upnp_stream_new();
+       session->video = linphone_upnp_stream_new();
        return session;
 }
 
-void upnp_session_destroy(LinphoneCall* call) {
-       LinphoneCore *lc = call->core;
+void linphone_upnp_session_destroy(UpnpSession *session) {
+       LinphoneCore *lc = session->call->core;
 
-       /* Remove bindings */
-       if(call->upnp_session->audio->rtp->state != LinphoneUpnpStateKo && call->upnp_session->audio->rtp->state != LinphoneUpnpStateIdle) {
-               upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtp);
-       }
-       if(call->upnp_session->audio->rtcp->state != LinphoneUpnpStateKo && call->upnp_session->audio->rtcp->state != LinphoneUpnpStateIdle) {
-               upnp_context_send_remove_port_binding(lc, call->upnp_session->audio->rtcp);
-       }
-       if(call->upnp_session->video->rtp->state != LinphoneUpnpStateKo && call->upnp_session->video->rtp->state != LinphoneUpnpStateIdle) {
-               upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtp);
-       }
-       if(call->upnp_session->video->rtcp->state != LinphoneUpnpStateKo && call->upnp_session->video->rtcp->state != LinphoneUpnpStateIdle) {
-               upnp_context_send_remove_port_binding(lc, call->upnp_session->video->rtcp);
+       if(lc->upnp != NULL) {
+               /* Remove bindings */
+               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);
+               }
        }
+       
+       session->call->stats[LINPHONE_CALL_STATS_AUDIO].upnp_state = LinphoneUpnpStateKo;
+       session->call->stats[LINPHONE_CALL_STATS_VIDEO].upnp_state = LinphoneUpnpStateKo;
+       
+       linphone_upnp_stream_destroy(session->audio);
+       linphone_upnp_stream_destroy(session->video);
+       ms_free(session);
+}
 
-       upnp_stream_destroy(call->upnp_session->audio);
-       upnp_stream_destroy(call->upnp_session->video);
-       ms_free(call->upnp_session);
-       call->upnp_session = NULL;
+LinphoneUpnpState linphone_upnp_session_get_state(UpnpSession *session) {
+       return session->state;
 }
 
 
@@ -833,64 +1226,75 @@ void upnp_session_destroy(LinphoneCall* call) {
  * uPnP Config
  */
 
-MSList *upnp_config_list_port_bindings(struct _LpConfig *lpc) {
+struct linphone_upnp_config_list_port_bindings_struct {
+       struct _LpConfig *lpc;
+       MSList *retList;
+       const char *device_id;
+};
+
+static void linphone_upnp_config_list_port_bindings_cb(const char *entry, struct linphone_upnp_config_list_port_bindings_struct *cookie) {
+       char device_id[UPNP_UUID_LEN + 1];
        char protocol_str[4]; // TCP or UDP
        upnp_igd_ip_protocol protocol;
        int external_port;
        int local_port;
-       MSList *retList = NULL;
+       int ret;
+       bool_t valid = TRUE;
        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 = 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);
-                       }
+       
+       ret = sscanf(entry, "%"UPNP_UUID_LEN_STR"[^-]-%3s-%i-%i", device_id, protocol_str, &external_port, &local_port);
+       if(ret == 4) {
+               // Handle only wanted device bindings
+               if(device_id != NULL && strcmp(cookie->device_id, device_id) != 0) {
+                       return; 
+               }
+               if(linphone_upnp_strncmpi(protocol_str, "TCP", 3) == 0) {
+                       protocol = UPNP_IGD_IP_PROTOCOL_TCP;
+               } else if(linphone_upnp_strncmpi(protocol_str, "UDP", 3) == 0) {
+                       protocol = UPNP_IGD_IP_PROTOCOL_UDP;
                } 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);
+               if(valid) {
+                       port = linphone_upnp_port_binding_new();
+                       linphone_upnp_port_binding_set_device_id(port, device_id);
+                       port->state = LinphoneUpnpStateOk;
+                       port->protocol = protocol;
+                       port->external_port = external_port;
+                       port->local_port = local_port;
+                       cookie->retList = ms_list_append(cookie->retList, port);
                }
+       } else {
+               valid = FALSE;
+       }
+       if(!valid) {
+               ms_warning("uPnP configuration invalid line: %s", entry);
        }
+}
 
-       return retList;
+MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc, const char *device_id) {
+       char *formated_device_id = linphone_upnp_format_device_id(device_id);
+       struct linphone_upnp_config_list_port_bindings_struct cookie = {lpc, NULL, formated_device_id};
+       lp_config_for_each_entry(lpc, UPNP_SECTION_NAME, (void(*)(const char *, void*))linphone_upnp_config_list_port_bindings_cb, &cookie);
+       ms_free(formated_device_id);
+       return cookie.retList;
 }
 
-void upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) {
-       UpnpContext *lupnp = &lc->upnp;
+void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) {
        MSList *list;
        UpnpPortBinding *list_port;
+       
+       if(port->device_id == NULL) {
+               ms_error("Can't remove port binding without device_id");
+               return;
+       }
 
        list = lupnp->removing_configs;
        while(list != NULL) {
                list_port = (UpnpPortBinding *)list->data;
-               if(upnp_port_binding_equal(list_port, port) == TRUE) {
+               if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
                        lupnp->removing_configs = ms_list_remove(lupnp->removing_configs, list_port);
-                       upnp_port_binding_release(list_port);
+                       linphone_upnp_port_binding_release(list_port);
                        return;
                }
                list = ms_list_next(list);
@@ -899,27 +1303,31 @@ void upnp_config_add_port_binding(LinphoneCore *lc, const UpnpPortBinding *port)
        list = lupnp->adding_configs;
        while(list != NULL) {
                list_port = (UpnpPortBinding *)list->data;
-               if(upnp_port_binding_equal(list_port, port) == TRUE) {
+               if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
                        return;
                }
                list = ms_list_next(list);
        }
 
-       list_port = upnp_port_binding_copy(port);
+       list_port = linphone_upnp_port_binding_copy(port);
        lupnp->adding_configs = ms_list_append(lupnp->adding_configs, list_port);
 }
 
-void upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *port) {
-       UpnpContext *lupnp = &lc->upnp;
+void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) {
        MSList *list;
        UpnpPortBinding *list_port;
 
+       if(port->device_id == NULL) {
+               ms_error("Can't remove port binding without device_id");
+               return;
+       }
+
        list = lupnp->adding_configs;
        while(list != NULL) {
                list_port = (UpnpPortBinding *)list->data;
-               if(upnp_port_binding_equal(list_port, port) == TRUE) {
+               if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
                        lupnp->adding_configs = ms_list_remove(lupnp->adding_configs, list_port);
-                       upnp_port_binding_release(list_port);
+                       linphone_upnp_port_binding_release(list_port);
                        return;
                }
                list = ms_list_next(list);
@@ -928,12 +1336,12 @@ void upnp_config_remove_port_binding(LinphoneCore *lc, const UpnpPortBinding *po
        list = lupnp->removing_configs;
        while(list != NULL) {
                list_port = (UpnpPortBinding *)list->data;
-               if(upnp_port_binding_equal(list_port, port) == TRUE) {
+               if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
                        return;
                }
                list = ms_list_next(list);
        }
 
-       list_port = upnp_port_binding_copy(port);
+       list_port = linphone_upnp_port_binding_copy(port);
        lupnp->removing_configs = ms_list_append(lupnp->removing_configs, list_port);
 }