]> sjero.net Git - linphone/blob - coreapi/upnp.c
Fix proxy update when using uPnP
[linphone] / coreapi / upnp.c
1 /*
2 linphone
3 Copyright (C) 2012  Belledonne Communications SARL
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 */
19
20 #include "upnp.h"
21 #include "private.h"
22 #include "lpconfig.h"
23
24 #define UPNP_ADD_MAX_RETRY    4
25 #define UPNP_REMOVE_MAX_RETRY 4
26 #define UPNP_SECTION_NAME     "uPnP"
27 #define UPNP_CORE_READY_CHECK 1 
28 #define UPNP_CORE_RETRY_DELAY 4
29 #define UPNP_CALL_RETRY_DELAY 1
30
31 /*
32  * uPnP Definitions
33  */
34
35 typedef struct _UpnpPortBinding {
36         ms_mutex_t mutex;
37         LinphoneUpnpState state;
38         upnp_igd_ip_protocol protocol;
39         char local_addr[LINPHONE_IPADDR_SIZE];
40         int local_port;
41         char external_addr[LINPHONE_IPADDR_SIZE];
42         int external_port;
43         int retry;
44         int ref;
45         bool_t to_remove;
46         bool_t to_add;
47         time_t last_update;
48 } UpnpPortBinding;
49
50 typedef struct _UpnpStream {
51         UpnpPortBinding *rtp;
52         UpnpPortBinding *rtcp;
53         LinphoneUpnpState state;
54 } UpnpStream;
55
56 struct _UpnpSession {
57         LinphoneCall *call;
58         UpnpStream *audio;
59         UpnpStream *video;
60         LinphoneUpnpState state;
61 };
62
63 struct _UpnpContext {
64         LinphoneCore *lc;
65         upnp_igd_context *upnp_igd_ctxt;
66         UpnpPortBinding *sip_tcp;
67         UpnpPortBinding *sip_tls;
68         UpnpPortBinding *sip_udp;
69         LinphoneUpnpState state;
70         MSList *removing_configs;
71         MSList *adding_configs;
72         MSList *pending_bindings;
73
74         ms_mutex_t mutex;
75         ms_cond_t empty_cond;
76         
77         time_t last_ready_check;
78         LinphoneUpnpState last_ready_state;
79 };
80
81
82 bool_t linphone_core_upnp_hook(void *data);
83 void linphone_core_upnp_refresh(UpnpContext *ctx);
84
85 UpnpPortBinding *linphone_upnp_port_binding_new();
86 UpnpPortBinding *linphone_upnp_port_binding_new_with_parameters(upnp_igd_ip_protocol protocol, int local_port, int external_port);
87 UpnpPortBinding *linphone_upnp_port_binding_new_or_collect(MSList *list, upnp_igd_ip_protocol protocol, int local_port, int external_port); 
88 UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port);
89 bool_t linphone_upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2);
90 UpnpPortBinding *linphone_upnp_port_binding_equivalent_in_list(MSList *list, const UpnpPortBinding *port);
91 UpnpPortBinding *linphone_upnp_port_binding_retain(UpnpPortBinding *port);
92 void linphone_upnp_update_port_binding(UpnpContext *lupnp, UpnpPortBinding **port_mapping, upnp_igd_ip_protocol protocol, int port, int retry_delay); 
93 void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port);
94 void linphone_upnp_port_binding_release(UpnpPortBinding *port);
95 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;
359         ms_mutex_lock(&lupnp->mutex);
360         state = lupnp->state;
361         ms_mutex_unlock(&lupnp->mutex);
362         return state;
363 }
364
365 bool_t _linphone_upnp_context_is_ready_for_register(UpnpContext *lupnp) {
366         bool_t ready = TRUE;
367         
368         // 1 Check global uPnP state
369         ready = (lupnp->state == LinphoneUpnpStateOk);
370         
371         // 2 Check external ip address
372         if(ready) {
373                 if (upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt) == NULL) {
374                         ready = FALSE;
375                 }
376         }
377         
378         // 3 Check sip ports bindings
379         if(ready) {
380                 if(lupnp->sip_udp != NULL) {
381                         if(lupnp->sip_udp->state != LinphoneUpnpStateOk) {
382                                 ready = FALSE;
383                         }
384                 } else if(lupnp->sip_tcp != NULL) {
385                         if(lupnp->sip_tcp->state != LinphoneUpnpStateOk) {
386                                 ready = FALSE;
387                         }
388                 } else if(lupnp->sip_tls != NULL) {
389                         if(lupnp->sip_tls->state != LinphoneUpnpStateOk) {
390                                 ready = FALSE;
391                         }
392                 } else {
393                         ready = FALSE;
394                 }
395         }
396         
397         return ready;
398 }
399
400 bool_t linphone_upnp_context_is_ready_for_register(UpnpContext *lupnp) {
401         bool_t ready;
402         ms_mutex_lock(&lupnp->mutex);
403         ready = _linphone_upnp_context_is_ready_for_register(lupnp);
404         ms_mutex_unlock(&lupnp->mutex);
405         return ready;
406 }
407
408 int linphone_upnp_context_get_external_port(UpnpContext *lupnp) {
409         int port = -1;
410         ms_mutex_lock(&lupnp->mutex);
411         
412         if(lupnp->sip_udp != NULL) {
413                 if(lupnp->sip_udp->state == LinphoneUpnpStateOk) {
414                         port = lupnp->sip_udp->external_port;
415                 }
416         } else if(lupnp->sip_tcp != NULL) {
417                 if(lupnp->sip_tcp->state == LinphoneUpnpStateOk) {
418                         port = lupnp->sip_tcp->external_port;
419                 }
420         } else if(lupnp->sip_tls != NULL) {
421                 if(lupnp->sip_tls->state == LinphoneUpnpStateOk) {
422                         port = lupnp->sip_tls->external_port;
423                 }
424         }
425         
426         ms_mutex_unlock(&lupnp->mutex);
427         return port;
428 }
429
430 const char* linphone_upnp_context_get_external_ipaddress(UpnpContext *lupnp) {
431         const char* addr = NULL;
432         ms_mutex_lock(&lupnp->mutex);
433         addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
434         ms_mutex_unlock(&lupnp->mutex);
435         return addr;
436 }
437
438 int linphone_upnp_context_send_add_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry) {
439         upnp_igd_port_mapping mapping;
440         char description[128];
441         int ret;
442         
443         if(lupnp->state != LinphoneUpnpStateOk) {
444                 return -2;
445         }
446
447         // Compute port binding state
448         if(port->state != LinphoneUpnpStateAdding) {
449                 port->to_remove = FALSE;
450                 switch(port->state) {
451                         case LinphoneUpnpStateKo:
452                         case LinphoneUpnpStateIdle: {
453                                 port->retry = 0;
454                                 port->state = LinphoneUpnpStateAdding;
455                         }
456                         break;
457                         case LinphoneUpnpStateRemoving: {
458                                 port->to_add = TRUE;
459                                 return 0;
460                         }
461                         break;
462                         default:
463                                 return 0;
464                 }
465         }
466         
467         // No retry if specified
468         if(port->retry != 0 && !retry) {
469                 return -1;
470         }
471
472         if(port->retry >= UPNP_ADD_MAX_RETRY) {
473                 ret = -1;
474         } else {
475                 mapping.cookie = linphone_upnp_port_binding_retain(port);
476                 lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie);
477
478                 mapping.local_port = port->local_port;
479                 mapping.local_host = port->local_addr;
480                 if(port->external_port == -1)
481                         mapping.remote_port = rand()%(0xffff - 1024) + 1024;
482                 else
483                         mapping.remote_port = port->external_port;
484                 mapping.remote_host = "";
485                 snprintf(description, 128, "%s %s at %s:%d",
486                                 PACKAGE_NAME,
487                                 (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP": "UDP",
488                                 port->local_addr, port->local_port);
489                 mapping.description = description;
490                 mapping.protocol = port->protocol;
491
492                 port->retry++;
493                 linphone_upnp_port_binding_log(ORTP_MESSAGE, "Try to add port binding", port);
494                 ret = upnp_igd_add_port_mapping(lupnp->upnp_igd_ctxt, &mapping);
495         }
496         if(ret != 0) {
497                 port->state = LinphoneUpnpStateKo;
498         }
499         return ret;
500 }
501
502 int linphone_upnp_context_send_remove_port_binding(UpnpContext *lupnp, UpnpPortBinding *port, bool_t retry) {
503         upnp_igd_port_mapping mapping;
504         int ret;
505         
506         if(lupnp->state != LinphoneUpnpStateOk) {
507                 return -2;
508         }
509
510         // Compute port binding state
511         if(port->state != LinphoneUpnpStateRemoving) {
512                 port->to_add = FALSE;
513                 switch(port->state) {
514                         case LinphoneUpnpStateOk: {
515                                 port->retry = 0;
516                                 port->state = LinphoneUpnpStateRemoving;
517                         }
518                         break;
519                         case LinphoneUpnpStateAdding: {
520                                 port->to_remove = TRUE;
521                                 return 0;
522                         }
523                         break;
524                         default:
525                                 return 0;
526                 }
527         }
528         
529         // No retry if specified
530         if(port->retry != 0 && !retry) {
531                 return 1;
532         }
533
534         if(port->retry >= UPNP_REMOVE_MAX_RETRY) {
535                 ret = -1;
536         } else {
537                 mapping.cookie = linphone_upnp_port_binding_retain(port);
538                 lupnp->pending_bindings = ms_list_append(lupnp->pending_bindings, mapping.cookie);
539
540                 mapping.remote_port = port->external_port;
541                 mapping.remote_host = "";
542                 mapping.protocol = port->protocol;
543                 port->retry++;
544                 linphone_upnp_port_binding_log(ORTP_MESSAGE, "Try to remove port binding", port);
545                 ret = upnp_igd_delete_port_mapping(lupnp->upnp_igd_ctxt, &mapping);
546         }
547         if(ret != 0) {
548                 port->state = LinphoneUpnpStateKo;
549         }
550         return ret;
551 }
552
553 /*
554  * uPnP Core interfaces
555  */
556
557 int linphone_core_update_upnp_audio_video(LinphoneCall *call, bool_t audio, bool_t video) {
558         LinphoneCore *lc = call->core;
559         UpnpContext *lupnp = lc->upnp;
560         int ret = -1;
561
562         if(lupnp == NULL) {
563                 return ret;
564         }
565
566         ms_mutex_lock(&lupnp->mutex);
567
568         // Don't handle when the call
569         if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) {
570                 ret = 0;
571
572                 /*
573                  * Audio part
574                  */
575                 linphone_upnp_update_port_binding(lupnp, &call->upnp_session->audio->rtp, 
576                         UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->audio_port:0, UPNP_CALL_RETRY_DELAY);
577
578                 linphone_upnp_update_port_binding(lupnp, &call->upnp_session->audio->rtcp, 
579                         UPNP_IGD_IP_PROTOCOL_UDP, (audio)? call->audio_port+1:0, UPNP_CALL_RETRY_DELAY);
580                 
581                 /*
582                  * Video part
583                  */
584                 linphone_upnp_update_port_binding(lupnp, &call->upnp_session->video->rtp, 
585                         UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->video_port:0, UPNP_CALL_RETRY_DELAY);
586
587                 linphone_upnp_update_port_binding(lupnp, &call->upnp_session->video->rtcp, 
588                         UPNP_IGD_IP_PROTOCOL_UDP, (video)? call->video_port+1:0, UPNP_CALL_RETRY_DELAY);
589         }
590
591         ms_mutex_unlock(&lupnp->mutex);
592
593         /*
594          * Update uPnP call state
595          */
596         linphone_upnp_call_process(call);
597
598         return ret;
599 }
600
601
602
603 int linphone_core_update_upnp_from_remote_media_description(LinphoneCall *call, const SalMediaDescription *md) {
604         bool_t audio = FALSE;
605         bool_t video = FALSE;
606         int i;
607         const SalStreamDescription *stream;
608
609         for (i = 0; i < md->n_total_streams; i++) {
610                 stream = &md->streams[i];
611                 if(stream->type == SalAudio) {
612                         audio = TRUE;
613                 } else if(stream->type == SalVideo) {
614                         video = TRUE;
615                 }
616         }
617
618         return linphone_core_update_upnp_audio_video(call, audio, video);
619 }
620
621 int linphone_core_update_upnp(LinphoneCore *lc, LinphoneCall *call) {
622         return linphone_core_update_upnp_audio_video(call, call->audiostream!=NULL, call->videostream!=NULL);
623 }
624
625 void linphone_core_update_upnp_state_in_call_stats(LinphoneCall *call) {
626         call->stats[LINPHONE_CALL_STATS_AUDIO].upnp_state = call->upnp_session->audio->state;
627         call->stats[LINPHONE_CALL_STATS_VIDEO].upnp_state = call->upnp_session->video->state;
628 }
629
630 void linphone_upnp_update_stream_state(UpnpStream *stream) {
631         if((stream->rtp == NULL || stream->rtp->state == LinphoneUpnpStateOk || stream->rtp->state == LinphoneUpnpStateIdle) &&
632            (stream->rtcp == NULL || stream->rtcp->state == LinphoneUpnpStateOk || stream->rtcp->state == LinphoneUpnpStateIdle)) {
633                 stream->state = LinphoneUpnpStateOk;
634         } else if((stream->rtp != NULL && 
635                      (stream->rtp->state == LinphoneUpnpStateAdding || stream->rtp->state == LinphoneUpnpStateRemoving)) ||
636                   (stream->rtcp != NULL && 
637                      (stream->rtcp->state == LinphoneUpnpStateAdding || stream->rtcp->state == LinphoneUpnpStateRemoving))) {
638                 stream->state = LinphoneUpnpStatePending;
639         } else if((stream->rtp != NULL && stream->rtp->state == LinphoneUpnpStateKo) ||
640                         (stream->rtcp != NULL && stream->rtcp->state == LinphoneUpnpStateKo)) {
641                 stream->state = LinphoneUpnpStateKo;
642         } else {
643                 ms_error("Invalid stream %p state", stream);            
644         }
645 }
646
647 int linphone_upnp_call_process(LinphoneCall *call) {
648         LinphoneCore *lc = call->core;
649         UpnpContext *lupnp = lc->upnp;
650         int ret = -1;
651         LinphoneUpnpState oldState = 0, newState = 0;
652
653         if(lupnp == NULL) {
654                 return ret;
655         }
656
657         ms_mutex_lock(&lupnp->mutex);
658
659         // Don't handle when the call
660         if(lupnp->state == LinphoneUpnpStateOk && call->upnp_session != NULL) {
661                 ret = 0;
662
663                 /*
664                  * Update Audio state
665                  */
666                 linphone_upnp_update_stream_state(call->upnp_session->audio);
667
668                 /*
669                  * Update Video state
670                  */
671                 linphone_upnp_update_stream_state(call->upnp_session->video);
672
673                 /*
674                  * Update stat
675                  */
676                 linphone_core_update_upnp_state_in_call_stats(call);
677                 
678                 /*
679                  * Update session state
680                  */
681                 oldState = call->upnp_session->state;
682                 if(call->upnp_session->audio->state == LinphoneUpnpStateOk &&
683                         call->upnp_session->video->state == LinphoneUpnpStateOk) {
684                         call->upnp_session->state = LinphoneUpnpStateOk;
685                 } else if(call->upnp_session->audio->state == LinphoneUpnpStatePending ||
686                                 call->upnp_session->video->state == LinphoneUpnpStatePending) {
687                         call->upnp_session->state = LinphoneUpnpStatePending;
688                 } else if(call->upnp_session->audio->state == LinphoneUpnpStateKo ||
689                                 call->upnp_session->video->state == LinphoneUpnpStateKo) {
690                         call->upnp_session->state = LinphoneUpnpStateKo;
691                 } else {
692                         call->upnp_session->state = LinphoneUpnpStateIdle;
693                 }
694                 newState = call->upnp_session->state;
695         }
696
697         ms_mutex_unlock(&lupnp->mutex);
698         
699         /* When change is done proceed update */
700         if(oldState != LinphoneUpnpStateOk && oldState != LinphoneUpnpStateKo &&
701                         (newState == LinphoneUpnpStateOk || newState == LinphoneUpnpStateKo)) {
702                 if(call->upnp_session->state == LinphoneUpnpStateOk)
703                         ms_message("uPnP IGD: uPnP for Call %p is ok", call);
704                 else
705                         ms_message("uPnP IGD: uPnP for Call %p is ko", call);
706
707                 switch (call->state) {
708                         case LinphoneCallUpdating:
709                                 linphone_core_start_update_call(lc, call);
710                                 break;
711                         case LinphoneCallUpdatedByRemote:
712                                 linphone_core_start_accept_call_update(lc, call);
713                                 break;
714                         case LinphoneCallOutgoingInit:
715                                 linphone_core_proceed_with_invite_if_ready(lc, call, NULL);
716                                 break;
717                         case LinphoneCallIdle:
718                                 linphone_core_notify_incoming_call(lc, call);
719                                 break;
720                         default:
721                                 break;
722                 }
723         }
724
725         return ret;
726 }
727
728 void linphone_core_upnp_refresh(UpnpContext *lupnp) {
729         MSList *global_list = NULL;
730         MSList *list = NULL;
731         MSList *item;
732         LinphoneCall *call;
733         UpnpPortBinding *port_mapping, *port_mapping2;
734
735         ms_message("uPnP IGD: Refresh mappings");
736
737         if(lupnp->sip_udp != NULL) {
738                 global_list = ms_list_append(global_list, lupnp->sip_udp);
739         }
740         if(lupnp->sip_tcp != NULL) {
741                 global_list = ms_list_append(global_list, lupnp->sip_tcp);
742         }
743         if(lupnp->sip_tls != NULL) {
744                 global_list = ms_list_append(global_list, lupnp->sip_tls);
745         }
746
747         list = lupnp->lc->calls;
748         while(list != NULL) {
749                 call = (LinphoneCall *)list->data;
750                 if(call->upnp_session != NULL) {
751                         if(call->upnp_session->audio->rtp != NULL) {
752                                 global_list = ms_list_append(global_list, call->upnp_session->audio->rtp);
753                         }
754                         if(call->upnp_session->audio->rtcp != NULL) {
755                                 global_list = ms_list_append(global_list, call->upnp_session->audio->rtcp);
756                         }
757                         if(call->upnp_session->video->rtp != NULL) {
758                                 global_list = ms_list_append(global_list, call->upnp_session->video->rtp);
759                         }
760                         if(call->upnp_session->video->rtcp != NULL) {
761                                 global_list = ms_list_append(global_list, call->upnp_session->video->rtcp);
762                         }
763                 }
764                 list = list->next;
765         }
766
767         list = linphone_upnp_config_list_port_bindings(lupnp->lc->config);
768         for(item = list;item != NULL; item = item->next) {
769                         port_mapping = (UpnpPortBinding *)item->data;
770                         port_mapping2 = linphone_upnp_port_binding_equivalent_in_list(global_list, port_mapping);
771                         if(port_mapping2 == NULL) {
772                                 linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE);
773                         } else if(port_mapping2->state == LinphoneUpnpStateIdle){
774                                 /* Force to remove */
775                                 port_mapping2->state = LinphoneUpnpStateOk;
776                         }
777         }
778         ms_list_for_each(list, (void (*)(void*))linphone_upnp_port_binding_release);
779         list = ms_list_free(list);
780
781
782         // (Re)Add removed port bindings
783         list = global_list;
784         while(list != NULL) {
785                 port_mapping = (UpnpPortBinding *)list->data;
786                 linphone_upnp_context_send_remove_port_binding(lupnp, port_mapping, TRUE);
787                 linphone_upnp_context_send_add_port_binding(lupnp, port_mapping, TRUE);
788                 list = list->next;
789         }
790         global_list = ms_list_free(global_list);
791 }
792
793 void linphone_upnp_update_port_binding(UpnpContext *lupnp, UpnpPortBinding **port_mapping, upnp_igd_ip_protocol protocol, int port, int retry_delay) {
794         const char *local_addr, *external_addr;
795         time_t now = time(NULL);
796         if(port != 0) {
797                 if(*port_mapping != NULL) {
798                         if(port != (*port_mapping)->local_port) {
799                                 linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
800                                 *port_mapping = NULL;
801                         }
802                 }
803                 if(*port_mapping == NULL) {
804                         *port_mapping = linphone_upnp_port_binding_new_or_collect(lupnp->pending_bindings, protocol, port, port);
805                 }
806                 
807                 // Get addresses
808                 local_addr = upnp_igd_get_local_ipaddress(lupnp->upnp_igd_ctxt);
809                 external_addr = upnp_igd_get_external_ipaddress(lupnp->upnp_igd_ctxt);
810
811                 // Force binding update on local address change
812                 if(local_addr != NULL) {
813                         if(strncmp((*port_mapping)->local_addr, local_addr, sizeof((*port_mapping)->local_addr))) {
814                                 linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
815                                 strncpy((*port_mapping)->local_addr, local_addr, sizeof((*port_mapping)->local_addr));
816                         }
817                 }
818                 if(external_addr != NULL) {
819                         strncpy((*port_mapping)->external_addr, external_addr, sizeof((*port_mapping)->external_addr));
820                 }
821
822                 // Add (if not already done) the binding
823                 if(now - (*port_mapping)->last_update >= retry_delay) {
824                         (*port_mapping)->last_update = now;
825                         linphone_upnp_context_send_add_port_binding(lupnp, *port_mapping, FALSE);
826                 }
827         } else {
828                 if(*port_mapping != NULL) {
829                         linphone_upnp_context_send_remove_port_binding(lupnp, *port_mapping, FALSE);
830                         *port_mapping = NULL;
831                 }
832         }
833 }
834
835 void linphone_upnp_update_config(UpnpContext* lupnp) {
836         char key[64];
837         const MSList *item;
838         UpnpPortBinding *port_mapping;
839         
840         /* Add configs */
841         for(item = lupnp->adding_configs;item!=NULL;item=item->next) {
842                 port_mapping = (UpnpPortBinding *)item->data;
843                 snprintf(key, sizeof(key), "%s-%d-%d",
844                                         (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
845                                                         port_mapping->external_port,
846                                                         port_mapping->local_port);
847                 lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, "uPnP");
848                 linphone_upnp_port_binding_log(ORTP_DEBUG, "Configuration: Added port binding", port_mapping);
849         }
850         ms_list_for_each(lupnp->adding_configs,(void (*)(void*))linphone_upnp_port_binding_release);
851         lupnp->adding_configs = ms_list_free(lupnp->adding_configs);
852
853         /* Remove configs */
854         for(item = lupnp->removing_configs;item!=NULL;item=item->next) {
855                 port_mapping = (UpnpPortBinding *)item->data;
856                 snprintf(key, sizeof(key), "%s-%d-%d",
857                                         (port_mapping->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
858                                                         port_mapping->external_port,
859                                                         port_mapping->local_port);
860                 lp_config_set_string(lupnp->lc->config, UPNP_SECTION_NAME, key, NULL);
861                 linphone_upnp_port_binding_log(ORTP_DEBUG, "Configuration: Removed port binding", port_mapping);
862         }
863         ms_list_for_each(lupnp->removing_configs,(void (*)(void*))linphone_upnp_port_binding_release);
864         lupnp->removing_configs = ms_list_free(lupnp->removing_configs);
865 }
866
867 void linphone_upnp_update_proxy(UpnpContext* lupnp, bool_t force) {
868         LinphoneUpnpState ready_state;
869         const MSList *item;
870         time_t now = (force)? (lupnp->last_ready_check + UPNP_CORE_READY_CHECK) : time(NULL);
871         
872         /* Refresh registers if we are ready */
873         if(now - lupnp->last_ready_check >= UPNP_CORE_READY_CHECK) {
874                 lupnp->last_ready_check = now;
875                 ready_state = (_linphone_upnp_context_is_ready_for_register(lupnp))? LinphoneUpnpStateOk: LinphoneUpnpStateKo;
876                 if(ready_state != lupnp->last_ready_state) {
877                         for(item=linphone_core_get_proxy_config_list(lupnp->lc);item!=NULL;item=item->next) {
878                                 LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)item->data;
879                                 if (linphone_proxy_config_register_enabled(cfg)) {
880                                         if (ready_state != LinphoneUpnpStateOk) {
881                                                 // Only reset ithe registration if we require that upnp should be ok
882                                                 if(lupnp->lc->sip_conf.register_only_when_upnp_is_ok) {
883                                                         linphone_proxy_config_set_state(cfg, LinphoneRegistrationNone, "Registration impossible (uPnP not ready)");
884                                                 } else {
885                                                         cfg->commit=TRUE;
886                                                 }
887                                         } else {
888                                                 cfg->commit=TRUE;
889                                         }
890                                 }
891                         }
892                         lupnp->last_ready_state = ready_state;
893                 }
894         }
895 }
896
897 bool_t linphone_core_upnp_hook(void *data) {
898         LCSipTransports transport;
899         UpnpContext *lupnp = (UpnpContext *)data;
900
901         ms_mutex_lock(&lupnp->mutex);
902
903         /* Update ports */
904         if(lupnp->state == LinphoneUpnpStateOk) {
905                 linphone_core_get_sip_transports(lupnp->lc, &transport);
906                 linphone_upnp_update_port_binding(lupnp, &lupnp->sip_udp, UPNP_IGD_IP_PROTOCOL_UDP, transport.udp_port, UPNP_CORE_RETRY_DELAY);
907                 linphone_upnp_update_port_binding(lupnp, &lupnp->sip_tcp, UPNP_IGD_IP_PROTOCOL_TCP, transport.tcp_port, UPNP_CORE_RETRY_DELAY);
908                 linphone_upnp_update_port_binding(lupnp, &lupnp->sip_tls, UPNP_IGD_IP_PROTOCOL_TCP, transport.tls_port, UPNP_CORE_RETRY_DELAY);
909         }
910
911         linphone_upnp_update_proxy(lupnp, FALSE);       
912         linphone_upnp_update_config(lupnp);
913
914         ms_mutex_unlock(&lupnp->mutex);
915         return TRUE;
916 }
917
918 int linphone_core_update_local_media_description_from_upnp(SalMediaDescription *desc, UpnpSession *session) {
919         int i;
920         SalStreamDescription *stream;
921         UpnpStream *upnpStream;
922
923         for (i = 0; i < desc->n_active_streams; i++) {
924                 stream = &desc->streams[i];
925                 upnpStream = NULL;
926                 if(stream->type == SalAudio) {
927                         upnpStream = session->audio;
928                 } else if(stream->type == SalVideo) {
929                         upnpStream = session->video;
930                 }
931                 if(upnpStream != NULL) {
932                         if(upnpStream->rtp != NULL && upnpStream->rtp->state == LinphoneUpnpStateOk) {
933                                 strncpy(stream->rtp_addr, upnpStream->rtp->external_addr, LINPHONE_IPADDR_SIZE);
934                                 stream->rtp_port = upnpStream->rtp->external_port;
935                         }
936                         if(upnpStream->rtcp != NULL && upnpStream->rtcp->state == LinphoneUpnpStateOk) {
937                                 strncpy(stream->rtcp_addr, upnpStream->rtcp->external_addr, LINPHONE_IPADDR_SIZE);
938                                 stream->rtcp_port = upnpStream->rtcp->external_port;
939                         }
940                 }
941         }
942         return 0;
943 }
944
945
946 /*
947  * uPnP Port Binding
948  */
949
950 UpnpPortBinding *linphone_upnp_port_binding_new() {
951         UpnpPortBinding *port = NULL;
952         port = ms_new0(UpnpPortBinding,1);
953         ms_mutex_init(&port->mutex, NULL);
954         port->state = LinphoneUpnpStateIdle;
955         port->protocol = UPNP_IGD_IP_PROTOCOL_UDP;      
956         port->local_addr[0] = '\0';
957         port->local_port = -1;
958         port->external_addr[0] = '\0';
959         port->external_port = -1;
960         port->to_remove = FALSE;
961         port->to_add = FALSE;
962         port->ref = 1;
963         port->last_update = 0;
964         return port;
965 }
966
967 UpnpPortBinding *linphone_upnp_port_binding_new_with_parameters(upnp_igd_ip_protocol protocol, int local_port, int external_port) {
968         UpnpPortBinding *port_binding = linphone_upnp_port_binding_new();
969         port_binding->protocol = protocol;
970         port_binding->local_port = local_port;
971         port_binding->external_port = external_port;
972         return port_binding;
973 }
974
975 UpnpPortBinding *linphone_upnp_port_binding_new_or_collect(MSList *list, upnp_igd_ip_protocol protocol, int local_port, int external_port) {
976         UpnpPortBinding *tmp_binding;
977         UpnpPortBinding *end_binding;
978         end_binding = linphone_upnp_port_binding_new_with_parameters(protocol, local_port, external_port);
979         tmp_binding = linphone_upnp_port_binding_equivalent_in_list(list, end_binding);
980         if(tmp_binding != NULL) {
981                 linphone_upnp_port_binding_release(end_binding);
982                 end_binding = tmp_binding;
983         }
984         return end_binding;     
985
986
987 UpnpPortBinding *linphone_upnp_port_binding_copy(const UpnpPortBinding *port) {
988         UpnpPortBinding *new_port = NULL;
989         new_port = ms_new0(UpnpPortBinding,1);
990         memcpy(new_port, port, sizeof(UpnpPortBinding));
991         ms_mutex_init(&new_port->mutex, NULL);
992         new_port->ref = 1;
993         return new_port;
994 }
995
996 void linphone_upnp_port_binding_log(int level, const char *msg, const UpnpPortBinding *port) {
997         if(strlen(port->local_addr)) {
998                 ortp_log(level, "uPnP IGD: %s %s|%d->%s:%d (retry %d)", msg,
999                                                         (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
1000                                                                         port->external_port,
1001                                                                         port->local_addr,
1002                                                                         port->local_port,
1003                                                                         port->retry - 1);
1004         } else {
1005                 ortp_log(level, "uPnP IGD: %s %s|%d->%d (retry %d)", msg,
1006                                                         (port->protocol == UPNP_IGD_IP_PROTOCOL_TCP)? "TCP":"UDP",
1007                                                                         port->external_port,
1008                                                                         port->local_port,
1009                                                                         port->retry - 1);
1010         }
1011 }
1012
1013 bool_t linphone_upnp_port_binding_equal(const UpnpPortBinding *port1, const UpnpPortBinding *port2) {
1014         return port1->protocol == port2->protocol &&
1015                         port1->local_port == port2->local_port &&
1016                         port1->external_port == port2->external_port;
1017 }
1018
1019 UpnpPortBinding *linphone_upnp_port_binding_equivalent_in_list(MSList *list, const UpnpPortBinding *port) {
1020         UpnpPortBinding *port_mapping;
1021         while(list != NULL) {
1022                 port_mapping = (UpnpPortBinding *)list->data;
1023                 if(linphone_upnp_port_binding_equal(port, port_mapping)) {
1024                         return port_mapping;
1025                 }
1026                 list = list->next;
1027         }
1028
1029         return NULL;
1030 }
1031
1032 UpnpPortBinding *linphone_upnp_port_binding_retain(UpnpPortBinding *port) {
1033         ms_mutex_lock(&port->mutex);
1034         port->ref++;
1035         ms_mutex_unlock(&port->mutex);
1036         return port;
1037 }
1038
1039 void linphone_upnp_port_binding_release(UpnpPortBinding *port) {
1040         ms_mutex_lock(&port->mutex);
1041         if(--port->ref == 0) {
1042                 ms_mutex_unlock(&port->mutex);
1043                 ms_mutex_destroy(&port->mutex);
1044                 ms_free(port);
1045                 return;
1046         }
1047         ms_mutex_unlock(&port->mutex);
1048 }
1049
1050
1051 /*
1052  * uPnP Stream
1053  */
1054
1055 UpnpStream* linphone_upnp_stream_new() {
1056         UpnpStream *stream = ms_new0(UpnpStream,1);
1057         stream->state = LinphoneUpnpStateIdle;
1058         stream->rtp = NULL; 
1059         stream->rtcp = NULL;
1060         return stream;
1061 }
1062
1063 void linphone_upnp_stream_destroy(UpnpStream* stream) {
1064         if(stream->rtp != NULL) {
1065                 linphone_upnp_port_binding_release(stream->rtp);
1066                 stream->rtp = NULL;
1067         }
1068         if(stream->rtcp != NULL) {
1069                 linphone_upnp_port_binding_release(stream->rtcp);
1070                 stream->rtcp = NULL;
1071         }
1072         ms_free(stream);
1073 }
1074
1075
1076 /*
1077  * uPnP Session
1078  */
1079
1080 UpnpSession* linphone_upnp_session_new(LinphoneCall* call) {
1081         UpnpSession *session = ms_new0(UpnpSession,1);
1082         session->call = call;
1083         session->state = LinphoneUpnpStateIdle;
1084         session->audio = linphone_upnp_stream_new();
1085         session->video = linphone_upnp_stream_new();
1086         return session;
1087 }
1088
1089 void linphone_upnp_session_destroy(UpnpSession *session) {
1090         LinphoneCore *lc = session->call->core;
1091
1092         if(lc->upnp != NULL) {
1093                 /* Remove bindings */
1094                 if(session->audio->rtp != NULL) {
1095                         linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtp, TRUE);
1096                 }
1097                 if(session->audio->rtcp != NULL) {
1098                         linphone_upnp_context_send_remove_port_binding(lc->upnp, session->audio->rtcp, TRUE);
1099                 }
1100                 if(session->video->rtp != NULL) {
1101                         linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtp, TRUE);
1102                 }
1103                 if(session->video->rtcp != NULL) {
1104                         linphone_upnp_context_send_remove_port_binding(lc->upnp, session->video->rtcp, TRUE);
1105                 }
1106         }
1107
1108         linphone_upnp_stream_destroy(session->audio);
1109         linphone_upnp_stream_destroy(session->video);
1110         ms_free(session);
1111 }
1112
1113 LinphoneUpnpState linphone_upnp_session_get_state(UpnpSession *session) {
1114         return session->state;
1115 }
1116
1117
1118 /*
1119  * uPnP Config
1120  */
1121
1122 struct linphone_upnp_config_list_port_bindings_struct {
1123         struct _LpConfig *lpc;
1124         MSList *retList;
1125 };
1126
1127 static void linphone_upnp_config_list_port_bindings_cb(const char *entry, struct linphone_upnp_config_list_port_bindings_struct *cookie) {
1128         char protocol_str[4]; // TCP or UDP
1129         upnp_igd_ip_protocol protocol;
1130         int external_port;
1131         int local_port;
1132         bool_t valid = TRUE;
1133         UpnpPortBinding *port;
1134         if(sscanf(entry, "%3s-%i-%i", protocol_str, &external_port, &local_port) == 3) {
1135                 if(strcasecmp(protocol_str, "TCP") == 0) {
1136                         protocol = UPNP_IGD_IP_PROTOCOL_TCP;
1137                 } else if(strcasecmp(protocol_str, "UDP") == 0) {
1138                         protocol = UPNP_IGD_IP_PROTOCOL_UDP;
1139                 } else {
1140                         valid = FALSE;
1141                 }
1142                 if(valid) {
1143                         port = linphone_upnp_port_binding_new();
1144                         port->state = LinphoneUpnpStateOk;
1145                         port->protocol = protocol;
1146                         port->external_port = external_port;
1147                         port->local_port = local_port;
1148                         cookie->retList = ms_list_append(cookie->retList, port);
1149                 }
1150         } else {
1151                 valid = FALSE;
1152         }
1153         if(!valid) {
1154                 ms_warning("uPnP configuration invalid line: %s", entry);
1155         }
1156 }
1157
1158 MSList *linphone_upnp_config_list_port_bindings(struct _LpConfig *lpc) {
1159         struct linphone_upnp_config_list_port_bindings_struct cookie = {lpc, NULL};
1160         lp_config_for_each_entry(lpc, UPNP_SECTION_NAME, (void(*)(const char *, void*))linphone_upnp_config_list_port_bindings_cb, &cookie);
1161         return cookie.retList;
1162 }
1163
1164 void linphone_upnp_config_add_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) {
1165         MSList *list;
1166         UpnpPortBinding *list_port;
1167
1168         list = lupnp->removing_configs;
1169         while(list != NULL) {
1170                 list_port = (UpnpPortBinding *)list->data;
1171                 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1172                         lupnp->removing_configs = ms_list_remove(lupnp->removing_configs, list_port);
1173                         linphone_upnp_port_binding_release(list_port);
1174                         return;
1175                 }
1176                 list = ms_list_next(list);
1177         }
1178
1179         list = lupnp->adding_configs;
1180         while(list != NULL) {
1181                 list_port = (UpnpPortBinding *)list->data;
1182                 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1183                         return;
1184                 }
1185                 list = ms_list_next(list);
1186         }
1187
1188         list_port = linphone_upnp_port_binding_copy(port);
1189         lupnp->adding_configs = ms_list_append(lupnp->adding_configs, list_port);
1190 }
1191
1192 void linphone_upnp_config_remove_port_binding(UpnpContext *lupnp, const UpnpPortBinding *port) {
1193         MSList *list;
1194         UpnpPortBinding *list_port;
1195
1196         list = lupnp->adding_configs;
1197         while(list != NULL) {
1198                 list_port = (UpnpPortBinding *)list->data;
1199                 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1200                         lupnp->adding_configs = ms_list_remove(lupnp->adding_configs, list_port);
1201                         linphone_upnp_port_binding_release(list_port);
1202                         return;
1203                 }
1204                 list = ms_list_next(list);
1205         }
1206
1207         list = lupnp->removing_configs;
1208         while(list != NULL) {
1209                 list_port = (UpnpPortBinding *)list->data;
1210                 if(linphone_upnp_port_binding_equal(list_port, port) == TRUE) {
1211                         return;
1212                 }
1213                 list = ms_list_next(list);
1214         }
1215
1216         list_port = linphone_upnp_port_binding_copy(port);
1217         lupnp->removing_configs = ms_list_append(lupnp->removing_configs, list_port);
1218 }