1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is Mozilla.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 2002
19 * the Initial Developer. All Rights Reserved.
22 * Steve Meredith <smeredith@netscape.com>
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 // This source is mostly a bunch of Windows API calls. It is only compiled for
41 // Registry entries for Autodial mappings are located here:
42 // HKEY_CURRENT_USER\Software\Microsoft\RAS Autodial\Addresses
47 #include "nsAutodialWin.h"
50 #define AUTODIAL_DEFAULT AUTODIAL_NEVER
53 // Log module for autodial logging...
55 // To enable logging (see prlog.h for full details):
57 // set NSPR_LOG_MODULES=Autodial:5
58 // set NSPR_LOG_FILE=nspr.log
60 // this enables PR_LOG_DEBUG level information and places all output in
65 static PRLogModuleInfo
* gLog
= nsnull
;
68 #define LOGD(args) PR_LOG(gLog, PR_LOG_DEBUG, args)
69 #define LOGE(args) PR_LOG(gLog, PR_LOG_ERROR, args)
71 // Don't try to dial again within a few seconds of when user pressed cancel.
72 #define NO_RETRY_PERIOD_SEC 5
73 PRIntervalTime
nsAutodial::mDontRetryUntil
= 0;
76 tRASPHONEBOOKDLG
nsAutodial::mpRasPhonebookDlg
= nsnull
;
77 tRASENUMCONNECTIONS
nsAutodial::mpRasEnumConnections
= nsnull
;
78 tRASENUMENTRIES
nsAutodial::mpRasEnumEntries
= nsnull
;
79 tRASDIALDLG
nsAutodial::mpRasDialDlg
= nsnull
;
80 tRASSETAUTODIALADDRESS
nsAutodial::mpRasSetAutodialAddress
= nsnull
;
81 tRASGETAUTODIALADDRESS
nsAutodial::mpRasGetAutodialAddress
= nsnull
;
82 tRASGETAUTODIALENABLE
nsAutodial::mpRasGetAutodialEnable
= nsnull
;
83 tRASGETAUTODIALPARAM
nsAutodial::mpRasGetAutodialParam
= nsnull
;
85 HINSTANCE
nsAutodial::mhRASdlg
= nsnull
;
86 HINSTANCE
nsAutodial::mhRASapi32
= nsnull
;
89 nsAutodial::nsAutodial()
90 : mAutodialBehavior(AUTODIAL_DEFAULT
),
91 mNumRASConnectionEntries(0),
92 mAutodialServiceDialingLocation(-1)
94 mOSVerInfo
.dwOSVersionInfoSize
= sizeof(mOSVerInfo
);
95 GetVersionEx(&mOSVerInfo
);
97 // Initializations that can be made again since RAS OS settings can
103 nsAutodial::~nsAutodial()
108 // Get settings from the OS. These are settings that might change during
109 // the OS session. Call Init() again to pick up those changes if required.
110 // Returns NS_ERROR_FAILURE if error or NS_OK if success.
111 nsresult
nsAutodial::Init()
115 gLog
= PR_NewLogModule("Autodial");
118 mDefaultEntryName
[0] = '\0';
119 mNumRASConnectionEntries
= 0;
120 mAutodialBehavior
= QueryAutodialBehavior();
122 // No need to continue in this case.
123 if (mAutodialBehavior
== AUTODIAL_NEVER
)
129 // Get the number of dialup entries in the phonebook.
130 mNumRASConnectionEntries
= NumRASEntries();
132 // Get the name of the default entry.
133 nsresult result
= GetDefaultEntryName(mDefaultEntryName
,
134 sizeof(mDefaultEntryName
));
140 // Should we attempt to dial on a network error? Yes if the Internet Options
141 // configured as such. Yes if the RAS autodial service is running (we'll try to
142 // force it to dial in that case by adding the network address to its db.)
143 PRBool
nsAutodial::ShouldDialOnNetworkError()
145 // Don't try to dial again within a few seconds of when user pressed cancel.
148 PRIntervalTime intervalNow
= PR_IntervalNow();
149 if (intervalNow
< mDontRetryUntil
)
151 LOGD(("Autodial: Not dialing: too soon."));
157 return ((mAutodialBehavior
== AUTODIAL_ALWAYS
)
158 || (mAutodialBehavior
== AUTODIAL_ON_NETWORKERROR
)
159 || (mAutodialBehavior
== AUTODIAL_USE_SERVICE
));
163 // The autodial info is set in Control Panel | Internet Options | Connections.
164 // The values are stored in the registry. This function gets those values from
165 // the registry and determines if we should never dial, always dial, or dial
166 // when there is no network found.
167 int nsAutodial::QueryAutodialBehavior()
169 if (IsAutodialServiceRunning())
171 if (!LoadRASapi32DLL())
172 return AUTODIAL_NEVER
;
174 // Is Autodial service enabled for the current login session?
176 DWORD size
= sizeof(DWORD
);
177 if ((*mpRasGetAutodialParam
)(RASADP_LoginSessionDisable
, &disabled
, &size
) == ERROR_SUCCESS
)
181 // If current dialing location has autodial on, we'll let the service dial.
182 mAutodialServiceDialingLocation
= GetCurrentLocation();
183 if (IsAutodialServiceEnabled(mAutodialServiceDialingLocation
))
185 return AUTODIAL_USE_SERVICE
;
191 // If we get to here, then the service is not going to dial on error, so we
192 // can dial ourselves if the control panel settings are set up that way.
194 LONG result
= ::RegOpenKeyExW(
196 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
201 if (result
!= ERROR_SUCCESS
)
203 LOGE(("Autodial: Error opening reg key Internet Settings"));
204 return AUTODIAL_NEVER
;
210 DWORD paramSize
= sizeof(DWORD
);
212 result
= ::RegQueryValueExW(hKey
, L
"EnableAutodial", nsnull
, &entryType
, (LPBYTE
)&autodial
, ¶mSize
);
213 if (result
!= ERROR_SUCCESS
)
216 LOGE(("Autodial: Error reading reg value EnableAutodial."));
217 return AUTODIAL_NEVER
;
220 result
= ::RegQueryValueExW(hKey
, L
"NoNetAutodial", nsnull
, &entryType
, (LPBYTE
)&onDemand
, ¶mSize
);
221 if (result
!= ERROR_SUCCESS
)
224 LOGE(("Autodial: Error reading reg value NoNetAutodial."));
225 return AUTODIAL_NEVER
;
232 return AUTODIAL_NEVER
;
238 return AUTODIAL_ON_NETWORKERROR
;
242 return AUTODIAL_ALWAYS
;
247 // If the RAS autodial service is running, use it. Otherwise, dial
248 // the default RAS connection. There are two possible RAS dialogs:
249 // one that dials a single entry, and one that lets the user choose which
250 // to dial. If there is only one connection entry in the phone book, or
251 // there are multiple entries but one is defined as the default, we'll use
252 // the single entry dial dialog. If there are multiple connection entries,
253 // and none is specified as default, we'll bring up the diallog which lets
254 // the user select the connection entry to use.
257 // NS_OK: dialing was successful and caller should retry
258 // all other values indicate that the caller should not retry
259 nsresult
nsAutodial::DialDefault(const PRUnichar
* hostName
)
263 if (mAutodialBehavior
== AUTODIAL_NEVER
)
265 return NS_ERROR_FAILURE
; // don't retry the network error
268 // If already a RAS connection, bail.
269 if (IsRASConnected())
271 LOGD(("Autodial: Not dialing: active connection."));
272 return NS_ERROR_FAILURE
; // don't retry
275 // If no dialup connections configured, bail.
276 if (mNumRASConnectionEntries
<= 0)
278 LOGD(("Autodial: Not dialing: no entries."));
279 return NS_ERROR_FAILURE
; // don't retry
283 // If autodial service is running, let it dial. In order for it to dial more
284 // reliably, we have to add the target address to the autodial database.
285 // This is the only way the autodial service dial if there is a network
286 // adapter installed. But even then it might not dial. We have to assume that
287 // it will though, or we could end up with two attempts to dial on the same
288 // network error if the user cancels the first one: one from the service and
290 // See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rras/ras4over_3dwl.asp
291 if (mAutodialBehavior
== AUTODIAL_USE_SERVICE
)
293 AddAddressToAutodialDirectory(hostName
);
294 return NS_ERROR_FAILURE
; // don't retry
297 // Do the dialing ourselves.
300 // Don't need to load the dll before this.
301 if (!LoadRASdlgDLL())
302 return NS_ERROR_NULL_POINTER
;
304 // If a default dial entry is configured, use it.
305 if (mDefaultEntryName
[0] != '\0')
307 LOGD(("Autodial: Dialing default: %s.",mDefaultEntryName
));
309 RASDIALDLG rasDialDlg
;
310 memset(&rasDialDlg
, 0, sizeof(rasDialDlg
));
311 rasDialDlg
.dwSize
= sizeof(rasDialDlg
);
314 (*mpRasDialDlg
)(nsnull
, mDefaultEntryName
, nsnull
, &rasDialDlg
);
318 if (rasDialDlg
.dwError
!= 0)
320 LOGE(("Autodial ::RasDialDlg failed: Error: %d.",
321 rasDialDlg
.dwError
));
325 mDontRetryUntil
= PR_IntervalNow() + PR_SecondsToInterval(NO_RETRY_PERIOD_SEC
);
326 LOGD(("Autodial: User cancelled dial."));
328 return NS_ERROR_FAILURE
; // don't retry
331 LOGD(("Autodial: RAS dialup connection successful."));
334 // If no default connection specified, open the dialup dialog that lets
335 // the user specifiy which connection to dial.
338 LOGD(("Autodial: Prompting for phonebook entry."));
341 memset(&rasPBDlg
, 0, sizeof(rasPBDlg
));
342 rasPBDlg
.dwSize
= sizeof(rasPBDlg
);
344 PRBool dialed
= (*mpRasPhonebookDlg
)(nsnull
, nsnull
, &rasPBDlg
);
348 if (rasPBDlg
.dwError
!= 0)
350 LOGE(("Autodial: ::RasPhonebookDlg failed: Error = %d.",
355 mDontRetryUntil
= PR_IntervalNow() + PR_SecondsToInterval(NO_RETRY_PERIOD_SEC
);
356 LOGD(("Autodial: User cancelled dial."));
359 return NS_ERROR_FAILURE
; // don't retry
362 LOGD(("Autodial: RAS dialup connection successful."));
366 // Retry because we just established a dialup connection.
371 // Check to see if RAS is already connected.
372 PRBool
nsAutodial::IsRASConnected()
376 rasConn
.dwSize
= sizeof(rasConn
);
377 DWORD structSize
= sizeof(rasConn
);
379 if (!LoadRASapi32DLL())
380 return NS_ERROR_NULL_POINTER
;
382 DWORD result
= (*mpRasEnumConnections
)(&rasConn
, &structSize
, &connections
);
384 // ERROR_BUFFER_TOO_SMALL is OK because we only need one struct.
385 if (result
== ERROR_SUCCESS
|| result
== ERROR_BUFFER_TOO_SMALL
)
387 return (connections
> 0);
390 LOGE(("Autodial: ::RasEnumConnections failed: Error = %d", result
));
394 // Get the first RAS dial entry name from the phonebook.
395 nsresult
nsAutodial::GetFirstEntryName(PRUnichar
* entryName
, int bufferSize
)
397 // Need to load the DLL if not loaded yet.
398 if (!LoadRASapi32DLL())
399 return NS_ERROR_NULL_POINTER
;
401 RASENTRYNAMEW rasEntryName
;
402 rasEntryName
.dwSize
= sizeof(rasEntryName
);
403 DWORD cb
= sizeof(rasEntryName
);
407 (*mpRasEnumEntries
)(nsnull
, nsnull
, &rasEntryName
, &cb
, &cEntries
);
409 // ERROR_BUFFER_TOO_SMALL is OK because we only need one struct.
410 if (result
== ERROR_SUCCESS
|| result
== ERROR_BUFFER_TOO_SMALL
)
412 wcsncpy(entryName
, rasEntryName
.szEntryName
,
413 bufferSize
/ sizeof(*entryName
));
417 return NS_ERROR_FAILURE
;
420 // Get the number of RAS dial entries in the phonebook.
421 int nsAutodial::NumRASEntries()
423 // Need to load the DLL if not loaded yet.
424 if (!LoadRASapi32DLL())
427 RASENTRYNAMEW rasEntryName
;
428 rasEntryName
.dwSize
= sizeof(rasEntryName
);
429 DWORD cb
= sizeof(rasEntryName
);
434 (*mpRasEnumEntries
)(nsnull
, nsnull
, &rasEntryName
, &cb
, &cEntries
);
436 // ERROR_BUFFER_TOO_SMALL is OK because we only need one struct.
437 if (result
== ERROR_SUCCESS
|| result
== ERROR_BUFFER_TOO_SMALL
)
439 return (int)cEntries
;
445 // Get the name of the default dial entry.
446 nsresult
nsAutodial::GetDefaultEntryName(PRUnichar
* entryName
, int bufferSize
)
448 // No RAS dialup entries.
449 if (mNumRASConnectionEntries
<= 0)
451 return NS_ERROR_FAILURE
;
454 // Single RAS dialup entry. Use it as the default to autodial.
455 if (mNumRASConnectionEntries
== 1)
457 return GetFirstEntryName(entryName
, bufferSize
);
460 // Multiple RAS dialup entries. If a default configured in the registry,
463 // For Windows XP: HKCU/Software/Microsoft/RAS Autodial/Default/DefaultInternet.
464 // or HKLM/Software/Microsoft/RAS Autodial/Default/DefaultInternet.
465 // For Windows 2K: HKCU/RemoteAccess/InternetProfile.
467 const PRUnichar
* key
= nsnull
;
468 const PRUnichar
* val
= nsnull
;
473 // Windows NT and 2000
474 if ((mOSVerInfo
.dwMajorVersion
== 4) // Windows NT
475 || ((mOSVerInfo
.dwMajorVersion
== 5) && (mOSVerInfo
.dwMinorVersion
== 0))) // Windows 2000
477 key
= L
"RemoteAccess";
478 val
= L
"InternetProfile";
480 result
= ::RegOpenKeyExW(
487 if (result
!= ERROR_SUCCESS
)
489 return NS_ERROR_FAILURE
;
494 key
= L
"Software\\Microsoft\\RAS Autodial\\Default";
495 val
= L
"DefaultInternet";
499 result
= ::RegOpenKeyExW(
506 if (result
!= ERROR_SUCCESS
)
508 // If not present, try HKLM.
509 result
= ::RegOpenKeyExW(
516 if (result
!= ERROR_SUCCESS
)
518 return NS_ERROR_FAILURE
;
525 DWORD buffSize
= bufferSize
;
527 result
= ::RegQueryValueExW(hKey
,
537 if (result
!= ERROR_SUCCESS
)
539 // Results in a prompt for which to use at dial time.
540 return NS_ERROR_FAILURE
;
547 // Determine if the autodial service is running on this PC.
548 PRBool
nsAutodial::IsAutodialServiceRunning()
550 SC_HANDLE hSCManager
=
551 OpenSCManager(nsnull
, SERVICES_ACTIVE_DATABASE
, SERVICE_QUERY_STATUS
);
553 if (hSCManager
== nsnull
)
555 LOGE(("Autodial: failed to open service control manager. Error %d.",
562 OpenServiceW(hSCManager
, L
"RasAuto", SERVICE_QUERY_STATUS
);
564 if (hSCManager
== nsnull
)
566 LOGE(("Autodial: failed to open RasAuto service."));
570 SERVICE_STATUS status
;
571 if (!QueryServiceStatus(hService
, &status
))
573 LOGE(("Autodial: ::QueryServiceStatus() failed. Error: %d",
579 return (status
.dwCurrentState
== SERVICE_RUNNING
);
582 // Add the specified address to the autodial directory.
583 PRBool
nsAutodial::AddAddressToAutodialDirectory(const PRUnichar
* hostName
)
585 // Need to load the DLL if not loaded yet.
586 if (!LoadRASapi32DLL())
589 // First see if there is already a db entry for this address.
590 RASAUTODIALENTRYW autodialEntry
;
591 autodialEntry
.dwSize
= sizeof(autodialEntry
);
592 DWORD size
= sizeof(autodialEntry
);
595 DWORD result
= (*mpRasGetAutodialAddress
)(hostName
,
601 // If there is already at least 1 entry in db for this address, return.
602 if (result
!= ERROR_FILE_NOT_FOUND
)
604 LOGD(("Autodial: Address %s already in autodial db.", hostName
));
608 autodialEntry
.dwSize
= sizeof(autodialEntry
);
609 autodialEntry
.dwFlags
= 0;
610 autodialEntry
.dwDialingLocation
= mAutodialServiceDialingLocation
;
611 GetDefaultEntryName(autodialEntry
.szEntry
, sizeof(autodialEntry
.szEntry
));
613 result
= (*mpRasSetAutodialAddress
)(hostName
,
616 sizeof(autodialEntry
),
619 if (result
!= ERROR_SUCCESS
)
621 LOGE(("Autodial ::RasSetAutodialAddress failed result %d.", result
));
625 LOGD(("Autodial: Added address %s to RAS autodial db for entry %s.",
626 hostName
, NS_ConvertUTF16toUTF8(autodialEntry
.szEntry
).get()));
631 // Get the current TAPI dialing location.
632 int nsAutodial::GetCurrentLocation()
635 LONG result
= ::RegOpenKeyExW(
637 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Telephony\\Locations",
642 if (result
!= ERROR_SUCCESS
)
644 LOGE(("Autodial: Error opening reg key ...CurrentVersion\\Telephony\\Locations"));
650 DWORD paramSize
= sizeof(DWORD
);
652 result
= ::RegQueryValueExW(hKey
, L
"CurrentID", nsnull
, &entryType
, (LPBYTE
)&location
, ¶mSize
);
653 if (result
!= ERROR_SUCCESS
)
656 LOGE(("Autodial: Error reading reg value CurrentID."));
665 // Check to see if autodial for the specified location is enabled.
666 PRBool
nsAutodial::IsAutodialServiceEnabled(int location
)
671 if (!LoadRASapi32DLL())
675 if ((*mpRasGetAutodialEnable
)(location
, &enabled
) != ERROR_SUCCESS
)
677 LOGE(("Autodial: Error calling RasGetAutodialEnable()"));
686 PRBool
nsAutodial::LoadRASapi32DLL()
690 mhRASapi32
= ::LoadLibraryW(L
"rasapi32.dll");
691 if ((UINT_PTR
)mhRASapi32
> 32)
693 // RasEnumConnections
694 mpRasEnumConnections
= (tRASENUMCONNECTIONS
)
695 ::GetProcAddress(mhRASapi32
, "RasEnumConnectionsW");
698 mpRasEnumEntries
= (tRASENUMENTRIES
)
699 ::GetProcAddress(mhRASapi32
, "RasEnumEntriesW");
701 // RasSetAutodialAddress
702 mpRasSetAutodialAddress
= (tRASSETAUTODIALADDRESS
)
703 ::GetProcAddress(mhRASapi32
, "RasSetAutodialAddressW");
705 // RasGetAutodialAddress
706 mpRasGetAutodialAddress
= (tRASGETAUTODIALADDRESS
)
707 ::GetProcAddress(mhRASapi32
, "RasGetAutodialAddressW");
709 // RasGetAutodialEnable
710 mpRasGetAutodialEnable
= (tRASGETAUTODIALENABLE
)
711 ::GetProcAddress(mhRASapi32
, "RasGetAutodialEnableW");
713 // RasGetAutodialParam
714 mpRasGetAutodialParam
= (tRASGETAUTODIALPARAM
)
715 ::GetProcAddress(mhRASapi32
, "RasGetAutodialParamW");
721 || !mpRasEnumConnections
723 || !mpRasSetAutodialAddress
724 || !mpRasGetAutodialAddress
725 || !mpRasGetAutodialEnable
726 || !mpRasGetAutodialParam
)
728 LOGE(("Autodial: Error loading RASAPI32.DLL."));
735 PRBool
nsAutodial::LoadRASdlgDLL()
739 mhRASdlg
= ::LoadLibraryW(L
"rasdlg.dll");
740 if ((UINT_PTR
)mhRASdlg
> 32)
744 (tRASPHONEBOOKDLG
)::GetProcAddress(mhRASdlg
, "RasPhonebookDlgW");
748 (tRASDIALDLG
)::GetProcAddress(mhRASdlg
, "RasDialDlgW");
753 if (!mhRASdlg
|| !mpRasPhonebookDlg
|| !mpRasDialDlg
)
755 LOGE(("Autodial: Error loading RASDLG.DLL."));