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