2 * @file network.c Network Implementation
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
30 #include <arpa/nameser.h>
32 #include <netinet/in.h>
34 #include <sys/ioctl.h>
40 #if defined (__SVR4) && defined (__sun)
41 #include <sys/sockio.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)
59 # define HX_SIZE_OF_IFREQ(a) sizeof(a)
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
;
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
;
85 struct _PurpleNetworkListenData
{
90 PurpleNetworkListenCallback cb
;
92 UPnPMappingAddRemove
*mapping_data
;
95 #ifdef HAVE_NETWORKMANAGER
96 static NMState
nm_get_network_state(void);
100 purple_network_ip_atoi(const char *ip
)
102 static unsigned char ret
[4];
103 gchar
*delimiter
= ".";
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
]);
114 /* i should always be 4 */
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
);
132 purple_network_get_public_ip(void)
134 return purple_prefs_get_string("/purple/network/public_ip");
138 purple_network_get_local_system_ip(int fd
)
145 struct sockaddr_in
*sinptr
;
146 guint32 lhost
= htonl(127 * 256 * 256 * 256 + 1);
147 long unsigned int add
;
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
);
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",
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
))
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();
209 /* Attempt to get the IP from a NAT device using NAT-PMP */
210 ip
= purple_pmp_get_public_ip();
215 /* Just fetch the IP of the local system */
216 return purple_network_get_local_system_ip(fd
);
221 purple_network_set_upnp_port_mapping_cb(gboolean success
, gpointer data
)
223 PurpleNetworkListenData
*listen_data
;
226 /* TODO: Once we're keeping track of upnp requests... */
227 /* listen_data->pnp_data = NULL; */
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
);
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
);
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
256 listen_data
->mapping_data
= NULL
;
257 purple_network_listen_cancel(listen_data
);
261 purple_network_finish_pmp_map_cb(gpointer data
)
263 PurpleNetworkListenData
*listen_data
;
268 listen_data
->cb(listen_data
->listenfd
, listen_data
->cb_data
);
270 purple_network_listen_cancel(listen_data
);
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
)
287 PurpleNetworkListenData
*listen_data
;
288 unsigned short actual_port
;
289 #ifdef HAVE_GETADDRINFO
291 struct addrinfo hints
, *res
, *next
;
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
);
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
));
309 purple_debug_warning("network", "getaddrinfo: Error Code = %d\n", errnum
);
315 * Go through the list of addresses and attempt to listen on
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
);
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)
327 /* XXX - It is unclear to me (datallah) whether we need to be
328 using a new socket each time */
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
));
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
));
358 if (socket_type
== SOCK_STREAM
&& listen(listenfd
, 4) != 0) {
359 purple_debug_warning("network", "listen: %s\n", g_strerror(errno
));
363 flags
= fcntl(listenfd
, F_GETFL
);
364 fcntl(listenfd
, F_SETFL
, flags
| O_NONBLOCK
);
366 fcntl(listenfd
, F_SETFD
, FD_CLOEXEC
);
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
);
396 /* Attempt a UPnP Mapping */
397 listen_data
->mapping_data
= purple_upnp_set_port_mapping(
399 (socket_type
== SOCK_STREAM
) ? "TCP" : "UDP",
400 purple_network_set_upnp_port_mapping_cb
, 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");
429 for (; start
<= end
; start
++) {
430 ret
= purple_network_do_listen(start
, socket_type
, cb
, cb_data
);
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
);
447 purple_network_get_port_from_fd(int fd
)
449 struct sockaddr_in addr
;
452 g_return_val_if_fail(fd
>= 0, 0);
455 if (getsockname(fd
, (struct sockaddr
*) &addr
, &len
) == -1) {
456 purple_debug_warning("network", "getsockname: %s\n", g_strerror(errno
));
460 return ntohs(addr
.sin_port
);
468 wpurple_get_connected_network_count(void)
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
) {
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",
494 WSAQUERYSET
*res
= (LPWSAQUERYSET
) buf
;
495 DWORD size
= sizeof(buf
);
496 while ((retval
= WSALookupServiceNext(h
, 0, &size
, res
)) == ERROR_SUCCESS
) {
498 purple_debug_info("network", "found network '%s'\n",
499 res
->lpszServiceInstanceName
? res
->lpszServiceInstanceName
: "(NULL)");
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
);
512 retval
= WSALookupServiceEnd(h
);
519 static gboolean
wpurple_network_change_thread_cb(gpointer data
)
522 PurpleConnectionUiOps
*ui_ops
= purple_connections_get_ui_ops();
524 new_count
= wpurple_get_connected_network_count();
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
;
545 static gboolean
_print_debug_msg(gpointer data
) {
547 purple_debug_warning("network", msg
);
552 static gpointer
wpurple_network_change_thread(gpointer data
)
556 time_t last_trigger
= time(NULL
) - 31;
558 WSAQUERYSET
*res
= (LPWSAQUERYSET
) buf
;
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
));
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
);
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",
599 WSACloseEvent(nla_event
);
600 g_static_mutex_unlock(&mutex
);
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
);
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",
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)
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
) {
645 WSACloseEvent(nla_event
);
646 g_static_mutex_unlock(&mutex
);
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)"));*/
659 WSAResetEvent(nla_event
);
660 g_static_mutex_unlock(&mutex
);
662 purple_timeout_add(0, wpurple_network_change_thread_cb
, NULL
);
671 purple_network_is_available(void)
673 #ifdef HAVE_NETWORKMANAGER
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
)
688 return (current_network_count
> 0);
694 #ifdef HAVE_NETWORKMANAGER
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
;
704 purple_signal_emit(purple_network_get_handle(), "network-configuration-changed", NULL
);
708 case NM_STATE_CONNECTED
:
709 /* Call res_init in case DNS servers have changed */
711 if (ui_ops
!= NULL
&& ui_ops
->network_connected
!= NULL
)
712 ui_ops
->network_connected();
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
)
719 if (ui_ops
!= NULL
&& ui_ops
->network_disconnected
!= NULL
)
720 ui_ops
->network_disconnected();
722 case NM_STATE_UNKNOWN
:
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
);
736 nm_get_network_state(void)
739 NMState state
= NM_STATE_UNKNOWN
;
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
)) {
746 return NM_STATE_UNKNOWN
;
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
);
773 purple_network_get_handle(void)
781 purple_network_init(void)
783 #ifdef HAVE_NETWORKMANAGER
784 GError
*error
= 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 */
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
: "");
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
);
817 purple_debug_warning("network", "Error connecting to DBus System service: %s.\n", error
->message
);
819 nm_proxy
= dbus_g_proxy_new_for_name(nm_conn
,
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
,
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
);
837 purple_signal_register(purple_network_get_handle(), "network-configuration-changed",
838 purple_marshal_VOID
, NULL
, 0);
845 purple_network_uninit(void)
847 #ifdef HAVE_NETWORKMANAGER
849 dbus_g_proxy_disconnect_signal(nm_proxy
, "StateChange", G_CALLBACK(nm_state_change_cb
), NULL
);
850 g_object_unref(G_OBJECT(nm_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
));
857 dbus_g_connection_unref(nm_conn
);
861 g_static_mutex_lock(&mutex
);
862 network_initialized
= FALSE
;
863 if (network_change_handle
!= NULL
) {
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",
875 network_change_handle
= NULL
;
878 g_static_mutex_unlock(&mutex
);
881 purple_signal_unregister(purple_network_get_handle(),
882 "network-configuration-changed");