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"
57 #define AUTODIAL_DEFAULT AUTODIAL_ALWAYS
59 #define AUTODIAL_DEFAULT AUTODIAL_NEVER
63 // Log module for autodial logging...
65 // To enable logging (see prlog.h for full details):
67 // set NSPR_LOG_MODULES=Autodial:5
68 // set NSPR_LOG_FILE=nspr.log
70 // this enables PR_LOG_DEBUG level information and places all output in
75 static PRLogModuleInfo
* gLog
= nsnull
;
78 #define LOGD(args) PR_LOG(gLog, PR_LOG_DEBUG, args)
79 #define LOGE(args) PR_LOG(gLog, PR_LOG_ERROR, args)
81 // Don't try to dial again within a few seconds of when user pressed cancel.
82 #define NO_RETRY_PERIOD_SEC 5
83 PRIntervalTime
nsRASAutodial::mDontRetryUntil
= 0;
86 tRASPHONEBOOKDLG
nsRASAutodial::mpRasPhonebookDlg
= nsnull
;
87 tRASENUMCONNECTIONS
nsRASAutodial::mpRasEnumConnections
= nsnull
;
88 tRASENUMENTRIES
nsRASAutodial::mpRasEnumEntries
= nsnull
;
89 tRASDIALDLG
nsRASAutodial::mpRasDialDlg
= nsnull
;
90 tRASSETAUTODIALADDRESS
nsRASAutodial::mpRasSetAutodialAddress
= nsnull
;
91 tRASGETAUTODIALADDRESS
nsRASAutodial::mpRasGetAutodialAddress
= nsnull
;
92 tRASGETAUTODIALENABLE
nsRASAutodial::mpRasGetAutodialEnable
= nsnull
;
93 tRASGETAUTODIALPARAM
nsRASAutodial::mpRasGetAutodialParam
= nsnull
;
95 HINSTANCE
nsRASAutodial::mhRASdlg
= nsnull
;
96 HINSTANCE
nsRASAutodial::mhRASapi32
= nsnull
;
99 nsRASAutodial::nsRASAutodial()
100 : mAutodialBehavior(AUTODIAL_DEFAULT
),
101 mNumRASConnectionEntries(0),
102 mAutodialServiceDialingLocation(-1)
104 mOSVerInfo
.dwOSVersionInfoSize
= sizeof(mOSVerInfo
);
105 GetVersionEx(&mOSVerInfo
);
107 // Initializations that can be made again since RAS OS settings can
113 nsRASAutodial::~nsRASAutodial()
118 // Get settings from the OS. These are settings that might change during
119 // the OS session. Call Init() again to pick up those changes if required.
120 // Returns NS_ERROR_FAILURE if error or NS_OK if success.
121 nsresult
nsRASAutodial::Init()
125 gLog
= PR_NewLogModule("Autodial");
128 mDefaultEntryName
[0] = '\0';
129 mNumRASConnectionEntries
= 0;
130 mAutodialBehavior
= QueryAutodialBehavior();
132 // No need to continue in this case.
133 if (mAutodialBehavior
== AUTODIAL_NEVER
)
139 // Get the number of dialup entries in the phonebook.
140 mNumRASConnectionEntries
= NumRASEntries();
142 // Get the name of the default entry.
143 nsresult result
= GetDefaultEntryName(mDefaultEntryName
,
144 sizeof(mDefaultEntryName
));
150 // Should we attempt to dial on a network error? Yes if the Internet Options
151 // configured as such. Yes if the RAS autodial service is running (we'll try to
152 // force it to dial in that case by adding the network address to its db.)
153 PRBool
nsRASAutodial::ShouldDialOnNetworkError()
156 // Don't try to dial again within a few seconds of when user pressed cancel.
159 PRIntervalTime intervalNow
= PR_IntervalNow();
160 if (intervalNow
< mDontRetryUntil
)
162 LOGD(("Autodial: Not dialing: too soon."));
168 return ((mAutodialBehavior
== AUTODIAL_ALWAYS
)
169 || (mAutodialBehavior
== AUTODIAL_ON_NETWORKERROR
)
170 || (mAutodialBehavior
== AUTODIAL_USE_SERVICE
));
177 // The autodial info is set in Control Panel | Internet Options | Connections.
178 // The values are stored in the registry. This function gets those values from
179 // the registry and determines if we should never dial, always dial, or dial
180 // when there is no network found.
181 int nsRASAutodial::QueryAutodialBehavior()
184 if (IsAutodialServiceRunning())
186 if (!LoadRASapi32DLL())
187 return AUTODIAL_NEVER
;
189 // Is Autodial service enabled for the current login session?
191 DWORD size
= sizeof(DWORD
);
192 if ((*mpRasGetAutodialParam
)(RASADP_LoginSessionDisable
, &disabled
, &size
) == ERROR_SUCCESS
)
196 // If current dialing location has autodial on, we'll let the service dial.
197 mAutodialServiceDialingLocation
= GetCurrentLocation();
198 if (IsAutodialServiceEnabled(mAutodialServiceDialingLocation
))
200 return AUTODIAL_USE_SERVICE
;
206 // If we get to here, then the service is not going to dial on error, so we
207 // can dial ourselves if the control panel settings are set up that way.
209 LONG result
= ::RegOpenKeyExW(
211 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
216 if (result
!= ERROR_SUCCESS
)
218 LOGE(("Autodial: Error opening reg key Internet Settings"));
219 return AUTODIAL_NEVER
;
225 DWORD paramSize
= sizeof(DWORD
);
227 result
= ::RegQueryValueExW(hKey
, L
"EnableAutodial", nsnull
, &entryType
, (LPBYTE
)&autodial
, ¶mSize
);
228 if (result
!= ERROR_SUCCESS
)
231 LOGE(("Autodial: Error reading reg value EnableAutodial."));
232 return AUTODIAL_NEVER
;
235 result
= ::RegQueryValueExW(hKey
, L
"NoNetAutodial", nsnull
, &entryType
, (LPBYTE
)&onDemand
, ¶mSize
);
236 if (result
!= ERROR_SUCCESS
)
239 LOGE(("Autodial: Error reading reg value NoNetAutodial."));
240 return AUTODIAL_NEVER
;
247 return AUTODIAL_NEVER
;
253 return AUTODIAL_ON_NETWORKERROR
;
257 return AUTODIAL_ALWAYS
;
261 return AUTODIAL_DEFAULT
;
267 static nsresult
DoPPCConnection()
269 static HANDLE gConnectionHandle
= NULL
;
271 // Make the connection to the new network
272 CONNMGR_CONNECTIONINFO conn_info
;
273 memset(&conn_info
, 0, sizeof(conn_info
));
275 conn_info
.cbSize
= sizeof(conn_info
);
276 conn_info
.dwParams
= CONNMGR_PARAM_GUIDDESTNET
;
277 conn_info
.dwPriority
= CONNMGR_PRIORITY_USERINTERACTIVE
;
278 conn_info
.guidDestNet
= IID_DestNetInternet
;
279 conn_info
.bExclusive
= FALSE
;
280 conn_info
.bDisabled
= FALSE
;
282 HANDLE tempConnectionHandle
;
284 HRESULT result
= ConnMgrEstablishConnectionSync(&conn_info
,
285 &tempConnectionHandle
,
291 return NS_ERROR_FAILURE
;
294 if (status
!= CONNMGR_STATUS_CONNECTED
)
296 // could not connect to this network. release the
298 ConnMgrReleaseConnection(tempConnectionHandle
, 0);
299 return NS_ERROR_FAILURE
;
302 // At this point, we have a new connection, so release
303 // the old connection
304 if (gConnectionHandle
)
305 ConnMgrReleaseConnection(gConnectionHandle
, 0);
307 gConnectionHandle
= tempConnectionHandle
;
313 // If the RAS autodial service is running, use it. Otherwise, dial
314 // the default RAS connection. There are two possible RAS dialogs:
315 // one that dials a single entry, and one that lets the user choose which
316 // to dial. If there is only one connection entry in the phone book, or
317 // there are multiple entries but one is defined as the default, we'll use
318 // the single entry dial dialog. If there are multiple connection entries,
319 // and none is specified as default, we'll bring up the diallog which lets
320 // the user select the connection entry to use.
323 // NS_OK: dialing was successful and caller should retry
324 // all other values indicate that the caller should not retry
325 nsresult
nsRASAutodial::DialDefault(const PRUnichar
* hostName
)
330 if (mAutodialBehavior
== AUTODIAL_NEVER
)
332 return NS_ERROR_FAILURE
; // don't retry the network error
335 // If already a RAS connection, bail.
336 if (IsRASConnected())
338 LOGD(("Autodial: Not dialing: active connection."));
339 return NS_ERROR_FAILURE
; // don't retry
342 // If no dialup connections configured, bail.
343 if (mNumRASConnectionEntries
<= 0)
345 LOGD(("Autodial: Not dialing: no entries."));
346 return NS_ERROR_FAILURE
; // don't retry
350 // If autodial service is running, let it dial. In order for it to dial more
351 // reliably, we have to add the target address to the autodial database.
352 // This is the only way the autodial service dial if there is a network
353 // adapter installed. But even then it might not dial. We have to assume that
354 // it will though, or we could end up with two attempts to dial on the same
355 // network error if the user cancels the first one: one from the service and
357 // See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/rras/ras4over_3dwl.asp
358 if (mAutodialBehavior
== AUTODIAL_USE_SERVICE
)
360 AddAddressToAutodialDirectory(hostName
);
361 return NS_ERROR_FAILURE
; // don't retry
364 // Do the dialing ourselves.
367 // Don't need to load the dll before this.
368 if (!LoadRASdlgDLL())
369 return NS_ERROR_NULL_POINTER
;
371 // If a default dial entry is configured, use it.
372 if (mDefaultEntryName
[0] != '\0')
374 LOGD(("Autodial: Dialing default: %s.",mDefaultEntryName
));
376 RASDIALDLG rasDialDlg
;
377 memset(&rasDialDlg
, 0, sizeof(rasDialDlg
));
378 rasDialDlg
.dwSize
= sizeof(rasDialDlg
);
381 (*mpRasDialDlg
)(nsnull
, mDefaultEntryName
, nsnull
, &rasDialDlg
);
385 if (rasDialDlg
.dwError
!= 0)
387 LOGE(("Autodial ::RasDialDlg failed: Error: %d.",
388 rasDialDlg
.dwError
));
392 mDontRetryUntil
= PR_IntervalNow() + PR_SecondsToInterval(NO_RETRY_PERIOD_SEC
);
393 LOGD(("Autodial: User cancelled dial."));
395 return NS_ERROR_FAILURE
; // don't retry
398 LOGD(("Autodial: RAS dialup connection successful."));
401 // If no default connection specified, open the dialup dialog that lets
402 // the user specifiy which connection to dial.
405 LOGD(("Autodial: Prompting for phonebook entry."));
408 memset(&rasPBDlg
, 0, sizeof(rasPBDlg
));
409 rasPBDlg
.dwSize
= sizeof(rasPBDlg
);
411 PRBool dialed
= (*mpRasPhonebookDlg
)(nsnull
, nsnull
, &rasPBDlg
);
415 if (rasPBDlg
.dwError
!= 0)
417 LOGE(("Autodial: ::RasPhonebookDlg failed: Error = %d.",
422 mDontRetryUntil
= PR_IntervalNow() + PR_SecondsToInterval(NO_RETRY_PERIOD_SEC
);
423 LOGD(("Autodial: User cancelled dial."));
426 return NS_ERROR_FAILURE
; // don't retry
429 LOGD(("Autodial: RAS dialup connection successful."));
433 // Retry because we just established a dialup connection.
437 return DoPPCConnection();
442 // Check to see if RAS is already connected.
443 PRBool
nsRASAutodial::IsRASConnected()
447 rasConn
.dwSize
= sizeof(rasConn
);
448 DWORD structSize
= sizeof(rasConn
);
450 if (!LoadRASapi32DLL())
451 return NS_ERROR_NULL_POINTER
;
453 DWORD result
= (*mpRasEnumConnections
)(&rasConn
, &structSize
, &connections
);
455 // ERROR_BUFFER_TOO_SMALL is OK because we only need one struct.
456 if (result
== ERROR_SUCCESS
|| result
== ERROR_BUFFER_TOO_SMALL
)
458 return (connections
> 0);
461 LOGE(("Autodial: ::RasEnumConnections failed: Error = %d", result
));
465 // Get the first RAS dial entry name from the phonebook.
466 nsresult
nsRASAutodial::GetFirstEntryName(PRUnichar
* entryName
, int bufferSize
)
468 // Need to load the DLL if not loaded yet.
469 if (!LoadRASapi32DLL())
470 return NS_ERROR_NULL_POINTER
;
472 RASENTRYNAMEW rasEntryName
;
473 rasEntryName
.dwSize
= sizeof(rasEntryName
);
474 DWORD cb
= sizeof(rasEntryName
);
478 (*mpRasEnumEntries
)(nsnull
, nsnull
, &rasEntryName
, &cb
, &cEntries
);
480 // ERROR_BUFFER_TOO_SMALL is OK because we only need one struct.
481 if (result
== ERROR_SUCCESS
|| result
== ERROR_BUFFER_TOO_SMALL
)
483 wcsncpy(entryName
, rasEntryName
.szEntryName
,
484 bufferSize
/ sizeof(*entryName
));
488 return NS_ERROR_FAILURE
;
491 // Get the number of RAS dial entries in the phonebook.
492 int nsRASAutodial::NumRASEntries()
494 // Need to load the DLL if not loaded yet.
495 if (!LoadRASapi32DLL())
498 RASENTRYNAMEW rasEntryName
;
499 rasEntryName
.dwSize
= sizeof(rasEntryName
);
500 DWORD cb
= sizeof(rasEntryName
);
505 (*mpRasEnumEntries
)(nsnull
, nsnull
, &rasEntryName
, &cb
, &cEntries
);
507 // ERROR_BUFFER_TOO_SMALL is OK because we only need one struct.
508 if (result
== ERROR_SUCCESS
|| result
== ERROR_BUFFER_TOO_SMALL
)
510 return (int)cEntries
;
516 // Get the name of the default dial entry.
517 nsresult
nsRASAutodial::GetDefaultEntryName(PRUnichar
* entryName
, int bufferSize
)
519 // No RAS dialup entries.
520 if (mNumRASConnectionEntries
<= 0)
522 return NS_ERROR_FAILURE
;
525 // Single RAS dialup entry. Use it as the default to autodial.
526 if (mNumRASConnectionEntries
== 1)
528 return GetFirstEntryName(entryName
, bufferSize
);
531 // Multiple RAS dialup entries. If a default configured in the registry,
534 // For Windows XP: HKCU/Software/Microsoft/RAS Autodial/Default/DefaultInternet.
535 // or HKLM/Software/Microsoft/RAS Autodial/Default/DefaultInternet.
536 // For Windows 2K: HKCU/RemoteAccess/InternetProfile.
538 const PRUnichar
* key
= nsnull
;
539 const PRUnichar
* val
= nsnull
;
544 // Windows NT and 2000
545 if ((mOSVerInfo
.dwMajorVersion
== 4) // Windows NT
546 || ((mOSVerInfo
.dwMajorVersion
== 5) && (mOSVerInfo
.dwMinorVersion
== 0))) // Windows 2000
548 key
= L
"RemoteAccess";
549 val
= L
"InternetProfile";
551 result
= ::RegOpenKeyExW(
558 if (result
!= ERROR_SUCCESS
)
560 return NS_ERROR_FAILURE
;
565 key
= L
"Software\\Microsoft\\RAS Autodial\\Default";
566 val
= L
"DefaultInternet";
570 result
= ::RegOpenKeyExW(
577 if (result
!= ERROR_SUCCESS
)
579 // If not present, try HKLM.
580 result
= ::RegOpenKeyExW(
587 if (result
!= ERROR_SUCCESS
)
589 return NS_ERROR_FAILURE
;
596 DWORD buffSize
= bufferSize
;
598 result
= ::RegQueryValueExW(hKey
,
608 if (result
!= ERROR_SUCCESS
)
610 // Results in a prompt for which to use at dial time.
611 return NS_ERROR_FAILURE
;
618 // Determine if the autodial service is running on this PC.
619 PRBool
nsRASAutodial::IsAutodialServiceRunning()
622 SC_HANDLE hSCManager
=
623 OpenSCManager(nsnull
, SERVICES_ACTIVE_DATABASE
, SERVICE_QUERY_STATUS
);
625 if (hSCManager
== nsnull
)
627 LOGE(("Autodial: failed to open service control manager. Error %d.",
634 OpenServiceW(hSCManager
, L
"RasAuto", SERVICE_QUERY_STATUS
);
636 if (hSCManager
== nsnull
)
638 LOGE(("Autodial: failed to open RasAuto service."));
642 SERVICE_STATUS status
;
643 if (!QueryServiceStatus(hService
, &status
))
645 LOGE(("Autodial: ::QueryServiceStatus() failed. Error: %d",
651 return (status
.dwCurrentState
== SERVICE_RUNNING
);
657 // Add the specified address to the autodial directory.
658 PRBool
nsRASAutodial::AddAddressToAutodialDirectory(const PRUnichar
* hostName
)
660 // Need to load the DLL if not loaded yet.
661 if (!LoadRASapi32DLL())
664 // First see if there is already a db entry for this address.
665 RASAUTODIALENTRYW autodialEntry
;
666 autodialEntry
.dwSize
= sizeof(autodialEntry
);
667 DWORD size
= sizeof(autodialEntry
);
670 DWORD result
= (*mpRasGetAutodialAddress
)(hostName
,
676 // If there is already at least 1 entry in db for this address, return.
677 if (result
!= ERROR_FILE_NOT_FOUND
)
679 LOGD(("Autodial: Address %s already in autodial db.", hostName
));
683 autodialEntry
.dwSize
= sizeof(autodialEntry
);
684 autodialEntry
.dwFlags
= 0;
685 autodialEntry
.dwDialingLocation
= mAutodialServiceDialingLocation
;
686 GetDefaultEntryName(autodialEntry
.szEntry
, sizeof(autodialEntry
.szEntry
));
688 result
= (*mpRasSetAutodialAddress
)(hostName
,
691 sizeof(autodialEntry
),
694 if (result
!= ERROR_SUCCESS
)
696 LOGE(("Autodial ::RasSetAutodialAddress failed result %d.", result
));
700 LOGD(("Autodial: Added address %s to RAS autodial db for entry %s.",
701 hostName
, NS_ConvertUTF16toUTF8(autodialEntry
.szEntry
).get()));
706 // Get the current TAPI dialing location.
707 int nsRASAutodial::GetCurrentLocation()
710 LONG result
= ::RegOpenKeyExW(
712 L
"Software\\Microsoft\\Windows\\CurrentVersion\\Telephony\\Locations",
717 if (result
!= ERROR_SUCCESS
)
719 LOGE(("Autodial: Error opening reg key ...CurrentVersion\\Telephony\\Locations"));
725 DWORD paramSize
= sizeof(DWORD
);
727 result
= ::RegQueryValueExW(hKey
, L
"CurrentID", nsnull
, &entryType
, (LPBYTE
)&location
, ¶mSize
);
728 if (result
!= ERROR_SUCCESS
)
731 LOGE(("Autodial: Error reading reg value CurrentID."));
740 // Check to see if autodial for the specified location is enabled.
741 PRBool
nsRASAutodial::IsAutodialServiceEnabled(int location
)
746 if (!LoadRASapi32DLL())
750 if ((*mpRasGetAutodialEnable
)(location
, &enabled
) != ERROR_SUCCESS
)
752 LOGE(("Autodial: Error calling RasGetAutodialEnable()"));
761 PRBool
nsRASAutodial::LoadRASapi32DLL()
765 mhRASapi32
= ::LoadLibraryW(L
"rasapi32.dll");
766 if ((UINT
)mhRASapi32
> 32)
768 // RasEnumConnections
769 mpRasEnumConnections
= (tRASENUMCONNECTIONS
)
770 ::GetProcAddress(mhRASapi32
, "RasEnumConnectionsW");
773 mpRasEnumEntries
= (tRASENUMENTRIES
)
774 ::GetProcAddress(mhRASapi32
, "RasEnumEntriesW");
776 // RasSetAutodialAddress
777 mpRasSetAutodialAddress
= (tRASSETAUTODIALADDRESS
)
778 ::GetProcAddress(mhRASapi32
, "RasSetAutodialAddressW");
780 // RasGetAutodialAddress
781 mpRasGetAutodialAddress
= (tRASGETAUTODIALADDRESS
)
782 ::GetProcAddress(mhRASapi32
, "RasGetAutodialAddressW");
784 // RasGetAutodialEnable
785 mpRasGetAutodialEnable
= (tRASGETAUTODIALENABLE
)
786 ::GetProcAddress(mhRASapi32
, "RasGetAutodialEnableW");
788 // RasGetAutodialParam
789 mpRasGetAutodialParam
= (tRASGETAUTODIALPARAM
)
790 ::GetProcAddress(mhRASapi32
, "RasGetAutodialParamW");
796 || !mpRasEnumConnections
798 || !mpRasSetAutodialAddress
799 || !mpRasGetAutodialAddress
800 || !mpRasGetAutodialEnable
801 || !mpRasGetAutodialParam
)
803 LOGE(("Autodial: Error loading RASAPI32.DLL."));
810 PRBool
nsRASAutodial::LoadRASdlgDLL()
814 mhRASdlg
= ::LoadLibraryW(L
"rasdlg.dll");
815 if ((UINT
)mhRASdlg
> 32)
819 (tRASPHONEBOOKDLG
)::GetProcAddress(mhRASdlg
, "RasPhonebookDlgW");
823 (tRASDIALDLG
)::GetProcAddress(mhRASdlg
, "RasDialDlgW");
828 if (!mhRASdlg
|| !mpRasPhonebookDlg
|| !mpRasDialDlg
)
830 LOGE(("Autodial: Error loading RASDLG.DLL."));