|
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, ¶mSize); |
|
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, ¶mSize); |
|
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, ¶mSize); |
|
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 } |