]> sjero.net Git - linphone/blob - coreapi/upnp.c
Fix uPnP issues. Correct registration and update with uPnP
[linphone] / coreapi / upnp.c
1 /*
2 linphone
3 Copyright (C) 2012  Belledonne Communications SARL
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 */
19
20 #include "upnp.h"
21 #include "private.h"
22 #include "lpconfig.h"
23
24 #define UPNP_ADD_MAX_RETRY    4
25 #define UPNP_REMOVE_MAX_RETRY 4
26 #define UPNP_SECTION_NAME     "uPnP"
27 #define UPNP_CORE_READY_CHECK 1 
28 #define UPNP_CORE_RETRY_DELAY 4
29 #define UPNP_CALL_RETRY_DELAY 1
30
31 /*
32  * uPnP Definitions
33  */
34
35 typedef struct _UpnpPortBinding {
36         ms_mutex_t mutex;
37         LinphoneUpnpState state;
38         upnp_igd_ip_protocol protocol;
39         char local_addr[LINPHONE_IPADDR_SIZE];
40         int local_port;
41         char external_addr[LINPHONE_IPADDR_SIZE];
42         int external_port;
43         int retry;
44         int ref;
45         bool_t to_remove;
46         bool_t to_add;
47         time_t last_update;
48 } UpnpPortBinding;
49
50 typedef struct _UpnpStream {
51         UpnpPortBinding *rtp;
52         UpnpPortBinding *rtcp;
53         LinphoneUpnpState state;
54 } UpnpStream;
55
56 struct _UpnpSession {
57         LinphoneCall *call;
58         UpnpStream *audio;
59         UpnpStream *video;
60         LinphoneUpnpState state;
61 };
62
63 struct _UpnpContext {
64         LinphoneCore *lc;
65         upnp_igd_context *upnp_igd_ctxt;
66         UpnpPortBinding *sip_tcp;
67         UpnpPortBinding *sip_tls;
68         UpnpPortBinding *sip_udp;
69         LinphoneUpnpState state;
70         MSList *removing_configs;
71         MSList *adding_configs;
72         MSList *pending_bindings;
73
74         ms_mutex_t mutex;
75         ms_cond_t empty_cond;
76         
77         time_t last_ready_check;
78         LinphoneUpnpState last_ready_state;
79 };
80
81
82 bool_t linphone_core_upnp_hook(void *data);
83 void linphone_core_upnp_refresh(UpnpContext *ctx);
84
85 UpnpPortBinding *linphone_upnp_port_binding_new();
86 UpnpPortBinding *linphone_upnp_port_binding_new_with_parameters(upnp_igd_ip_protocol protocol, int local_port, int external_port);
87 UpnpPortBinding *linphone_upnp_port_binding_new_or_collect(MSList *list, upnp_igd_ip_protocol protocol, int local_port, int external_port); 
88 UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port);
89 bool_t linphone_upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2);
90 UpnpPortBinding *linphone_upnp_port_binding_equivalent_in_list(MSList *list, const UpnpPortBinding *port);
91 UpnpPortBinding *linphone_upnp_port_binding_retain(UpnpPortBinding *port);
92 void linphone_upnp_update_port_binding(UpnpContext *lupnp, UpnpPortBinding **port_mapping, upnp_igd_ip_protocol protocol, int port, int retry_delay); 
93 void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port);
94 void linphone_upnp_port_binding_release(UpnpPortBinding *port);
95
96 // Configuration
97 MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc);
98 void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port);
99 void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port);
100
101 // uPnP 
102 int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry);
103 int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry);
104
105
106 /**
107  * uPnP Callbacks
108  */
109
110 /* Convert uPnP IGD logs to ortp logs */
111 void linphone_upnp_igd_print(void *cookie, upnp_igd_print_level level, const char *fmt, va_list list) {
112         int ortp_level = ORTP_DEBUG;
113         switch(level) {
114         case UPNP_IGD_MESSAGE:
115                 ortp_level = ORTP_MESSAGE;
116                 break;
117         case UPNP_IGD_WARNING:
118                 ortp_level = ORTP_DEBUG; // Too verbose otherwise
119                 break;
120         case UPNP_IGD_ERROR:
121                 ortp_level = ORTP_DEBUG; // Too verbose otherwise
122                 break;
123         default:
124                 break;
125         }
126         ortp_logv(ortp_level, fmt, list);
127 }
128
129 void linphone_upnp_igd_callback(void *cookie, upnp_igd_event event, void *arg) {
130         UpnpContext *lupnp = (UpnpContext *)cookie;
131         upnp_igd_port_mapping *mapping = NULL;
132         UpnpPortBinding *port_mapping = NULL;
133         const char *ip_address = NULL;
134         const char *connection_status = NULL;
135         bool_t nat_enabled = FALSE;
136         LinphoneUpnpState old_state;
137
138         if(lupnp == NULL || lupnp->upnp_igd_ctxt == NULL) {
139                 ms_error("uPnP IGD: Invalid context in callback");
140                 return;
141         }
142
143         ms_mutex_lock(&lupnp->mutex);
144         old_state = lupnp->state;
145
146         switch(event) {
147         case UPNP_IGD_DEVICE_ADDED:
148         case UPNP_IGD_DEVICE_REMOVED:
149         case UPNP_IGD_EXTERNAL_IPADDRESS_CHANGED:
150         case UPNP_IGD_NAT_ENABLED_CHANGED:
151         case UPNP_IGD_CONNECTION_STATUS_CHANGED:
152                 ip_address = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
153                 connection_status = upnp_igd_get_connection_status(lupnp->upnp_igd_ctxt);
154                 nat_enabled = upnp_igd_get_nat_enabled(lupnp->upnp_igd_ctxt);
155
156                 if(ip_address == NULL || connection_status == NULL) {
157                         ms_message("uPnP IGD: Pending");
158                         lupnp->state = LinphoneUpnpStatePending;
159                 } else if(strcasecmp(connection_status, "Connected")  || !nat_enabled) {
160                         ms_message("uPnP IGD: Not Available");
161                         lupnp->state = LinphoneUpnpStateNotAvailable;
162                 } else {
163                         ms_message("uPnP IGD: Connected");
164                         lupnp->state = LinphoneUpnpStateOk;
165                         if(old_state != LinphoneUpnpStateOk) {
166                                 linphone_core_upnp_refresh(lupnp);
167                         }
168                 }
169
170                 break;
171
172         case UPNP_IGD_PORT_MAPPING_ADD_SUCCESS:
173                 mapping = (upnp_igd_port_mapping *) arg;
174                 port_mapping = (UpnpPortBinding*) mapping->cookie;
175                 port_mapping->external_port = mapping->remote_port;
176                 port_mapping->state = LinphoneUpnpStateOk;
177                 linphone_upnp_port_binding_log(ORTP_MESSAGE, "Added port binding", port_mapping);
178                 linphone_upnp_config_add_port_binding(lupnp, port_mapping);
179
180                 break;
181
182         case UPNP_IGD_PORT_MAPPING_ADD_FAILURE:
183                 mapping = (upnp_igd_port_mapping *) arg;
184                 port_mapping = (UpnpPortBinding*) mapping->cookie;
185                 port_mapping->external_port = -1; //Force random external port
186                 if(linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, TRUE) != 0) {
187                         linphone_upnp_port_binding_log(ORTP_ERROR, "Can't add port binding", port_mapping);
188                 }
189
190                 break;
191
192         case UPNP_IGD_PORT_MAPPING_REMOVE_SUCCESS:
193                 mapping = (upnp_igd_port_mapping *) arg;
194                 port_mapping = (UpnpPortBinding*) mapping->cookie;
195                 port_mapping->state = LinphoneUpnpStateIdle;
196                 linphone_upnp_port_binding_log(ORTP_MESSAGE, "Removed port binding", port_mapping);
197                 linphone_upnp_config_remove_port_binding(lupnp, port_mapping);
198
199                 break;
200
201         case UPNP_IGD_PORT_MAPPING_REMOVE_FAILURE:
202                 mapping = (upnp_igd_port_mapping *) arg;
203                 port_mapping = (UpnpPortBinding*) mapping->cookie;
204                 if(linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE) != 0) {
205                         linphone_upnp_port_binding_log(ORTP_ERROR, "Can't remove port binding", port_mapping);
206                         linphone_upnp_config_remove_port_binding(lupnp, port_mapping);
207                 }
208
209                 break;
210
211         default:
212                 break;
213         }
214
215         if(port_mapping != NULL) {
216                 /*
217                  * Execute delayed actions
218                  */
219                 if(port_mapping->to_remove) {
220                         if(port_mapping->state == LinphoneUpnpStateOk) {
221                                 port_mapping->to_remove = FALSE;
222                                 linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, FALSE);
223                         } else if(port_mapping->state == LinphoneUpnpStateKo) {
224                                 port_mapping->to_remove = FALSE;
225                         }
226                 }
227                 if(port_mapping->to_add) {
228                         if(port_mapping->state == LinphoneUpnpStateIdle || port_mapping->state == LinphoneUpnpStateKo) {
229                                 port_mapping->to_add = FALSE;
230                                 linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, FALSE);
231                         }
232                 }
233
234                 lupnp->pending_bindings = ms_list_remove(lupnp->pending_bindings, port_mapping);
235                 linphone_upnp_port_binding_release(port_mapping);
236         }
237
238         /*
239          * If there is no pending binding emit a signal
240          */
241         if(lupnp->pending_bindings == NULL) {
242                 pthread_cond_signal(&lupnp->empty_cond);
243         }
244         ms_mutex_unlock(&lupnp->mutex);
245 }
246
247
248 /**
249  * uPnP Context
250  */
251
252 UpnpContext* linphone_upnp_context_new(LinphoneCore *lc) {
253         UpnpContext *lupnp = (UpnpContext *)ms_new0(UpnpContext,1);
254
255         ms_mutex_init(&lupnp->mutex, NULL);
256         ms_cond_init(&lupnp->empty_cond, NULL);
257
258         lupnp->last_ready_check = 0;
259         lupnp->last_ready_state = LinphoneUpnpStateIdle;
260
261         lupnp->lc = lc;
262         lupnp->pending_bindings = NULL;
263         lupnp->adding_configs = NULL;
264         lupnp->removing_configs = NULL;
265         lupnp->state = LinphoneUpnpStateIdle;
266         ms_message("uPnP IGD: New %p for core %p", lupnp, lc);
267
268         // Init ports
269         lupnp->sip_udp = NULL;
270         lupnp->sip_tcp = NULL;
271         lupnp->sip_tls = NULL;
272
273         linphone_core_add_iterate_hook(lc, linphone_core_upnp_hook, lupnp);
274
275         lupnp->upnp_igd_ctxt = NULL;
276         lupnp->upnp_igd_ctxt = upnp_igd_create(linphone_upnp_igd_callback, linphone_upnp_igd_print, lupnp);
277         if(lupnp->upnp_igd_ctxt == NULL) {
278                 lupnp->state = LinphoneUpnpStateKo;
279                 ms_error("Can't create uPnP IGD context");
280                 return NULL;
281         }
282
283         lupnp->state = LinphoneUpnpStatePending;
284         upnp_igd_start(lupnp->upnp_igd_ctxt);
285
286         return lupnp;
287 }
288
289 void linphone_upnp_context_destroy(UpnpContext *lupnp) {
290         linphone_core_remove_iterate_hook(lupnp->lc, linphone_core_upnp_hook, lupnp);
291
292         ms_mutex_lock(&lupnp->mutex);
293         
294         /* Send port binding removes */
295         if(lupnp->sip_udp != NULL) {
296                 linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_udp, TRUE);
297         }
298         if(lupnp->sip_tcp != NULL) {
299                 linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tcp, TRUE);
300         }
301         if(lupnp->sip_tls != NULL) {
302                 linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_tls, TRUE);
303         }
304
305         /* Wait all pending bindings are done */
306         if(lupnp->pending_bindings != NULL) {
307                 ms_message("uPnP IGD: Wait all pending port bindings ...");
308                 ms_cond_wait(&lupnp->empty_cond, &lupnp->mutex);
309         }
310         ms_mutex_unlock(&lupnp->mutex);
311
312         if(lupnp->upnp_igd_ctxt != NULL) {
313                 upnp_igd_destroy(lupnp->upnp_igd_ctxt);
314         }
315
316         /* Run one time the hook for configuration update */
317         linphone_core_upnp_hook(lupnp);
318
319         /* Release port bindings */
320         if(lupnp->sip_udp != NULL) {
321                 linphone_upnp_port_binding_release(lupnp->sip_udp);
322                 lupnp->sip_udp = NULL;
323         }
324         if(lupnp->sip_tcp != NULL) {
325                 linphone_upnp_port_binding_release(lupnp->sip_tcp);
326                 lupnp->sip_tcp = NULL;
327         }
328         if(lupnp->sip_tls != NULL) {
329                 linphone_upnp_port_binding_release(lupnp->sip_tls);
330                 lupnp->sip_tcp = NULL;
331         }
332
333         /* Release lists */
334         ms_list_for_each(lupnp->adding_configs,(void (*)(void*))linphone_upnp_port_binding_release);
335         lupnp->adding_configs = ms_list_free(lupnp->adding_configs);
336         ms_list_for_each(lupnp->removing_configs,(void (*)(void*))linphone_upnp_port_binding_release);
337         lupnp->removing_configs = ms_list_free(lupnp->removing_configs);
338         ms_list_for_each(lupnp->pending_bindings,(void (*)(void*))linphone_upnp_port_binding_release);
339         lupnp->pending_bindings = ms_list_free(lupnp->pending_bindings);
340
341         ms_mutex_destroy(&lupnp->mutex);
342         ms_cond_destroy(&lupnp->empty_cond);
343
344         ms_message("uPnP IGD: destroy %p", lupnp);
345         ms_free(lupnp);
346 }
347
348 LinphoneUpnpState linphone_upnp_context_get_state(UpnpContext *lupnp) {
349         LinphoneUpnpState state;
350         ms_mutex_lock(&lupnp->mutex);
351         state = lupnp->state;
352         ms_mutex_unlock(&lupnp->mutex);
353         return state;
354 }
355
356 bool_t _linphone_upnp_context_is_ready_for_register(UpnpContext *lupnp) {
357         bool_t ready = TRUE;
358         
359         // 1 Check global uPnP state
360         ready = (lupnp->state == LinphoneUpnpStateOk);
361         
362         // 2 Check external ip address
363         if(ready) {
364                 if (upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt) == NULL) {
365                         ready = FALSE;
366                 }
367         }
368         
369         // 3 Check sip ports bindings
370         if(ready) {
371                 if(lupnp->sip_udp != NULL) {
372                         if(lupnp->sip_udp->state != LinphoneUpnpStateOk) {
373                                 ready = FALSE;
374                         }
375                 } else if(lupnp->sip_tcp != NULL) {
376                         if(lupnp->sip_tcp->state != LinphoneUpnpStateOk) {
377                                 ready = FALSE;
378                         }
379                 } else if(lupnp->sip_tls != NULL) {
380                         if(lupnp->sip_tls->state != LinphoneUpnpStateOk) {
381                                 ready = FALSE;
382                         }
383                 } else {
384                         ready = FALSE;
385                 }
386         }
387         
388         return ready;
389 }
390
391 bool_t linphone_upnp_context_is_ready_for_register(UpnpContext *lupnp) {
392         bool_t ready;
393         ms_mutex_lock(&lupnp->mutex);
394         ready = _linphone_upnp_context_is_ready_for_register(lupnp);
395         ms_mutex_unlock(&lupnp->mutex);
396         return ready;
397 }
398
399 int linphone_upnp_context_get_external_port(UpnpContext *lupnp) {
400         int port = -1;
401         ms_mutex_lock(&lupnp->mutex);
402         
403         if(lupnp->sip_udp != NULL) {
404                 if(lupnp->sip_udp->state == LinphoneUpnpStateOk) {
405                         port = lupnp->sip_udp->external_port;
406                 }
407         } else if(lupnp->sip_tcp != NULL) {
408                 if(lupnp->sip_tcp->state == LinphoneUpnpStateOk) {
409                         port = lupnp->sip_tcp->external_port;
410                 }
411         } else if(lupnp->sip_tls != NULL) {
412                 if(lupnp->sip_tls->state == LinphoneUpnpStateOk) {
413                         port = lupnp->sip_tls->external_port;
414                 }
415         }
416         
417         ms_mutex_unlock(&lupnp->mutex);
418         return port;
419 }
420
421 const char* linphone_upnp_context_get_external_ipaddress(UpnpContext *lupnp) {
422         const char* addr = NULL;
423         ms_mutex_lock(&lupnp->mutex);
424         addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
425         ms_mutex_unlock(&lupnp->mutex);
426         return addr;
427 }
428
429 int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry) {
430         upnp_igd_port_mapping mapping;
431         char description[128];
432         int ret;
433         
434         if(lupnp->state != LinphoneUpnpStateOk) {
435                 return -2;
436         }
437
438         // Compute port binding state
439         if(port->state != LinphoneUpnpStateAdding) {
440                 port->to_remove = FALSE;
441                 switch(port->state) {
442                         case LinphoneUpnpStateKo:
443                         case LinphoneUpnpStateIdle: {
444                                 port->retry = 0;
445                                 port->state = LinphoneUpnpStateAdding;
446                         }
447                         break;
448                         case LinphoneUpnpStateRemoving: {
449                                 port->to_add = TRUE;
450                                 return 0;
451                         }
452                         break;
453                         default:
454                                 return 0;
455                 }
456         }
457         
458         // No retry if specified
459         if(port->retry != 0 && !retry) {
460                 return -1;
461         }
462
463         if(port->retry >= UPNP_ADD_MAX_RETRY) {
464                 ret = -1;
465         } else {
466                 mapping.cookie = linphone_upnp_port_binding_retain(port);
467                 lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie);
468
469                 mapping.local_port = port->local_port;
470                 mapping.local_host = port->local_addr;
471                 if(port->external_port == -1)
472                         mapping.remote_port = rand()%(0xffff - 1024) + 1024;
473                 else
474                         mapping.remote_port = port->external_port;
475                 mapping.remote_host = "";
476                 snprintf(description, 128, "%s %s at %s:%d",
477                                 PACKAGE_NAME,
478                                 (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP": "UDP",
479                                 port->local_addr, port->local_port);
480                 mapping.description = description;
481                 mapping.protocol = port->protocol;
482
483                 port->retry++;
484                 linphone_upnp_port_binding_log(ORTP_MESSAGE, "Try to add port binding", port);
485                 ret = upnp_igd_add_port_mapping(lupnp->upnp_igd_ctxt, &mapping);
486         }
487         if(ret != 0) {
488                 port->state = LinphoneUpnpStateKo;
489         }
490         return ret;
491 }
492
493 int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry) {
494         upnp_igd_port_mapping mapping;
495         int ret;
496         
497         if(lupnp->state != LinphoneUpnpStateOk) {
498                 return -2;
499         }
500
501         // Compute port binding state
502         if(port->state != LinphoneUpnpStateRemoving) {
503                 port->to_add = FALSE;
504                 switch(port->state) {
505                         case LinphoneUpnpStateOk: {
506                                 port->retry = 0;
507                                 port->state = LinphoneUpnpStateRemoving;
508                         }
509                         break;
510                         case LinphoneUpnpStateAdding: {
511                                 port->to_remove = TRUE;
512                                 return 0;
513                         }
514                         break;
515                         default:
516                                 return 0;
517                 }
518         }
519         
520         // No retry if specified
521         if(port->retry != 0 && !retry) {
522                 return 1;
523         }
524
525         if(port->retry >= UPNP_REMOVE_MAX_RETRY) {
526                 ret = -1;
527         } else {
528                 mapping.cookie = linphone_upnp_port_binding_retain(port);
529                 lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie);
530
531                 mapping.remote_port = port->external_port;
532                 mapping.remote_host = "";
533                 mapping.protocol = port->protocol;
534                 port->retry++;
535                 linphone_upnp_port_binding_log(ORTP_MESSAGE, "Try to remove port binding", port);
536                 ret = upnp_igd_delete_port_mapping(lupnp->upnp_igd_ctxt, &mapping);
537         }
538         if(ret != 0) {
539                 port->state = LinphoneUpnpStateKo;
540         }
541         return ret;
542 }
543
544 /*
545  * uPnP Core interfaces
546  */
547
548 int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool_t video) {
549         LinphoneCore *lc = call->core;
550         UpnpContext *lupnp = lc->upnp;
551         int ret = -1;
552
553         if(lupnp == NULL) {
554                 return ret;
555         }
556
557         ms_mutex_lock(&lupnp->mutex);
558
559         // Don't handle when the call
560         if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) {
561                 ret = 0;
562
563                 /*
564                  * Audio part
565                  */
566                 linphone_upnp_update_port_binding(lupnp, &call->upnp_session->audio->rtp, 
567                         UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->audio_port:0, UPNP_CALL_RETRY_DELAY);
568
569                 linphone_upnp_update_port_binding(lupnp, &call->upnp_session->audio->rtcp, 
570                         UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->audio_port+1:0, UPNP_CALL_RETRY_DELAY);
571                 
572                 /*
573                  * Video part
574                  */
575                 linphone_upnp_update_port_binding(lupnp, &call->upnp_session->video->rtp, 
576                         UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->video_port:0, UPNP_CALL_RETRY_DELAY);
577
578                 linphone_upnp_update_port_binding(lupnp, &call->upnp_session->video->rtcp, 
579                         UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->video_port+1:0, UPNP_CALL_RETRY_DELAY);
580         }
581
582         ms_mutex_unlock(&lupnp->mutex);
583
584         /*
585          * Update uPnP call state
586          */
587         linphone_upnp_call_process(call);
588
589         return ret;
590 }
591
592
593
594 int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md) {
595         bool_t audio = FALSE;
596         bool_t video = FALSE;
597         int i;
598         const SalStreamDescription *stream;
599
600         for (i = 0; i < md->n_total_streams; i++) {
601                 stream = &md->streams[i];
602                 if(stream->type == SalAudio) {
603                         audio = TRUE;
604                 } else if(stream->type == SalVideo) {
605                         video = TRUE;
606                 }
607         }
608
609         return linphone_core_update_upnp_audio_video(call, audio, video);
610 }
611
612 int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call) {
613         return linphone_core_update_upnp_audio_video(call, call->audiostream!=NULL, call->videostream!=NULL);
614 }
615
616 void linphone_core_update_upnp_state_in_call_stats(LinphoneCall *call) {
617         call->stats[LINPHONE_CALL_STATS_AUDIO].upnp_state = call->upnp_session->audio->state;
618         call->stats[LINPHONE_CALL_STATS_VIDEO].upnp_state = call->upnp_session->video->state;
619 }
620
621 void linphone_upnp_update_stream_state(UpnpStream *stream) {
622         if((stream->rtp == NULL || stream->rtp->state == LinphoneUpnpStateOk || stream->rtp->state == LinphoneUpnpStateIdle) &&
623            (stream->rtcp == NULL || stream->rtcp->state == LinphoneUpnpStateOk || stream->rtcp->state == LinphoneUpnpStateIdle)) {
624                 stream->state = LinphoneUpnpStateOk;
625         } else if((stream->rtp != NULL && 
626                      (stream->rtp->state == LinphoneUpnpStateAdding || stream->rtp->state == LinphoneUpnpStateRemoving)) ||
627                   (stream->rtcp != NULL && 
628                      (stream->rtcp->state == LinphoneUpnpStateAdding || stream->rtcp->state == LinphoneUpnpStateRemoving))) {
629                 stream->state = LinphoneUpnpStatePending;
630         } else if((stream->rtp != NULL && stream->rtp->state == LinphoneUpnpStateKo) ||
631                         (stream->rtcp != NULL && stream->rtcp->state == LinphoneUpnpStateKo)) {
632                 stream->state = LinphoneUpnpStateKo;
633         } else {
634                 ms_error("Invalid stream %p state", stream);            
635         }
636 }
637
638 int linphone_upnp_call_process(LinphoneCall *call) {
639         LinphoneCore *lc = call->core;
640         UpnpContext *lupnp = lc->upnp;
641         int ret = -1;
642         LinphoneUpnpState oldState = 0, newState = 0;
643
644         if(lupnp == NULL) {
645                 return ret;
646         }
647
648         ms_mutex_lock(&lupnp->mutex);
649
650         // Don't handle when the call
651         if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) {
652                 ret = 0;
653
654                 /*
655                  * Update Audio state
656                  */
657                 linphone_upnp_update_stream_state(call->upnp_session->audio);
658
659                 /*
660                  * Update Video state
661                  */
662                 linphone_upnp_update_stream_state(call->upnp_session->video);
663
664                 /*
665                  * Update session state
666                  */
667                 oldState = call->upnp_session->state;
668                 if(call->upnp_session->audio->state == LinphoneUpnpStateOk &&
669                         call->upnp_session->video->state == LinphoneUpnpStateOk) {
670                         call->upnp_session->state = LinphoneUpnpStateOk;
671                 } else if(call->upnp_session->audio->state == LinphoneUpnpStatePending ||
672                                 call->upnp_session->video->state == LinphoneUpnpStatePending) {
673                         call->upnp_session->state = LinphoneUpnpStatePending;
674                 } else if(call->upnp_session->audio->state == LinphoneUpnpStateKo ||
675                                 call->upnp_session->video->state == LinphoneUpnpStateKo) {
676                         call->upnp_session->state = LinphoneUpnpStateKo;
677                 } else {
678                         call->upnp_session->state = LinphoneUpnpStateIdle;
679                 }
680                 newState = call->upnp_session->state;
681
682                 /* When change is done proceed update */
683                 if(oldState != LinphoneUpnpStateOk && oldState != LinphoneUpnpStateKo &&
684                                 (call->upnp_session->state == LinphoneUpnpStateOk || call->upnp_session->state == LinphoneUpnpStateKo)) {
685                         if(call->upnp_session->state == LinphoneUpnpStateOk)
686                                 ms_message("uPnP IGD: uPnP for Call %p is ok", call);
687                         else
688                                 ms_message("uPnP IGD: uPnP for Call %p is ko", call);
689
690                         switch (call->state) {
691                                 case LinphoneCallUpdating:
692                                         linphone_core_start_update_call(lc, call);
693                                         break;
694                                 case LinphoneCallUpdatedByRemote:
695                                         linphone_core_start_accept_call_update(lc, call);
696                                         break;
697                                 case LinphoneCallOutgoingInit:
698                                         linphone_core_proceed_with_invite_if_ready(lc, call, NULL);
699                                         break;
700                                 case LinphoneCallIdle:
701                                         linphone_core_notify_incoming_call(lc, call);
702                                         break;
703                                 default:
704                                         break;
705                         }
706                 }
707         }
708
709         ms_mutex_unlock(&lupnp->mutex);
710
711         /*
712          * Update uPnP call stats
713          */
714         if(oldState != newState) {
715                 linphone_core_update_upnp_state_in_call_stats(call);
716         }
717
718         return ret;
719 }
720
721 void linphone_core_upnp_refresh(UpnpContext *lupnp) {
722         MSList *global_list = NULL;
723         MSList *list = NULL;
724         MSList *item;
725         LinphoneCall *call;
726         UpnpPortBinding *port_mapping, *port_mapping2;
727
728         ms_message("uPnP IGD: Refresh mappings");
729
730         if(lupnp->sip_udp != NULL) {
731                 global_list = ms_list_append(global_list, lupnp->sip_udp);
732         }
733         if(lupnp->sip_tcp != NULL) {
734                 global_list = ms_list_append(global_list, lupnp->sip_tcp);
735         }
736         if(lupnp->sip_tls != NULL) {
737                 global_list = ms_list_append(global_list, lupnp->sip_tls);
738         }
739
740         list = lupnp->lc->calls;
741         while(list != NULL) {
742                 call = (LinphoneCall *)list->data;
743                 if(call->upnp_session != NULL) {
744                         if(call->upnp_session->audio->rtp != NULL) {
745                                 global_list = ms_list_append(global_list, call->upnp_session->audio->rtp);
746                         }
747                         if(call->upnp_session->audio->rtcp != NULL) {
748                                 global_list = ms_list_append(global_list, call->upnp_session->audio->rtcp);
749                         }
750                         if(call->upnp_session->video->rtp != NULL) {
751                                 global_list = ms_list_append(global_list, call->upnp_session->video->rtp);
752                         }
753                         if(call->upnp_session->video->rtcp != NULL) {
754                                 global_list = ms_list_append(global_list, call->upnp_session->video->rtcp);
755                         }
756                 }
757                 list = list->next;
758         }
759
760         list = linphone_upnp_config_list_port_bindings(lupnp->lc->config);
761         for(item = list;item != NULL; item = item->next) {
762                         port_mapping = (UpnpPortBinding *)item->data;
763                         port_mapping2 = linphone_upnp_port_binding_equivalent_in_list(global_list, port_mapping);
764                         if(port_mapping2 == NULL) {
765                                 linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE);
766                         } else if(port_mapping2->state == LinphoneUpnpStateIdle){
767                                 /* Force to remove */
768                                 port_mapping2->state = LinphoneUpnpStateOk;
769                         }
770         }
771         ms_list_for_each(list, (void (*)(void*))linphone_upnp_port_binding_release);
772         list = ms_list_free(list);
773
774
775         // (Re)Add removed port bindings
776         list = global_list;
777         while(list != NULL) {
778                 port_mapping = (UpnpPortBinding *)list->data;
779                 linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE);
780                 linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, TRUE);
781                 list = list->next;
782         }
783         global_list = ms_list_free(global_list);
784 }
785
786 void linphone_upnp_update_port_binding(UpnpContext *lupnp, UpnpPortBinding **port_mapping, upnp_igd_ip_protocol protocol, int port, int retry_delay) {
787         const char *local_addr, *external_addr;
788         time_t now = time(NULL);
789         if(port != 0) {
790                 if(*port_mapping != NULL) {
791                         if(port != (*port_mapping)->local_port) {
792                                 linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
793                                 *port_mapping = NULL;
794                         }
795                 }
796                 if(*port_mapping == NULL) {
797                         *port_mapping = linphone_upnp_port_binding_new_or_collect(lupnp->pending_bindings, protocol, port, port);
798                 }
799                 
800                 // Get addresses
801                 local_addr = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt);
802                 external_addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
803
804                 // Force binding update on local address change
805                 if(local_addr != NULL) {
806                         if(strncmp((*port_mapping)->local_addr, local_addr, sizeof((*port_mapping)->local_addr))) {
807                                 linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
808                                 strncpy((*port_mapping)->local_addr, local_addr, sizeof((*port_mapping)->local_addr));
809                         }
810                 }
811                 if(external_addr != NULL) {
812                         strncpy((*port_mapping)->external_addr, external_addr, sizeof((*port_mapping)->external_addr));
813                 }
814
815                 // Add (if not already done) the binding
816                 if(now - (*port_mapping)->last_update >= retry_delay) {
817                         (*port_mapping)->last_update = now;
818                         linphone_upnp_context_send_add_port_binding(lupnp, *port_mapping, FALSE);
819                 }
820         } else {
821                 if(*port_mapping != NULL) {
822                         linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
823                         *port_mapping = NULL;
824                 }
825         }
826 }
827
828 bool_t linphone_core_upnp_hook(void *data) {
829         char key[64];
830         LCSipTransports transport;
831         const MSList *item;
832         LinphoneUpnpState ready_state;
833         time_t now = time(NULL);
834         UpnpPortBinding *port_mapping;
835         UpnpContext *lupnp = (UpnpContext *)data;
836
837         ms_mutex_lock(&lupnp->mutex);
838
839         /* Update ports */
840         if(lupnp->state == LinphoneUpnpStateOk) {
841                 linphone_core_get_sip_transports(lupnp->lc, &transport);
842                 linphone_upnp_update_port_binding(lupnp, &lupnp->sip_udp, UPNP_IGD_IP_PROTOCOL_UDP, transport.udp_port, UPNP_CORE_RETRY_DELAY);
843                 linphone_upnp_update_port_binding(lupnp, &lupnp->sip_tcp, UPNP_IGD_IP_PROTOCOL_TCP, transport.tcp_port, UPNP_CORE_RETRY_DELAY);
844                 linphone_upnp_update_port_binding(lupnp, &lupnp->sip_tls, UPNP_IGD_IP_PROTOCOL_TCP, transport.tls_port, UPNP_CORE_RETRY_DELAY);
845         }
846
847         /* Refresh registers if we are ready */
848         if(now - lupnp->last_ready_check >= UPNP_CORE_READY_CHECK) {
849                 lupnp->last_ready_check = now;
850                 ready_state = (_linphone_upnp_context_is_ready_for_register(lupnp))? LinphoneUpnpStateOk: LinphoneUpnpStateKo;
851                 if(ready_state != lupnp->last_ready_state) {
852                         for(item=linphone_core_get_proxy_config_list(lupnp->lc);item!=NULL;item=item->next) {
853                                 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)item->data;
854                                 if (linphone_proxy_config_register_enabled(cfg)) {
855                                         if (ready_state != LinphoneUpnpStateOk) {
856                                                 // Only reset ithe registration if we require that upnp should be ok
857                                                 if(lupnp->lc->sip_conf.register_only_when_upnp_is_ok) {
858                                                         linphone_proxy_config_set_state(cfg, LinphoneRegistrationNone, "Registration impossible (uPnP not ready)");
859                                                 }
860                                         } else {
861                                                 cfg->commit=TRUE;
862                                         }
863                                 }
864                         }
865                         lupnp->last_ready_state = ready_state;
866                 }
867         }
868                         
869         /* Add configs */
870         for(item = lupnp->adding_configs;item!=NULL;item=item->next) {
871                 port_mapping = (UpnpPortBinding *)item->data;
872                 snprintf(key, sizeof(key), "%s-%d-%d",
873                                         (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
874                                                         port_mapping->external_port,
875                                                         port_mapping->local_port);
876                 lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, "uPnP");
877                 linphone_upnp_port_binding_log(ORTP_DEBUG, "Configuration: Added port binding", port_mapping);
878         }
879         ms_list_for_each(lupnp->adding_configs,(void (*)(void*))linphone_upnp_port_binding_release);
880         lupnp->adding_configs = ms_list_free(lupnp->adding_configs);
881
882         /* Remove configs */
883         for(item = lupnp->removing_configs;item!=NULL;item=item->next) {
884                 port_mapping = (UpnpPortBinding *)item->data;
885                 snprintf(key, sizeof(key), "%s-%d-%d",
886                                         (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
887                                                         port_mapping->external_port,
888                                                         port_mapping->local_port);
889                 lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, NULL);
890                 linphone_upnp_port_binding_log(ORTP_DEBUG, "Configuration: Removed port binding", port_mapping);
891         }
892         ms_list_for_each(lupnp->removing_configs,(void (*)(void*))linphone_upnp_port_binding_release);
893         lupnp->removing_configs = ms_list_free(lupnp->removing_configs);
894
895         ms_mutex_unlock(&lupnp->mutex);
896         return TRUE;
897 }
898
899 int linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session) {
900         int i;
901         SalStreamDescription *stream;
902         UpnpStream *upnpStream;
903
904         for (i = 0; i < desc->n_active_streams; i++) {
905                 stream = &desc->streams[i];
906                 upnpStream = NULL;
907                 if(stream->type == SalAudio) {
908                         upnpStream = session->audio;
909                 } else if(stream->type == SalVideo) {
910                         upnpStream = session->video;
911                 }
912                 if(upnpStream != NULL) {
913                         if(upnpStream->rtp != NULL && upnpStream->rtp->state == LinphoneUpnpStateOk) {
914                                 strncpy(stream->rtp_addr, upnpStream->rtp->external_addr, LINPHONE_IPADDR_SIZE);
915                                 stream->rtp_port = upnpStream->rtp->external_port;
916                         }
917                         if(upnpStream->rtcp != NULL && upnpStream->rtcp->state == LinphoneUpnpStateOk) {
918                                 strncpy(stream->rtcp_addr, upnpStream->rtcp->external_addr, LINPHONE_IPADDR_SIZE);
919                                 stream->rtcp_port = upnpStream->rtcp->external_port;
920                         }
921                 }
922         }
923         return 0;
924 }
925
926
927 /*
928  * uPnP Port Binding
929  */
930
931 UpnpPortBinding *linphone_upnp_port_binding_new() {
932         UpnpPortBinding *port = NULL;
933         port = ms_new0(UpnpPortBinding,1);
934         ms_mutex_init(&port->mutex, NULL);
935         port->state = LinphoneUpnpStateIdle;
936         port->protocol = UPNP_IGD_IP_PROTOCOL_UDP;      
937         port->local_addr[0] = '\0';
938         port->local_port = -1;
939         port->external_addr[0] = '\0';
940         port->external_port = -1;
941         port->to_remove = FALSE;
942         port->to_add = FALSE;
943         port->ref = 1;
944         port->last_update = 0;
945         return port;
946 }
947
948 UpnpPortBinding *linphone_upnp_port_binding_new_with_parameters(upnp_igd_ip_protocol protocol, int local_port, int external_port) {
949         UpnpPortBinding *port_binding = linphone_upnp_port_binding_new();
950         port_binding->protocol = protocol;
951         port_binding->local_port = local_port;
952         port_binding->external_port = external_port;
953         return port_binding;
954 }
955
956 UpnpPortBinding *linphone_upnp_port_binding_new_or_collect(MSList *list, upnp_igd_ip_protocol protocol, int local_port, int external_port) {
957         UpnpPortBinding *tmp_binding;
958         UpnpPortBinding *end_binding;
959         end_binding = linphone_upnp_port_binding_new_with_parameters(protocol, local_port, external_port);
960         tmp_binding = linphone_upnp_port_binding_equivalent_in_list(list, end_binding);
961         if(tmp_binding != NULL) {
962                 linphone_upnp_port_binding_release(end_binding);
963                 end_binding = tmp_binding;
964         }
965         return end_binding;     
966
967
968 UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port) {
969         UpnpPortBinding *new_port = NULL;
970         new_port = ms_new0(UpnpPortBinding,1);
971         memcpy(new_port, port, sizeof(UpnpPortBinding));
972         ms_mutex_init(&new_port->mutex, NULL);
973         new_port->ref = 1;
974         return new_port;
975 }
976
977 void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port) {
978         if(strlen(port->local_addr)) {
979                 ortp_log(level, "uPnP IGD: %s %s|%d->%s:%d (retry %d)", msg,
980                                                         (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
981                                                                         port->external_port,
982                                                                         port->local_addr,
983                                                                         port->local_port,
984                                                                         port->retry - 1);
985         } else {
986                 ortp_log(level, "uPnP IGD: %s %s|%d->%d (retry %d)", msg,
987                                                         (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
988                                                                         port->external_port,
989                                                                         port->local_port,
990                                                                         port->retry - 1);
991         }
992 }
993
994 bool_t linphone_upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2) {
995         return port1->protocol == port2->protocol &&
996                         port1->local_port == port2->local_port &&
997                         port1->external_port == port2->external_port;
998 }
999
1000 UpnpPortBinding *linphone_upnp_port_binding_equivalent_in_list(MSList *list, const UpnpPortBinding *port) {
1001         UpnpPortBinding *port_mapping;
1002         while(list != NULL) {
1003                 port_mapping = (UpnpPortBinding *)list->data;
1004                 if(linphone_upnp_port_binding_equal(port, port_mapping)) {
1005                         return port_mapping;
1006                 }
1007                 list = list->next;
1008         }
1009
1010         return NULL;
1011 }
1012
1013 UpnpPortBinding *linphone_upnp_port_binding_retain(UpnpPortBinding *port) {
1014         ms_mutex_lock(&port->mutex);
1015         port->ref++;
1016         ms_mutex_unlock(&port->mutex);
1017         return port;
1018 }
1019
1020 void linphone_upnp_port_binding_release(UpnpPortBinding *port) {
1021         ms_mutex_lock(&port->mutex);
1022         if(--port->ref == 0) {
1023                 ms_mutex_unlock(&port->mutex);
1024                 ms_mutex_destroy(&port->mutex);
1025                 ms_free(port);
1026                 return;
1027         }
1028         ms_mutex_unlock(&port->mutex);
1029 }
1030
1031
1032 /*
1033  * uPnP Stream
1034  */
1035
1036 UpnpStream* linphone_upnp_stream_new() {
1037         UpnpStream *stream = ms_new0(UpnpStream,1);
1038         stream->state = LinphoneUpnpStateIdle;
1039         stream->rtp = NULL; 
1040         stream->rtcp = NULL;
1041         return stream;
1042 }
1043
1044 void linphone_upnp_stream_destroy(UpnpStream* stream) {
1045         if(stream->rtp != NULL) {
1046                 linphone_upnp_port_binding_release(stream->rtp);
1047                 stream->rtp = NULL;
1048         }
1049         if(stream->rtcp != NULL) {
1050                 linphone_upnp_port_binding_release(stream->rtcp);
1051                 stream->rtcp = NULL;
1052         }
1053         ms_free(stream);
1054 }
1055
1056
1057 /*
1058  * uPnP Session
1059  */
1060
1061 UpnpSession* linphone_upnp_session_new(LinphoneCall* call) {
1062         UpnpSession *session = ms_new0(UpnpSession,1);
1063         session->call = call;
1064         session->state = LinphoneUpnpStateIdle;
1065         session->audio = linphone_upnp_stream_new();
1066         session->video = linphone_upnp_stream_new();
1067         return session;
1068 }
1069
1070 void linphone_upnp_session_destroy(UpnpSession *session) {
1071         LinphoneCore *lc = session->call->core;
1072
1073         if(lc->upnp != NULL) {
1074                 /* Remove bindings */
1075                 if(session->audio->rtp != NULL) {
1076                         linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtp, TRUE);
1077                 }
1078                 if(session->audio->rtcp != NULL) {
1079                         linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtcp, TRUE);
1080                 }
1081                 if(session->video->rtp != NULL) {
1082                         linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtp, TRUE);
1083                 }
1084                 if(session->video->rtcp != NULL) {
1085                         linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtcp, TRUE);
1086                 }
1087         }
1088
1089         linphone_upnp_stream_destroy(session->audio);
1090         linphone_upnp_stream_destroy(session->video);
1091         ms_free(session);
1092 }
1093
1094 LinphoneUpnpState linphone_upnp_session_get_state(UpnpSession *session) {
1095         return session->state;
1096 }
1097
1098
1099 /*
1100  * uPnP Config
1101  */
1102
1103 struct linphone_upnp_config_list_port_bindings_struct {
1104         struct _LpConfig *lpc;
1105         MSList *retList;
1106 };
1107
1108 static void linphone_upnp_config_list_port_bindings_cb(const char *entry, struct linphone_upnp_config_list_port_bindings_struct *cookie) {
1109         char protocol_str[4]; // TCP or UDP
1110         upnp_igd_ip_protocol protocol;
1111         int external_port;
1112         int local_port;
1113         bool_t valid = TRUE;
1114         UpnpPortBinding *port;
1115         if(sscanf(entry, "%3s-%i-%i", protocol_str, &external_port, &local_port) == 3) {
1116                 if(strcasecmp(protocol_str, "TCP") == 0) {
1117                         protocol = UPNP_IGD_IP_PROTOCOL_TCP;
1118                 } else if(strcasecmp(protocol_str, "UDP") == 0) {
1119                         protocol = UPNP_IGD_IP_PROTOCOL_UDP;
1120                 } else {
1121                         valid = FALSE;
1122                 }
1123                 if(valid) {
1124                         port = linphone_upnp_port_binding_new();
1125                         port->state = LinphoneUpnpStateOk;
1126                         port->protocol = protocol;
1127                         port->external_port = external_port;
1128                         port->local_port = local_port;
1129                         cookie->retList = ms_list_append(cookie->retList, port);
1130                 }
1131         } else {
1132                 valid = FALSE;
1133         }
1134         if(!valid) {
1135                 ms_warning("uPnP configuration invalid line: %s", entry);
1136         }
1137 }
1138
1139 MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc) {
1140         struct linphone_upnp_config_list_port_bindings_struct cookie = {lpc, NULL};
1141         lp_config_for_each_entry(lpc, UPNP_SECTION_NAME, (void(*)(const char *, void*))linphone_upnp_config_list_port_bindings_cb, &cookie);
1142         return cookie.retList;
1143 }
1144
1145 void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) {
1146         MSList *list;
1147         UpnpPortBinding *list_port;
1148
1149         list = lupnp->removing_configs;
1150         while(list != NULL) {
1151                 list_port = (UpnpPortBinding *)list->data;
1152                 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1153                         lupnp->removing_configs = ms_list_remove(lupnp->removing_configs, list_port);
1154                         linphone_upnp_port_binding_release(list_port);
1155                         return;
1156                 }
1157                 list = ms_list_next(list);
1158         }
1159
1160         list = lupnp->adding_configs;
1161         while(list != NULL) {
1162                 list_port = (UpnpPortBinding *)list->data;
1163                 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1164                         return;
1165                 }
1166                 list = ms_list_next(list);
1167         }
1168
1169         list_port = linphone_upnp_port_binding_copy(port);
1170         lupnp->adding_configs = ms_list_append(lupnp->adding_configs, list_port);
1171 }
1172
1173 void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) {
1174         MSList *list;
1175         UpnpPortBinding *list_port;
1176
1177         list = lupnp->adding_configs;
1178         while(list != NULL) {
1179                 list_port = (UpnpPortBinding *)list->data;
1180                 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1181                         lupnp->adding_configs = ms_list_remove(lupnp->adding_configs, list_port);
1182                         linphone_upnp_port_binding_release(list_port);
1183                         return;
1184                 }
1185                 list = ms_list_next(list);
1186         }
1187
1188         list = lupnp->removing_configs;
1189         while(list != NULL) {
1190                 list_port = (UpnpPortBinding *)list->data;
1191                 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1192                         return;
1193                 }
1194                 list = ms_list_next(list);
1195         }
1196
1197         list_port = linphone_upnp_port_binding_copy(port);
1198         lupnp->removing_configs = ms_list_append(lupnp->removing_configs, list_port);
1199 }