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