Bug 460546 - nsBrowserGlue should use a smart getter for the pref service. r=gavin
[wine-gecko.git] / netwerk / system / win32 / nsNotifyAddrListener.cpp
blob1a6a247faaef25354f1726c78f5450d902dd8c0b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /* vim:set et sw=4 ts=4: */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License
7 * Version 1.1 (the "License"); you may not use this file except in
8 * compliance with the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is written by Juan Lang.
18 * The Initial Developer of the Original Code is
19 * Juan Lang.
20 * Portions created by the Initial Developer are Copyright (C) 2003,2006
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 #include <stdarg.h>
40 #include <windef.h>
41 #include <winbase.h>
42 #include <wingdi.h>
43 #include <winuser.h>
44 #include <winsock2.h>
45 #include <iprtrmib.h>
46 #include <time.h>
47 #include "prmem.h"
48 #include "plstr.h"
49 #include "nsThreadUtils.h"
50 #include "nsIObserverService.h"
51 #include "nsServiceManagerUtils.h"
52 #include "nsNotifyAddrListener.h"
53 #include "nsString.h"
55 #include <iptypes.h>
56 #include <iphlpapi.h>
58 typedef DWORD (WINAPI *GetAdaptersAddressesFunc)(ULONG, DWORD, PVOID,
59 PIP_ADAPTER_ADDRESSES,
60 PULONG);
61 typedef DWORD (WINAPI *GetAdaptersInfoFunc)(PIP_ADAPTER_INFO, PULONG);
62 typedef DWORD (WINAPI *GetIfEntryFunc)(PMIB_IFROW);
63 typedef DWORD (WINAPI *GetIpAddrTableFunc)(PMIB_IPADDRTABLE, PULONG, BOOL);
64 typedef DWORD (WINAPI *NotifyAddrChangeFunc)(PHANDLE, LPOVERLAPPED);
66 static HMODULE sIPHelper;
67 static GetAdaptersAddressesFunc sGetAdaptersAddresses;
68 static GetAdaptersInfoFunc sGetAdaptersInfo;
69 static GetIfEntryFunc sGetIfEntry;
70 static GetIpAddrTableFunc sGetIpAddrTable;
71 static NotifyAddrChangeFunc sNotifyAddrChange;
73 static void InitIPHelperLibrary(void)
75 if (sIPHelper)
76 return;
78 sIPHelper = LoadLibraryW(L"iphlpapi.dll");
79 if (!sIPHelper)
80 return;
82 sGetAdaptersAddresses = (GetAdaptersAddressesFunc)
83 GetProcAddress(sIPHelper, "GetAdaptersAddresses");
84 sGetAdaptersInfo = (GetAdaptersInfoFunc)
85 GetProcAddress(sIPHelper, "GetAdaptersInfo");
86 sGetIfEntry = (GetIfEntryFunc)
87 GetProcAddress(sIPHelper, "GetIfEntry");
88 sGetIpAddrTable = (GetIpAddrTableFunc)
89 GetProcAddress(sIPHelper, "GetIpAddrTable");
90 sNotifyAddrChange = (NotifyAddrChangeFunc)
91 GetProcAddress(sIPHelper, "NotifyAddrChange");
94 static void FreeIPHelperLibrary(void)
96 if (!sIPHelper)
97 return;
99 sGetAdaptersAddresses = nsnull;
100 sGetAdaptersInfo = nsnull;
101 sGetIfEntry = nsnull;
102 sGetIpAddrTable = nsnull;
103 sNotifyAddrChange = nsnull;
105 FreeLibrary(sIPHelper);
106 sIPHelper = nsnull;
109 NS_IMPL_THREADSAFE_ISUPPORTS3(nsNotifyAddrListener,
110 nsINetworkLinkService,
111 nsIRunnable,
112 nsIObserver)
114 nsNotifyAddrListener::nsNotifyAddrListener()
115 : mLinkUp(PR_TRUE) // assume true by default
116 , mStatusKnown(PR_FALSE)
117 , mShutdownEvent(nsnull)
119 mOSVerInfo.dwOSVersionInfoSize = sizeof(mOSVerInfo);
120 GetVersionEx(&mOSVerInfo);
121 InitIPHelperLibrary();
124 nsNotifyAddrListener::~nsNotifyAddrListener()
126 NS_ASSERTION(!mThread, "nsNotifyAddrListener thread shutdown failed");
127 FreeIPHelperLibrary();
130 NS_IMETHODIMP
131 nsNotifyAddrListener::GetIsLinkUp(PRBool *aIsUp)
133 *aIsUp = mLinkUp;
134 return NS_OK;
137 NS_IMETHODIMP
138 nsNotifyAddrListener::GetLinkStatusKnown(PRBool *aIsUp)
140 *aIsUp = mStatusKnown;
141 return NS_OK;
144 NS_IMETHODIMP
145 nsNotifyAddrListener::Run()
147 HANDLE ev = CreateEvent(nsnull, FALSE, FALSE, nsnull);
148 NS_ENSURE_TRUE(ev, NS_ERROR_OUT_OF_MEMORY);
150 HANDLE handles[2] = { ev, mShutdownEvent };
151 OVERLAPPED overlapped = { 0 };
152 PRBool shuttingDown = PR_FALSE;
154 overlapped.hEvent = ev;
155 while (!shuttingDown) {
156 HANDLE h;
157 DWORD ret = sNotifyAddrChange(&h, &overlapped);
159 if (ret == ERROR_IO_PENDING) {
160 ret = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
161 if (ret == WAIT_OBJECT_0) {
162 CheckLinkStatus();
163 } else {
164 shuttingDown = PR_TRUE;
166 } else {
167 shuttingDown = PR_TRUE;
170 CloseHandle(ev);
172 return NS_OK;
175 NS_IMETHODIMP
176 nsNotifyAddrListener::Observe(nsISupports *subject,
177 const char *topic,
178 const PRUnichar *data)
180 if (!strcmp("xpcom-shutdown-threads", topic))
181 Shutdown();
183 return NS_OK;
186 nsresult
187 nsNotifyAddrListener::Init(void)
189 // XXX this call is very expensive (~650 milliseconds), so we
190 // don't want to call it synchronously. Instead, we just
191 // start up assuming we have a network link, but we'll
192 // report that the status isn't known.
194 // CheckLinkStatus();
196 // only start a thread on Windows 2000 or later
197 if (mOSVerInfo.dwPlatformId != VER_PLATFORM_WIN32_NT ||
198 mOSVerInfo.dwMajorVersion < 5)
199 return NS_OK;
201 nsresult rv;
202 nsCOMPtr<nsIObserverService> observerService =
203 do_GetService("@mozilla.org/observer-service;1", &rv);
204 NS_ENSURE_SUCCESS(rv, rv);
206 rv = observerService->AddObserver(this, "xpcom-shutdown-threads",
207 PR_FALSE);
208 NS_ENSURE_SUCCESS(rv, rv);
210 mShutdownEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
211 NS_ENSURE_TRUE(mShutdownEvent, NS_ERROR_OUT_OF_MEMORY);
213 rv = NS_NewThread(getter_AddRefs(mThread), this);
214 NS_ENSURE_SUCCESS(rv, rv);
216 return NS_OK;
219 nsresult
220 nsNotifyAddrListener::Shutdown(void)
222 // remove xpcom shutdown observer
223 nsCOMPtr<nsIObserverService> observerService =
224 do_GetService("@mozilla.org/observer-service;1");
225 if (observerService)
226 observerService->RemoveObserver(this, "xpcom-shutdown-threads");
228 if (!mShutdownEvent)
229 return NS_OK;
231 SetEvent(mShutdownEvent);
233 nsresult rv = mThread->Shutdown();
235 // Have to break the cycle here, otherwise nsNotifyAddrListener holds
236 // onto the thread and the thread holds onto the nsNotifyAddrListener
237 // via its mRunnable
238 mThread = nsnull;
240 CloseHandle(mShutdownEvent);
241 mShutdownEvent = NULL;
243 return rv;
246 /* Sends the given event to the UI thread. Assumes aEventID never goes out
247 * of scope (static strings are ideal).
249 nsresult
250 nsNotifyAddrListener::SendEventToUI(const char *aEventID)
252 if (!aEventID)
253 return NS_ERROR_NULL_POINTER;
255 nsresult rv;
256 nsCOMPtr<nsIRunnable> event = new ChangeEvent(this, aEventID);
257 if (NS_FAILED(rv = NS_DispatchToMainThread(event)))
258 NS_WARNING("Failed to dispatch ChangeEvent");
259 return rv;
262 NS_IMETHODIMP
263 nsNotifyAddrListener::ChangeEvent::Run()
265 nsCOMPtr<nsIObserverService> observerService =
266 do_GetService("@mozilla.org/observer-service;1");
267 if (observerService)
268 observerService->NotifyObservers(
269 mService, NS_NETWORK_LINK_TOPIC,
270 NS_ConvertASCIItoUTF16(mEventID).get());
271 return NS_OK;
274 DWORD
275 nsNotifyAddrListener::GetOperationalStatus(DWORD aAdapterIndex)
277 DWORD status = MIB_IF_OPER_STATUS_CONNECTED;
279 // try to get operational status on WinNT--on Win98, it consistently gives
280 // me the wrong status, dagnabbit
281 if (mOSVerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
282 // If this fails, assume it's connected. Didn't find a KB, but it
283 // failed for me w/Win2K SP2, and succeeded for me w/Win2K SP3.
284 if (sGetIfEntry) {
285 MIB_IFROW ifRow;
287 ifRow.dwIndex = aAdapterIndex;
288 if (sGetIfEntry(&ifRow) == ERROR_SUCCESS)
289 status = ifRow.dwOperStatus;
292 return status;
296 * Calls GetIpAddrTable to check whether a link is up. Assumes so if any
297 * adapter has a non-zero IP (v4) address. Sets mLinkUp if GetIpAddrTable
298 * succeeds, but doesn't set mStatusKnown.
299 * Returns ERROR_SUCCESS on success, and a Win32 error code otherwise.
301 DWORD
302 nsNotifyAddrListener::CheckIPAddrTable(void)
304 if (!sGetIpAddrTable)
305 return ERROR_CALL_NOT_IMPLEMENTED;
307 ULONG size = 0;
308 DWORD ret = sGetIpAddrTable(nsnull, &size, FALSE);
309 if (ret == ERROR_INSUFFICIENT_BUFFER && size > 0) {
310 PMIB_IPADDRTABLE table = (PMIB_IPADDRTABLE) malloc(size);
311 if (!table)
312 return ERROR_OUTOFMEMORY;
314 ret = sGetIpAddrTable(table, &size, FALSE);
315 if (ret == ERROR_SUCCESS) {
316 PRBool linkUp = PR_FALSE;
318 for (DWORD i = 0; !linkUp && i < table->dwNumEntries; i++) {
319 if (GetOperationalStatus(table->table[i].dwIndex) >=
320 MIB_IF_OPER_STATUS_CONNECTED &&
321 table->table[i].dwAddr != 0)
322 linkUp = PR_TRUE;
324 mLinkUp = linkUp;
326 free(table);
328 return ret;
332 * Checks whether a link is up by calling GetAdaptersInfo. If any adapter's
333 * operational status is at least MIB_IF_OPER_STATUS_CONNECTED, checks:
334 * 1. If it's configured for DHCP, the link is considered up if the DHCP
335 * server is initialized.
336 * 2. If it's not configured for DHCP, the link is considered up if it has a
337 * nonzero IP address.
338 * Sets mLinkUp and mStatusKnown if GetAdaptersInfo succeeds.
339 * Returns ERROR_SUCCESS on success, and a Win32 error code otherwise. If the
340 * call is not present on the current platform, returns ERROR_NOT_SUPPORTED.
342 DWORD
343 nsNotifyAddrListener::CheckAdaptersInfo(void)
345 if (!sGetAdaptersInfo)
346 return ERROR_NOT_SUPPORTED;
348 ULONG adaptersLen = 0;
350 DWORD ret = sGetAdaptersInfo(0, &adaptersLen);
351 if (ret == ERROR_BUFFER_OVERFLOW && adaptersLen > 0) {
352 PIP_ADAPTER_INFO adapters = (PIP_ADAPTER_INFO) malloc(adaptersLen);
353 if (sGetAdaptersInfo(adapters, &adaptersLen) == ERROR_SUCCESS) {
354 PRBool linkUp = PR_FALSE;
355 PIP_ADAPTER_INFO ptr;
357 for (ptr = adapters; ptr && !linkUp; ptr = ptr->Next) {
358 if (GetOperationalStatus(ptr->Index) >=
359 MIB_IF_OPER_STATUS_CONNECTED) {
360 if (ptr->DhcpEnabled) {
361 if (PL_strcmp(ptr->DhcpServer.IpAddress.String,
362 "255.255.255.255")) {
363 // it has a DHCP server, therefore it must have
364 // a usable address
365 linkUp = PR_TRUE;
368 else {
369 PIP_ADDR_STRING ipAddr;
370 for (ipAddr = &ptr->IpAddressList; ipAddr && !linkUp;
371 ipAddr = ipAddr->Next)
372 if (PL_strcmp(ipAddr->IpAddress.String, "0.0.0.0"))
373 linkUp = PR_TRUE;
377 mLinkUp = linkUp;
378 mStatusKnown = PR_TRUE;
379 free(adapters);
382 return ret;
385 DWORD
386 nsNotifyAddrListener::CheckAdaptersAddresses(void)
388 static const DWORD flags =
389 GAA_FLAG_SKIP_FRIENDLY_NAME | GAA_FLAG_SKIP_ANYCAST |
390 GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
392 if (!sGetAdaptersAddresses)
393 return ERROR_NOT_SUPPORTED;
395 ULONG len = 0;
397 DWORD ret = sGetAdaptersAddresses(AF_UNSPEC, 0, NULL, NULL, &len);
398 if (ret == ERROR_BUFFER_OVERFLOW) {
399 PIP_ADAPTER_ADDRESSES addresses = (PIP_ADAPTER_ADDRESSES) malloc(len);
400 if (addresses) {
401 ret = sGetAdaptersAddresses(AF_UNSPEC, 0, NULL, addresses, &len);
402 if (ret == ERROR_SUCCESS) {
403 PIP_ADAPTER_ADDRESSES ptr;
404 BOOL linkUp = FALSE;
406 for (ptr = addresses; !linkUp && ptr; ptr = ptr->Next) {
407 if (ptr->OperStatus == IfOperStatusUp &&
408 ptr->IfType != IF_TYPE_SOFTWARE_LOOPBACK)
409 linkUp = TRUE;
411 mLinkUp = linkUp;
412 mStatusKnown = TRUE;
414 free(addresses);
417 return ret;
421 * Checks the status of all network adapters. If one is up and has a valid IP
422 * address, sets mLinkUp to true. Sets mStatusKnown to true if the link status
423 * is definitive.
425 void
426 nsNotifyAddrListener::CheckLinkStatus(void)
428 DWORD ret;
429 const char *event;
431 ret = CheckAdaptersAddresses();
432 if (ret == ERROR_NOT_SUPPORTED)
433 ret = CheckAdaptersInfo();
434 if (ret == ERROR_NOT_SUPPORTED)
435 ret = CheckIPAddrTable();
436 if (ret != ERROR_SUCCESS)
437 mLinkUp = PR_TRUE; // I can't tell, so assume there's a link
439 if (mStatusKnown)
440 event = mLinkUp ? NS_NETWORK_LINK_DATA_UP : NS_NETWORK_LINK_DATA_DOWN;
441 else
442 event = NS_NETWORK_LINK_DATA_UNKNOWN;
443 SendEventToUI(event);