netwerk/base/src/nsAutodialWin.cpp

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:f6409f176c94
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5 // This source is mostly a bunch of Windows API calls. It is only compiled for
6 // Windows builds.
7 //
8 // Registry entries for Autodial mappings are located here:
9 // HKEY_CURRENT_USER\Software\Microsoft\RAS Autodial\Addresses
10
11 #include <windows.h>
12 #include <winsvc.h>
13 #include "nsString.h"
14 #include "nsAutodialWin.h"
15 #include "prlog.h"
16 #include "nsWindowsHelpers.h"
17
18 #define AUTODIAL_DEFAULT AUTODIAL_NEVER
19
20 //
21 // Log module for autodial logging...
22 //
23 // To enable logging (see prlog.h for full details):
24 //
25 // set NSPR_LOG_MODULES=Autodial:5
26 // set NSPR_LOG_FILE=nspr.log
27 //
28 // this enables PR_LOG_DEBUG level information and places all output in
29 // the file nspr.log
30 //
31
32 #ifdef PR_LOGGING
33 static PRLogModuleInfo* gLog = nullptr;
34 #endif
35
36 #undef LOGD
37 #undef LOGE
38 #define LOGD(args) PR_LOG(gLog, PR_LOG_DEBUG, args)
39 #define LOGE(args) PR_LOG(gLog, PR_LOG_ERROR, args)
40
41 // Don't try to dial again within a few seconds of when user pressed cancel.
42 #define NO_RETRY_PERIOD_SEC 5
43 PRIntervalTime nsAutodial::mDontRetryUntil = 0;
44
45 // ctor.
46 nsAutodial::nsAutodial()
47 : mAutodialBehavior(AUTODIAL_DEFAULT),
48 mAutodialServiceDialingLocation(-1),
49 mNumRASConnectionEntries(0)
50 {
51 // Initializations that can be made again since RAS OS settings can
52 // change.
53 Init();
54 }
55
56 // dtor
57 nsAutodial::~nsAutodial()
58 {
59 }
60
61
62 // Get settings from the OS. These are settings that might change during
63 // the OS session. Call Init() again to pick up those changes if required.
64 // Returns NS_ERROR_FAILURE if error or NS_OK if success.
65 nsresult nsAutodial::Init()
66 {
67 #ifdef PR_LOGGING
68 if (!gLog)
69 gLog = PR_NewLogModule("Autodial");
70 #endif
71
72 mDefaultEntryName[0] = '\0';
73 mNumRASConnectionEntries = 0;
74 mAutodialBehavior = QueryAutodialBehavior();
75
76 // No need to continue in this case.
77 if (mAutodialBehavior == AUTODIAL_NEVER)
78 {
79 return NS_OK;
80 }
81
82
83 // Get the number of dialup entries in the phonebook.
84 mNumRASConnectionEntries = NumRASEntries();
85
86 // Get the name of the default entry.
87 nsresult result = GetDefaultEntryName(mDefaultEntryName,
88 sizeof(mDefaultEntryName));
89
90 return result;
91 }
92
93
94 // Should we attempt to dial on a network error? Yes if the Internet Options
95 // configured as such. Yes if the RAS autodial service is running (we'll try to
96 // force it to dial in that case by adding the network address to its db.)
97 bool nsAutodial::ShouldDialOnNetworkError()
98 {
99 // Don't try to dial again within a few seconds of when user pressed cancel.
100 if (mDontRetryUntil)
101 {
102 PRIntervalTime intervalNow = PR_IntervalNow();
103 if (intervalNow < mDontRetryUntil)
104 {
105 LOGD(("Autodial: Not dialing: too soon."));
106 return false;
107 }
108 }
109
110
111 return ((mAutodialBehavior == AUTODIAL_ALWAYS)
112 || (mAutodialBehavior == AUTODIAL_ON_NETWORKERROR)
113 || (mAutodialBehavior == AUTODIAL_USE_SERVICE));
114 }
115
116
117 // The autodial info is set in Control Panel | Internet Options | Connections.
118 // The values are stored in the registry. This function gets those values from
119 // the registry and determines if we should never dial, always dial, or dial
120 // when there is no network found.
121 int nsAutodial::QueryAutodialBehavior()
122 {
123 if (IsAutodialServiceRunning())
124 {
125 // Is Autodial service enabled for the current login session?
126 DWORD disabled = 0;
127 DWORD size = sizeof(DWORD);
128 if (RasGetAutodialParamW(RASADP_LoginSessionDisable, &disabled, &size) == ERROR_SUCCESS)
129 {
130 if (!disabled)
131 {
132 // If current dialing location has autodial on, we'll let the service dial.
133 mAutodialServiceDialingLocation = GetCurrentLocation();
134 if (IsAutodialServiceEnabled(mAutodialServiceDialingLocation))
135 {
136 return AUTODIAL_USE_SERVICE;
137 }
138 }
139 }
140 }
141
142 // If we get to here, then the service is not going to dial on error, so we
143 // can dial ourselves if the control panel settings are set up that way.
144 HKEY hKey = 0;
145 LONG result = ::RegOpenKeyExW(
146 HKEY_CURRENT_USER,
147 L"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
148 0,
149 KEY_READ,
150 &hKey);
151
152 if (result != ERROR_SUCCESS)
153 {
154 LOGE(("Autodial: Error opening reg key Internet Settings"));
155 return AUTODIAL_NEVER;
156 }
157
158 DWORD entryType = 0;
159 DWORD autodial = 0;
160 DWORD onDemand = 0;
161 DWORD paramSize = sizeof(DWORD);
162
163 result = ::RegQueryValueExW(hKey, L"EnableAutodial", nullptr, &entryType, (LPBYTE)&autodial, &paramSize);
164 if (result != ERROR_SUCCESS)
165 {
166 ::RegCloseKey(hKey);
167 LOGE(("Autodial: Error reading reg value EnableAutodial."));
168 return AUTODIAL_NEVER;
169 }
170
171 result = ::RegQueryValueExW(hKey, L"NoNetAutodial", nullptr, &entryType, (LPBYTE)&onDemand, &paramSize);
172 if (result != ERROR_SUCCESS)
173 {
174 ::RegCloseKey(hKey);
175 LOGE(("Autodial: Error reading reg value NoNetAutodial."));
176 return AUTODIAL_NEVER;
177 }
178
179 ::RegCloseKey(hKey);
180
181 if (!autodial)
182 {
183 return AUTODIAL_NEVER;
184 }
185 else
186 {
187 if (onDemand)
188 {
189 return AUTODIAL_ON_NETWORKERROR;
190 }
191 else
192 {
193 return AUTODIAL_ALWAYS;
194 }
195 }
196 }
197
198 // If the RAS autodial service is running, use it. Otherwise, dial
199 // the default RAS connection. There are two possible RAS dialogs:
200 // one that dials a single entry, and one that lets the user choose which
201 // to dial. If there is only one connection entry in the phone book, or
202 // there are multiple entries but one is defined as the default, we'll use
203 // the single entry dial dialog. If there are multiple connection entries,
204 // and none is specified as default, we'll bring up the diallog which lets
205 // the user select the connection entry to use.
206 //
207 // Return values:
208 // NS_OK: dialing was successful and caller should retry
209 // all other values indicate that the caller should not retry
210 nsresult nsAutodial::DialDefault(const char16_t* hostName)
211 {
212 mDontRetryUntil = 0;
213
214 if (mAutodialBehavior == AUTODIAL_NEVER)
215 {
216 return NS_ERROR_FAILURE; // don't retry the network error
217 }
218
219 // If already a RAS connection, bail.
220 if (IsRASConnected())
221 {
222 LOGD(("Autodial: Not dialing: active connection."));
223 return NS_ERROR_FAILURE; // don't retry
224 }
225
226 // If no dialup connections configured, bail.
227 if (mNumRASConnectionEntries <= 0)
228 {
229 LOGD(("Autodial: Not dialing: no entries."));
230 return NS_ERROR_FAILURE; // don't retry
231 }
232
233
234 // If autodial service is running, let it dial. In order for it to dial more
235 // reliably, we have to add the target address to the autodial database.
236 // This is the only way the autodial service dial if there is a network
237 // adapter installed. But even then it might not dial. We have to assume that
238 // it will though, or we could end up with two attempts to dial on the same
239 // network error if the user cancels the first one: one from the service and
240 // one from us.
241 // See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rras/ras4over_3dwl.asp
242 if (mAutodialBehavior == AUTODIAL_USE_SERVICE)
243 {
244 AddAddressToAutodialDirectory(hostName);
245 return NS_ERROR_FAILURE; // don't retry
246 }
247
248 // Do the dialing ourselves.
249 else
250 {
251 // If a default dial entry is configured, use it.
252 if (mDefaultEntryName[0] != '\0')
253 {
254 LOGD(("Autodial: Dialing default: %s.",mDefaultEntryName));
255
256 RASDIALDLG rasDialDlg;
257 memset(&rasDialDlg, 0, sizeof(rasDialDlg));
258 rasDialDlg.dwSize = sizeof(rasDialDlg);
259
260 BOOL dialed =
261 RasDialDlgW(nullptr, mDefaultEntryName, nullptr, &rasDialDlg);
262
263 if (!dialed)
264 {
265 if (rasDialDlg.dwError != 0)
266 {
267 LOGE(("Autodial ::RasDialDlg failed: Error: %d.",
268 rasDialDlg.dwError));
269 }
270 else
271 {
272 mDontRetryUntil = PR_IntervalNow() + PR_SecondsToInterval(NO_RETRY_PERIOD_SEC);
273 LOGD(("Autodial: User cancelled dial."));
274 }
275 return NS_ERROR_FAILURE; // don't retry
276 }
277
278 LOGD(("Autodial: RAS dialup connection successful."));
279 }
280
281 // If no default connection specified, open the dialup dialog that lets
282 // the user specifiy which connection to dial.
283 else
284 {
285 LOGD(("Autodial: Prompting for phonebook entry."));
286
287 RASPBDLG rasPBDlg;
288 memset(&rasPBDlg, 0, sizeof(rasPBDlg));
289 rasPBDlg.dwSize = sizeof(rasPBDlg);
290
291 BOOL dialed = RasPhonebookDlgW(nullptr, nullptr, &rasPBDlg);
292
293 if (!dialed)
294 {
295 if (rasPBDlg.dwError != 0)
296 {
297 LOGE(("Autodial: ::RasPhonebookDlg failed: Error = %d.",
298 rasPBDlg.dwError));
299 }
300 else
301 {
302 mDontRetryUntil = PR_IntervalNow() + PR_SecondsToInterval(NO_RETRY_PERIOD_SEC);
303 LOGD(("Autodial: User cancelled dial."));
304 }
305
306 return NS_ERROR_FAILURE; // don't retry
307 }
308
309 LOGD(("Autodial: RAS dialup connection successful."));
310 }
311 }
312
313 // Retry because we just established a dialup connection.
314 return NS_OK;
315 }
316
317
318 // Check to see if RAS is already connected.
319 bool nsAutodial::IsRASConnected()
320 {
321 DWORD connections;
322 RASCONN rasConn;
323 rasConn.dwSize = sizeof(rasConn);
324 DWORD structSize = sizeof(rasConn);
325
326 DWORD result = RasEnumConnectionsW(&rasConn, &structSize, &connections);
327
328 // ERROR_BUFFER_TOO_SMALL is OK because we only need one struct.
329 if (result == ERROR_SUCCESS || result == ERROR_BUFFER_TOO_SMALL)
330 {
331 return (connections > 0);
332 }
333
334 LOGE(("Autodial: ::RasEnumConnections failed: Error = %d", result));
335 return false;
336 }
337
338 // Get the first RAS dial entry name from the phonebook.
339 nsresult nsAutodial::GetFirstEntryName(wchar_t* entryName, int bufferSize)
340 {
341 RASENTRYNAMEW rasEntryName;
342 rasEntryName.dwSize = sizeof(rasEntryName);
343 DWORD cb = sizeof(rasEntryName);
344 DWORD cEntries = 0;
345
346 DWORD result =
347 RasEnumEntriesW(nullptr, nullptr, &rasEntryName, &cb, &cEntries);
348
349 // ERROR_BUFFER_TOO_SMALL is OK because we only need one struct.
350 if (result == ERROR_SUCCESS || result == ERROR_BUFFER_TOO_SMALL)
351 {
352 wcsncpy(entryName, rasEntryName.szEntryName,
353 bufferSize / sizeof(*entryName));
354 return NS_OK;
355 }
356
357 return NS_ERROR_FAILURE;
358 }
359
360 // Get the number of RAS dial entries in the phonebook.
361 int nsAutodial::NumRASEntries()
362 {
363 RASENTRYNAMEW rasEntryName;
364 rasEntryName.dwSize = sizeof(rasEntryName);
365 DWORD cb = sizeof(rasEntryName);
366 DWORD cEntries = 0;
367
368
369 DWORD result =
370 RasEnumEntriesW(nullptr, nullptr, &rasEntryName, &cb, &cEntries);
371
372 // ERROR_BUFFER_TOO_SMALL is OK because we only need one struct.
373 if (result == ERROR_SUCCESS || result == ERROR_BUFFER_TOO_SMALL)
374 {
375 return (int)cEntries;
376 }
377
378 return 0;
379 }
380
381 // Get the name of the default dial entry.
382 nsresult nsAutodial::GetDefaultEntryName(wchar_t* entryName, int bufferSize)
383 {
384 // No RAS dialup entries.
385 if (mNumRASConnectionEntries <= 0)
386 {
387 return NS_ERROR_FAILURE;
388 }
389
390 // Single RAS dialup entry. Use it as the default to autodial.
391 if (mNumRASConnectionEntries == 1)
392 {
393 return GetFirstEntryName(entryName, bufferSize);
394 }
395
396 // Multiple RAS dialup entries. If a default configured in the registry,
397 // use it.
398 //
399 // For Windows XP: HKCU/Software/Microsoft/RAS Autodial/Default/DefaultInternet.
400 // or HKLM/Software/Microsoft/RAS Autodial/Default/DefaultInternet.
401
402 const wchar_t* key = L"Software\\Microsoft\\RAS Autodial\\Default";
403 const wchar_t* val = L"DefaultInternet";
404
405 HKEY hKey = 0;
406 LONG result = 0;
407
408
409 // Try HKCU first.
410 result = ::RegOpenKeyExW(
411 HKEY_CURRENT_USER,
412 key,
413 0,
414 KEY_READ,
415 &hKey);
416
417 if (result != ERROR_SUCCESS)
418 {
419 // If not present, try HKLM.
420 result = ::RegOpenKeyExW(
421 HKEY_LOCAL_MACHINE,
422 key,
423 0,
424 KEY_READ,
425 &hKey);
426
427 if (result != ERROR_SUCCESS)
428 {
429 return NS_ERROR_FAILURE;
430 }
431 }
432
433
434 DWORD entryType = 0;
435 DWORD buffSize = bufferSize;
436
437 result = ::RegQueryValueExW(hKey,
438 val,
439 nullptr,
440 &entryType,
441 (LPBYTE)entryName,
442 &buffSize);
443
444 ::RegCloseKey(hKey);
445
446
447 if (result != ERROR_SUCCESS)
448 {
449 // Results in a prompt for which to use at dial time.
450 return NS_ERROR_FAILURE;
451 }
452
453 return NS_OK;
454 }
455
456
457 // Determine if the autodial service is running on this PC.
458 bool nsAutodial::IsAutodialServiceRunning()
459 {
460 nsAutoServiceHandle hSCManager(OpenSCManager(nullptr,
461 SERVICES_ACTIVE_DATABASE,
462 SERVICE_QUERY_STATUS));
463
464 if (hSCManager == nullptr)
465 {
466 LOGE(("Autodial: failed to open service control manager. Error %d.",
467 ::GetLastError()));
468
469 return false;
470 }
471
472 nsAutoServiceHandle hService(OpenServiceW(hSCManager,
473 L"RasAuto",
474 SERVICE_QUERY_STATUS));
475
476 if (hSCManager == nullptr)
477 {
478 LOGE(("Autodial: failed to open RasAuto service."));
479 return false;
480 }
481
482 SERVICE_STATUS status;
483 if (!QueryServiceStatus(hService, &status))
484 {
485 LOGE(("Autodial: ::QueryServiceStatus() failed. Error: %d",
486 ::GetLastError()));
487
488 return false;
489 }
490
491 return (status.dwCurrentState == SERVICE_RUNNING);
492 }
493
494 // Add the specified address to the autodial directory.
495 bool nsAutodial::AddAddressToAutodialDirectory(char16ptr_t hostName)
496 {
497 // First see if there is already a db entry for this address.
498 RASAUTODIALENTRYW autodialEntry;
499 autodialEntry.dwSize = sizeof(autodialEntry);
500 DWORD size = sizeof(autodialEntry);
501 DWORD entries = 0;
502
503 DWORD result = RasGetAutodialAddressW(hostName,
504 nullptr,
505 &autodialEntry,
506 &size,
507 &entries);
508
509 // If there is already at least 1 entry in db for this address, return.
510 if (result != ERROR_FILE_NOT_FOUND)
511 {
512 LOGD(("Autodial: Address %s already in autodial db.", hostName));
513 return false;
514 }
515
516 autodialEntry.dwSize = sizeof(autodialEntry);
517 autodialEntry.dwFlags = 0;
518 autodialEntry.dwDialingLocation = mAutodialServiceDialingLocation;
519 GetDefaultEntryName(autodialEntry.szEntry, sizeof(autodialEntry.szEntry));
520
521 result = RasSetAutodialAddressW(hostName,
522 0,
523 &autodialEntry,
524 sizeof(autodialEntry),
525 1);
526
527 if (result != ERROR_SUCCESS)
528 {
529 LOGE(("Autodial ::RasSetAutodialAddress failed result %d.", result));
530 return false;
531 }
532
533 LOGD(("Autodial: Added address %s to RAS autodial db for entry %s.",
534 hostName, NS_ConvertUTF16toUTF8(autodialEntry.szEntry).get()));
535
536 return true;
537 }
538
539 // Get the current TAPI dialing location.
540 int nsAutodial::GetCurrentLocation()
541 {
542 HKEY hKey = 0;
543 LONG result = ::RegOpenKeyExW(
544 HKEY_LOCAL_MACHINE,
545 L"Software\\Microsoft\\Windows\\CurrentVersion\\Telephony\\Locations",
546 0,
547 KEY_READ,
548 &hKey);
549
550 if (result != ERROR_SUCCESS)
551 {
552 LOGE(("Autodial: Error opening reg key ...CurrentVersion\\Telephony\\Locations"));
553 return -1;
554 }
555
556 DWORD entryType = 0;
557 DWORD location = 0;
558 DWORD paramSize = sizeof(DWORD);
559
560 result = ::RegQueryValueExW(hKey, L"CurrentID", nullptr, &entryType, (LPBYTE)&location, &paramSize);
561 if (result != ERROR_SUCCESS)
562 {
563 ::RegCloseKey(hKey);
564 LOGE(("Autodial: Error reading reg value CurrentID."));
565 return -1;
566 }
567
568 ::RegCloseKey(hKey);
569 return location;
570
571 }
572
573 // Check to see if autodial for the specified location is enabled.
574 bool nsAutodial::IsAutodialServiceEnabled(int location)
575 {
576 if (location < 0)
577 return false;
578
579 BOOL enabled;
580 if (RasGetAutodialEnableW(location, &enabled) != ERROR_SUCCESS)
581 {
582 LOGE(("Autodial: Error calling RasGetAutodialEnable()"));
583 return false;
584 }
585
586 return enabled;
587 }

mercurial