]> sjero.net Git - linphone/blob - coreapi/upnp.c
Fix uPnP dead lock
[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 stat
666                  */
667                 linphone_core_update_upnp_state_in_call_stats(call);
668                 
669                 /*
670                  * Update session state
671                  */
672                 oldState = call->upnp_session->state;
673                 if(call->upnp_session->audio->state == LinphoneUpnpStateOk &&
674                         call->upnp_session->video->state == LinphoneUpnpStateOk) {
675                         call->upnp_session->state = LinphoneUpnpStateOk;
676                 } else if(call->upnp_session->audio->state == LinphoneUpnpStatePending ||
677                                 call->upnp_session->video->state == LinphoneUpnpStatePending) {
678                         call->upnp_session->state = LinphoneUpnpStatePending;
679                 } else if(call->upnp_session->audio->state == LinphoneUpnpStateKo ||
680                                 call->upnp_session->video->state == LinphoneUpnpStateKo) {
681                         call->upnp_session->state = LinphoneUpnpStateKo;
682                 } else {
683                         call->upnp_session->state = LinphoneUpnpStateIdle;
684                 }
685                 newState = call->upnp_session->state;
686         }
687
688         ms_mutex_unlock(&lupnp->mutex);
689         
690         /* When change is done proceed update */
691         if(oldState != LinphoneUpnpStateOk && oldState != LinphoneUpnpStateKo &&
692                         (newState == LinphoneUpnpStateOk || newState == LinphoneUpnpStateKo)) {
693                 if(call->upnp_session->state == LinphoneUpnpStateOk)
694                         ms_message("uPnP IGD: uPnP for Call %p is ok", call);
695                 else
696                         ms_message("uPnP IGD: uPnP for Call %p is ko", call);
697
698                 switch (call->state) {
699                         case LinphoneCallUpdating:
700                                 linphone_core_start_update_call(lc, call);
701                                 break;
702                         case LinphoneCallUpdatedByRemote:
703                                 linphone_core_start_accept_call_update(lc, call);
704                                 break;
705                         case LinphoneCallOutgoingInit:
706                                 linphone_core_proceed_with_invite_if_ready(lc, call, NULL);
707                                 break;
708                         case LinphoneCallIdle:
709                                 linphone_core_notify_incoming_call(lc, call);
710                                 break;
711                         default:
712                                 break;
713                 }
714         }
715
716         return ret;
717 }
718
719 void linphone_core_upnp_refresh(UpnpContext *lupnp) {
720         MSList *global_list = NULL;
721         MSList *list = NULL;
722         MSList *item;
723         LinphoneCall *call;
724         UpnpPortBinding *port_mapping, *port_mapping2;
725
726         ms_message("uPnP IGD: Refresh mappings");
727
728         if(lupnp->sip_udp != NULL) {
729                 global_list = ms_list_append(global_list, lupnp->sip_udp);
730         }
731         if(lupnp->sip_tcp != NULL) {
732                 global_list = ms_list_append(global_list, lupnp->sip_tcp);
733         }
734         if(lupnp->sip_tls != NULL) {
735                 global_list = ms_list_append(global_list, lupnp->sip_tls);
736         }
737
738         list = lupnp->lc->calls;
739         while(list != NULL) {
740                 call = (LinphoneCall *)list->data;
741                 if(call->upnp_session != NULL) {
742                         if(call->upnp_session->audio->rtp != NULL) {
743                                 global_list = ms_list_append(global_list, call->upnp_session->audio->rtp);
744                         }
745                         if(call->upnp_session->audio->rtcp != NULL) {
746                                 global_list = ms_list_append(global_list, call->upnp_session->audio->rtcp);
747                         }
748                         if(call->upnp_session->video->rtp != NULL) {
749                                 global_list = ms_list_append(global_list, call->upnp_session->video->rtp);
750                         }
751                         if(call->upnp_session->video->rtcp != NULL) {
752                                 global_list = ms_list_append(global_list, call->upnp_session->video->rtcp);
753                         }
754                 }
755                 list = list->next;
756         }
757
758         list = linphone_upnp_config_list_port_bindings(lupnp->lc->config);
759         for(item = list;item != NULL; item = item->next) {
760                         port_mapping = (UpnpPortBinding *)item->data;
761                         port_mapping2 = linphone_upnp_port_binding_equivalent_in_list(global_list, port_mapping);
762                         if(port_mapping2 == NULL) {
763                                 linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE);
764                         } else if(port_mapping2->state == LinphoneUpnpStateIdle){
765                                 /* Force to remove */
766                                 port_mapping2->state = LinphoneUpnpStateOk;
767                         }
768         }
769         ms_list_for_each(list, (void (*)(void*))linphone_upnp_port_binding_release);
770         list = ms_list_free(list);
771
772
773         // (Re)Add removed port bindings
774         list = global_list;
775         while(list != NULL) {
776                 port_mapping = (UpnpPortBinding *)list->data;
777                 linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE);
778                 linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, TRUE);
779                 list = list->next;
780         }
781         global_list = ms_list_free(global_list);
782 }
783
784 void linphone_upnp_update_port_binding(UpnpContext *lupnp, UpnpPortBinding **port_mapping, upnp_igd_ip_protocol protocol, int port, int retry_delay) {
785         const char *local_addr, *external_addr;
786         time_t now = time(NULL);
787         if(port != 0) {
788                 if(*port_mapping != NULL) {
789                         if(port != (*port_mapping)->local_port) {
790                                 linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
791                                 *port_mapping = NULL;
792                         }
793                 }
794                 if(*port_mapping == NULL) {
795                         *port_mapping = linphone_upnp_port_binding_new_or_collect(lupnp->pending_bindings, protocol, port, port);
796                 }
797                 
798                 // Get addresses
799                 local_addr = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt);
800                 external_addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
801
802                 // Force binding update on local address change
803                 if(local_addr != NULL) {
804                         if(strncmp((*port_mapping)->local_addr, local_addr, sizeof((*port_mapping)->local_addr))) {
805                                 linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
806                                 strncpy((*port_mapping)->local_addr, local_addr, sizeof((*port_mapping)->local_addr));
807                         }
808                 }
809                 if(external_addr != NULL) {
810                         strncpy((*port_mapping)->external_addr, external_addr, sizeof((*port_mapping)->external_addr));
811                 }
812
813                 // Add (if not already done) the binding
814                 if(now - (*port_mapping)->last_update >= retry_delay) {
815                         (*port_mapping)->last_update = now;
816                         linphone_upnp_context_send_add_port_binding(lupnp, *port_mapping, FALSE);
817                 }
818         } else {
819                 if(*port_mapping != NULL) {
820                         linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
821                         *port_mapping = NULL;
822                 }
823         }
824 }
825
826 bool_t linphone_core_upnp_hook(void *data) {
827         char key[64];
828         LCSipTransports transport;
829         const MSList *item;
830         LinphoneUpnpState ready_state;
831         time_t now = time(NULL);
832         UpnpPortBinding *port_mapping;
833         UpnpContext *lupnp = (UpnpContext *)data;
834
835         ms_mutex_lock(&lupnp->mutex);
836
837         /* Update ports */
838         if(lupnp->state == LinphoneUpnpStateOk) {
839                 linphone_core_get_sip_transports(lupnp->lc, &transport);
840                 linphone_upnp_update_port_binding(lupnp, &lupnp->sip_udp, UPNP_IGD_IP_PROTOCOL_UDP, transport.udp_port, UPNP_CORE_RETRY_DELAY);
841                 linphone_upnp_update_port_binding(lupnp, &lupnp->sip_tcp, UPNP_IGD_IP_PROTOCOL_TCP, transport.tcp_port, UPNP_CORE_RETRY_DELAY);
842                 linphone_upnp_update_port_binding(lupnp, &lupnp->sip_tls, UPNP_IGD_IP_PROTOCOL_TCP, transport.tls_port, UPNP_CORE_RETRY_DELAY);
843         }
844
845         /* Refresh registers if we are ready */
846         if(now - lupnp->last_ready_check >= UPNP_CORE_READY_CHECK) {
847                 lupnp->last_ready_check = now;
848                 ready_state = (_linphone_upnp_context_is_ready_for_register(lupnp))? LinphoneUpnpStateOk: LinphoneUpnpStateKo;
849                 if(ready_state != lupnp->last_ready_state) {
850                         for(item=linphone_core_get_proxy_config_list(lupnp->lc);item!=NULL;item=item->next) {
851                                 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)item->data;
852                                 if (linphone_proxy_config_register_enabled(cfg)) {
853                                         if (ready_state != LinphoneUpnpStateOk) {
854                                                 // Only reset ithe registration if we require that upnp should be ok
855                                                 if(lupnp->lc->sip_conf.register_only_when_upnp_is_ok) {
856                                                         linphone_proxy_config_set_state(cfg, LinphoneRegistrationNone, "Registration impossible (uPnP not ready)");
857                                                 }
858                                         } else {
859                                                 cfg->commit=TRUE;
860                                         }
861                                 }
862                         }
863                         lupnp->last_ready_state = ready_state;
864                 }
865         }
866                         
867         /* Add configs */
868         for(item = lupnp->adding_configs;item!=NULL;item=item->next) {
869                 port_mapping = (UpnpPortBinding *)item->data;
870                 snprintf(key, sizeof(key), "%s-%d-%d",
871                                         (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
872                                                         port_mapping->external_port,
873                                                         port_mapping->local_port);
874                 lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, "uPnP");
875                 linphone_upnp_port_binding_log(ORTP_DEBUG, "Configuration: Added port binding", port_mapping);
876         }
877         ms_list_for_each(lupnp->adding_configs,(void (*)(void*))linphone_upnp_port_binding_release);
878         lupnp->adding_configs = ms_list_free(lupnp->adding_configs);
879
880         /* Remove configs */
881         for(item = lupnp->removing_configs;item!=NULL;item=item->next) {
882                 port_mapping = (UpnpPortBinding *)item->data;
883                 snprintf(key, sizeof(key), "%s-%d-%d",
884                                         (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
885                                                         port_mapping->external_port,
886                                                         port_mapping->local_port);
887                 lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, NULL);
888                 linphone_upnp_port_binding_log(ORTP_DEBUG, "Configuration: Removed port binding", port_mapping);
889         }
890         ms_list_for_each(lupnp->removing_configs,(void (*)(void*))linphone_upnp_port_binding_release);
891         lupnp->removing_configs = ms_list_free(lupnp->removing_configs);
892
893         ms_mutex_unlock(&lupnp->mutex);
894         return TRUE;
895 }
896
897 int linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session) {
898         int i;
899         SalStreamDescription *stream;
900         UpnpStream *upnpStream;
901
902         for (i = 0; i < desc->n_active_streams; i++) {
903                 stream = &desc->streams[i];
904                 upnpStream = NULL;
905                 if(stream->type == SalAudio) {
906                         upnpStream = session->audio;
907                 } else if(stream->type == SalVideo) {
908                         upnpStream = session->video;
909                 }
910                 if(upnpStream != NULL) {
911                         if(upnpStream->rtp != NULL && upnpStream->rtp->state == LinphoneUpnpStateOk) {
912                                 strncpy(stream->rtp_addr, upnpStream->rtp->external_addr, LINPHONE_IPADDR_SIZE);
913                                 stream->rtp_port = upnpStream->rtp->external_port;
914                         }
915                         if(upnpStream->rtcp != NULL && upnpStream->rtcp->state == LinphoneUpnpStateOk) {
916                                 strncpy(stream->rtcp_addr, upnpStream->rtcp->external_addr, LINPHONE_IPADDR_SIZE);
917                                 stream->rtcp_port = upnpStream->rtcp->external_port;
918                         }
919                 }
920         }
921         return 0;
922 }
923
924
925 /*
926  * uPnP Port Binding
927  */
928
929 UpnpPortBinding *linphone_upnp_port_binding_new() {
930         UpnpPortBinding *port = NULL;
931         port = ms_new0(UpnpPortBinding,1);
932         ms_mutex_init(&port->mutex, NULL);
933         port->state = LinphoneUpnpStateIdle;
934         port->protocol = UPNP_IGD_IP_PROTOCOL_UDP;      
935         port->local_addr[0] = '\0';
936         port->local_port = -1;
937         port->external_addr[0] = '\0';
938         port->external_port = -1;
939         port->to_remove = FALSE;
940         port->to_add = FALSE;
941         port->ref = 1;
942         port->last_update = 0;
943         return port;
944 }
945
946 UpnpPortBinding *linphone_upnp_port_binding_new_with_parameters(upnp_igd_ip_protocol protocol, int local_port, int external_port) {
947         UpnpPortBinding *port_binding = linphone_upnp_port_binding_new();
948         port_binding->protocol = protocol;
949         port_binding->local_port = local_port;
950         port_binding->external_port = external_port;
951         return port_binding;
952 }
953
954 UpnpPortBinding *linphone_upnp_port_binding_new_or_collect(MSList *list, upnp_igd_ip_protocol protocol, int local_port, int external_port) {
955         UpnpPortBinding *tmp_binding;
956         UpnpPortBinding *end_binding;
957         end_binding = linphone_upnp_port_binding_new_with_parameters(protocol, local_port, external_port);
958         tmp_binding = linphone_upnp_port_binding_equivalent_in_list(list, end_binding);
959         if(tmp_binding != NULL) {
960                 linphone_upnp_port_binding_release(end_binding);
961                 end_binding = tmp_binding;
962         }
963         return end_binding;     
964
965
966 UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port) {
967         UpnpPortBinding *new_port = NULL;
968         new_port = ms_new0(UpnpPortBinding,1);
969         memcpy(new_port, port, sizeof(UpnpPortBinding));
970         ms_mutex_init(&new_port->mutex, NULL);
971         new_port->ref = 1;
972         return new_port;
973 }
974
975 void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port) {
976         if(strlen(port->local_addr)) {
977                 ortp_log(level, "uPnP IGD: %s %s|%d->%s:%d (retry %d)", msg,
978                                                         (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
979                                                                         port->external_port,
980                                                                         port->local_addr,
981                                                                         port->local_port,
982                                                                         port->retry - 1);
983         } else {
984                 ortp_log(level, "uPnP IGD: %s %s|%d->%d (retry %d)", msg,
985                                                         (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
986                                                                         port->external_port,
987                                                                         port->local_port,
988                                                                         port->retry - 1);
989         }
990 }
991
992 bool_t linphone_upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2) {
993         return port1->protocol == port2->protocol &&
994                         port1->local_port == port2->local_port &&
995                         port1->external_port == port2->external_port;
996 }
997
998 UpnpPortBinding *linphone_upnp_port_binding_equivalent_in_list(MSList *list, const UpnpPortBinding *port) {
999         UpnpPortBinding *port_mapping;
1000         while(list != NULL) {
1001                 port_mapping = (UpnpPortBinding *)list->data;
1002                 if(linphone_upnp_port_binding_equal(port, port_mapping)) {
1003                         return port_mapping;
1004                 }
1005                 list = list->next;
1006         }
1007
1008         return NULL;
1009 }
1010
1011 UpnpPortBinding *linphone_upnp_port_binding_retain(UpnpPortBinding *port) {
1012         ms_mutex_lock(&port->mutex);
1013         port->ref++;
1014         ms_mutex_unlock(&port->mutex);
1015         return port;
1016 }
1017
1018 void linphone_upnp_port_binding_release(UpnpPortBinding *port) {
1019         ms_mutex_lock(&port->mutex);
1020         if(--port->ref == 0) {
1021                 ms_mutex_unlock(&port->mutex);
1022                 ms_mutex_destroy(&port->mutex);
1023                 ms_free(port);
1024                 return;
1025         }
1026         ms_mutex_unlock(&port->mutex);
1027 }
1028
1029
1030 /*
1031  * uPnP Stream
1032  */
1033
1034 UpnpStream* linphone_upnp_stream_new() {
1035         UpnpStream *stream = ms_new0(UpnpStream,1);
1036         stream->state = LinphoneUpnpStateIdle;
1037         stream->rtp = NULL; 
1038         stream->rtcp = NULL;
1039         return stream;
1040 }
1041
1042 void linphone_upnp_stream_destroy(UpnpStream* stream) {
1043         if(stream->rtp != NULL) {
1044                 linphone_upnp_port_binding_release(stream->rtp);
1045                 stream->rtp = NULL;
1046         }
1047         if(stream->rtcp != NULL) {
1048                 linphone_upnp_port_binding_release(stream->rtcp);
1049                 stream->rtcp = NULL;
1050         }
1051         ms_free(stream);
1052 }
1053
1054
1055 /*
1056  * uPnP Session
1057  */
1058
1059 UpnpSession* linphone_upnp_session_new(LinphoneCall* call) {
1060         UpnpSession *session = ms_new0(UpnpSession,1);
1061         session->call = call;
1062         session->state = LinphoneUpnpStateIdle;
1063         session->audio = linphone_upnp_stream_new();
1064         session->video = linphone_upnp_stream_new();
1065         return session;
1066 }
1067
1068 void linphone_upnp_session_destroy(UpnpSession *session) {
1069         LinphoneCore *lc = session->call->core;
1070
1071         if(lc->upnp != NULL) {
1072                 /* Remove bindings */
1073                 if(session->audio->rtp != NULL) {
1074                         linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtp, TRUE);
1075                 }
1076                 if(session->audio->rtcp != NULL) {
1077                         linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtcp, TRUE);
1078                 }
1079                 if(session->video->rtp != NULL) {
1080                         linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtp, TRUE);
1081                 }
1082                 if(session->video->rtcp != NULL) {
1083                         linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtcp, TRUE);
1084                 }
1085         }
1086
1087         linphone_upnp_stream_destroy(session->audio);
1088         linphone_upnp_stream_destroy(session->video);
1089         ms_free(session);
1090 }
1091
1092 LinphoneUpnpState linphone_upnp_session_get_state(UpnpSession *session) {
1093         return session->state;
1094 }
1095
1096
1097 /*
1098  * uPnP Config
1099  */
1100
1101 struct linphone_upnp_config_list_port_bindings_struct {
1102         struct _LpConfig *lpc;
1103         MSList *retList;
1104 };
1105
1106 static void linphone_upnp_config_list_port_bindings_cb(const char *entry, struct linphone_upnp_config_list_port_bindings_struct *cookie) {
1107         char protocol_str[4]; // TCP or UDP
1108         upnp_igd_ip_protocol protocol;
1109         int external_port;
1110         int local_port;
1111         bool_t valid = TRUE;
1112         UpnpPortBinding *port;
1113         if(sscanf(entry, "%3s-%i-%i", protocol_str, &external_port, &local_port) == 3) {
1114                 if(strcasecmp(protocol_str, "TCP") == 0) {
1115                         protocol = UPNP_IGD_IP_PROTOCOL_TCP;
1116                 } else if(strcasecmp(protocol_str, "UDP") == 0) {
1117                         protocol = UPNP_IGD_IP_PROTOCOL_UDP;
1118                 } else {
1119                         valid = FALSE;
1120                 }
1121                 if(valid) {
1122                         port = linphone_upnp_port_binding_new();
1123                         port->state = LinphoneUpnpStateOk;
1124                         port->protocol = protocol;
1125                         port->external_port = external_port;
1126                         port->local_port = local_port;
1127                         cookie->retList = ms_list_append(cookie->retList, port);
1128                 }
1129         } else {
1130                 valid = FALSE;
1131         }
1132         if(!valid) {
1133                 ms_warning("uPnP configuration invalid line: %s", entry);
1134         }
1135 }
1136
1137 MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc) {
1138         struct linphone_upnp_config_list_port_bindings_struct cookie = {lpc, NULL};
1139         lp_config_for_each_entry(lpc, UPNP_SECTION_NAME, (void(*)(const char *, void*))linphone_upnp_config_list_port_bindings_cb, &cookie);
1140         return cookie.retList;
1141 }
1142
1143 void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) {
1144         MSList *list;
1145         UpnpPortBinding *list_port;
1146
1147         list = lupnp->removing_configs;
1148         while(list != NULL) {
1149                 list_port = (UpnpPortBinding *)list->data;
1150                 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1151                         lupnp->removing_configs = ms_list_remove(lupnp->removing_configs, list_port);
1152                         linphone_upnp_port_binding_release(list_port);
1153                         return;
1154                 }
1155                 list = ms_list_next(list);
1156         }
1157
1158         list = lupnp->adding_configs;
1159         while(list != NULL) {
1160                 list_port = (UpnpPortBinding *)list->data;
1161                 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1162                         return;
1163                 }
1164                 list = ms_list_next(list);
1165         }
1166
1167         list_port = linphone_upnp_port_binding_copy(port);
1168         lupnp->adding_configs = ms_list_append(lupnp->adding_configs, list_port);
1169 }
1170
1171 void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) {
1172         MSList *list;
1173         UpnpPortBinding *list_port;
1174
1175         list = lupnp->adding_configs;
1176         while(list != NULL) {
1177                 list_port = (UpnpPortBinding *)list->data;
1178                 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1179                         lupnp->adding_configs = ms_list_remove(lupnp->adding_configs, list_port);
1180                         linphone_upnp_port_binding_release(list_port);
1181                         return;
1182                 }
1183                 list = ms_list_next(list);
1184         }
1185
1186         list = lupnp->removing_configs;
1187         while(list != NULL) {
1188                 list_port = (UpnpPortBinding *)list->data;
1189                 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1190                         return;
1191                 }
1192                 list = ms_list_next(list);
1193         }
1194
1195         list_port = linphone_upnp_port_binding_copy(port);
1196         lupnp->removing_configs = ms_list_append(lupnp->removing_configs, list_port);
1197 }