Fix crashes when filenames end up being NULL in some prpls.
[pidgin-git.git] / libpurple / network.c
blob508d3c3e13f40be00ac23a544940268ec893603a
1 /**
2 * @file network.c Network Implementation
3 * @ingroup core
4 */
6 /* purple
8 * Purple is the legal property of its developers, whose names are too numerous
9 * to list here. Please refer to the COPYRIGHT file distributed with this
10 * source distribution.
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
27 #include "internal.h"
29 #ifndef _WIN32
30 #include <arpa/nameser.h>
31 #include <resolv.h>
32 #include <netinet/in.h>
33 #include <net/if.h>
34 #include <sys/ioctl.h>
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 #ifdef HAVE_NETWORKMANAGER
63 #include <dbus/dbus-glib.h>
64 #include <NetworkManager.h>
66 static DBusGConnection *nm_conn = NULL;
67 static DBusGProxy *nm_proxy = NULL;
68 static DBusGProxy *dbus_proxy = NULL;
69 static NMState nm_state = NM_STATE_UNKNOWN;
70 static gboolean have_nm_state = FALSE;
72 #elif defined _WIN32
73 static int current_network_count;
75 /* Mutex for the other global vars */
76 static GStaticMutex mutex = G_STATIC_MUTEX_INIT;
77 static gboolean network_initialized = FALSE;
78 static HANDLE network_change_handle = NULL;
79 static int (WSAAPI *MyWSANSPIoctl) (
80 HANDLE hLookup, DWORD dwControlCode, LPVOID lpvInBuffer,
81 DWORD cbInBuffer, LPVOID lpvOutBuffer, DWORD cbOutBuffer,
82 LPDWORD lpcbBytesReturned, LPWSACOMPLETION lpCompletion) = NULL;
83 #endif
85 struct _PurpleNetworkListenData {
86 int listenfd;
87 int socket_type;
88 gboolean retry;
89 gboolean adding;
90 PurpleNetworkListenCallback cb;
91 gpointer cb_data;
92 UPnPMappingAddRemove *mapping_data;
95 #ifdef HAVE_NETWORKMANAGER
96 static NMState nm_get_network_state(void);
97 #endif
99 const unsigned char *
100 purple_network_ip_atoi(const char *ip)
102 static unsigned char ret[4];
103 gchar *delimiter = ".";
104 gchar **split;
105 int i;
107 g_return_val_if_fail(ip != NULL, NULL);
109 split = g_strsplit(ip, delimiter, 4);
110 for (i = 0; split[i] != NULL; i++)
111 ret[i] = atoi(split[i]);
112 g_strfreev(split);
114 /* i should always be 4 */
115 if (i != 4)
116 return NULL;
118 return ret;
121 void
122 purple_network_set_public_ip(const char *ip)
124 g_return_if_fail(ip != NULL);
126 /* XXX - Ensure the IP address is valid */
128 purple_prefs_set_string("/purple/network/public_ip", ip);
131 const char *
132 purple_network_get_public_ip(void)
134 return purple_prefs_get_string("/purple/network/public_ip");
137 const char *
138 purple_network_get_local_system_ip(int fd)
140 char buffer[1024];
141 static char ip[16];
142 char *tmp;
143 struct ifconf ifc;
144 struct ifreq *ifr;
145 struct sockaddr_in *sinptr;
146 guint32 lhost = htonl(127 * 256 * 256 * 256 + 1);
147 long unsigned int add;
148 int source = fd;
150 if (fd < 0)
151 source = socket(PF_INET,SOCK_STREAM, 0);
153 ifc.ifc_len = sizeof(buffer);
154 ifc.ifc_req = (struct ifreq *)buffer;
155 ioctl(source, SIOCGIFCONF, &ifc);
157 if (fd < 0)
158 close(source);
160 tmp = buffer;
161 while (tmp < buffer + ifc.ifc_len)
163 ifr = (struct ifreq *)tmp;
164 tmp += HX_SIZE_OF_IFREQ(*ifr);
166 if (ifr->ifr_addr.sa_family == AF_INET)
168 sinptr = (struct sockaddr_in *)&ifr->ifr_addr;
169 if (sinptr->sin_addr.s_addr != lhost)
171 add = ntohl(sinptr->sin_addr.s_addr);
172 g_snprintf(ip, 16, "%lu.%lu.%lu.%lu",
173 ((add >> 24) & 255),
174 ((add >> 16) & 255),
175 ((add >> 8) & 255),
176 add & 255);
178 return ip;
183 return "0.0.0.0";
186 const char *
187 purple_network_get_my_ip(int fd)
189 const char *ip = NULL;
190 PurpleStunNatDiscovery *stun;
192 /* Check if the user specified an IP manually */
193 if (!purple_prefs_get_bool("/purple/network/auto_ip")) {
194 ip = purple_network_get_public_ip();
195 /* Make sure the IP address entered by the user is valid */
196 if ((ip != NULL) && (purple_network_ip_atoi(ip) != NULL))
197 return ip;
198 } else {
199 /* Check if STUN discovery was already done */
200 stun = purple_stun_discover(NULL);
201 if ((stun != NULL) && (stun->status == PURPLE_STUN_STATUS_DISCOVERED))
202 return stun->publicip;
204 /* Attempt to get the IP from a NAT device using UPnP */
205 ip = purple_upnp_get_public_ip();
206 if (ip != NULL)
207 return ip;
209 /* Attempt to get the IP from a NAT device using NAT-PMP */
210 ip = purple_pmp_get_public_ip();
211 if (ip != NULL)
212 return ip;
215 /* Just fetch the IP of the local system */
216 return purple_network_get_local_system_ip(fd);
220 static void
221 purple_network_set_upnp_port_mapping_cb(gboolean success, gpointer data)
223 PurpleNetworkListenData *listen_data;
225 listen_data = data;
226 /* TODO: Once we're keeping track of upnp requests... */
227 /* listen_data->pnp_data = NULL; */
229 if (!success) {
230 purple_debug_info("network", "Couldn't create UPnP mapping\n");
231 if (listen_data->retry) {
232 listen_data->retry = FALSE;
233 listen_data->adding = FALSE;
234 listen_data->mapping_data = purple_upnp_remove_port_mapping(
235 purple_network_get_port_from_fd(listen_data->listenfd),
236 (listen_data->socket_type == SOCK_STREAM) ? "TCP" : "UDP",
237 purple_network_set_upnp_port_mapping_cb, listen_data);
238 return;
240 } else if (!listen_data->adding) {
241 /* We've tried successfully to remove the port mapping.
242 * Try to add it again */
243 listen_data->adding = TRUE;
244 listen_data->mapping_data = purple_upnp_set_port_mapping(
245 purple_network_get_port_from_fd(listen_data->listenfd),
246 (listen_data->socket_type == SOCK_STREAM) ? "TCP" : "UDP",
247 purple_network_set_upnp_port_mapping_cb, listen_data);
248 return;
251 if (listen_data->cb)
252 listen_data->cb(listen_data->listenfd, listen_data->cb_data);
254 /* Clear the UPnP mapping data, since it's complete and purple_netweork_listen_cancel() will try to cancel
255 * it otherwise. */
256 listen_data->mapping_data = NULL;
257 purple_network_listen_cancel(listen_data);
260 static gboolean
261 purple_network_finish_pmp_map_cb(gpointer data)
263 PurpleNetworkListenData *listen_data;
265 listen_data = data;
267 if (listen_data->cb)
268 listen_data->cb(listen_data->listenfd, listen_data->cb_data);
270 purple_network_listen_cancel(listen_data);
272 return FALSE;
275 static gboolean listen_map_external = TRUE;
276 void purple_network_listen_map_external(gboolean map_external)
278 listen_map_external = map_external;
281 static PurpleNetworkListenData *
282 purple_network_do_listen(unsigned short port, int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
284 int listenfd = -1;
285 int flags;
286 const int on = 1;
287 PurpleNetworkListenData *listen_data;
288 unsigned short actual_port;
289 #ifdef HAVE_GETADDRINFO
290 int errnum;
291 struct addrinfo hints, *res, *next;
292 char serv[6];
295 * Get a list of addresses on this machine.
297 snprintf(serv, sizeof(serv), "%hu", port);
298 memset(&hints, 0, sizeof(struct addrinfo));
299 hints.ai_flags = AI_PASSIVE;
300 hints.ai_family = AF_UNSPEC;
301 hints.ai_socktype = socket_type;
302 errnum = getaddrinfo(NULL /* any IP */, serv, &hints, &res);
303 if (errnum != 0) {
304 #ifndef _WIN32
305 purple_debug_warning("network", "getaddrinfo: %s\n", purple_gai_strerror(errnum));
306 if (errnum == EAI_SYSTEM)
307 purple_debug_warning("network", "getaddrinfo: system error: %s\n", g_strerror(errno));
308 #else
309 purple_debug_warning("network", "getaddrinfo: Error Code = %d\n", errnum);
310 #endif
311 return NULL;
315 * Go through the list of addresses and attempt to listen on
316 * one of them.
317 * XXX - Try IPv6 addresses first?
319 for (next = res; next != NULL; next = next->ai_next) {
320 listenfd = socket(next->ai_family, next->ai_socktype, next->ai_protocol);
321 if (listenfd < 0)
322 continue;
323 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
324 purple_debug_warning("network", "setsockopt: %s\n", g_strerror(errno));
325 if (bind(listenfd, next->ai_addr, next->ai_addrlen) == 0)
326 break; /* success */
327 /* XXX - It is unclear to me (datallah) whether we need to be
328 using a new socket each time */
329 close(listenfd);
332 freeaddrinfo(res);
334 if (next == NULL)
335 return NULL;
336 #else
337 struct sockaddr_in sockin;
339 if ((listenfd = socket(AF_INET, socket_type, 0)) < 0) {
340 purple_debug_warning("network", "socket: %s\n", g_strerror(errno));
341 return NULL;
344 if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
345 purple_debug_warning("network", "setsockopt: %s\n", g_strerror(errno));
347 memset(&sockin, 0, sizeof(struct sockaddr_in));
348 sockin.sin_family = PF_INET;
349 sockin.sin_port = htons(port);
351 if (bind(listenfd, (struct sockaddr *)&sockin, sizeof(struct sockaddr_in)) != 0) {
352 purple_debug_warning("network", "bind: %s\n", g_strerror(errno));
353 close(listenfd);
354 return NULL;
356 #endif
358 if (socket_type == SOCK_STREAM && listen(listenfd, 4) != 0) {
359 purple_debug_warning("network", "listen: %s\n", g_strerror(errno));
360 close(listenfd);
361 return NULL;
363 flags = fcntl(listenfd, F_GETFL);
364 fcntl(listenfd, F_SETFL, flags | O_NONBLOCK);
365 #ifndef _WIN32
366 fcntl(listenfd, F_SETFD, FD_CLOEXEC);
367 #endif
368 actual_port = purple_network_get_port_from_fd(listenfd);
370 purple_debug_info("network", "Listening on port: %hu\n", actual_port);
372 listen_data = g_new0(PurpleNetworkListenData, 1);
373 listen_data->listenfd = listenfd;
374 listen_data->adding = TRUE;
375 listen_data->retry = TRUE;
376 listen_data->cb = cb;
377 listen_data->cb_data = cb_data;
378 listen_data->socket_type = socket_type;
380 if (!listen_map_external || !purple_prefs_get_bool("/purple/network/map_ports"))
382 purple_debug_info("network", "Skipping external port mapping.\n");
383 /* The pmp_map_cb does what we want to do */
384 purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
386 /* Attempt a NAT-PMP Mapping, which will return immediately */
387 else if (purple_pmp_create_map(((socket_type == SOCK_STREAM) ? PURPLE_PMP_TYPE_TCP : PURPLE_PMP_TYPE_UDP),
388 actual_port, actual_port, PURPLE_PMP_LIFETIME))
390 purple_debug_info("network", "Created NAT-PMP mapping on port %i\n", actual_port);
391 /* We want to return listen_data now, and on the next run loop trigger the cb and destroy listen_data */
392 purple_timeout_add(0, purple_network_finish_pmp_map_cb, listen_data);
394 else
396 /* Attempt a UPnP Mapping */
397 listen_data->mapping_data = purple_upnp_set_port_mapping(
398 actual_port,
399 (socket_type == SOCK_STREAM) ? "TCP" : "UDP",
400 purple_network_set_upnp_port_mapping_cb, listen_data);
403 return listen_data;
406 PurpleNetworkListenData *
407 purple_network_listen(unsigned short port, int socket_type,
408 PurpleNetworkListenCallback cb, gpointer cb_data)
410 g_return_val_if_fail(port != 0, NULL);
412 return purple_network_do_listen(port, socket_type, cb, cb_data);
415 PurpleNetworkListenData *
416 purple_network_listen_range(unsigned short start, unsigned short end,
417 int socket_type, PurpleNetworkListenCallback cb, gpointer cb_data)
419 PurpleNetworkListenData *ret = NULL;
421 if (purple_prefs_get_bool("/purple/network/ports_range_use")) {
422 start = purple_prefs_get_int("/purple/network/ports_range_start");
423 end = purple_prefs_get_int("/purple/network/ports_range_end");
424 } else {
425 if (end < start)
426 end = start;
429 for (; start <= end; start++) {
430 ret = purple_network_do_listen(start, socket_type, cb, cb_data);
431 if (ret != NULL)
432 break;
435 return ret;
438 void purple_network_listen_cancel(PurpleNetworkListenData *listen_data)
440 if (listen_data->mapping_data != NULL)
441 purple_upnp_cancel_port_mapping(listen_data->mapping_data);
443 g_free(listen_data);
446 unsigned short
447 purple_network_get_port_from_fd(int fd)
449 struct sockaddr_in addr;
450 socklen_t len;
452 g_return_val_if_fail(fd >= 0, 0);
454 len = sizeof(addr);
455 if (getsockname(fd, (struct sockaddr *) &addr, &len) == -1) {
456 purple_debug_warning("network", "getsockname: %s\n", g_strerror(errno));
457 return 0;
460 return ntohs(addr.sin_port);
463 #ifdef _WIN32
464 #ifndef NS_NLA
465 #define NS_NLA 15
466 #endif
467 static gint
468 wpurple_get_connected_network_count(void)
470 gint net_cnt = 0;
472 WSAQUERYSET qs;
473 HANDLE h;
474 gint retval;
475 int errorid;
477 memset(&qs, 0, sizeof(WSAQUERYSET));
478 qs.dwSize = sizeof(WSAQUERYSET);
479 qs.dwNameSpace = NS_NLA;
481 retval = WSALookupServiceBegin(&qs, LUP_RETURN_ALL, &h);
482 if (retval != ERROR_SUCCESS) {
483 gchar *msg;
484 errorid = WSAGetLastError();
485 msg = g_win32_error_message(errorid);
486 purple_debug_warning("network", "Couldn't retrieve NLA SP lookup handle. "
487 "NLA service is probably not running. Message: %s (%d).\n",
488 msg, errorid);
489 g_free(msg);
491 return -1;
492 } else {
493 char buf[4096];
494 WSAQUERYSET *res = (LPWSAQUERYSET) buf;
495 DWORD size = sizeof(buf);
496 while ((retval = WSALookupServiceNext(h, 0, &size, res)) == ERROR_SUCCESS) {
497 net_cnt++;
498 purple_debug_info("network", "found network '%s'\n",
499 res->lpszServiceInstanceName ? res->lpszServiceInstanceName : "(NULL)");
500 size = sizeof(buf);
503 errorid = WSAGetLastError();
504 if (!(errorid == WSA_E_NO_MORE || errorid == WSAENOMORE)) {
505 gchar *msg = g_win32_error_message(errorid);
506 purple_debug_error("network", "got unexpected NLA response %s (%d)\n", msg, errorid);
507 g_free(msg);
509 net_cnt = -1;
512 retval = WSALookupServiceEnd(h);
515 return net_cnt;
519 static gboolean wpurple_network_change_thread_cb(gpointer data)
521 gint new_count;
522 PurpleConnectionUiOps *ui_ops = purple_connections_get_ui_ops();
524 new_count = wpurple_get_connected_network_count();
526 if (new_count < 0)
527 return FALSE;
529 purple_debug_info("network", "Received Network Change Notification. Current network count is %d, previous count was %d.\n", new_count, current_network_count);
531 purple_signal_emit(purple_network_get_handle(), "network-configuration-changed", NULL);
533 if (new_count > 0 && ui_ops != NULL && ui_ops->network_connected != NULL) {
534 ui_ops->network_connected();
535 } else if (new_count == 0 && current_network_count > 0 &&
536 ui_ops != NULL && ui_ops->network_disconnected != NULL) {
537 ui_ops->network_disconnected();
540 current_network_count = new_count;
542 return FALSE;
545 static gboolean _print_debug_msg(gpointer data) {
546 gchar *msg = data;
547 purple_debug_warning("network", msg);
548 g_free(msg);
549 return FALSE;
552 static gpointer wpurple_network_change_thread(gpointer data)
554 WSAQUERYSET qs;
555 WSAEVENT *nla_event;
556 time_t last_trigger = time(NULL) - 31;
557 char buf[4096];
558 WSAQUERYSET *res = (LPWSAQUERYSET) buf;
559 DWORD size;
561 if ((nla_event = WSACreateEvent()) == WSA_INVALID_EVENT) {
562 int errorid = WSAGetLastError();
563 gchar *msg = g_win32_error_message(errorid);
564 purple_timeout_add(0, _print_debug_msg,
565 g_strdup_printf("Couldn't create WSA event. "
566 "Message: %s (%d).\n", msg, errorid));
567 g_free(msg);
568 g_thread_exit(NULL);
569 return NULL;
572 while (TRUE) {
573 int retval;
574 DWORD retLen = 0;
575 WSACOMPLETION completion;
576 WSAOVERLAPPED overlapped;
578 g_static_mutex_lock(&mutex);
579 if (network_initialized == FALSE) {
580 /* purple_network_uninit has been called */
581 WSACloseEvent(nla_event);
582 g_static_mutex_unlock(&mutex);
583 g_thread_exit(NULL);
584 return NULL;
587 if (network_change_handle == NULL) {
588 memset(&qs, 0, sizeof(WSAQUERYSET));
589 qs.dwSize = sizeof(WSAQUERYSET);
590 qs.dwNameSpace = NS_NLA;
591 if (WSALookupServiceBegin(&qs, 0, &network_change_handle) == SOCKET_ERROR) {
592 int errorid = WSAGetLastError();
593 gchar *msg = g_win32_error_message(errorid);
594 purple_timeout_add(0, _print_debug_msg,
595 g_strdup_printf("Couldn't retrieve NLA SP lookup handle. "
596 "NLA service is probably not running. Message: %s (%d).\n",
597 msg, errorid));
598 g_free(msg);
599 WSACloseEvent(nla_event);
600 g_static_mutex_unlock(&mutex);
601 g_thread_exit(NULL);
602 return NULL;
605 g_static_mutex_unlock(&mutex);
607 memset(&completion, 0, sizeof(WSACOMPLETION));
608 completion.Type = NSP_NOTIFY_EVENT;
609 overlapped.hEvent = nla_event;
610 completion.Parameters.Event.lpOverlapped = &overlapped;
612 if (MyWSANSPIoctl(network_change_handle, SIO_NSP_NOTIFY_CHANGE, NULL, 0, NULL, 0, &retLen, &completion) == SOCKET_ERROR) {
613 int errorid = WSAGetLastError();
614 if (errorid == WSA_INVALID_HANDLE) {
615 purple_timeout_add(0, _print_debug_msg,
616 g_strdup("Invalid NLA handle; resetting.\n"));
617 g_static_mutex_lock(&mutex);
618 retval = WSALookupServiceEnd(network_change_handle);
619 network_change_handle = NULL;
620 g_static_mutex_unlock(&mutex);
621 continue;
622 /* WSA_IO_PENDING indicates successful async notification will happen */
623 } else if (errorid != WSA_IO_PENDING) {
624 gchar *msg = g_win32_error_message(errorid);
625 purple_timeout_add(0, _print_debug_msg,
626 g_strdup_printf("Unable to wait for changes. Message: %s (%d).\n",
627 msg, errorid));
628 g_free(msg);
632 /* Make sure at least 30 seconds have elapsed since the last
633 * notification so we don't peg the cpu if this keeps changing. */
634 if ((time(NULL) - last_trigger) < 30)
635 Sleep(30000);
637 /* This will block until NLA notifies us */
638 retval = WaitForSingleObjectEx(nla_event, WSA_INFINITE, TRUE);
640 last_trigger = time(NULL);
642 g_static_mutex_lock(&mutex);
643 if (network_initialized == FALSE) {
644 /* Time to die */
645 WSACloseEvent(nla_event);
646 g_static_mutex_unlock(&mutex);
647 g_thread_exit(NULL);
648 return NULL;
651 size = sizeof(buf);
652 while ((retval = WSALookupServiceNext(network_change_handle, 0, &size, res)) == ERROR_SUCCESS) {
653 /*purple_timeout_add(0, _print_debug_msg,
654 g_strdup_printf("thread found network '%s'\n",
655 res->lpszServiceInstanceName ? res->lpszServiceInstanceName : "(NULL)"));*/
656 size = sizeof(buf);
659 WSAResetEvent(nla_event);
660 g_static_mutex_unlock(&mutex);
662 purple_timeout_add(0, wpurple_network_change_thread_cb, NULL);
665 g_thread_exit(NULL);
666 return NULL;
668 #endif
670 gboolean
671 purple_network_is_available(void)
673 #ifdef HAVE_NETWORKMANAGER
674 if (!have_nm_state)
676 have_nm_state = TRUE;
677 nm_state = nm_get_network_state();
678 if (nm_state == NM_STATE_UNKNOWN)
679 purple_debug_warning("network", "NetworkManager not active. Assuming connection exists.\n");
682 if (nm_state == NM_STATE_UNKNOWN || nm_state == NM_STATE_CONNECTED)
683 return TRUE;
685 return FALSE;
687 #elif defined _WIN32
688 return (current_network_count > 0);
689 #else
690 return TRUE;
691 #endif
694 #ifdef HAVE_NETWORKMANAGER
695 static void
696 nm_update_state(NMState state)
698 NMState prev = nm_state;
699 PurpleConnectionUiOps *ui_ops = purple_connections_get_ui_ops();
701 have_nm_state = TRUE;
702 nm_state = state;
704 purple_signal_emit(purple_network_get_handle(), "network-configuration-changed", NULL);
706 switch(state)
708 case NM_STATE_CONNECTED:
709 /* Call res_init in case DNS servers have changed */
710 res_init();
711 if (ui_ops != NULL && ui_ops->network_connected != NULL)
712 ui_ops->network_connected();
713 break;
714 case NM_STATE_ASLEEP:
715 case NM_STATE_CONNECTING:
716 case NM_STATE_DISCONNECTED:
717 if (prev != NM_STATE_CONNECTED && prev != NM_STATE_UNKNOWN)
718 break;
719 if (ui_ops != NULL && ui_ops->network_disconnected != NULL)
720 ui_ops->network_disconnected();
721 break;
722 case NM_STATE_UNKNOWN:
723 default:
724 break;
728 static void
729 nm_state_change_cb(DBusGProxy *proxy, NMState state, gpointer user_data)
731 purple_debug_info("network", "Got StateChange from NetworkManager: %d.\n", state);
732 nm_update_state(state);
735 static NMState
736 nm_get_network_state(void)
738 GError *err = NULL;
739 NMState state = NM_STATE_UNKNOWN;
741 if (!nm_proxy)
742 return NM_STATE_UNKNOWN;
744 if (!dbus_g_proxy_call(nm_proxy, "state", &err, G_TYPE_INVALID, G_TYPE_UINT, &state, G_TYPE_INVALID)) {
745 g_error_free(err);
746 return NM_STATE_UNKNOWN;
749 return state;
752 static void
753 nm_dbus_name_owner_changed_cb(DBusGProxy *proxy, char *service, char *old_owner, char *new_owner, gpointer user_data)
755 if (g_str_equal(service, NM_DBUS_SERVICE)) {
756 gboolean old_owner_good = old_owner && (old_owner[0] != '\0');
757 gboolean new_owner_good = new_owner && (new_owner[0] != '\0');
759 purple_debug_info("network", "Got NameOwnerChanged signal, service = '%s', old_owner = '%s', new_owner = '%s'\n", service, old_owner, new_owner);
760 if (!old_owner_good && new_owner_good) { /* Equivalent to old ServiceCreated signal */
761 purple_debug_info("network", "NetworkManager has started.\n");
762 nm_update_state(nm_get_network_state());
763 } else if (old_owner_good && !new_owner_good) { /* Equivalent to old ServiceDeleted signal */
764 purple_debug_info("network", "NetworkManager has gone away.\n");
765 nm_update_state(NM_STATE_UNKNOWN);
770 #endif
772 void *
773 purple_network_get_handle(void)
775 static int handle;
777 return &handle;
780 void
781 purple_network_init(void)
783 #ifdef HAVE_NETWORKMANAGER
784 GError *error = NULL;
785 #endif
786 #ifdef _WIN32
787 GError *err = NULL;
788 gint cnt = wpurple_get_connected_network_count();
790 network_initialized = TRUE;
791 if (cnt < 0) /* Assume there is a network */
792 current_network_count = 1;
793 /* Don't listen for network changes if we can't tell anyway */
794 else {
795 current_network_count = cnt;
796 if ((MyWSANSPIoctl = (void*) wpurple_find_and_loadproc("ws2_32.dll", "WSANSPIoctl"))) {
797 if (!g_thread_create(wpurple_network_change_thread, NULL, FALSE, &err))
798 purple_debug_error("network", "Couldn't create Network Monitor thread: %s\n", err ? err->message : "");
801 #endif
803 purple_prefs_add_none ("/purple/network");
804 purple_prefs_add_bool ("/purple/network/auto_ip", TRUE);
805 purple_prefs_add_string("/purple/network/public_ip", "");
806 purple_prefs_add_bool ("/purple/network/map_ports", TRUE);
807 purple_prefs_add_bool ("/purple/network/ports_range_use", FALSE);
808 purple_prefs_add_int ("/purple/network/ports_range_start", 1024);
809 purple_prefs_add_int ("/purple/network/ports_range_end", 2048);
811 if(purple_prefs_get_bool("/purple/network/map_ports") || purple_prefs_get_bool("/purple/network/auto_ip"))
812 purple_upnp_discover(NULL, NULL);
814 #ifdef HAVE_NETWORKMANAGER
815 nm_conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
816 if (!nm_conn) {
817 purple_debug_warning("network", "Error connecting to DBus System service: %s.\n", error->message);
818 } else {
819 nm_proxy = dbus_g_proxy_new_for_name(nm_conn,
820 NM_DBUS_SERVICE,
821 NM_DBUS_PATH,
822 NM_DBUS_INTERFACE);
823 dbus_g_proxy_add_signal(nm_proxy, "StateChange", G_TYPE_UINT, G_TYPE_INVALID);
824 dbus_g_proxy_connect_signal(nm_proxy, "StateChange",
825 G_CALLBACK(nm_state_change_cb), NULL, NULL);
827 dbus_proxy = dbus_g_proxy_new_for_name(nm_conn,
828 DBUS_SERVICE_DBUS,
829 DBUS_PATH_DBUS,
830 DBUS_INTERFACE_DBUS);
831 dbus_g_proxy_add_signal(dbus_proxy, "NameOwnerChanged", G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
832 dbus_g_proxy_connect_signal(dbus_proxy, "NameOwnerChanged",
833 G_CALLBACK(nm_dbus_name_owner_changed_cb), NULL, NULL);
835 #endif
837 purple_signal_register(purple_network_get_handle(), "network-configuration-changed",
838 purple_marshal_VOID, NULL, 0);
840 purple_pmp_init();
841 purple_upnp_init();
844 void
845 purple_network_uninit(void)
847 #ifdef HAVE_NETWORKMANAGER
848 if (nm_proxy) {
849 dbus_g_proxy_disconnect_signal(nm_proxy, "StateChange", G_CALLBACK(nm_state_change_cb), NULL);
850 g_object_unref(G_OBJECT(nm_proxy));
852 if (dbus_proxy) {
853 dbus_g_proxy_disconnect_signal(dbus_proxy, "NameOwnerChanged", G_CALLBACK(nm_dbus_name_owner_changed_cb), NULL);
854 g_object_unref(G_OBJECT(dbus_proxy));
856 if (nm_conn)
857 dbus_g_connection_unref(nm_conn);
858 #endif
860 #ifdef _WIN32
861 g_static_mutex_lock(&mutex);
862 network_initialized = FALSE;
863 if (network_change_handle != NULL) {
864 int retval;
865 /* Trigger the NLA thread to stop waiting for network changes. Not
866 * doing this can cause hangs on WSACleanup. */
867 purple_debug_warning("network", "Terminating the NLA thread\n");
868 if ((retval = WSALookupServiceEnd(network_change_handle)) == SOCKET_ERROR) {
869 int errorid = WSAGetLastError();
870 gchar *msg = g_win32_error_message(errorid);
871 purple_debug_warning("network", "Unable to kill NLA thread. Message: %s (%d).\n",
872 msg, errorid);
873 g_free(msg);
875 network_change_handle = NULL;
878 g_static_mutex_unlock(&mutex);
880 #endif
881 purple_signal_unregister(purple_network_get_handle(),
882 "network-configuration-changed");