Replace functions which called once with their bodies
[pidgin-git.git] / libpurple / network.c
blobb4b661ed15b690ee0bbbe425be8b8a2518459d83
1 /* purple
3 * Purple is the legal property of its developers, whose names are too numerous
4 * to list here. Please refer to the COPYRIGHT file distributed with this
5 * source distribution.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
22 #include "internal.h"
24 #include <gio/gio.h>
26 #ifndef _WIN32
27 #include <arpa/nameser.h>
28 #include <resolv.h>
29 #include <netinet/in.h>
30 #include <net/if.h>
31 #include <sys/ioctl.h>
32 #ifdef HAVE_GETIFADDRS
33 #include <ifaddrs.h>
34 #endif
35 #else
36 #include <nspapi.h>
37 #endif
39 /* Solaris */
40 #if defined (__SVR4) && defined (__sun)
41 #include <sys/sockio.h>
42 #endif
44 #include "debug.h"
45 #include "account.h"
46 #include "nat-pmp.h"
47 #include "network.h"
48 #include "prefs.h"
49 #include "stun.h"
50 #include "upnp.h"
53 * Calling sizeof(struct ifreq) isn't always correct on
54 * Mac OS X (and maybe others).
56 #ifdef _SIZEOF_ADDR_IFREQ
57 # define HX_SIZE_OF_IFREQ(a) _SIZEOF_ADDR_IFREQ(a)
58 #else
59 # define HX_SIZE_OF_IFREQ(a) sizeof(a)
60 #endif
62 struct _PurpleNetworkListenData {
63 int listenfd;
64 int socket_type;
65 gboolean retry;
66 gboolean adding;
67 PurpleNetworkListenCallback cb;
68 gpointer cb_data;
69 PurpleUPnPMappingAddRemove *mapping_data;
70 int timer;
73 static gboolean force_online = FALSE;
75 /* Cached IP addresses for STUN and TURN servers (set globally in prefs) */
76 static gchar *stun_ip = NULL;
77 static gchar *turn_ip = NULL;
79 /* Keep track of port mappings done with UPnP and NAT-PMP */
80 static GHashTable *upnp_port_mappings = NULL;
81 static GHashTable *nat_pmp_port_mappings = NULL;
83 void
84 purple_network_set_public_ip(const char *ip)
86 g_return_if_fail(ip != NULL);
88 /* XXX - Ensure the IP address is valid */
90 purple_prefs_set_string("/purple/network/public_ip", ip);
93 const char *
94 purple_network_get_public_ip(void)
96 return purple_prefs_get_string("/purple/network/public_ip");
99 const char *
100 purple_network_get_local_system_ip(int fd)
102 struct ifreq buffer[100];
103 guchar *it, *it_end;
104 static char ip[16];
105 struct ifconf ifc;
106 struct ifreq *ifr;
107 struct sockaddr_in *sinptr;
108 guint32 lhost = htonl((127 << 24) + 1); /* 127.0.0.1 */
109 long unsigned int add;
110 int source = fd;
112 if (fd < 0)
113 source = socket(PF_INET,SOCK_STREAM, 0);
115 ifc.ifc_len = sizeof(buffer);
116 ifc.ifc_req = buffer;
117 ioctl(source, SIOCGIFCONF, &ifc);
119 if (fd < 0 && source >= 0)
120 close(source);
122 it = (guchar*)buffer;
123 it_end = it + ifc.ifc_len;
124 while (it < it_end) {
125 /* in this case "it" is:
126 * a) (struct ifreq)-aligned
127 * b) not aligned, because of OS quirks (see
128 * _SIZEOF_ADDR_IFREQ), so the OS should deal with it.
130 ifr = (struct ifreq *)(gpointer)it;
131 it += HX_SIZE_OF_IFREQ(*ifr);
133 if (ifr->ifr_addr.sa_family == AF_INET)
135 sinptr = (struct sockaddr_in *)(gpointer)&ifr->ifr_addr;
136 if (sinptr->sin_addr.s_addr != lhost)
138 add = ntohl(sinptr->sin_addr.s_addr);
139 g_snprintf(ip, 16, "%lu.%lu.%lu.%lu",
140 ((add >> 24) & 255),
141 ((add >> 16) & 255),
142 ((add >> 8) & 255),
143 add & 255);
145 return ip;
150 return "0.0.0.0";
153 GList *
154 purple_network_get_all_local_system_ips(void)
156 #if defined(HAVE_GETIFADDRS) && defined(HAVE_INET_NTOP)
157 GList *result = NULL;
158 struct ifaddrs *start, *ifa;
159 int ret;
161 ret = getifaddrs(&start);
162 if (ret < 0) {
163 purple_debug_warning("network",
164 "getifaddrs() failed: %s\n", g_strerror(errno));
165 return NULL;
168 for (ifa = start; ifa; ifa = ifa->ifa_next) {
169 int family = ifa->ifa_addr ? ifa->ifa_addr->sa_family : AF_UNSPEC;
170 char host[INET6_ADDRSTRLEN];
171 const char *tmp = NULL;
172 common_sockaddr_t *addr =
173 (common_sockaddr_t *)(gpointer)ifa->ifa_addr;
175 if ((family != AF_INET && family != AF_INET6) || ifa->ifa_flags & IFF_LOOPBACK)
176 continue;
178 if (family == AF_INET)
179 tmp = inet_ntop(family, &addr->in.sin_addr, host, sizeof(host));
180 else {
181 /* Peer-peer link-local communication is a big TODO. I am not sure
182 * how communicating link-local addresses is supposed to work, and
183 * it seems like it would require attempting the cartesian product
184 * of the local and remote interfaces to see if any match (eww).
186 if (!IN6_IS_ADDR_LINKLOCAL(&addr->in6.sin6_addr))
187 tmp = inet_ntop(family, &addr->in6.sin6_addr, host, sizeof(host));
189 if (tmp != NULL)
190 result = g_list_prepend(result, g_strdup(tmp));
193 freeifaddrs(start);
195 return g_list_reverse(result);
196 #else /* HAVE_GETIFADDRS && HAVE_INET_NTOP */
197 GList *result = NULL;
198 int source = socket(PF_INET,SOCK_STREAM, 0);
199 struct ifreq buffer[100];
200 guchar *it, *it_end;
201 struct ifconf ifc;
202 struct ifreq *ifr;
204 ifc.ifc_len = sizeof(buffer);
205 ifc.ifc_req = buffer;
206 ioctl(source, SIOCGIFCONF, &ifc);
207 close(source);
209 it = (guchar*)buffer;
210 it_end = it + ifc.ifc_len;
211 while (it < it_end) {
212 char dst[INET_ADDRSTRLEN];
214 /* alignment: see purple_network_get_local_system_ip */
215 ifr = (struct ifreq *)(gpointer)it;
216 it += HX_SIZE_OF_IFREQ(*ifr);
218 if (ifr->ifr_addr.sa_family == AF_INET) {
219 struct sockaddr_in *sinptr =
220 (struct sockaddr_in *)(gpointer)&ifr->ifr_addr;
222 inet_ntop(AF_INET, &sinptr->sin_addr, dst,
223 sizeof(dst));
224 purple_debug_info("network",
225 "found local i/f with address %s on IPv4\n", dst);
226 if (!purple_strequal(dst, "127.0.0.1")) {
227 result = g_list_append(result, g_strdup(dst));
232 return result;
233 #endif /* HAVE_GETIFADDRS && HAVE_INET_NTOP */
237 * purple_network_is_ipv4:
238 * @hostname: The hostname to be verified.
240 * Checks, if specified hostname is valid ipv4 address.
242 * Returns: TRUE, if the hostname is valid.
244 static gboolean
245 purple_network_is_ipv4(const gchar *hostname)
247 g_return_val_if_fail(hostname != NULL, FALSE);
249 /* We don't accept ipv6 here. */
250 if (strchr(hostname, ':') != NULL)
251 return FALSE;
253 return g_hostname_is_ip_address(hostname);
256 const char *
257 purple_network_get_my_ip(int fd)
259 const char *ip = NULL;
260 PurpleStunNatDiscovery *stun;
262 /* Check if the user specified an IP manually */
263 if (!purple_prefs_get_bool("/purple/network/auto_ip")) {
264 ip = purple_network_get_public_ip();
265 /* Make sure the IP address entered by the user is valid */
266 if ((ip != NULL) && (purple_network_is_ipv4(ip)))
267 return ip;
268 } else {
269 /* Check if STUN discovery was already done */
270 stun = purple_stun_discover(NULL);
271 if ((stun != NULL) && (stun->status == PURPLE_STUN_STATUS_DISCOVERED))
272 return stun->publicip;
274 /* Attempt to get the IP from a NAT device using UPnP */
275 ip = purple_upnp_get_public_ip();
276 if (ip != NULL)
277 return ip;
279 /* Attempt to get the IP from a NAT device using NAT-PMP */
280 ip = purple_pmp_get_public_ip();
281 if (ip != NULL)
282 return ip;
285 /* Just fetch the IP of the local system */
286 return purple_network_get_local_system_ip(fd);
290 static void
291 purple_network_set_upnp_port_mapping_cb(gboolean success, gpointer data)
293 PurpleNetworkListenData *listen_data;
295 listen_data = data;
296 /* TODO: Once we're keeping track of upnp requests... */
297 /* listen_data->pnp_data = NULL; */
299 if (!success) {
300 purple_debug_warning("network", "Couldn't create UPnP mapping\n");
301 if (listen_data->retry) {
302 listen_data->retry = FALSE;
303 listen_data->adding = FALSE;
304 listen_data->mapping_data = purple_upnp_remove_port_mapping(
305 purple_network_get_port_from_fd(listen_data->listenfd),
306 (listen_data->socket_type == SOCK_STREAM) ? "TCP" : "UDP",
307 purple_network_set_upnp_port_mapping_cb, listen_data);
308 return;
310 } else if (!listen_data->adding) {
311 /* We've tried successfully to remove the port mapping.
312 * Try to add it again */
313 listen_data->adding = TRUE;
314 listen_data->mapping_data = purple_upnp_set_port_mapping(
315 purple_network_get_port_from_fd(listen_data->listenfd),
316 (listen_data->socket_type == SOCK_STREAM) ? "TCP" : "UDP",
317 purple_network_set_upnp_port_mapping_cb, listen_data);
318 return;
321 if (success) {
322 /* add port mapping to hash table */
323 gint key = purple_network_get_port_from_fd(listen_data->listenfd);
324 gint value = listen_data->socket_type;
325 g_hash_table_insert(upnp_port_mappings, GINT_TO_POINTER(key), GINT_TO_POINTER(value));
328 if (listen_data->cb)
329 listen_data->cb(listen_data->listenfd, listen_data->cb_data);
331 /* Clear the UPnP mapping data, since it's complete and purple_network_listen_cancel() will try to cancel
332 * it otherwise. */
333 listen_data->mapping_data = NULL;
334 purple_network_listen_cancel(listen_data);
337 static gboolean
338 purple_network_finish_pmp_map_cb(gpointer data)
340 PurpleNetworkListenData *listen_data;
341 gint key;
342 gint value;
344 listen_data = data;
345 listen_data->timer = 0;
347 /* add port mapping to hash table */
348 key = purple_network_get_port_from_fd(listen_data->listenfd);
349 value = listen_data->socket_type;
350 g_hash_table_insert(nat_pmp_port_mappings, GINT_TO_POINTER(key), GINT_TO_POINTER(value));
352 if (listen_data->cb)
353 listen_data->cb(listen_data->listenfd, listen_data->cb_data);
355 purple_network_listen_cancel(listen_data);
357 return FALSE;
360 static PurpleNetworkListenData *
361 purple_network_do_listen(unsigned short port, int socket_family, int socket_type, gboolean map_external,
362 PurpleNetworkListenCallback cb, gpointer cb_data)
364 int listenfd = -1;
365 const int on = 1;
366 PurpleNetworkListenData *listen_data;
367 unsigned short actual_port;
368 #ifdef HAVE_GETADDRINFO
369 int errnum;
370 struct addrinfo hints, *res, *next;
371 char serv[6];
374 * Get a list of addresses on this machine.
376 g_snprintf(serv, sizeof(serv), "%hu", port);
377 memset(&hints, 0, sizeof(struct addrinfo));
378 hints.ai_flags = AI_PASSIVE;
379 hints.ai_family = socket_family;
380 hints.ai_socktype = socket_type;
381 errnum = getaddrinfo(NULL /* any IP */, serv, &hints, &res);
382 if (errnum != 0) {
383 #ifndef _WIN32
384 purple_debug_warning("network", "getaddrinfo: %s\n", purple_gai_strerror(errnum));
385 if (errnum == EAI_SYSTEM)
386 purple_debug_warning("network", "getaddrinfo: system error: %s\n", g_strerror(errno));
387 #else
388 purple_debug_warning("network", "getaddrinfo: Error Code = %d\n", errnum);
389 #endif
390 return NULL;
394 * Go through the list of addresses and attempt to listen on
395 * one of them.
396 * XXX - Try IPv6 addresses first?
398 for (next = res; next != NULL; next = next->ai_next) {
399 listenfd = socket(next->ai_family, next->ai_socktype, next->ai_protocol);
400 if (listenfd < 0)
401 continue;
402 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
403 purple_debug_warning("network", "setsockopt(SO_REUSEADDR): %s\n", g_strerror(errno));
404 if (bind(listenfd, next->ai_addr, next->ai_addrlen) == 0)
405 break; /* success */
406 /* XXX - It is unclear to me (datallah) whether we need to be
407 using a new socket each time */
408 close(listenfd);
411 freeaddrinfo(res);
413 if (next == NULL)
414 return NULL;
415 #else
416 struct sockaddr_in sockin;
418 if (socket_family != AF_INET && socket_family != AF_UNSPEC) {
419 purple_debug_warning("network", "Address family %d only "
420 "supported when built with getaddrinfo() "
421 "support\n", socket_family);
422 return NULL;
425 if ((listenfd = socket(AF_INET, socket_type, 0)) < 0) {
426 purple_debug_warning("network", "socket: %s\n", g_strerror(errno));
427 return NULL;
430 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
431 purple_debug_warning("network", "setsockopt: %s\n", g_strerror(errno));
433 memset(&sockin, 0, sizeof(struct sockaddr_in));
434 sockin.sin_family = PF_INET;
435 sockin.sin_port = htons(port);
437 if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) {
438 purple_debug_warning("network", "bind: %s\n", g_strerror(errno));
439 close(listenfd);
440 return NULL;
442 #endif
444 if (socket_type == SOCK_STREAM && listen(listenfd, 4) != 0) {
445 purple_debug_warning("network", "listen: %s\n", g_strerror(errno));
446 close(listenfd);
447 return NULL;
449 _purple_network_set_common_socket_flags(listenfd);
450 actual_port = purple_network_get_port_from_fd(listenfd);
452 purple_debug_info("network", "Listening on port: %hu\n", actual_port);
454 listen_data = g_new0(PurpleNetworkListenData, 1);
455 listen_data->listenfd = listenfd;
456 listen_data->adding = TRUE;
457 listen_data->retry = TRUE;
458 listen_data->cb = cb;
459 listen_data->cb_data = cb_data;
460 listen_data->socket_type = socket_type;
462 if (!purple_socket_speaks_ipv4(listenfd) || !map_external ||
463 !purple_prefs_get_bool("/purple/network/map_ports"))
465 purple_debug_info("network", "Skipping external port mapping.\n");
466 /* The pmp_map_cb does what we want to do */
467 listen_data->timer = g_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
469 /* Attempt a NAT-PMP Mapping, which will return immediately */
470 else if (purple_pmp_create_map(((socket_type == SOCK_STREAM) ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP),
471 actual_port, actual_port, PURPLE_PMP_LIFETIME))
473 purple_debug_info("network", "Created NAT-PMP mapping on port %i\n", actual_port);
474 /* We want to return listen_data now, and on the next run loop trigger the cb and destroy listen_data */
475 listen_data->timer = g_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
477 else
479 /* Attempt a UPnP Mapping */
480 listen_data->mapping_data = purple_upnp_set_port_mapping(
481 actual_port,
482 (socket_type == SOCK_STREAM) ? "TCP" : "UDP",
483 purple_network_set_upnp_port_mapping_cb, listen_data);
486 return listen_data;
489 PurpleNetworkListenData *
490 purple_network_listen(unsigned short port, int socket_family, int socket_type,
491 gboolean map_external, PurpleNetworkListenCallback cb,
492 gpointer cb_data)
494 g_return_val_if_fail(port != 0, NULL);
496 return purple_network_do_listen(port, socket_family, socket_type, map_external,
497 cb, cb_data);
500 PurpleNetworkListenData *
501 purple_network_listen_range(unsigned short start, unsigned short end,
502 int socket_family, int socket_type, gboolean map_external,
503 PurpleNetworkListenCallback cb,
504 gpointer cb_data)
506 PurpleNetworkListenData *ret = NULL;
508 if (purple_prefs_get_bool("/purple/network/ports_range_use")) {
509 start = purple_prefs_get_int("/purple/network/ports_range_start");
510 end = purple_prefs_get_int("/purple/network/ports_range_end");
511 } else {
512 if (end < start)
513 end = start;
516 for (; start <= end; start++) {
517 ret = purple_network_do_listen(start, AF_UNSPEC, socket_type, map_external, cb, cb_data);
518 if (ret != NULL)
519 break;
522 return ret;
525 void purple_network_listen_cancel(PurpleNetworkListenData *listen_data)
527 if (listen_data->mapping_data != NULL)
528 purple_upnp_cancel_port_mapping(listen_data->mapping_data);
530 if (listen_data->timer > 0)
531 g_source_remove(listen_data->timer);
533 g_free(listen_data);
536 unsigned short
537 purple_network_get_port_from_fd(int fd)
539 struct sockaddr_in addr;
540 socklen_t len;
542 g_return_val_if_fail(fd >= 0, 0);
544 len = sizeof(addr);
545 if (getsockname(fd, (struct sockaddr *) &addr, &len) == -1) {
546 purple_debug_warning("network", "getsockname: %s\n", g_strerror(errno));
547 return 0;
550 return ntohs(addr.sin_port);
553 gboolean
554 purple_network_is_available(void)
556 if(force_online) {
557 return TRUE;
560 return g_network_monitor_get_network_available(g_network_monitor_get_default());
563 void
564 purple_network_force_online()
566 force_online = TRUE;
569 static void
570 purple_network_ip_lookup_cb(GObject *sender, GAsyncResult *result, gpointer data) {
571 GError *error = NULL;
572 GList *addresses = NULL;
573 GInetAddress *address = NULL;
574 const gchar **ip_address = (const gchar **)data;
576 addresses = g_resolver_lookup_by_name_finish(G_RESOLVER(sender),
577 result, &error);
578 if(error) {
579 purple_debug_info("network", "lookup of IP address failed: %s\n", error->message);
581 g_error_free(error);
583 return;
586 address = G_INET_ADDRESS(addresses->data);
588 *ip_address = g_inet_address_to_string(address);
590 g_resolver_free_addresses(addresses);
593 void
594 purple_network_set_stun_server(const gchar *stun_server)
596 if (stun_server && stun_server[0] != '\0') {
597 if (purple_network_is_available()) {
598 GResolver *resolver = g_resolver_get_default();
599 g_resolver_lookup_by_name_async(resolver,
600 stun_server,
601 NULL,
602 purple_network_ip_lookup_cb,
603 &stun_ip);
604 g_object_unref(resolver);
605 } else {
606 purple_debug_info("network",
607 "network is unavailable, don't try to update STUN IP");
609 } else {
610 g_free(stun_ip);
611 stun_ip = NULL;
615 void
616 purple_network_set_turn_server(const gchar *turn_server)
618 if (turn_server && turn_server[0] != '\0') {
619 if (purple_network_is_available()) {
620 GResolver *resolver = g_resolver_get_default();
621 g_resolver_lookup_by_name_async(resolver,
622 turn_server,
623 NULL,
624 purple_network_ip_lookup_cb,
625 &turn_server);
626 g_object_unref(resolver);
627 } else {
628 purple_debug_info("network",
629 "network is unavailable, don't try to update TURN IP");
631 } else {
632 g_free(turn_ip);
633 turn_ip = NULL;
638 const gchar *
639 purple_network_get_stun_ip(void)
641 return stun_ip;
644 const gchar *
645 purple_network_get_turn_ip(void)
647 return turn_ip;
650 void *
651 purple_network_get_handle(void)
653 static int handle;
655 return &handle;
658 static void
659 purple_network_upnp_mapping_remove_cb(gboolean sucess, gpointer data)
661 purple_debug_info("network", "done removing UPnP port mapping\n");
664 /* the reason for these functions to have these signatures is to be able to
665 use them for g_hash_table_foreach to clean remaining port mappings, which is
666 not yet done */
667 static void
668 purple_network_upnp_mapping_remove(gpointer key, gpointer value,
669 gpointer user_data)
671 gint port = GPOINTER_TO_INT(key);
672 gint protocol = GPOINTER_TO_INT(value);
673 purple_debug_info("network", "removing UPnP port mapping for port %d\n",
674 port);
675 purple_upnp_remove_port_mapping(port,
676 protocol == SOCK_STREAM ? "TCP" : "UDP",
677 purple_network_upnp_mapping_remove_cb, NULL);
678 g_hash_table_remove(upnp_port_mappings, GINT_TO_POINTER(port));
681 static void
682 purple_network_nat_pmp_mapping_remove(gpointer key, gpointer value,
683 gpointer user_data)
685 gint port = GPOINTER_TO_INT(key);
686 gint protocol = GPOINTER_TO_INT(value);
687 purple_debug_info("network", "removing NAT-PMP port mapping for port %d\n",
688 port);
689 purple_pmp_destroy_map(
690 protocol == SOCK_STREAM ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP,
691 port);
692 g_hash_table_remove(nat_pmp_port_mappings, GINT_TO_POINTER(port));
695 void
696 purple_network_remove_port_mapping(gint fd)
698 int port = purple_network_get_port_from_fd(fd);
699 gint protocol = GPOINTER_TO_INT(g_hash_table_lookup(upnp_port_mappings, GINT_TO_POINTER(port)));
701 if (protocol) {
702 purple_network_upnp_mapping_remove(GINT_TO_POINTER(port), GINT_TO_POINTER(protocol), NULL);
703 } else {
704 protocol = GPOINTER_TO_INT(g_hash_table_lookup(nat_pmp_port_mappings, GINT_TO_POINTER(port)));
705 if (protocol) {
706 purple_network_nat_pmp_mapping_remove(GINT_TO_POINTER(port), GINT_TO_POINTER(protocol), NULL);
711 gboolean
712 _purple_network_set_common_socket_flags(int fd)
714 int flags;
715 gboolean succ = TRUE;
717 g_return_val_if_fail(fd >= 0, FALSE);
719 flags = fcntl(fd, F_GETFL);
721 if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) != 0) {
722 purple_debug_warning("network",
723 "Couldn't set O_NONBLOCK flag\n");
724 succ = FALSE;
727 #ifndef _WIN32
728 if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) {
729 purple_debug_warning("network",
730 "Couldn't set FD_CLOEXEC flag\n");
731 succ = FALSE;
733 #endif
735 return succ;
738 void
739 purple_network_init(void)
741 purple_prefs_add_none ("/purple/network");
742 purple_prefs_add_string("/purple/network/stun_server", "");
743 purple_prefs_add_string("/purple/network/turn_server", "");
744 purple_prefs_add_int ("/purple/network/turn_port", 3478);
745 purple_prefs_add_int ("/purple/network/turn_port_tcp", 3478);
746 purple_prefs_add_string("/purple/network/turn_username", "");
747 purple_prefs_add_string("/purple/network/turn_password", "");
748 purple_prefs_add_bool ("/purple/network/auto_ip", TRUE);
749 purple_prefs_add_string("/purple/network/public_ip", "");
750 purple_prefs_add_bool ("/purple/network/map_ports", TRUE);
751 purple_prefs_add_bool ("/purple/network/ports_range_use", FALSE);
752 purple_prefs_add_int ("/purple/network/ports_range_start", 1024);
753 purple_prefs_add_int ("/purple/network/ports_range_end", 2048);
755 if(purple_prefs_get_bool("/purple/network/map_ports") || purple_prefs_get_bool("/purple/network/auto_ip"))
756 purple_upnp_discover(NULL, NULL);
758 purple_pmp_init();
759 purple_upnp_init();
761 purple_network_set_stun_server(
762 purple_prefs_get_string("/purple/network/stun_server"));
763 purple_network_set_turn_server(
764 purple_prefs_get_string("/purple/network/turn_server"));
766 upnp_port_mappings = g_hash_table_new(g_direct_hash, g_direct_equal);
767 nat_pmp_port_mappings = g_hash_table_new(g_direct_hash, g_direct_equal);
772 void
773 purple_network_uninit(void)
775 g_free(stun_ip);
777 g_hash_table_destroy(upnp_port_mappings);
778 g_hash_table_destroy(nat_pmp_port_mappings);
780 /* TODO: clean up remaining port mappings, note calling
781 purple_upnp_remove_port_mapping from here doesn't quite work... */
783 purple_upnp_uninit();