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