mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5598 lines
171 KiB
5598 lines
171 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
connect.cxx
|
|
|
|
Abstract:
|
|
|
|
Contains the entry points for the Winnet Connection API supported by the
|
|
Multi-Provider Router.
|
|
Contains:
|
|
WNetAddConnectionW
|
|
WNetAddConnection2W
|
|
WNetAddConnection3W
|
|
WNetUseConnectionW
|
|
WNetCancelConnection2W
|
|
WNetCancelConnectionW
|
|
WNetGetConnectionW
|
|
WNetSetConnectionW
|
|
WNetRestoreConnectionW
|
|
WNetRestoreConnection2W
|
|
DoRestoreConnection
|
|
MprRestoreThisConnection
|
|
MprCreateConnectionArray
|
|
MprSortConnectionArray
|
|
MprRefcountConnectionArray
|
|
MprAddPrintersToConnArray
|
|
MprForgetPrintConnection
|
|
MprFreeConnectionArray
|
|
MprNotifyErrors
|
|
MprNotifyShell
|
|
MprCancelConnection2
|
|
|
|
Author:
|
|
|
|
Dan Lafferty (danl) 09-Oct-1991
|
|
|
|
Environment:
|
|
|
|
User Mode -Win32
|
|
|
|
Notes:
|
|
|
|
Revision History:
|
|
|
|
09-Oct-1991 danl
|
|
created
|
|
03-Jan-1992 terryk
|
|
Changed WNetRestoreConnections WNetRestoreConnection
|
|
13-Jan-1992 Johnl
|
|
Added MPR.H to include file list
|
|
31-Jan-1992 Johnl
|
|
Added fForgetConnection to WNetCancelConnectionW
|
|
01-Apr-1992 Johnl
|
|
Changed CONNECTION_REMEMBER to CONNECT_UPDATE_PROFILE, updated
|
|
WNetCancelConnection2 to match spec
|
|
22-Jul-1992 danl
|
|
WNetAddConnection2: If attempting to connect to a drive that is
|
|
already remembered in the registry, we will allow the connect as
|
|
long as the remote name for the connection is the same as the
|
|
one that is remembered. If the remote names are not the same
|
|
return ERROR_DEVICE_ALREADY_REMEMBERED.
|
|
26-Aug-1992 danl
|
|
WNetAddConnectionW: Put Try & except around STRLEN(lpLocalName).
|
|
04-Sept-1992 danl
|
|
Re-Write MprRestoreThisConnection.
|
|
08-Sept-1992 danl
|
|
WNetCancelConnect2W: If no providers claim responsibility for
|
|
the cancel, and if the connect info is in the registry, then
|
|
remove it, and return SUCCESS.
|
|
09-Sept-1992 danl
|
|
WNetCancelConnect2W: If the provider returns WN_BAD_LOCALNAME,
|
|
WN_NO_NETWORK, or WN_BAD_NETNAME, then return WN_NOT_CONNECTED.
|
|
22-Sept-1992 danl
|
|
WNetRestoreConnection: For WN_CANCEL case, set continue Flag to false.
|
|
We don't want to ask for password again if they already said CANCEL.
|
|
02-Nov-1992 danl
|
|
Fail with NO_NETWORK if there are no providers.
|
|
24-Nov-1992 Yi-HsinS
|
|
Added checking in the registry to see whether we need to
|
|
restore connection or not. ( support of RAS )
|
|
16-Nov-1993 Danl
|
|
AddConnect2: If provider returns ERROR_INVALID_LEVEL or
|
|
ERROR_INVALID_PARAMETER, continue trying other providers.
|
|
19-Apr-1994 Danl
|
|
DoRestoreConnection: Fix timeout logic where we would ignore the
|
|
provider-supplied timeout if the timeout was smaller than the default.
|
|
Now, if all the providers know their timeouts, the larger of those
|
|
timeouts is used. Even if smaller than the default.
|
|
AddConnection3: Fixed Prototype to be more like AddConnection2.
|
|
19-May-1994 Danl
|
|
AddConnection3: Changed comments for dwType probe to match the code.
|
|
Also re-arrange the code here to make it more efficient.
|
|
07-Feb-1995 AnirudhS
|
|
MprGetConnection: Fixed so that, like AddConnection and
|
|
CancelConnection, it doesn't stop routing on errors.
|
|
10-Feb-1995 AnirudhS
|
|
WNetAddConnection3: If provider returns WN_ALREADY_CONNECTED, stop
|
|
trying other providers.
|
|
08-May-1995 AnirudhS
|
|
Add WNetUseConnection and make WNetAddConnection3 call through to it.
|
|
12-Jun-1995 AnirudhS
|
|
Send WM_DEVICECHANGE message to notify shell of connections.
|
|
13-Jun-1995 AnirudhS
|
|
Add WNetSetConnection.
|
|
07-Jul-1995 AnirudhS
|
|
Tidy up auto-picking logic in WNetUseConnection and CAutoPickedDevice.
|
|
12-Jul-1995 AnirudhS
|
|
Rename WNetRestoreConnection to WNetRestoreConnectionW to match
|
|
winnetwk.w.
|
|
14-Sep-1995 AnirudhS
|
|
WNetGetConnection: Optimize for non-network drives by not calling
|
|
the providers. This enables the shell to open the "My Computer"
|
|
folder much quicker.
|
|
18-Oct-1995 AnirudhS
|
|
WNetUseConnection: When saving the connection to the registry, if a
|
|
username is not supplied, get it from the provider, so that the
|
|
right username will be used at the next logon.
|
|
15-Jan-1996 AnirudhS
|
|
WNetRestoreConnection, etc.: Restore the connections in a deferred
|
|
state if the remembered user name is the default user.
|
|
08-Mar-1996 AnirudhS
|
|
WNetRestoreConnection, etc.: Use the provider type in preference to
|
|
the provider name when saving and restoring connections, so that
|
|
floating profiles work in a multi-language environment. See
|
|
comments at MprReadConnectionInfo for details.
|
|
20-Mar-1996 Anirudhs
|
|
Add WNetGetConnection3.
|
|
25-Mar-1996 AnirudhS
|
|
WNetRestoreConnection, etc.: Don't display the "restoring connections"
|
|
dialog if all connections are deferred. Don't defer connections if
|
|
a registry flag says not to.
|
|
11-Apr-1996 AnirudhS
|
|
WNetUseConnection: Convert to CRoutedOperation base class.
|
|
Return WN_NO_MORE_DEVICES instead of WN_NO_MORE_ENTRIES.
|
|
04-Jun-1996 AnirudhS
|
|
WNetRestoreConnection: Don't defer connections if logged on locally.
|
|
This is a temporary fix for bug 36827 for NT 4.0.
|
|
07-Jun-1996 AnirudhS
|
|
MprNotifyShell: Don't call BroadcastSystemMessage here. Instead use
|
|
a simple scheme to have a trusted system component broadcast on our
|
|
behalf. This fixes a deadlock that caused 20-second delays that were
|
|
visible to the user.
|
|
28-Jun-1996 AnirudhS
|
|
Don't defer a connection if a password was explicitly supplied when
|
|
the connection was made.
|
|
08-Feb-1997 AnirudhS
|
|
QFE 55011: Add WN_CONNECTED_OTHER_PASSWORD so the MPR knows if the
|
|
provider prompted the user for an explicit password.
|
|
21-Feb-1997 AnirudhS
|
|
Add DEFER_UNKNOWN so MPR automatically discovers whether to defer a
|
|
connection at future logons.
|
|
03-Mar-1998 jschwart
|
|
Fix bugs caused by moving WNetRestoreConnection from userinit.exe
|
|
into winlogon.exe: Add impersonation code to WNetRestoreConnection
|
|
so a reconnection is attempted in the user's context. Add code to
|
|
close registry key handles that were leaked inWNetRestoreConnection,
|
|
MprRestoreThisConnection, and CreateConnectionArray.
|
|
05-May-1999 jschwart
|
|
Make provider addition/removal dynamic
|
|
04-Mar-2000 dsheldon
|
|
Added WNetRestoreConnection2W with a flags field that allows us to
|
|
reduce the amount of user interaction required to reconnect drives,
|
|
and allow the shell to handle errors after logon instead.
|
|
|
|
--*/
|
|
|
|
//
|
|
// INCLUDES
|
|
//
|
|
|
|
#include "precomp.hxx"
|
|
extern "C" {
|
|
#include <ntlsa.h> // LsaGetUserName
|
|
#include <winsvcp.h> // SC_BSM_EVENT_NAME
|
|
}
|
|
#include <wincred.h>
|
|
|
|
#include <shellapi.h>
|
|
#include <shlapip.h>
|
|
|
|
#include <tstr.h> // WCSSIZE, STRLEN
|
|
#include "connify.h" // MprAddConnectNotify
|
|
#include "mprui.h" // ShowReconnectDialog, etc.
|
|
#include <apperr.h>
|
|
|
|
|
|
//
|
|
// DATA STRUCTURES
|
|
//
|
|
|
|
typedef struct _CONNECTION_INFO
|
|
{
|
|
BOOL ContinueFlag;
|
|
DWORD ProviderIndex;
|
|
DWORD ProviderWait;
|
|
DWORD ProviderFlags;
|
|
DWORD DeferFlags; // flags read from registry
|
|
DWORD Status;
|
|
LPTSTR UserName;
|
|
BOOL Defer; // our computed decision on whether to defer
|
|
NETRESOURCEW NetResource;
|
|
HKEY RegKey; // handle to the key with the connection info
|
|
HINSTANCE hProviderDll; // Variables to keep provider DLLs loaded
|
|
PF_NPGetCaps pfGetCaps;
|
|
PF_NPAddConnection3 pfAddConnection3;
|
|
PF_NPAddConnection pfAddConnection;
|
|
PF_NPCancelConnection pfCancelConnection;
|
|
DWORD dwConnectCaps;
|
|
}
|
|
CONNECTION_INFO, *LPCONNECTION_INFO;
|
|
|
|
|
|
//
|
|
// EXTERNAL GLOBALS
|
|
//
|
|
|
|
extern DWORD GlobalNumActiveProviders;
|
|
extern BOOL g_LUIDDeviceMapsEnabled;
|
|
|
|
//
|
|
// Defines
|
|
//
|
|
|
|
#define INVALID_WINDOW_HANDLE ((HWND) INVALID_HANDLE_VALUE)
|
|
|
|
|
|
//
|
|
// Local Function Prototypes
|
|
//
|
|
|
|
VOID
|
|
DoRestoreConnection(
|
|
PARAMETERS * Params
|
|
);
|
|
|
|
DWORD
|
|
MprRestoreThisConnection(
|
|
HWND hWnd,
|
|
PARAMETERS * Params,
|
|
LPCONNECTION_INFO ConnectInfo,
|
|
DWORD dwFlags
|
|
);
|
|
|
|
DWORD
|
|
MprCreateConnectionArray(
|
|
LPDWORD lpNumConnections,
|
|
LPCTSTR lpDevice,
|
|
LPDWORD lpRegMaxWait,
|
|
LPCONNECTION_INFO *ConnectArray
|
|
);
|
|
|
|
VOID
|
|
MprSortConnectionArray(
|
|
LPCONNECTION_INFO lpcConnectArray,
|
|
DWORD dwNumSubKeys
|
|
);
|
|
|
|
VOID
|
|
MprRefcountConnectionArray(
|
|
LPCONNECTION_INFO lpcConnectArray,
|
|
DWORD dwNumSubkeys
|
|
);
|
|
|
|
DWORD
|
|
MprAddPrintersToConnArray(
|
|
LPDWORD lpNumConnections,
|
|
LPCONNECTION_INFO *ConnectArray
|
|
);
|
|
|
|
VOID
|
|
MprFreeConnectionArray(
|
|
LPCONNECTION_INFO ConnectArray,
|
|
DWORD NumConnections
|
|
);
|
|
|
|
BOOL
|
|
MprUserNameMatch (
|
|
IN PUNICODE_STRING DomainName,
|
|
IN PUNICODE_STRING UserName,
|
|
IN LPCWSTR RememberedName,
|
|
IN BOOL fMustMatchCompletely
|
|
);
|
|
|
|
DWORD
|
|
MprNotifyErrors(
|
|
HWND hWnd,
|
|
LPCONNECTION_INFO ConnectArray,
|
|
DWORD NumConnections,
|
|
DWORD dwFlags
|
|
);
|
|
|
|
VOID
|
|
MprNotifyShell(
|
|
IN LPCWSTR pwszDevice
|
|
);
|
|
|
|
DWORD
|
|
MprCancelConnection2 (
|
|
IN LPCWSTR lpName,
|
|
IN DWORD dwFlags,
|
|
IN BOOL fForce,
|
|
IN BOOL fCheckProviders
|
|
);
|
|
|
|
BOOL
|
|
MprBroadcastDriveChange(
|
|
IN LPCWSTR pwszDevice,
|
|
IN BOOL DeleteAction
|
|
);
|
|
|
|
DWORD
|
|
WNetAddConnectionW (
|
|
IN LPCWSTR lpRemoteName,
|
|
IN LPCWSTR lpPassword,
|
|
IN LPCWSTR lpLocalName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function allows the caller to redirect (connect) a local device
|
|
to a network resource. The connection is remembered.
|
|
|
|
Arguments:
|
|
|
|
lpRemoteName - Specifies the network resource to connect to.
|
|
|
|
lpPassword - Specifies the password to be used in making the connection.
|
|
The NULL value may be passed in to indicate use of the 'default'
|
|
password. An empty string may be used to indicate no password.
|
|
|
|
lpLocalName - This should contain the name of a local device to be
|
|
redirected, such as "F:" or "LPT1:" The string is treated in a
|
|
case insensitive manner, and may be the empty string in which case
|
|
a connection to the network resource is made without making a
|
|
decision.
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD status = WN_SUCCESS;
|
|
NETRESOURCEW netResource;
|
|
DWORD numchars;
|
|
|
|
//
|
|
// load up the net resource structure
|
|
//
|
|
|
|
netResource.dwScope = 0;
|
|
netResource.dwUsage = 0;
|
|
netResource.lpRemoteName = (LPWSTR) lpRemoteName;
|
|
netResource.lpLocalName = (LPWSTR) lpLocalName;
|
|
netResource.lpProvider = NULL;
|
|
netResource.lpComment = NULL;
|
|
|
|
|
|
__try {
|
|
numchars = STRLEN(lpLocalName);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
status = GetExceptionCode();
|
|
if (status != EXCEPTION_ACCESS_VIOLATION) {
|
|
MPR_LOG(ERROR,"WNetAddConnectionW:Unexpected Exception 0x%lx\n",status);
|
|
}
|
|
status = WN_BAD_POINTER;
|
|
}
|
|
if (status != WN_SUCCESS) {
|
|
SetLastError(status);
|
|
return status;
|
|
}
|
|
|
|
if (numchars == 0) {
|
|
netResource.dwType = RESOURCETYPE_ANY;
|
|
}
|
|
else if (numchars > 2) {
|
|
netResource.dwType = RESOURCETYPE_PRINT;
|
|
}
|
|
else {
|
|
netResource.dwType = RESOURCETYPE_DISK;
|
|
}
|
|
|
|
//
|
|
// Call WNetUseConnection so it can do all the work.
|
|
//
|
|
|
|
return(WNetUseConnectionW (
|
|
NULL, // hwndOwner
|
|
&netResource, // lpNetResource
|
|
lpPassword, // lpPassword
|
|
NULL, // lpUserID
|
|
CONNECT_UPDATE_PROFILE, // dwFlags
|
|
NULL, // lpAccessName
|
|
NULL, // lpBufferSize
|
|
NULL)); // lpResult
|
|
|
|
}
|
|
|
|
DWORD
|
|
WNetAddConnection2W (
|
|
IN LPNETRESOURCEW lpNetResource,
|
|
IN LPCWSTR lpPassword,
|
|
IN LPCWSTR lpUserName,
|
|
IN DWORD dwFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function allows the caller to redirect (connect) a local device
|
|
to a network resource. It is similar to WNetAddConnection, except
|
|
that it takes a pointer to a NETRESOURCE structure to describe the
|
|
network resource to connect to. It also takes the additional parameters
|
|
lpUserName, and dwFlags.
|
|
|
|
Arguments:
|
|
|
|
lpNetResource - This is a pointer to a network resource structure that
|
|
specifies the network resource to connect to. The following
|
|
fields must be set when making a connection, the others are ignored.
|
|
lpRemoteName
|
|
lpLocalName
|
|
lpProvider
|
|
dwType
|
|
|
|
lpPassword - Specifies the password to be used in making the connection.
|
|
The NULL value may be passed in to indicate use of the 'default'
|
|
password. An empty string may be used to indicate no password.
|
|
|
|
lpUserName- This specifies the username used to make the connection.
|
|
If NULL, the default username (currently logged on user) will be
|
|
applied. This is used when the user wishes to connect to a
|
|
resource, but has a different user name or account assigned to him
|
|
for that resource.
|
|
|
|
dwFlags - This is a bitmask which may have any of the following bits set:
|
|
CONNECT_UPDATE_PROFILE
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Call WNetUseConnection so it can do all the work.
|
|
// It is called with a NULL HWND.
|
|
//
|
|
|
|
return(WNetUseConnectionW (
|
|
NULL, // hwndOwner
|
|
lpNetResource, // lpNetResource
|
|
lpPassword, // lpPassword
|
|
lpUserName, // lpUserID
|
|
dwFlags, // dwFlags
|
|
NULL, // lpAccessName
|
|
NULL, // lpBufferSize
|
|
NULL)); // lpResult
|
|
|
|
}
|
|
|
|
DWORD
|
|
WNetAddConnection3W (
|
|
IN HWND hwndOwner,
|
|
IN LPNETRESOURCEW lpNetResource,
|
|
IN LPCWSTR lpPassword,
|
|
IN LPCWSTR lpUserName,
|
|
IN DWORD dwFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function allows the caller to redirect (connect) a local device
|
|
to a network resource. It is similar to WNetAddConnection2, except
|
|
that it takes the additional parameter hwndOwner.
|
|
|
|
Arguments:
|
|
|
|
hwndOwner - A handle to a window which should be the owner for any
|
|
messages or dialogs that the network provider might display.
|
|
|
|
lpNetResource - This is a pointer to a network resource structure that
|
|
specifies the network resource to connect to. The following
|
|
fields must be set when making a connection, the others are ignored.
|
|
lpRemoteName
|
|
lpLocalName
|
|
lpProvider
|
|
dwType
|
|
|
|
lpPassword - Specifies the password to be used in making the connection.
|
|
The NULL value may be passed in to indicate use of the 'default'
|
|
password. An empty string may be used to indicate no password.
|
|
|
|
lpUserName- This specifies the username used to make the connection.
|
|
If NULL, the default username (currently logged on user) will be
|
|
applied. This is used when the user wishes to connect to a
|
|
resource, but has a different user name or account assigned to him
|
|
for that resource.
|
|
|
|
dwFlags - This is a bitmask which may have any of the following bits set:
|
|
CONNECT_UPDATE_PROFILE
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Call WNetUseConnection so it can do all the work.
|
|
// It is called with no buffer for the access name and result flags.
|
|
//
|
|
|
|
return(WNetUseConnectionW (
|
|
hwndOwner, // hwndOwner
|
|
lpNetResource, // lpNetResource
|
|
lpPassword, // lpPassword
|
|
lpUserName, // lpUserID
|
|
dwFlags, // dwFlags
|
|
NULL, // lpAccessName
|
|
NULL, // lpBufferSize
|
|
NULL)); // lpResult
|
|
}
|
|
|
|
|
|
/*************************************************************************
|
|
|
|
NAME: CAutoPickedDevice
|
|
|
|
SYNOPSIS: This class is meant for use by the WNetUseConnection
|
|
function. It iterates through all possible local device names
|
|
and finds an unused device name to be redirected. For efficiency,
|
|
it does this only once in the lifetime of the object, viz. the
|
|
first time the PickDevice() method is called. On subsequent calls
|
|
it returns the result of the first call.
|
|
|
|
The results of the PickDevice() call are saved in member variables,
|
|
rather than being copied to the caller's buffer, because we don't
|
|
know whether to return them to the caller until later.
|
|
|
|
INTERFACE:
|
|
|
|
CAVEATS: The Init method must be called before the PickDevice method.
|
|
|
|
NOTES:
|
|
|
|
HISTORY:
|
|
AnirudhS 24-May-1995 Created
|
|
|
|
**************************************************************************/
|
|
|
|
class CAutoPickedDevice
|
|
{
|
|
private:
|
|
DWORD _bPicked; // whether already attempted to pick a device
|
|
DWORD _dwError; // result of attempt to pick a device
|
|
|
|
DWORD _dwDeviceType; // saved parameter from Init()
|
|
DWORD _cchBufferSize; // saved parameter from Init()
|
|
|
|
DWORD _cchReqBufferSize; // saved results of PickDevice()
|
|
WCHAR _wszPickedName[ max(sizeof("A:"), sizeof("LPT99:")) ];
|
|
|
|
public:
|
|
|
|
DWORD Init(
|
|
IN LPWSTR lpAccessName,
|
|
IN LPDWORD lpcchBufferSize,
|
|
IN LPCWSTR pwszLocalName,
|
|
IN LPCWSTR pwszRemoteName,
|
|
IN DWORD dwDeviceType,
|
|
IN DWORD dwFlags
|
|
);
|
|
|
|
DWORD PickDevice();
|
|
|
|
DWORD dwError()
|
|
{ return _dwError; }
|
|
|
|
DWORD cchReqBufferSize()
|
|
{ return _cchReqBufferSize; }
|
|
|
|
LPWSTR wszPickedName()
|
|
{ return _wszPickedName; }
|
|
} ;
|
|
|
|
|
|
/*************************************************************************
|
|
|
|
NAME: CAutoPickedDevice::Init
|
|
|
|
SYNOPSIS: This function validates the parameters related to access
|
|
names and auto-picking of local device names, and saves away some
|
|
of them to be used in PickDevice().
|
|
Depending on the severity of the errors it finds, it does one of
|
|
the following:
|
|
- cause an access violation
|
|
- return an error
|
|
- return success, but save away an error to be returned from
|
|
PickDevice()
|
|
- return success, and save away WN_SUCCESS so that PickDevice()
|
|
will try to pick a device
|
|
|
|
**************************************************************************/
|
|
|
|
DWORD CAutoPickedDevice::Init(
|
|
IN LPWSTR lpAccessName,
|
|
IN LPDWORD lpcchBufferSize,
|
|
IN LPCWSTR pwszLocalName,
|
|
IN LPCWSTR pwszRemoteName,
|
|
IN DWORD dwDeviceType,
|
|
IN DWORD dwFlags
|
|
)
|
|
{
|
|
_bPicked = FALSE;
|
|
_dwError = WN_SUCCESS;
|
|
|
|
//
|
|
// If out pointers are supplied, make sure they're writeable
|
|
// (even if we don't use them)
|
|
//
|
|
if (ARGUMENT_PRESENT(lpcchBufferSize))
|
|
{
|
|
_cchBufferSize = *(volatile DWORD *)lpcchBufferSize; // Probe
|
|
*(volatile DWORD *)lpcchBufferSize = _cchBufferSize; // Probe
|
|
}
|
|
else
|
|
{
|
|
_cchBufferSize = 0;
|
|
}
|
|
|
|
|
|
if (ARGUMENT_PRESENT(lpAccessName))
|
|
{
|
|
//
|
|
// If an AccessName buffer is supplied, the size parameter must
|
|
// be present and non-zero, else return right away
|
|
//
|
|
if (_cchBufferSize == 0)
|
|
{
|
|
return WN_BAD_VALUE; // Win95 compatibility
|
|
}
|
|
|
|
if (IsBadWritePtr(lpAccessName, _cchBufferSize * sizeof(WCHAR)))
|
|
{
|
|
return WN_BAD_POINTER;
|
|
}
|
|
|
|
_cchReqBufferSize = 0;
|
|
|
|
//
|
|
// If an access name is requested, and a local name is
|
|
// specified, then we know that the access name will be the
|
|
// specified local name, so we can validate the buffer
|
|
// length up front
|
|
//
|
|
if (! IS_EMPTY_STRING(pwszLocalName))
|
|
{
|
|
_cchReqBufferSize = wcslen(pwszLocalName)+1;
|
|
}
|
|
|
|
//
|
|
// If an access name is requested, and no local name is
|
|
// specified, and we aren't asked to auto-pick a local
|
|
// device, then the access name is likely to be the specified
|
|
// remote name, so we validate the buffer length up front
|
|
//
|
|
else if ((dwFlags & CONNECT_REDIRECT) == 0)
|
|
{
|
|
_cchReqBufferSize = wcslen(pwszRemoteName)+1;
|
|
}
|
|
|
|
if (_cchBufferSize < _cchReqBufferSize) {
|
|
*lpcchBufferSize = _cchReqBufferSize;
|
|
return WN_MORE_DATA;
|
|
}
|
|
|
|
//
|
|
// Save other parameters for use in PickDevice()
|
|
//
|
|
_dwDeviceType = dwDeviceType;
|
|
|
|
//
|
|
// (If we auto-pick a local device, the buffer length will
|
|
// be validated later, when we auto-pick)
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We won't be able to autopick, because no access name buffer
|
|
// is supplied. But don't return the error until we're asked
|
|
// to autopick.
|
|
//
|
|
_bPicked = TRUE;
|
|
_dwError = WN_BAD_POINTER;
|
|
}
|
|
|
|
return WN_SUCCESS;
|
|
}
|
|
|
|
|
|
|
|
/*************************************************************************
|
|
|
|
NAME: CAutoPickedDevice::PickDevice
|
|
|
|
**************************************************************************/
|
|
|
|
DWORD CAutoPickedDevice::PickDevice()
|
|
{
|
|
//
|
|
// If we've been called before then return the error we returned
|
|
// last time
|
|
//
|
|
if (_bPicked)
|
|
{
|
|
return _dwError;
|
|
}
|
|
|
|
_bPicked = TRUE;
|
|
|
|
|
|
//
|
|
// Validate the access name buffer size depending on the device type
|
|
//
|
|
if (_dwDeviceType == RESOURCETYPE_DISK)
|
|
{
|
|
_cchReqBufferSize = sizeof( "A:" );
|
|
}
|
|
else if (_dwDeviceType == RESOURCETYPE_PRINT)
|
|
{
|
|
_cchReqBufferSize = sizeof( "LPT1" );
|
|
}
|
|
else // RESOURCETYPE_ANY -- can't autopick
|
|
{
|
|
_dwError = WN_BAD_VALUE;
|
|
return _dwError;
|
|
}
|
|
|
|
if ( _cchBufferSize < _cchReqBufferSize )
|
|
{
|
|
_dwError = WN_MORE_DATA;
|
|
return _dwError;
|
|
}
|
|
|
|
if (_dwDeviceType == RESOURCETYPE_DISK)
|
|
{
|
|
DWORD dwDrives = GetLogicalDrives();
|
|
DWORD dwAvail = (1 << ('Z' - 'A'));
|
|
WCHAR i;
|
|
|
|
//
|
|
// Try each drive from z: to c: and see if it's been used.
|
|
// Check in reverse order to reduce the risk of conflicts
|
|
// between remote and local drive letters (e.g., if hardware
|
|
// is added after a drive is mapped).
|
|
//
|
|
for (i = L'Z'; i >= L'C'; i--, dwDrives <<= 1)
|
|
{
|
|
if (!(dwDrives & dwAvail))
|
|
{
|
|
//
|
|
// This drive is available
|
|
//
|
|
_wszPickedName[0] = i;
|
|
_wszPickedName[1] = L':';
|
|
_wszPickedName[2] = L'\0';
|
|
|
|
_dwError = WN_SUCCESS;
|
|
return _dwError;
|
|
}
|
|
}
|
|
}
|
|
else // (_dwDeviceType == RESOURCETYPE_PRINT)
|
|
{
|
|
WCHAR wszDeviceList[128];
|
|
DWORD cch;
|
|
|
|
//
|
|
// Try each device from LPT1 to LPT99 and see if it's been used
|
|
//
|
|
wcscpy(_wszPickedName, L"LPT");
|
|
for (ULONG i = 1; i <= 99; i++)
|
|
{
|
|
wsprintf(&_wszPickedName[sizeof("LPT")-1], L"%lu", i);
|
|
|
|
cch = QueryDosDevice(_wszPickedName,
|
|
wszDeviceList,
|
|
sizeof(wszDeviceList)/sizeof(WCHAR));
|
|
|
|
if ((cch == 0) && (GetLastError() != ERROR_INSUFFICIENT_BUFFER))
|
|
{
|
|
//
|
|
// QueryDosDevice failed -- this device is free
|
|
//
|
|
_dwError = WN_SUCCESS;
|
|
return _dwError;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// No unused devices found
|
|
//
|
|
_dwError = WN_NO_MORE_DEVICES;
|
|
return _dwError;
|
|
}
|
|
|
|
|
|
|
|
//===================================================================
|
|
// WNetUseConnectionW
|
|
//===================================================================
|
|
|
|
class CUseConnection : public CRoutedOperation
|
|
{
|
|
public:
|
|
CUseConnection(
|
|
HWND hwndOwner,
|
|
LPNETRESOURCEW lpNetResource,
|
|
LPCWSTR lpPassword,
|
|
LPCWSTR lpUserName,
|
|
DWORD dwFlags,
|
|
LPWSTR lpAccessName,
|
|
LPDWORD lpBufferSize,
|
|
LPDWORD lpResult
|
|
) :
|
|
CRoutedOperation(
|
|
DBGPARM("UseConnection")
|
|
NULL,
|
|
ROUTE_AGGRESSIVE
|
|
),
|
|
_hwndOwner (hwndOwner ),
|
|
_lpNetResource(lpNetResource),
|
|
_lpPassword (lpPassword ),
|
|
_lpUserName (lpUserName ),
|
|
_dwFlags (dwFlags ),
|
|
_lpAccessName (lpAccessName ),
|
|
_lpBufferSize (lpBufferSize ),
|
|
_lpResult (lpResult )
|
|
{
|
|
_ExplicitPassword = (_lpPassword != NULL);
|
|
_UsedDefaultCreds = FALSE;
|
|
}
|
|
|
|
protected:
|
|
|
|
DWORD GetResult(); // overrides CRoutedOperation implementation
|
|
|
|
private:
|
|
DWORD TestProviderWorker( \
|
|
const PROVIDER *pProvider \
|
|
);
|
|
|
|
HWND _hwndOwner;
|
|
LPNETRESOURCEW _lpNetResource;
|
|
LPCWSTR _lpPassword;
|
|
LPCWSTR _lpUserName;
|
|
DWORD _dwFlags;
|
|
LPWSTR _lpAccessName;
|
|
LPDWORD _lpBufferSize;
|
|
LPDWORD _lpResult;
|
|
|
|
BOOL _ExplicitPassword;
|
|
BOOL _UsedDefaultCreds;
|
|
CAutoPickedDevice _AutoPickedDevice;
|
|
NETRESOURCEW _ProviderNetResource;
|
|
DWORD _dwProviderFlags;
|
|
|
|
DECLARE_CROUTED
|
|
};
|
|
|
|
|
|
DWORD
|
|
CUseConnection::ValidateRoutedParameters(
|
|
LPCWSTR * ppProviderName,
|
|
LPCWSTR * ppRemoteName,
|
|
LPCWSTR * ppLocalName
|
|
)
|
|
{
|
|
DWORD status;
|
|
|
|
//
|
|
// lpRemoteName must be non-empty and readable.
|
|
//
|
|
if (wcslen(_lpNetResource->lpRemoteName) == 0) // Probe
|
|
{
|
|
return WN_BAD_NETNAME;
|
|
}
|
|
|
|
if (! IS_EMPTY_STRING(_lpNetResource->lpLocalName))
|
|
{
|
|
//
|
|
// If a lpLocalName is supplied, it must be a redirectable device
|
|
// name.
|
|
//
|
|
if (MprDeviceType(_lpNetResource->lpLocalName) != REDIR_DEVICE)
|
|
{
|
|
return WN_BAD_LOCALNAME;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If a result pointer is supplied, make sure it's writeable
|
|
//
|
|
if (ARGUMENT_PRESENT(_lpResult))
|
|
{
|
|
*_lpResult = 0;
|
|
}
|
|
|
|
if ((_lpNetResource->dwType != RESOURCETYPE_DISK) &&
|
|
(_lpNetResource->dwType != RESOURCETYPE_PRINT) &&
|
|
(_lpNetResource->dwType != RESOURCETYPE_ANY))
|
|
{
|
|
return WN_BAD_VALUE;
|
|
}
|
|
|
|
if (_dwFlags &
|
|
~(CONNECT_TEMPORARY |
|
|
CONNECT_INTERACTIVE |
|
|
CONNECT_COMMANDLINE |
|
|
CONNECT_CMD_SAVECRED |
|
|
CONNECT_PROMPT |
|
|
CONNECT_UPDATE_PROFILE |
|
|
CONNECT_UPDATE_RECENT |
|
|
CONNECT_REDIRECT |
|
|
CONNECT_CURRENT_MEDIA))
|
|
{
|
|
return WN_BAD_VALUE;
|
|
}
|
|
|
|
//
|
|
// Win95 compatibility: Ignore CONNECT_PROMPT if CONNECT_INTERACTIVE
|
|
// isn't set
|
|
//
|
|
if (!(_dwFlags & CONNECT_INTERACTIVE))
|
|
{
|
|
_dwFlags &= ~CONNECT_PROMPT;
|
|
|
|
//
|
|
// Ditch the other prompting bits just to prevent later confusion
|
|
//
|
|
|
|
_dwFlags &= ~(CONNECT_COMMANDLINE|CONNECT_CMD_SAVECRED);
|
|
}
|
|
|
|
|
|
//
|
|
// Validate parameters related to auto-picking of local device names.
|
|
// Some errors are returned immediately; others are returned only
|
|
// when we actually need to auto-pick a device.
|
|
//
|
|
status = _AutoPickedDevice.Init(
|
|
_lpAccessName,
|
|
_lpBufferSize,
|
|
_lpNetResource->lpLocalName,
|
|
_lpNetResource->lpRemoteName,
|
|
_lpNetResource->dwType,
|
|
_dwFlags
|
|
);
|
|
if (status != WN_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Set parameters used by base class. Set the local name to NULL
|
|
// to prevent the check for "localness" in the base class.
|
|
//
|
|
*ppProviderName = _lpNetResource->lpProvider;
|
|
*ppRemoteName = _lpNetResource->lpRemoteName;
|
|
*ppLocalName = NULL;
|
|
|
|
return WN_SUCCESS;
|
|
}
|
|
|
|
|
|
DWORD
|
|
CUseConnection::TestProviderWorker(
|
|
const PROVIDER * pProvider
|
|
)
|
|
{
|
|
DWORD status;
|
|
|
|
ASSERT_INITIALIZED(NETWORK);
|
|
|
|
if (pProvider->AddConnection3 == NULL &&
|
|
pProvider->AddConnection == NULL)
|
|
{
|
|
return WN_NOT_SUPPORTED;
|
|
}
|
|
|
|
//
|
|
// We will retry this provider, with an autopicked local name,
|
|
// at most once
|
|
//
|
|
for (BOOL fRetried = FALSE; ; fRetried = TRUE)
|
|
{
|
|
//
|
|
// Call the provider's appropriate entry point
|
|
//
|
|
|
|
//
|
|
// If, in the future, there are cases when the lpRemoteName
|
|
// is some alias understood by the provider but not by the
|
|
// NT name space, we'll need to add a NPAddConnection4 which
|
|
// allows the provider to return an access name.
|
|
//
|
|
if (pProvider->AddConnection3 != NULL)
|
|
{
|
|
//**************************************
|
|
// Actual call to Provider.
|
|
//**************************************
|
|
status = pProvider->AddConnection3(
|
|
_hwndOwner, // hwndOwner
|
|
&_ProviderNetResource, // lpNetResource
|
|
(LPWSTR)_lpPassword, // lpPassword
|
|
(LPWSTR)_lpUserName, // lpUserName
|
|
_dwProviderFlags & pProvider->ConnectFlagCaps ); // dwFlags
|
|
|
|
if (status == WN_CONNECTED_OTHER_PASSWORD)
|
|
{
|
|
//
|
|
// The user had to be prompted for a password, so don't
|
|
// defer the connection when restoring it at the next logon.
|
|
//
|
|
ASSERT(_dwProviderFlags & CONNECT_INTERACTIVE);
|
|
_ExplicitPassword = TRUE;
|
|
status = WN_SUCCESS;
|
|
}
|
|
else if (status == WN_CONNECTED_OTHER_PASSWORD_DEFAULT)
|
|
{
|
|
//
|
|
// The provider successfully used the default credentials
|
|
// for this connection.
|
|
//
|
|
_UsedDefaultCreds = TRUE;
|
|
status = WN_SUCCESS;
|
|
}
|
|
}
|
|
else // (pProvider->AddConnection != NULL)
|
|
{
|
|
//**************************************
|
|
// Actual call to Provider.
|
|
//**************************************
|
|
status = pProvider->AddConnection(
|
|
&_ProviderNetResource, // lpNetResource
|
|
(LPWSTR)_lpPassword, // lpPassword
|
|
(LPWSTR)_lpUserName); // lpUserName
|
|
|
|
ASSERT(status != WN_CONNECTED_OTHER_PASSWORD);
|
|
}
|
|
|
|
// The provider mustn't return this error, or it will mess us
|
|
// up later
|
|
ASSERT(status != WN_MORE_DATA);
|
|
|
|
if (fRetried)
|
|
{
|
|
if (status != WN_SUCCESS)
|
|
{
|
|
// Restore the null local name
|
|
_ProviderNetResource.lpLocalName = NULL;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If this provider can't handle a NULL local name,
|
|
// and we've been allowed to auto-pick a local name,
|
|
// and we successfully auto-pick a local name,
|
|
// try again with the auto-picked local name
|
|
//
|
|
// Note that status is updated iff we've been allowed
|
|
// to auto-pick
|
|
//
|
|
if (status == WN_BAD_LOCALNAME &&
|
|
_ProviderNetResource.lpLocalName == NULL &&
|
|
_lpAccessName != NULL &&
|
|
(status = _AutoPickedDevice.PickDevice()) == WN_SUCCESS)
|
|
{
|
|
_ProviderNetResource.lpLocalName =
|
|
_AutoPickedDevice.wszPickedName();
|
|
MPR_LOG2(ROUTE, "CUseConnection: retrying %ws with device %ws ...\n",
|
|
pProvider->Resource.lpProvider,
|
|
_ProviderNetResource.lpLocalName);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Don't retry
|
|
//
|
|
break;
|
|
}
|
|
} // end retry loop for this provider
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
BOOL
|
|
InitCredUI(
|
|
IN PWSTR pszRemoteName,
|
|
OUT PWSTR pszServer,
|
|
IN ULONG ulServerLength
|
|
)
|
|
{
|
|
// Make sure the first 2 characters are path separators:
|
|
|
|
if ((pszRemoteName == NULL) ||
|
|
(pszRemoteName[0] != L'\\') ||
|
|
(pszRemoteName[1] != L'\\'))
|
|
{
|
|
SetLastError(WN_BAD_NETNAME);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
PWSTR pszStart = pszRemoteName + 2;
|
|
PWSTR pszEnd = NULL;
|
|
|
|
// send only the server name, the string up to the first slash
|
|
pszEnd = wcschr(pszStart, L'\\');
|
|
|
|
if (pszEnd == NULL)
|
|
{
|
|
pszEnd = pszStart + wcslen(pszStart);
|
|
}
|
|
|
|
DWORD_PTR dwLength = (DWORD_PTR)(pszEnd - pszStart);
|
|
|
|
if ((dwLength == 0) || (dwLength >= ulServerLength))
|
|
{
|
|
// The server is either an empty string or more than the maximum
|
|
// number of characters we support:
|
|
|
|
SetLastError(WN_BAD_NETNAME);
|
|
return FALSE;
|
|
}
|
|
|
|
wcsncpy(pszServer, pszStart, dwLength);
|
|
pszServer[dwLength] = L'\0';
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
DisplayFailureReason(
|
|
DWORD Status,
|
|
LPCWSTR Path
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine displays the reason for the authentication failure.
|
|
The complete path name is displayed.
|
|
|
|
We could modify credui to display this, but credui doesn't have the full path name.
|
|
|
|
Arguments:
|
|
|
|
Status - Status of the failure
|
|
|
|
Path - Full path name of the failed authentication.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
DWORD MessageId;
|
|
LPWSTR InsertionStrings[2];
|
|
|
|
HMODULE lpSource = NULL;
|
|
DWORD MessageLength = 0;
|
|
LPWSTR Buffer = NULL;
|
|
BOOL MessageFormatted = FALSE;
|
|
|
|
//
|
|
// Pick the message based in the failure status
|
|
//
|
|
|
|
MessageId = ( Status == ERROR_LOGON_FAILURE) ? APE_UseBadPassOrUser : APE_UseBadPass;
|
|
InsertionStrings[0] = (LPWSTR) Path;
|
|
|
|
|
|
//
|
|
// Load the message library and format the message from it
|
|
//
|
|
|
|
lpSource = LoadLibraryEx(MESSAGE_FILENAME,
|
|
NULL,
|
|
LOAD_LIBRARY_AS_DATAFILE);
|
|
|
|
if ( lpSource ) {
|
|
|
|
//
|
|
// Format the message
|
|
//
|
|
|
|
MessageLength = FormatMessageW( FORMAT_MESSAGE_ARGUMENT_ARRAY |
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_HMODULE,
|
|
lpSource,
|
|
MessageId,
|
|
0, // LanguageId defaulted
|
|
(LPWSTR) &Buffer,
|
|
1024, // Limit the buffer size
|
|
(va_list *) InsertionStrings);
|
|
|
|
if ( MessageLength ) {
|
|
MessageFormatted = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If it failed, print a generic message
|
|
//
|
|
|
|
if ( !MessageFormatted ) {
|
|
WCHAR NumberString[18];
|
|
|
|
//
|
|
// get the message number in Unicode
|
|
//
|
|
_ultow(MessageId, NumberString, 16);
|
|
|
|
//
|
|
// setup insert strings
|
|
//
|
|
InsertionStrings[0] = NumberString;
|
|
InsertionStrings[1] = MESSAGE_FILENAME;
|
|
|
|
//
|
|
// Use the system messge file.
|
|
//
|
|
|
|
MessageLength = FormatMessageW( FORMAT_MESSAGE_ARGUMENT_ARRAY |
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
FORMAT_MESSAGE_FROM_SYSTEM,
|
|
NULL,
|
|
ERROR_MR_MID_NOT_FOUND,
|
|
0, // LanguageId defaulted
|
|
(LPWSTR) &Buffer,
|
|
1024,
|
|
(va_list *) InsertionStrings);
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Avoid double newlines
|
|
//
|
|
|
|
if ( MessageLength >= 2) {
|
|
|
|
if ( Buffer[MessageLength - 1] == L'\n' &&
|
|
Buffer[MessageLength - 2] == L'\r') {
|
|
|
|
//
|
|
// "\r\n" shows up as two newlines when using the CRT and piping
|
|
// output to a file. Make it just one newline.
|
|
//
|
|
|
|
Buffer[MessageLength - 1] = L'\0';
|
|
Buffer[MessageLength - 2] = L'\n';
|
|
MessageLength --;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Output the message to stdout
|
|
//
|
|
|
|
if ( MessageLength > 0 ) {
|
|
HANDLE StdoutHandle = GetStdHandle( STD_OUTPUT_HANDLE );
|
|
|
|
if ( StdoutHandle != INVALID_HANDLE_VALUE ) {
|
|
DWORD CharsWritten;
|
|
|
|
if ( !WriteConsoleW( StdoutHandle,
|
|
Buffer,
|
|
MessageLength,
|
|
&CharsWritten,
|
|
NULL )) {
|
|
|
|
//
|
|
// Convert to ANSI before doing the write file
|
|
//
|
|
|
|
|
|
if ( NO_ERROR == OutputStringToAnsiInPlace( Buffer ) ) {
|
|
CharsWritten = strlen( (LPSTR) Buffer );
|
|
|
|
WriteFile( StdoutHandle,
|
|
Buffer,
|
|
CharsWritten,
|
|
&CharsWritten,
|
|
NULL );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Append a newline for formatting reasons
|
|
//
|
|
|
|
Buffer[0] = '\n';
|
|
WriteFile( StdoutHandle,
|
|
Buffer,
|
|
1,
|
|
&CharsWritten,
|
|
NULL );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Clean up before exitting
|
|
//
|
|
|
|
if ( lpSource != NULL ) {
|
|
FreeLibrary( lpSource );
|
|
}
|
|
|
|
if ( Buffer != NULL ) {
|
|
LocalFree( Buffer );
|
|
}
|
|
|
|
}
|
|
|
|
DWORD
|
|
CUseConnection::TestProvider(
|
|
const PROVIDER * pProvider
|
|
)
|
|
{
|
|
DWORD dwErr;
|
|
DWORD LocalProviderFlags;
|
|
BOOL DoCommandLinePrompting = FALSE;
|
|
|
|
//
|
|
// If the caller asked for CONNECT_COMMANDLINE,
|
|
// and the provider doesn't support it,
|
|
// this routine should take over responsibility for doing it.
|
|
//
|
|
|
|
LocalProviderFlags = _dwProviderFlags;
|
|
|
|
if ( (_dwProviderFlags & CONNECT_COMMANDLINE) != 0 &&
|
|
(pProvider->ConnectFlagCaps & CONNECT_COMMANDLINE) == 0 ) {
|
|
|
|
DoCommandLinePrompting = TRUE;
|
|
|
|
//
|
|
// Don't let the provider do GUI prompting.
|
|
//
|
|
|
|
_dwProviderFlags &= ~(CONNECT_INTERACTIVE | CONNECT_PROMPT | CONNECT_COMMANDLINE | CONNECT_CMD_SAVECRED);
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Try the connection once
|
|
// (Unless we're asked not to before prompting.)
|
|
//
|
|
|
|
if ( !DoCommandLinePrompting || (LocalProviderFlags & CONNECT_PROMPT) == 0 ) {
|
|
dwErr = TestProviderWorker( pProvider );
|
|
} else {
|
|
dwErr = ERROR_LOGON_FAILURE;
|
|
}
|
|
|
|
//
|
|
// If this routine is handling command line prompting,
|
|
// and the connection failed for authentication reasons,
|
|
// prompt for credentials here.
|
|
//
|
|
// Ignore COMMENT_COMMANDLINE_SAVECRED since we don't know what authentication mechanism
|
|
// the provider uses.
|
|
//
|
|
|
|
if ( DoCommandLinePrompting && CREDUI_IS_AUTHENTICATION_ERROR(dwErr) )
|
|
{
|
|
WCHAR szPassword[CREDUI_MAX_PASSWORD_LENGTH + 1]; // if the user needs to enter one
|
|
WCHAR szUserName[CREDUI_MAX_USERNAME_LENGTH + 1];
|
|
|
|
WCHAR szServer[CRED_MAX_DOMAIN_TARGET_NAME_LENGTH + 1];
|
|
|
|
//
|
|
// Display the reason for the failure with the full path name
|
|
//
|
|
|
|
DisplayFailureReason( dwErr, _ProviderNetResource.lpRemoteName );
|
|
|
|
//
|
|
// Default the user name to the one provided by the caller.
|
|
//
|
|
|
|
szUserName[0] = L'\0';
|
|
if ( _lpUserName != NULL ) {
|
|
if (wcslen(_lpUserName) > CREDUI_MAX_USERNAME_LENGTH) {
|
|
return WN_BAD_USER;
|
|
}
|
|
wcscpy(szUserName, _lpUserName);
|
|
}
|
|
|
|
//
|
|
// Prepare to use the credential manager user interface:
|
|
//
|
|
|
|
|
|
if (!InitCredUI(_ProviderNetResource.lpRemoteName,
|
|
szServer,
|
|
CRED_MAX_DOMAIN_TARGET_NAME_LENGTH))
|
|
{
|
|
dwErr = GetLastError();
|
|
}
|
|
else
|
|
{
|
|
DWORD dwCredErr;
|
|
DWORD dwAuthErr;
|
|
LPWSTR pszNewPassword;
|
|
DWORD dwCredUIFlags = 0;
|
|
|
|
//
|
|
// Remember the original failure reason
|
|
//
|
|
dwAuthErr = dwErr;
|
|
|
|
|
|
//
|
|
// Set the appropriate flag to set the behavior of the common UI.
|
|
//
|
|
|
|
// Ask to not save the credential
|
|
dwCredUIFlags |= CREDUI_FLAGS_DO_NOT_PERSIST |
|
|
CREDUI_FLAGS_GENERIC_CREDENTIALS;
|
|
|
|
// We don't (yet) know how to handle certificates
|
|
dwCredUIFlags |= CREDUI_FLAGS_EXCLUDE_CERTIFICATES;
|
|
|
|
// Ensure that the username syntax is correct (Not in MPR)
|
|
// dwCredUIFlags |= CREDUI_FLAGS_VALIDATE_USERNAME;
|
|
|
|
dwCredErr = CredUICmdLinePromptForCredentials(
|
|
szServer,
|
|
NULL,
|
|
dwAuthErr,
|
|
szUserName,
|
|
CREDUI_MAX_USERNAME_LENGTH,
|
|
szPassword,
|
|
CREDUI_MAX_PASSWORD_LENGTH,
|
|
NULL, // Creds not saved
|
|
dwCredUIFlags);
|
|
|
|
|
|
if (dwCredErr == ERROR_CANCELLED) {
|
|
dwErr = WN_CANCEL;
|
|
|
|
} else if ( dwCredErr == ERROR_SUCCESS ) {
|
|
LPCWSTR OldPassword;
|
|
|
|
//
|
|
// Use the returned username and password
|
|
//
|
|
|
|
_lpUserName = szUserName;
|
|
OldPassword = _lpPassword;
|
|
_lpPassword = szPassword;
|
|
|
|
//
|
|
// Try the connection one more time
|
|
//
|
|
|
|
dwErr = TestProviderWorker( pProvider );
|
|
|
|
if ( dwErr == NO_ERROR )
|
|
{
|
|
//
|
|
// If typed password is different than the one passed in,
|
|
// this is now an explicit password.
|
|
//
|
|
|
|
if ( OldPassword == NULL ||
|
|
wcscmp(OldPassword, _lpPassword) != 0 )
|
|
{
|
|
_ExplicitPassword = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// clear any password from memory
|
|
//
|
|
ZeroMemory(szPassword, sizeof(szPassword)) ;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Restore the flag to their initial value
|
|
//
|
|
_dwProviderFlags = LocalProviderFlags;
|
|
|
|
return dwErr;
|
|
}
|
|
|
|
|
|
DWORD
|
|
CUseConnection::GetResult()
|
|
{
|
|
DWORD status;
|
|
|
|
//
|
|
// If we're given a local name,
|
|
// check the current list of remembered drives in the registry
|
|
// to determine if the localName is already connected.
|
|
//
|
|
if (! IS_EMPTY_STRING(_lpNetResource->lpLocalName))
|
|
{
|
|
//
|
|
// If the local drive is already in the registry, and it is
|
|
// for a different connection than that specified in the
|
|
// lpNetResource, then indicate an error because the device
|
|
// is already remembered.
|
|
//
|
|
LPWSTR remoteName;
|
|
if (MprFindDriveInRegistry(_lpNetResource->lpLocalName, &remoteName))
|
|
{
|
|
if (remoteName != NULL)
|
|
{
|
|
if (STRICMP(_lpNetResource->lpRemoteName, remoteName)!=0)
|
|
{
|
|
LocalFree(remoteName);
|
|
return WN_DEVICE_ALREADY_REMEMBERED;
|
|
}
|
|
LocalFree(remoteName);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We modify some parameters before passing them to the provider
|
|
//
|
|
_ProviderNetResource = *_lpNetResource;
|
|
if (IS_EMPTY_STRING(_ProviderNetResource.lpLocalName))
|
|
{
|
|
_ProviderNetResource.lpLocalName = NULL;
|
|
}
|
|
_dwProviderFlags = _dwFlags & WNNC_CF_MAXIMUM;
|
|
|
|
//
|
|
// If we're not given a local name, but are explicitly asked to
|
|
// redirect a device, we must autopick one
|
|
//
|
|
if ((_dwFlags & CONNECT_REDIRECT) &&
|
|
(_ProviderNetResource.lpLocalName == NULL) )
|
|
{
|
|
status = _AutoPickedDevice.PickDevice();
|
|
|
|
if (status != WN_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
_ProviderNetResource.lpLocalName = _AutoPickedDevice.wszPickedName();
|
|
}
|
|
|
|
INIT_IF_NECESSARY(NOTIFIEE_LEVEL,status);
|
|
|
|
//
|
|
// Notify all interested parties that a connection is being made.
|
|
// (All providers are required to support deviceless connections, so
|
|
// we shouldn't get a case where a notifyee might reject a connection
|
|
// on the grounds that it has no local name, even though we would have
|
|
// auto-picked a local name later)
|
|
//
|
|
NOTIFYADD NotifyAdd;
|
|
NOTIFYINFO NotifyInfo;
|
|
|
|
NotifyInfo.dwNotifyStatus = NOTIFY_PRE;
|
|
NotifyInfo.dwOperationStatus= 0L;
|
|
NotifyInfo.lpContext = MprAllocConnectContext();
|
|
NotifyAdd.hwndOwner = _hwndOwner;
|
|
NotifyAdd.NetResource = *_lpNetResource;
|
|
NotifyAdd.dwAddFlags = _dwFlags;
|
|
|
|
__try
|
|
{
|
|
status = MprAddConnectNotify(&NotifyInfo, &NotifyAdd);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
status = GetExceptionCode();
|
|
if (status != EXCEPTION_ACCESS_VIOLATION)
|
|
{
|
|
MPR_LOG(ERROR,"WNetUseConnectionW: ConnectNotify, Unexpected "
|
|
"Exception %#lx\n",status);
|
|
}
|
|
status = WN_BAD_POINTER;
|
|
}
|
|
|
|
if (status != WN_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
|
|
do // Notification loop
|
|
{
|
|
//
|
|
// Let the base class try all providers and figure out the best error
|
|
// CRoutedOperation::GetResult calls INIT_IF_NECESSARY
|
|
//
|
|
status = CRoutedOperation::GetResult();
|
|
|
|
//
|
|
// Notify all interested parties of the status of the connection.
|
|
//
|
|
NotifyInfo.dwNotifyStatus = NOTIFY_POST;
|
|
NotifyInfo.dwOperationStatus = status;
|
|
}
|
|
while (MprAddConnectNotify(&NotifyInfo, &NotifyAdd) == WN_RETRY);
|
|
|
|
MprFreeConnectContext(NotifyInfo.lpContext);
|
|
|
|
//
|
|
// Write info to the informational parameters
|
|
// (lpAccessName, lpBufferSize, lpResult)
|
|
//
|
|
|
|
if (status == WN_MORE_DATA)
|
|
{
|
|
//
|
|
// This error must have come from CAutoPickedDevice::PickDevice,
|
|
// indicating that the access name buffer is too small
|
|
// (unless it came from a buggy provider)
|
|
//
|
|
ASSERT(_AutoPickedDevice.dwError() == WN_MORE_DATA);
|
|
if (ARGUMENT_PRESENT(_lpBufferSize))
|
|
{
|
|
*_lpBufferSize = _AutoPickedDevice.cchReqBufferSize();
|
|
}
|
|
}
|
|
else if (status == WN_SUCCESS &&
|
|
ARGUMENT_PRESENT(_lpAccessName))
|
|
{
|
|
LPWSTR pwszAccessName = _ProviderNetResource.lpLocalName;
|
|
if (pwszAccessName == NULL)
|
|
{
|
|
pwszAccessName = _ProviderNetResource.lpRemoteName;
|
|
}
|
|
|
|
if (*_lpBufferSize < wcslen(pwszAccessName)+1)
|
|
{
|
|
//
|
|
// We validated most of the cases up front, so the only way
|
|
// this could happen is if we auto-picked and got a local
|
|
// name that was longer than the remote name.
|
|
//
|
|
ASSERT(0);
|
|
*_lpBufferSize = wcslen(pwszAccessName)+1;
|
|
status = WN_MORE_DATA;
|
|
}
|
|
else
|
|
{
|
|
wcscpy(_lpAccessName, pwszAccessName);
|
|
if (ARGUMENT_PRESENT(_lpResult) &&
|
|
_ProviderNetResource.lpLocalName != NULL)
|
|
{
|
|
*_lpResult = CONNECT_LOCALDRIVE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (status == WN_SUCCESS)
|
|
{
|
|
ASSERT_INITIALIZED(NETWORK);
|
|
|
|
//
|
|
// If the connection was added successfully, then write the connection
|
|
// information to the registry to make it persistent.
|
|
// Note: Failure to write to the registry is ignored.
|
|
//
|
|
if ((_dwFlags & CONNECT_UPDATE_PROFILE) &&
|
|
!IS_EMPTY_STRING(_ProviderNetResource.lpLocalName))
|
|
{
|
|
BYTE ProviderFlags = 0;
|
|
|
|
//
|
|
// Get the username for the connection if the provider didn't report
|
|
// that default creds were used:
|
|
//
|
|
// 1. If the username is explicitly given, store the
|
|
// username returned by the provider
|
|
//
|
|
// 2. Otherwise, compare the username given by the
|
|
// provider with the default username. If they're
|
|
// different, store it. If they're the same, don't
|
|
//
|
|
// 3. If we can't get a username for whatever reason,
|
|
// use the username in _lpUserName (which will be
|
|
// NULL if no name was explicitly given)
|
|
//
|
|
|
|
if (LastProvider()->GetUser != NULL)
|
|
{
|
|
WCHAR wszUser[MAX_PATH + 1];
|
|
|
|
__try
|
|
{
|
|
DWORD cbBuffer = LENGTH(wszUser);
|
|
DWORD status2 = LastProvider()->GetUser(
|
|
_ProviderNetResource.lpLocalName,
|
|
wszUser,
|
|
&cbBuffer);
|
|
|
|
ASSERT(status2 != WN_MORE_DATA);
|
|
|
|
if (status2 == WN_SUCCESS)
|
|
{
|
|
//
|
|
// Step 1 -- is the username explicitly given?
|
|
//
|
|
if (ARGUMENT_PRESENT(_lpUserName))
|
|
{
|
|
MPR_LOG1(TRACE,
|
|
"Explicit username being saved -- %ws\n",
|
|
wszUser);
|
|
_lpUserName = wszUser;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Step 2 -- is the user the default user?
|
|
//
|
|
PUNICODE_STRING UserName;
|
|
PUNICODE_STRING DomainName;
|
|
NTSTATUS ntStatus = LsaGetUserName(&UserName, &DomainName);
|
|
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
if (MprUserNameMatch(DomainName, UserName, wszUser, TRUE))
|
|
{
|
|
MPR_LOG1(TRACE,
|
|
"User %ws is default user -- not saving username\n",
|
|
wszUser);
|
|
_lpUserName = NULL;
|
|
}
|
|
else
|
|
{
|
|
MPR_LOG1(TRACE,
|
|
"User %ws is not default user -- saving username\n",
|
|
wszUser);
|
|
_lpUserName = wszUser;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (status2 == WN_CONNECTED_OTHER_PASSWORD_DEFAULT)
|
|
{
|
|
//
|
|
// WN_CONNECTED_OTHER_PASSWORD_DEFAULT will be returned
|
|
// when user X mapped a drive as user Y and the credentials
|
|
// for user Y were stored in CredMan when the connection
|
|
// was made. Treat this as the default username so we don't
|
|
// explicitly write user Y's username into the registry
|
|
// (since CredMan will be unable to reestablish the connection
|
|
// automatically in that case).
|
|
//
|
|
|
|
_lpUserName = NULL;
|
|
}
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
MPR_LOG(ERROR,
|
|
"WNetUseConnectionW: exception %#lx from NPGetUser\n",
|
|
GetExceptionCode());
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get an 8-bit datum that the provider may want saved along
|
|
// with the connection. This will be passed back to the provider
|
|
// in NPAddConnection3 when the connection is restored.
|
|
// (Actually this is a hack for the NTLM provider to remember
|
|
// whether a connection is a DFS connection or a regular LM
|
|
// connection.)
|
|
//
|
|
if (LastProvider()->GetReconnectFlags != NULL)
|
|
{
|
|
// This is an internal entry point so we don't bother with
|
|
// try-except
|
|
DWORD status2 = LastProvider()->GetReconnectFlags(
|
|
_ProviderNetResource.lpLocalName,
|
|
&ProviderFlags
|
|
);
|
|
if (status2 != WN_SUCCESS)
|
|
{
|
|
ProviderFlags = 0;
|
|
}
|
|
|
|
MPR_LOG3(RESTORE, "%ws wants flags %#x saved for %ws\n",
|
|
LastProvider()->Resource.lpProvider,
|
|
ProviderFlags,
|
|
_ProviderNetResource.lpLocalName);
|
|
}
|
|
|
|
I_MprSaveConn(
|
|
HKEY_CURRENT_USER,
|
|
LastProvider()->Resource.lpProvider,
|
|
LastProvider()->Type,
|
|
_lpUserName,
|
|
_ProviderNetResource.lpLocalName,
|
|
_ProviderNetResource.lpRemoteName,
|
|
_ProviderNetResource.dwType,
|
|
ProviderFlags,
|
|
_ExplicitPassword ? DEFER_EXPLICIT_PASSWORD : ( _UsedDefaultCreds ? DEFER_DEFAULT_CRED : 0 )
|
|
);
|
|
}
|
|
|
|
//
|
|
// Notify the shell of the connection.
|
|
// (This will be replaced by real Plug'n'Play)
|
|
//
|
|
if( (g_LUIDDeviceMapsEnabled == TRUE) &&
|
|
(_ProviderNetResource.lpLocalName != NULL) )
|
|
{
|
|
// Use LUID broadcast mechanism
|
|
MprBroadcastDriveChange(
|
|
_ProviderNetResource.lpLocalName,
|
|
FALSE ); // not a Delete Message
|
|
}
|
|
else
|
|
{
|
|
MprNotifyShell(_ProviderNetResource.lpLocalName);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
DWORD
|
|
WNetUseConnectionW (
|
|
IN HWND hwndOwner,
|
|
IN LPNETRESOURCEW lpNetResource,
|
|
IN LPCWSTR lpPassword,
|
|
IN LPCWSTR lpUserName,
|
|
IN DWORD dwFlags,
|
|
OUT LPWSTR lpAccessName OPTIONAL,
|
|
IN OUT LPDWORD lpBufferSize OPTIONAL,
|
|
OUT LPDWORD lpResult OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function allows the caller to redirect (connect) a local device
|
|
to a network resource. It is similar to WNetAddConnection3, except
|
|
that it has the ability to auto-pick a local device to redirect.
|
|
It also returns additional information about the connection in the
|
|
lpResult parameter.
|
|
|
|
This API is used by the shell to make links to:
|
|
- Objects on networks that require a local device redirection
|
|
- Objects served by applications that require a local device redirection
|
|
(i.e. can't handle UNC paths)
|
|
|
|
This API must redirect a local device in any of the following conditions:
|
|
- The API is given a local device name to redirect
|
|
- The API is asked to redirect a local device, by the CONNECT_REDIRECT
|
|
flag
|
|
- The network provider requires a redirection
|
|
In the last 2 cases, if a local device name isn't given, the API must
|
|
auto-pick a local device name.
|
|
|
|
Arguments:
|
|
|
|
hwndOwner - A handle to a window which should be the owner for any
|
|
messages or dialogs that the network provider might display.
|
|
|
|
lpNetResource - This is a pointer to a network resource structure that
|
|
specifies the network resource to connect to. The following
|
|
fields must be set when making a connection, the others are ignored.
|
|
lpRemoteName
|
|
lpLocalName
|
|
lpProvider
|
|
dwType
|
|
|
|
lpPassword - Specifies the password to be used in making the connection.
|
|
The NULL value may be passed in to indicate use of the 'default'
|
|
password. An empty string may be used to indicate no password.
|
|
|
|
lpUserName- This specifies the username used to make the connection.
|
|
If NULL, the default username (currently logged on user) will be
|
|
applied. This is used when the user wishes to connect to a
|
|
resource, but has a different user name or account assigned to him
|
|
for that resource.
|
|
|
|
dwFlags - This is a bitmask which may have any of the following bits set:
|
|
CONNECT_UPDATE_PROFILE
|
|
|
|
lpAccessName - Points to a buffer to receive the name that can be used
|
|
to make system requests on the connection. This conversion is useful
|
|
when lpRemoteName is understood by the provider but not by the
|
|
system's name space, and when this API autopicks a local device.
|
|
If lpLocalName specifies a local device, then this buffer is optional,
|
|
and if specified will have the local device name copied into it.
|
|
Otherwise, if the network requires a local device redirection, or
|
|
CONNECT_REDIRECT is set, then this buffer is required and the
|
|
redirected local device is returned here. Otherwise, the name copied
|
|
into the buffer is that of a remote resource, and if specified, this
|
|
buffer must be at least as large as the string pointed to by
|
|
lpRemoteName.
|
|
|
|
lpBufferSize - Specifies the size, in characters, of the lpAccessName
|
|
buffer. If the API returns WN_MORE_DATA the required size will be
|
|
returned here.
|
|
|
|
lpResult - Pointer to a DWORD in which is returned additional information
|
|
about the connection. Currently has the following bit values:
|
|
CONNECT_LOCALDRIVE - If set, the connection was made using a local
|
|
device redirection. If lpAccessName points to a buffer then the
|
|
local device name is copied to the buffer.
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
CUseConnection UseConnection(hwndOwner, lpNetResource, lpPassword,
|
|
lpUserName, dwFlags, lpAccessName,
|
|
lpBufferSize, lpResult);
|
|
|
|
return (UseConnection.Perform(TRUE));
|
|
}
|
|
|
|
|
|
//===================================================================
|
|
// WNetCancelConnection2W
|
|
//===================================================================
|
|
|
|
class CCancelConnection2 : public CRoutedOperation
|
|
{
|
|
public:
|
|
CCancelConnection2(
|
|
LPCWSTR lpName,
|
|
DWORD dwFlags,
|
|
BOOL fForce
|
|
) :
|
|
CRoutedOperation(DBGPARM("CancelConnection2")
|
|
PROVIDERFUNC(CancelConnection)),
|
|
_lpName (lpName),
|
|
_dwFlags(dwFlags),
|
|
_fForce (fForce)
|
|
{ }
|
|
|
|
protected:
|
|
|
|
DWORD GetResult(); // overrides CRoutedOperation implementation
|
|
|
|
private:
|
|
|
|
LPCWSTR _lpName;
|
|
DWORD _dwFlags;
|
|
BOOL _fForce;
|
|
|
|
DECLARE_CROUTED
|
|
};
|
|
|
|
|
|
DWORD
|
|
CCancelConnection2::ValidateRoutedParameters(
|
|
LPCWSTR * ppProviderName,
|
|
LPCWSTR * ppRemoteName,
|
|
LPCWSTR * ppLocalName
|
|
)
|
|
{
|
|
if (((_dwFlags != 0) && (_dwFlags != CONNECT_UPDATE_PROFILE)) ||
|
|
wcslen(_lpName) == 0) // probe lpName
|
|
{
|
|
return WN_BAD_VALUE;
|
|
}
|
|
|
|
//
|
|
// Use the specified remote name as a hint to pick the provider, but not
|
|
// if it's a local device name
|
|
//
|
|
if (MprDeviceType(_lpName) != REDIR_DEVICE)
|
|
{
|
|
*ppRemoteName = _lpName;
|
|
}
|
|
|
|
*ppLocalName = _lpName;
|
|
|
|
return WN_SUCCESS;
|
|
}
|
|
|
|
|
|
DWORD
|
|
CCancelConnection2::TestProvider(
|
|
const PROVIDER * pProvider
|
|
)
|
|
{
|
|
ASSERT_INITIALIZED(NETWORK);
|
|
|
|
return (pProvider->CancelConnection((LPWSTR)_lpName, _fForce));
|
|
}
|
|
|
|
|
|
DWORD
|
|
CCancelConnection2::GetResult()
|
|
{
|
|
DWORD status;
|
|
|
|
NOTIFYCANCEL NotifyCancel;
|
|
NOTIFYINFO NotifyInfo;
|
|
|
|
INIT_IF_NECESSARY(NOTIFIEE_LEVEL, status);
|
|
|
|
//
|
|
// Notify all interested parties that a connection is being cancelled
|
|
//
|
|
NotifyInfo.dwNotifyStatus = NOTIFY_PRE;
|
|
NotifyInfo.dwOperationStatus= 0L;
|
|
NotifyInfo.lpContext = MprAllocConnectContext();
|
|
NotifyCancel.lpName = (LPWSTR)_lpName;
|
|
NotifyCancel.lpProvider = NULL;
|
|
NotifyCancel.dwFlags = _dwFlags;
|
|
NotifyCancel.fForce = _fForce;
|
|
|
|
__try
|
|
{
|
|
status = MprCancelConnectNotify(&NotifyInfo, &NotifyCancel);
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
status = GetExceptionCode();
|
|
if (status != EXCEPTION_ACCESS_VIOLATION)
|
|
{
|
|
MPR_LOG(ERROR,"WNetCancelConnection2W: ConnectNotify, Unexpected "
|
|
"Exception %#lx\n",status);
|
|
}
|
|
status = WN_BAD_POINTER;
|
|
}
|
|
|
|
if (status != WN_SUCCESS)
|
|
{
|
|
return status;
|
|
}
|
|
|
|
do // Notification loop
|
|
{
|
|
//
|
|
// Let the base class try all providers and figure out the best error
|
|
// CRoutedOperation::GetResult calls INIT_IF_NECESSARY
|
|
//
|
|
status = CRoutedOperation::GetResult();
|
|
|
|
//
|
|
// Map these errors to a friendlier one for this API
|
|
//
|
|
if (status == WN_BAD_NETNAME ||
|
|
status == ERROR_BAD_NETPATH ||
|
|
status == WN_BAD_LOCALNAME ||
|
|
status == WN_NO_NETWORK ||
|
|
status == WN_NO_NET_OR_BAD_PATH)
|
|
{
|
|
MPR_LOG2(ROUTE, "CCancelConnection2: remapping %ld to %ld\n",
|
|
status, WN_NOT_CONNECTED);
|
|
status = WN_NOT_CONNECTED;
|
|
}
|
|
|
|
//
|
|
// Notify all interested parties of the status of the connection.
|
|
//
|
|
NotifyInfo.dwNotifyStatus = NOTIFY_POST;
|
|
NotifyInfo.dwOperationStatus = status; // Is this the right status??
|
|
if (status == WN_SUCCESS)
|
|
{
|
|
NotifyCancel.lpProvider = LastProvider()->Resource.lpProvider;
|
|
}
|
|
}
|
|
while (MprCancelConnectNotify(&NotifyInfo, &NotifyCancel) == WN_RETRY);
|
|
|
|
MprFreeConnectContext(NotifyInfo.lpContext);
|
|
|
|
//
|
|
// Regardless of whether the connection was cancelled successfully,
|
|
// we still want to remove any connection information from the
|
|
// registry if told to do so (dwFlags has CONNECT_UPDATE_PROFILE set).
|
|
//
|
|
if (_dwFlags & CONNECT_UPDATE_PROFILE)
|
|
{
|
|
if (MprDeviceType((LPWSTR)_lpName) == REDIR_DEVICE)
|
|
{
|
|
if (MprFindDriveInRegistry((LPWSTR)_lpName,NULL))
|
|
{
|
|
//
|
|
// If the connection was found in the registry, we want to
|
|
// forget it and if no providers claimed responsibility,
|
|
// return success.
|
|
//
|
|
MprForgetRedirConnection((LPWSTR)_lpName);
|
|
if (status == WN_NOT_CONNECTED)
|
|
{
|
|
status = WN_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (status == WN_SUCCESS)
|
|
{
|
|
//
|
|
// Notify the shell of the disconnection.
|
|
//
|
|
if( (g_LUIDDeviceMapsEnabled == TRUE) && (_lpName != NULL) )
|
|
{
|
|
// Use LUID broadcast mechanism
|
|
MprBroadcastDriveChange(
|
|
_lpName,
|
|
TRUE ); // a Delete Message
|
|
}
|
|
else
|
|
{
|
|
MprNotifyShell(_lpName);
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
DWORD
|
|
WNetCancelConnection2W (
|
|
IN LPCWSTR lpName,
|
|
IN DWORD dwFlags,
|
|
IN BOOL fForce
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function breaks an existing network connection. The persistance
|
|
of the connection is determined by the dwFlags parameter.
|
|
|
|
Arguments:
|
|
|
|
lpName - The name of either the redirected local device, or the remote
|
|
network resource to disconnect from. In the former case, only the
|
|
redirection specified is broken. In the latter case, all
|
|
connections to the remote network resource are broken.
|
|
|
|
dwFlags - This is a bitmask which may have any of the following bits set:
|
|
CONNECT_UPDATE_PROFILE
|
|
|
|
fForce - Used to indicate if the disconnect should be done forcefully
|
|
in the event of open files or jobs on the connection. If FALSE is
|
|
specified, the call will fail if there are open files or jobs.
|
|
|
|
|
|
Return Value:
|
|
|
|
WN_SUCCESS - The call was successful. Otherwise, GetLastError should be
|
|
called for extended error information. Extended error codes include
|
|
the following:
|
|
|
|
WN_NOT_CONNECTED - lpName is not a redirected device. or not currently
|
|
connected to lpName.
|
|
|
|
WN_OPEN_FILES - There are open files and fForce was FALSE.
|
|
|
|
WN_EXTENDED_ERROR - A network specific error occured. WNetGetLastError
|
|
should be called to obtain a description of the error.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Check the providers
|
|
//
|
|
return MprCancelConnection2(lpName, dwFlags, fForce, TRUE);
|
|
}
|
|
|
|
|
|
DWORD
|
|
MprCancelConnection2 (
|
|
IN LPCWSTR lpName,
|
|
IN DWORD dwFlags,
|
|
IN BOOL fForce,
|
|
IN BOOL fCheckProviders
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wrapper to use the CCancelConnection2 class -- needed because otherwise,
|
|
WNetRestoreConnectionW could call WNetCancelConnection2W, causing a
|
|
deadlock on the provider lock.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
CCancelConnection2 CancelConnection2(lpName, dwFlags, fForce);
|
|
|
|
return (CancelConnection2.Perform(fCheckProviders));
|
|
}
|
|
|
|
|
|
DWORD
|
|
WNetCancelConnectionW (
|
|
IN LPCWSTR lpName,
|
|
IN BOOL fForce
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function breaks an existing network connection. The connection is
|
|
always made non-persistent.
|
|
|
|
Note that this is a stub routine that calls WNetCancelConnection2W and
|
|
is only provided for Win3.1 compatibility.
|
|
|
|
Arguments:
|
|
|
|
Parameters are the same as WNetCancelConnection2W
|
|
|
|
Return Value:
|
|
|
|
Same as WNetCancelConnection2W
|
|
|
|
|
|
--*/
|
|
{
|
|
return MprCancelConnection2( lpName, CONNECT_UPDATE_PROFILE, fForce, TRUE ) ;
|
|
}
|
|
|
|
DWORD
|
|
WNetGetConnectionW (
|
|
IN LPCWSTR lpLocalName,
|
|
OUT LPWSTR lpRemoteName,
|
|
IN OUT LPDWORD lpBufferSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
MprCheckProviders();
|
|
|
|
CProviderSharedLock PLock;
|
|
|
|
return MprGetConnection( lpLocalName, lpRemoteName, lpBufferSize, NULL ) ;
|
|
}
|
|
|
|
|
|
DWORD
|
|
MprGetConnection (
|
|
IN LPCWSTR lpLocalName,
|
|
OUT LPTSTR lpRemoteName,
|
|
IN OUT LPDWORD lpBufferSize,
|
|
OUT LPDWORD lpProviderIndex OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves the remote name associated with a device name and optionally
|
|
the provider index.
|
|
|
|
Behaviour is exactly the same as WNetGetConnectionW.
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD status = WN_SUCCESS;
|
|
LPDWORD indexArray;
|
|
DWORD localArray[DEFAULT_MAX_PROVIDERS];
|
|
DWORD numProviders;
|
|
LPPROVIDER provider;
|
|
DWORD statusFlag = 0; // used to indicate major error types
|
|
BOOL fcnSupported = FALSE; // Is fcn supported by a provider?
|
|
DWORD i;
|
|
DWORD dwFirstError = WN_SUCCESS;
|
|
DWORD dwFirstSignificantError = WN_SUCCESS;
|
|
|
|
//
|
|
// Validate the LocalName
|
|
//
|
|
__try {
|
|
if (MprDeviceType((LPWSTR) lpLocalName) != REDIR_DEVICE) {
|
|
status = WN_BAD_LOCALNAME;
|
|
}
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
status = GetExceptionCode();
|
|
if (status != EXCEPTION_ACCESS_VIOLATION) {
|
|
MPR_LOG(ERROR,"WNetGetConnection:Unexpected Exception 0x%lx\n",status);
|
|
}
|
|
status = WN_BAD_POINTER;
|
|
}
|
|
|
|
if (status != WN_SUCCESS) {
|
|
SetLastError(status);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Optimization: If the local name is a drive, load the providers
|
|
// only if it's a remote drive.
|
|
//
|
|
if (lpLocalName[1] == L':')
|
|
{
|
|
WCHAR wszRootPath[] = L" :\\";
|
|
UINT uDriveType;
|
|
|
|
wszRootPath[0] = lpLocalName[0];
|
|
uDriveType = GetDriveType(wszRootPath);
|
|
|
|
if (uDriveType == DRIVE_REMOVABLE ||
|
|
uDriveType == DRIVE_FIXED ||
|
|
uDriveType == DRIVE_CDROM ||
|
|
uDriveType == DRIVE_RAMDISK)
|
|
{
|
|
status = WN_NOT_CONNECTED;
|
|
SetLastError(status);
|
|
return status;
|
|
}
|
|
else if (uDriveType != DRIVE_REMOTE) {
|
|
|
|
//
|
|
// It's not a remote drive, but it's not one in hardware
|
|
// (either DRIVE_UNKNOWN or DRIVE_NO_ROOT_DIR). Since it
|
|
// might have a name associated with it in the registry
|
|
// (e.g., remembered connections), we have to load the
|
|
// providers and check if necessary (done in
|
|
// MprReadConnectionInformation)
|
|
//
|
|
status = WN_NOT_CONNECTED;
|
|
goto CheckRemembered;
|
|
}
|
|
}
|
|
|
|
INIT_IF_NECESSARY(NETWORK_LEVEL,status);
|
|
|
|
//
|
|
// Find the list of providers to call for this request.
|
|
//
|
|
indexArray = localArray;
|
|
|
|
status = MprFindCallOrder(
|
|
NULL,
|
|
&indexArray,
|
|
&numProviders,
|
|
NETWORK_TYPE);
|
|
|
|
if (status != WN_SUCCESS) {
|
|
SetLastError(status);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Loop through the list of providers until one answers the request,
|
|
// or the list is exhausted.
|
|
//
|
|
|
|
for (i=0; i<numProviders; i++) {
|
|
|
|
//
|
|
// Call the appropriate providers API entry point
|
|
//
|
|
provider = GlobalProviderInfo + indexArray[i];
|
|
|
|
if (provider->GetConnection != NULL) {
|
|
|
|
fcnSupported = TRUE;
|
|
|
|
__try {
|
|
//**************************************
|
|
// Actual call to Provider.
|
|
//**************************************
|
|
status = provider->GetConnection(
|
|
(LPWSTR) lpLocalName,
|
|
lpRemoteName,
|
|
lpBufferSize);
|
|
}
|
|
|
|
__except(EXCEPTION_EXECUTE_HANDLER) {
|
|
status = GetExceptionCode();
|
|
if (status != EXCEPTION_ACCESS_VIOLATION) {
|
|
MPR_LOG(ERROR,"WNetGetConnection:Unexpected Exception 0x%lx\n",status);
|
|
}
|
|
status = WN_BAD_POINTER;
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////
|
|
// //
|
|
// The following code attempts to give the user the //
|
|
// most sensible error message. We have 3 clasess of //
|
|
// errors. On first class we stop routing. On second //
|
|
// continue but ignore that error (not significant //
|
|
// because the provider didnt think it was his). On //
|
|
// the last the provider returned an interesting (or //
|
|
// significant) error. We still route, but remember //
|
|
// it so it takes precedence over the non significant //
|
|
// ones. //
|
|
// //
|
|
////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// Remember the first error, if it hasn't already been set
|
|
//
|
|
if (dwFirstError == WN_SUCCESS)
|
|
dwFirstError = status ;
|
|
|
|
//
|
|
// If the provider returns one of these errors, stop routing.
|
|
//
|
|
if ((status == WN_BAD_POINTER) ||
|
|
(status == WN_MORE_DATA) ||
|
|
(status == WN_SUCCESS))
|
|
{
|
|
//
|
|
// we either succeeded or have problems that means we
|
|
// should not continue (eg. bad input causing exception).
|
|
// and we make sure this is the error reported.
|
|
//
|
|
dwFirstError = status ;
|
|
dwFirstSignificantError = status ;
|
|
statusFlag = 0;
|
|
if ( lpProviderIndex != NULL ) {
|
|
*lpProviderIndex = indexArray[i];
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the provider returns one of these errors, continue
|
|
// trying other providers, but do not remember as a
|
|
// significant error, because the provider is probably not
|
|
// interested. StatusFlag is use to detect the case where
|
|
// a provider is not started.
|
|
//
|
|
else if (status == WN_NO_NETWORK)
|
|
{
|
|
statusFlag |= NO_NET;
|
|
}
|
|
else if ((status == WN_NOT_CONNECTED) ||
|
|
(status == WN_BAD_LOCALNAME))
|
|
{
|
|
//
|
|
// WN_NOT_CONNECTED means that lpLocalName is not a
|
|
// redirected device for this provider.
|
|
//
|
|
statusFlag |= BAD_NAME;
|
|
}
|
|
|
|
//
|
|
// If a provider returns one of these errors, we continue
|
|
// trying, but remember the error as a significant one. We
|
|
// report it to the user if it is the first. We do this so
|
|
// that if the first provider returns NotConnected and last
|
|
// returns BadPassword, we report the Password Error.
|
|
//
|
|
else
|
|
{
|
|
//
|
|
// All other errors are considered more significant
|
|
// than the ones above
|
|
//
|
|
if (!dwFirstSignificantError && status)
|
|
dwFirstSignificantError = status ;
|
|
|
|
statusFlag = OTHER_ERRS;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we failed, set final error, in order of importance.
|
|
// Significant errors take precedence. Otherwise we always
|
|
// report the first error.
|
|
//
|
|
|
|
if (status != WN_SUCCESS)
|
|
{
|
|
status = (dwFirstSignificantError != WN_SUCCESS) ?
|
|
dwFirstSignificantError :
|
|
dwFirstError ;
|
|
}
|
|
|
|
if (fcnSupported == FALSE) {
|
|
//
|
|
// No providers in the list support the API function. Therefore,
|
|
// we assume that no networks are installed.
|
|
//
|
|
status = WN_NOT_SUPPORTED;
|
|
}
|
|
|
|
//
|
|
// If memory was allocated by MprFindCallOrder, free it.
|
|
//
|
|
if (indexArray != localArray) {
|
|
LocalFree(indexArray);
|
|
}
|
|
|
|
//
|
|
// Handle special errors.
|
|
//
|
|
if (statusFlag == (NO_NET | BAD_NAME)) {
|
|
//
|
|
// Check to see if there was a mix of special errors that occured.
|
|
// If so, pass back the combined error message. Otherwise, let the
|
|
// last error returned get passed back.
|
|
//
|
|
status = WN_NO_NET_OR_BAD_PATH;
|
|
}
|
|
|
|
CheckRemembered:
|
|
|
|
//
|
|
// Handle normal errors passed back from the provider
|
|
//
|
|
if (status != WN_SUCCESS) {
|
|
|
|
if (status == WN_NOT_CONNECTED) {
|
|
//
|
|
// If not connected, but there is an entry for the LocalName
|
|
// in the registry, then return the remote name that was stored
|
|
// with it.
|
|
//
|
|
if (MprGetRemoteName(
|
|
(LPWSTR) lpLocalName,
|
|
lpBufferSize,
|
|
lpRemoteName,
|
|
&status)) {
|
|
|
|
if (status == WN_SUCCESS) {
|
|
status = WN_CONNECTION_CLOSED;
|
|
}
|
|
|
|
}
|
|
}
|
|
SetLastError(status);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
DWORD
|
|
WNetGetConnection2W (
|
|
IN LPWSTR lpLocalName,
|
|
OUT LPVOID lpBuffer,
|
|
IN OUT LPDWORD lpBufferSize
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Just like WNetGetConnectionW except this one returns the provider name
|
|
that the device is attached through
|
|
|
|
Arguments:
|
|
|
|
lpBuffer will contain a WNET_CONNECTIONINFO structure
|
|
lpBufferSize is the number of bytes required for the buffer
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD status = WN_SUCCESS ;
|
|
DWORD iProvider = 0 ;
|
|
DWORD nBytesNeeded = 0 ;
|
|
DWORD cchBuff = 0 ;
|
|
DWORD nTotalSize = *lpBufferSize ;
|
|
LPTSTR lpRemoteName= (LPTSTR) ((BYTE*) lpBuffer +
|
|
sizeof(WNET_CONNECTIONINFO)) ;
|
|
WNET_CONNECTIONINFO * pconninfo = (WNET_CONNECTIONINFO *) lpBuffer ;
|
|
|
|
//
|
|
// If they didn't pass in a big enough buffer for even the structure,
|
|
// then make the size zero (so the buffer isn't accessed at all) and
|
|
// let the API figure out the buffer size
|
|
//
|
|
|
|
if ( *lpBufferSize < sizeof( WNET_CONNECTIONINFO) ) {
|
|
*lpBufferSize = 0 ;
|
|
cchBuff = 0 ;
|
|
}
|
|
else {
|
|
//
|
|
// MprGetConnection is expecting character counts, so convert
|
|
// after offsetting into the structure (places remote name directly
|
|
// in the structure).
|
|
//
|
|
cchBuff = (*lpBufferSize - sizeof(WNET_CONNECTIONINFO))/sizeof(TCHAR) ;
|
|
}
|
|
|
|
MprCheckProviders();
|
|
|
|
CProviderSharedLock PLock;
|
|
|
|
status = MprGetConnection(
|
|
lpLocalName,
|
|
lpRemoteName,
|
|
&cchBuff,
|
|
&iProvider);
|
|
|
|
if ( status == WN_SUCCESS ||
|
|
status == WN_CONNECTION_CLOSED ||
|
|
status == WN_MORE_DATA ) {
|
|
//
|
|
// Now we need to determine the buffer requirements for the
|
|
// structure and provider name
|
|
//
|
|
// (Note that if MprGetConnection returns WN_CONNECTION_CLOSED, it
|
|
// does not touch the value of iProvider. So iProvider will retain
|
|
// its somewhat arbitrary initial value of 0, meaning the first
|
|
// provider in the array.)
|
|
//
|
|
|
|
LPTSTR lpProvider = GlobalProviderInfo[iProvider].Resource.lpProvider;
|
|
|
|
//
|
|
// Calculate the required buffer size.
|
|
//
|
|
|
|
nBytesNeeded = sizeof( WNET_CONNECTIONINFO ) +
|
|
(STRLEN( lpProvider) + 1) * sizeof(TCHAR);
|
|
|
|
if ( status == WN_MORE_DATA )
|
|
{
|
|
nBytesNeeded += cchBuff * sizeof(TCHAR);
|
|
}
|
|
else
|
|
{
|
|
nBytesNeeded += (STRLEN( lpRemoteName) + 1) * sizeof(TCHAR);
|
|
}
|
|
|
|
if ( nTotalSize < nBytesNeeded ) {
|
|
status = WN_MORE_DATA;
|
|
*lpBufferSize = nBytesNeeded;
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Place the provider name in the buffer and initialize the
|
|
// structure to point to the strings.
|
|
//
|
|
pconninfo->lpRemoteName = lpRemoteName ;
|
|
pconninfo->lpProvider = STRCPY( (LPTSTR)
|
|
((BYTE*) lpBuffer + sizeof(WNET_CONNECTIONINFO) +
|
|
(STRLEN( lpRemoteName ) + 1) * sizeof(TCHAR)),
|
|
lpProvider);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
//===================================================================
|
|
// WNetGetConnection3W
|
|
//===================================================================
|
|
|
|
class CGetConnection3 : public CRoutedOperation
|
|
{
|
|
public:
|
|
CGetConnection3(
|
|
LPCWSTR lpLocalName,
|
|
LPCWSTR lpProviderName,
|
|
DWORD dwLevel,
|
|
LPVOID lpBuffer,
|
|
LPDWORD lpBufferSize
|
|
) :
|
|
DBGPARM(CRoutedOperation("GetConnection3"))
|
|
_lpLocalName (lpLocalName ),
|
|
_lpProviderName(lpProviderName),
|
|
_dwLevel (dwLevel ),
|
|
_lpBuffer (lpBuffer ),
|
|
_lpBufferSize (lpBufferSize )
|
|
{ }
|
|
|
|
private:
|
|
|
|
LPCWSTR _lpLocalName;
|
|
LPCWSTR _lpProviderName;
|
|
DWORD _dwLevel;
|
|
LPVOID _lpBuffer;
|
|
LPDWORD _lpBufferSize;
|
|
|
|
DECLARE_CROUTED
|
|
};
|
|
|
|
|
|
DWORD
|
|
CGetConnection3::ValidateRoutedParameters(
|
|
LPCWSTR * ppProviderName,
|
|
LPCWSTR * ppRemoteName,
|
|
LPCWSTR * ppLocalName
|
|
)
|
|
{
|
|
if (_dwLevel != WNGC_INFOLEVEL_DISCONNECTED)
|
|
{
|
|
return WN_BAD_LEVEL;
|
|
}
|
|
|
|
if (MprDeviceType(_lpLocalName) != REDIR_DEVICE)
|
|
{
|
|
return WN_BAD_LOCALNAME;
|
|
}
|
|
|
|
if (IS_BAD_BYTE_BUFFER(_lpBuffer, _lpBufferSize))
|
|
{
|
|
return WN_BAD_POINTER;
|
|
}
|
|
|
|
//
|
|
// Set parameters used by base class. Note that we set *ppLocalName
|
|
// to NULL to prevent the base class from checking it for "localness"
|
|
// since this is a private API called by the shell for network drives
|
|
// only. If we ever want to check the local name, the line below will
|
|
// have to be changed to:
|
|
//
|
|
// *ppLocalName = _lpLocalName;
|
|
//
|
|
*ppProviderName = _lpProviderName;
|
|
*ppLocalName = NULL;
|
|
|
|
return WN_SUCCESS;
|
|
}
|
|
|
|
|
|
DWORD
|
|
CGetConnection3::TestProvider(
|
|
const PROVIDER * pProvider
|
|
)
|
|
{
|
|
ASSERT_INITIALIZED(NETWORK);
|
|
|
|
if (pProvider->GetConnection3 != NULL)
|
|
{
|
|
return ( pProvider->GetConnection3(
|
|
_lpLocalName,
|
|
_dwLevel,
|
|
_lpBuffer,
|
|
_lpBufferSize) );
|
|
}
|
|
else if (pProvider->GetConnection == NULL)
|
|
{
|
|
return WN_NOT_SUPPORTED;
|
|
}
|
|
else
|
|
{
|
|
// Just verify that the provider owns the connection, and if so,
|
|
// assume that it's not disconnected
|
|
WCHAR wszRemoteName[40];
|
|
DWORD nLength = LENGTH(wszRemoteName);
|
|
DWORD status = pProvider->GetConnection(
|
|
(LPWSTR)_lpLocalName,
|
|
wszRemoteName,
|
|
&nLength);
|
|
if (status == WN_SUCCESS || status == WN_MORE_DATA)
|
|
{
|
|
// The provider owns the connection
|
|
if (*_lpBufferSize < sizeof(WNGC_CONNECTION_STATE))
|
|
{
|
|
*_lpBufferSize = sizeof(WNGC_CONNECTION_STATE);
|
|
status = WN_MORE_DATA;
|
|
}
|
|
else
|
|
{
|
|
((LPWNGC_CONNECTION_STATE)_lpBuffer)->dwState =
|
|
WNGC_CONNECTED;
|
|
status = WN_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
}
|
|
|
|
|
|
DWORD APIENTRY
|
|
WNetGetConnection3W(
|
|
IN LPCWSTR lpLocalName,
|
|
IN LPCWSTR lpProviderName OPTIONAL,
|
|
IN DWORD dwLevel,
|
|
OUT LPVOID lpBuffer,
|
|
IN OUT LPDWORD lpBufferSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function returns miscellaneous information about a network
|
|
connection, as specified by the info level parameter.
|
|
|
|
Arguments:
|
|
|
|
lpLocalName - The name of a redirected local device for which
|
|
information is required.
|
|
|
|
lpProviderName - The name of the provider responsible for the connection,
|
|
if known.
|
|
|
|
dwLevel - Level of information required. Supported levels are:
|
|
|
|
1 - Determine whether the connection is currently disconnected.
|
|
|
|
lpBuffer - Buffer in which to return the information if the call is
|
|
successful. The format of the information returned is as follows,
|
|
depending on dwLevel:
|
|
|
|
Level 1 - A DWORD is returned whose value is one of the following:
|
|
WNGETCON_CONNECTED
|
|
WNGETCON_DISCONNECTED
|
|
|
|
lpBufferSize - On input, size of the buffer in bytes. If the buffer
|
|
is too small, the required size will be written here.
|
|
For level 1 the required size is sizeof(DWORD).
|
|
|
|
Return Value:
|
|
|
|
WN_SUCCESS - successful.
|
|
WN_MORE_DATA - buffer is too small.
|
|
WN_BAD_LOCALNAME - lpLocalName is not a valid device name.
|
|
WN_BAD_PROVIDER - lpProviderName is not a recognized provider name.
|
|
WN_NOT_CONNECTED - the device specified by lpLocalName is not redirected.
|
|
WN_CONNECTION_CLOSED - the device specified by lpLocalName is not
|
|
redirected, but is a remembered (unavailable) connection.
|
|
|
|
--*/
|
|
{
|
|
CGetConnection3 GetConnection3(lpLocalName,
|
|
lpProviderName,
|
|
dwLevel,
|
|
lpBuffer,
|
|
lpBufferSize);
|
|
|
|
DWORD status = GetConnection3.Perform(TRUE);
|
|
|
|
if (status == WN_NOT_CONNECTED &&
|
|
MprFindDriveInRegistry((LPWSTR)lpLocalName,NULL))
|
|
{
|
|
status = WN_CONNECTION_CLOSED;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
DWORD
|
|
WNetRestoreConnectionW(
|
|
IN HWND hWnd,
|
|
IN LPCWSTR lpDevice
|
|
)
|
|
{
|
|
return WNetRestoreConnection2W(hWnd, lpDevice, 0, NULL);
|
|
}
|
|
|
|
|
|
DWORD
|
|
WNetRestoreConnection2W(
|
|
IN HWND hWnd,
|
|
IN LPCWSTR lpDevice,
|
|
IN DWORD dwFlags,
|
|
OUT BOOL* pfReconnectFailed
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function create another thread which does the connection.
|
|
In the main thread it create a dialog window to monitor the state of
|
|
the connection.
|
|
|
|
Arguments:
|
|
|
|
hwnd - This is a window handle that may be used as owner of any
|
|
dialog brought up by MPR (eg. password prompt).
|
|
|
|
lpDevice - This may be NULL or may contain a device name such as
|
|
"x:". If NULL, all remembered connections are restored. Otherwise,
|
|
the remembered connection for the specified device, if any are
|
|
restored.
|
|
|
|
dwFlags - WNRC_NOUI specifies that no UI should be shown. Connections that
|
|
fail to restore will be reattempted at next logon.
|
|
|
|
Return Value:
|
|
--*/
|
|
{
|
|
DWORD status = WN_SUCCESS;
|
|
DWORD print_connect_status = WN_SUCCESS;
|
|
DWORD numSubKeys;
|
|
DWORD RegMaxWait = 0;
|
|
HANDLE hThread = NULL;
|
|
HANDLE ThreadID;
|
|
LPCONNECTION_INFO ConnectArray;
|
|
PARAMETERS *lpParams = NULL;
|
|
BOOL DontDefer = FALSE;
|
|
LONG fDoCleanup = FALSE;
|
|
|
|
if (pfReconnectFailed)
|
|
{
|
|
*pfReconnectFailed = FALSE;
|
|
}
|
|
|
|
//
|
|
// Check the registry to see if we need to restore connections or not,
|
|
// and whether we can defer them.
|
|
// This is done only if lpDevice is NULL (restoring all).
|
|
// (If a particular device is specified, it is always restored undeferred.)
|
|
//
|
|
// Interpretation of the RestoreConnection value is:
|
|
// 0 - don't restore connections (and ignore the DeferConnection value)
|
|
// default - restore connections
|
|
//
|
|
// Interpretation of the DeferConnection value is:
|
|
// 0 - don't defer any connections
|
|
// default - defer every connection that can be deferred
|
|
//
|
|
if ( lpDevice == NULL )
|
|
{
|
|
HKEY providerKeyHandle;
|
|
DWORD ValueType;
|
|
DWORD fRestoreConnection = TRUE;
|
|
DWORD Temp = sizeof( fRestoreConnection );
|
|
|
|
if( MprOpenKey( HKEY_LOCAL_MACHINE, // hKey
|
|
NET_PROVIDER_KEY, // lpSubKey
|
|
&providerKeyHandle, // Newly Opened Key Handle
|
|
DA_READ)) // Desired Access
|
|
{
|
|
if ( RegQueryValueEx(
|
|
providerKeyHandle,
|
|
RESTORE_CONNECTION_VALUE,
|
|
NULL,
|
|
&ValueType, // not used
|
|
(LPBYTE) &fRestoreConnection,
|
|
&Temp) == NO_ERROR )
|
|
{
|
|
if ( !fRestoreConnection )
|
|
{
|
|
MPR_LOG0(RESTORE, "Registry says NOT to restore connections\n");
|
|
RegCloseKey( providerKeyHandle );
|
|
return WN_SUCCESS;
|
|
}
|
|
}
|
|
|
|
DWORD fDeferConnection;
|
|
|
|
if (MprGetKeyDwordValue(
|
|
providerKeyHandle,
|
|
DEFER_CONNECTION_VALUE,
|
|
&fDeferConnection) &&
|
|
fDeferConnection == 0 )
|
|
{
|
|
MPR_LOG0(RESTORE, "Registry says NOT to defer restored connections\n");
|
|
DontDefer = TRUE;
|
|
}
|
|
|
|
RegCloseKey( providerKeyHandle );
|
|
}
|
|
}
|
|
|
|
__try {
|
|
if (lpDevice != NULL)
|
|
{
|
|
if (MprDeviceType (lpDevice) != REDIR_DEVICE)
|
|
{
|
|
status = WN_BAD_LOCALNAME;
|
|
}
|
|
}
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
status = GetExceptionCode();
|
|
if (status != EXCEPTION_ACCESS_VIOLATION)
|
|
{
|
|
MPR_LOG (ERROR, "WNetRestoreConnectionW:Unexpected Exception 0x%1x\n",status);
|
|
}
|
|
status = WN_BAD_POINTER;
|
|
}
|
|
if (status != WN_SUCCESS)
|
|
{
|
|
SetLastError(status);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// MprCreateConnectionArray may use the providers
|
|
//
|
|
|
|
MprCheckProviders();
|
|
|
|
{
|
|
CProviderSharedLock PLock;
|
|
|
|
//
|
|
// Read all the connection info from the registry.
|
|
//
|
|
status = MprCreateConnectionArray (&numSubKeys,
|
|
lpDevice,
|
|
&RegMaxWait,
|
|
&ConnectArray);
|
|
if (lpDevice == NULL)
|
|
{
|
|
//
|
|
// only wory about Print if restoring all
|
|
//
|
|
print_connect_status = MprAddPrintersToConnArray (&numSubKeys,
|
|
&ConnectArray);
|
|
}
|
|
|
|
//
|
|
// if both failed, report first error. else do the best we can.
|
|
//
|
|
if (status != WN_SUCCESS && print_connect_status != WN_SUCCESS)
|
|
{
|
|
SetLastError (status);
|
|
return status;
|
|
}
|
|
|
|
if (numSubKeys == 0)
|
|
{
|
|
return(WN_SUCCESS);
|
|
}
|
|
|
|
INIT_IF_NECESSARY(NETWORK_LEVEL,status);
|
|
|
|
//
|
|
// If there are no providers, return NO_NETWORK
|
|
//
|
|
if (GlobalNumActiveProviders == 0) {
|
|
SetLastError(WN_NO_NETWORK);
|
|
return(WN_NO_NETWORK);
|
|
}
|
|
|
|
//
|
|
// Refcount all the provider DLLs we may use since we may
|
|
// have to call DoProfileErrorDialog, which calls outside
|
|
// mpr.dll and can potentially loop back, causing deadlock.
|
|
// By refcounting here, we don't have to worry about
|
|
// releasing/reacquiring the lock or over-refcounting the
|
|
// provider DLLs later on.
|
|
//
|
|
|
|
MprRefcountConnectionArray(ConnectArray, numSubKeys);
|
|
}
|
|
|
|
// If lpDevice is not NULL, call MprRestoreThisConnection directly.
|
|
|
|
if (lpDevice)
|
|
{
|
|
status = MprRestoreThisConnection (hWnd, NULL, &ConnectArray[0], dwFlags);
|
|
ConnectArray[0].Status = status;
|
|
if ((status != WN_SUCCESS) &&
|
|
(status != WN_CANCEL) &&
|
|
(status != WN_CONTINUE))
|
|
{
|
|
if (!(dwFlags & WNRC_NOUI))
|
|
{
|
|
DoProfileErrorDialog (hWnd,
|
|
ConnectArray[0].NetResource.lpLocalName,
|
|
ConnectArray[0].NetResource.lpRemoteName,
|
|
ConnectArray[0].NetResource.lpProvider,
|
|
ConnectArray[0].Status,
|
|
FALSE, //No cancel button.
|
|
NULL,
|
|
NULL,
|
|
NULL); // no skip future errors checkbox
|
|
}
|
|
|
|
if (pfReconnectFailed)
|
|
{
|
|
*pfReconnectFailed = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else do // not a loop. error break out.
|
|
{
|
|
//
|
|
// Initialize lpParams.
|
|
//
|
|
lpParams = (PARAMETERS *) LocalAlloc (LPTR,
|
|
sizeof (PARAMETERS));
|
|
if ((lpParams == NULL) ||
|
|
(lpParams->hDlgCreated
|
|
= CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL ||
|
|
(lpParams->hDlgFailed
|
|
= CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL ||
|
|
(lpParams->hDonePassword
|
|
= CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL)
|
|
{
|
|
status = GetLastError();
|
|
|
|
if (lpParams != NULL)
|
|
{
|
|
if (lpParams->hDlgCreated != NULL)
|
|
CloseHandle(lpParams->hDlgCreated);
|
|
|
|
if (lpParams->hDlgFailed != NULL)
|
|
CloseHandle(lpParams->hDlgFailed);
|
|
|
|
if (lpParams->hDonePassword != NULL)
|
|
CloseHandle(lpParams->hDonePassword);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
lpParams->numSubKeys = numSubKeys;
|
|
lpParams->RegMaxWait = RegMaxWait;
|
|
lpParams->ConnectArray = ConnectArray;
|
|
lpParams->dwRestoreFlags = dwFlags;
|
|
lpParams->fReconnectFailed = FALSE;
|
|
|
|
//
|
|
// Decide whether to show the "Restoring Connections" dialog.
|
|
// In general, if a connection will be made as a user other than the
|
|
// default logged-on user, we need to give the user a chance to enter
|
|
// the password and have it validated, hence we need to contact the
|
|
// server, so we should show the dialog.
|
|
//
|
|
BOOL NeedDialog;
|
|
|
|
if (DontDefer)
|
|
{
|
|
NeedDialog = TRUE;
|
|
}
|
|
else
|
|
{
|
|
NeedDialog = FALSE;
|
|
|
|
//
|
|
// Get the default user and domain names to connect as
|
|
//
|
|
PUNICODE_STRING UserName;
|
|
PUNICODE_STRING DomainName;
|
|
NTSTATUS ntStatus = LsaGetUserName(&UserName, &DomainName);
|
|
if (NT_SUCCESS(ntStatus))
|
|
{
|
|
MPR_LOG2(RESTORE,"Default domain name = \"%ws\", user name = \"%ws\"\n",
|
|
DomainName->Buffer, UserName->Buffer);
|
|
}
|
|
else
|
|
{
|
|
MPR_LOG(ERROR, "LsaGetUserName failed, %#lx\n", ntStatus);
|
|
DomainName = NULL;
|
|
UserName = NULL;
|
|
}
|
|
|
|
//
|
|
// If the logon domain is the local machine, don't defer connections.
|
|
// This is a NT 4.0 workaround for the most common case of bug 36827.
|
|
// When connecting to an LM server, if the user name happens to
|
|
// match a local user name on the target server, the server will
|
|
// normally attempt to log on using THAT user's account; hence we
|
|
// need to prompt for the password. We need to do this since this
|
|
// behavior is by design in the redirector.
|
|
//
|
|
WCHAR ComputerName[MAX_COMPUTERNAME_LENGTH+1];
|
|
DWORD nSize = LENGTH(ComputerName);
|
|
if (DomainName == NULL ||
|
|
GetComputerName(ComputerName, &nSize) == FALSE ||
|
|
_wcsicmp(ComputerName, DomainName->Buffer) == 0)
|
|
{
|
|
MPR_LOG0(RESTORE, "Local logon, will not defer connections\n");
|
|
NeedDialog = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Prescan the connections to determine which ones we can
|
|
// defer and whether we need the reconnect dialog. If all
|
|
// connections are deferred, we do not bother with the dialog.
|
|
//
|
|
for (DWORD i = 0; i < numSubKeys; i++)
|
|
{
|
|
if (! ConnectArray[i].ContinueFlag)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// The DEFER_UNKNOWN flag means don't defer the connection
|
|
// on this logon, but try the default credentials and see
|
|
// if they work; if so, the connection can be deferred at
|
|
// subsequent logons.
|
|
//
|
|
// Don't defer the connection if a password was explicitly
|
|
// specified when the connection was made. This covers
|
|
// cases in which the redir won't send the default password
|
|
// to the server at connect time because the server doesn't
|
|
// support encrypted passwords.
|
|
//
|
|
if (! (ConnectArray[i].DeferFlags & DEFER_UNKNOWN)
|
|
&&
|
|
! (ConnectArray[i].DeferFlags & DEFER_EXPLICIT_PASSWORD)
|
|
&&
|
|
MprUserNameMatch(DomainName,
|
|
UserName,
|
|
ConnectArray[i].UserName,
|
|
FALSE))
|
|
{
|
|
//
|
|
// If the user name is the default user name, we can safely
|
|
// replace it with a NULL. (This is not only more optimal, but
|
|
// also required in order to work around a LM redir problem
|
|
// with the way credentials for deferred connections are stored.)
|
|
//
|
|
LocalFree(ConnectArray[i].UserName);
|
|
ConnectArray[i].UserName = NULL;
|
|
|
|
//
|
|
// It's OK to defer the connection iff the remembered user
|
|
// name matches the default user name and the provider
|
|
// supports deferred connections.
|
|
//
|
|
if ((ConnectArray[i].dwConnectCaps & WNNC_CON_DEFER)
|
|
&&
|
|
(ConnectArray[i].pfAddConnection3 != NULL))
|
|
{
|
|
//
|
|
// Defer was initialized to 0 when the array was
|
|
// allocated.
|
|
// Note that we don't defer if an lpDevice was supplied.
|
|
//
|
|
ConnectArray[i].Defer = TRUE;
|
|
}
|
|
}
|
|
|
|
if (! ConnectArray[i].Defer)
|
|
{
|
|
NeedDialog = TRUE;
|
|
}
|
|
} // for each connection
|
|
|
|
MprSortConnectionArray(ConnectArray, numSubKeys);
|
|
}
|
|
|
|
if (DomainName != NULL)
|
|
{
|
|
LsaFreeMemory(DomainName->Buffer);
|
|
LsaFreeMemory(DomainName);
|
|
}
|
|
if (UserName != NULL)
|
|
{
|
|
LsaFreeMemory(UserName->Buffer);
|
|
LsaFreeMemory(UserName);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If we are:
|
|
// USING DIALOGS WHEN RESTORING CONNECTIONS...
|
|
//
|
|
// This main thread will used to service a windows event loop
|
|
// by calling ShowReconnectDialog. Therefore, a new thread
|
|
// must be created to actually restore the connections. As we
|
|
// attempt to restore each connection, a message is posted in
|
|
// this event loop that causes it to put up a dialog that
|
|
// describes the connection and has a "cancel" option.
|
|
//
|
|
|
|
if (! NeedDialog)
|
|
{
|
|
lpParams->hDlg = INVALID_WINDOW_HANDLE;
|
|
// CODEWORK: We are using this INVALID_WINDOW_HANDLE to tell
|
|
// the various routines whether there are 2 threads or not.
|
|
// Instead we should add a new field, BOOL fSeparateThread,
|
|
// to PARAMETERS. This requires changing routines in mprui.dll
|
|
// as well as mpr.dll.
|
|
|
|
DoRestoreConnection(lpParams);
|
|
}
|
|
else
|
|
{
|
|
HANDLE hThreadToken;
|
|
|
|
// If the main thread is impersonating, we have to copy its token to
|
|
// the new thread that is being spawned so it runs in the correct context
|
|
|
|
if (!OpenThreadToken (GetCurrentThread(),
|
|
TOKEN_IMPERSONATE,
|
|
TRUE,
|
|
&hThreadToken)
|
|
&&
|
|
!OpenProcessToken(GetCurrentProcess(),
|
|
TOKEN_IMPERSONATE,
|
|
&hThreadToken))
|
|
{
|
|
//
|
|
// Couldn't open a token on the thread or the process
|
|
//
|
|
status = GetLastError();
|
|
}
|
|
else
|
|
{
|
|
// lpParams->hDlg was initialized to 0 by LocalAlloc
|
|
// and will be set to an HWND by ShowReconnectDialog
|
|
|
|
hThread = CreateThread (NULL,
|
|
0,
|
|
(LPTHREAD_START_ROUTINE) &DoRestoreConnection,
|
|
(LPVOID) lpParams,
|
|
CREATE_SUSPENDED,
|
|
(LPDWORD) &ThreadID);
|
|
if (hThread == NULL)
|
|
{
|
|
status = GetLastError();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Make sure the worker thread isn't killed if
|
|
// this thread finishes while it's wedged in a
|
|
// provider call (in MprRestoreThisConnection).
|
|
// The DLL is unloaded by DoRestoreConnection
|
|
//
|
|
lpParams->hDll = LoadLibraryEx( L"mpr.dll",
|
|
NULL,
|
|
LOAD_WITH_ALTERED_SEARCH_PATH );
|
|
|
|
if (lpParams->hDll == NULL) {
|
|
|
|
MPR_LOG1(RESTORE,
|
|
"LoadLibraryEx for mpr.dll FAILED %d\n",
|
|
GetLastError());
|
|
|
|
//
|
|
// This shouldn't happen since all we're
|
|
// doing is upping our refcount
|
|
//
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
//
|
|
// Assign the impersonation token to the
|
|
// thread and resume/start it
|
|
//
|
|
|
|
if (!SetThreadToken(&hThread, hThreadToken))
|
|
{
|
|
status = GetLastError();
|
|
TerminateThread(hThread, NO_ERROR);
|
|
CloseHandle(hThread);
|
|
}
|
|
else
|
|
{
|
|
ResumeThread(hThread);
|
|
CloseHandle(hThread);
|
|
|
|
status = ShowReconnectDialog(hWnd, lpParams);
|
|
}
|
|
|
|
if (status == WN_SUCCESS && lpParams->status != WN_CANCEL &&
|
|
lpParams->status != WN_SUCCESS )
|
|
{
|
|
SetLastError (lpParams->status);
|
|
}
|
|
|
|
if (lpParams->status != WN_CANCEL)
|
|
{
|
|
status = MprNotifyErrors (hWnd, ConnectArray, numSubKeys, dwFlags);
|
|
}
|
|
}
|
|
|
|
CloseHandle(hThreadToken);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Only if we make it to the end of the do-while loop do the thread
|
|
// parameters have to be cleaned up. Since either this thread or
|
|
// the worker thread might exit first (if the user hits Cancel and
|
|
// the worker thread is stuck in a provider call, this thread could
|
|
// exit first), the InterlockedExchange calls here and at the end
|
|
// of DoRestoreConnection ensure that the last thread leaving
|
|
// does the cleanup.
|
|
//
|
|
// NOTE: This relies on the fact that the only break out of
|
|
// this do-while occurs when LocalAlloc for lpParams FAILS.
|
|
// Adding new break statements may break this logic!
|
|
//
|
|
//
|
|
// Case 1: lpParams never gets allocated -- this bit of code
|
|
// never gets hit since we break out of the loop above
|
|
// and nobody cleans up (correctly)
|
|
//
|
|
// Case 2: CreateThread fails -- hThread is NULL, so this thread
|
|
// needs to clean up
|
|
//
|
|
// (CODEWORK -- should this thread call DoRestoreConnection
|
|
// directly in that case?)
|
|
//
|
|
// Case 3: The worker thread exits first -- it changes fDoCleanup
|
|
// to TRUE before it exits and this thread cleans up
|
|
//
|
|
// Case 4: This thread exits first (user cancels dialog) -- this
|
|
// thread changes fDoCleanup to TRUE and orphans the
|
|
// worker thread. When it finishes its provider call,
|
|
// it reads fDoCleanup and cleans up.
|
|
//
|
|
|
|
// Poll the worker thread's data to see if reconnect failed.
|
|
if (pfReconnectFailed)
|
|
{
|
|
*pfReconnectFailed = lpParams->fReconnectFailed;
|
|
}
|
|
|
|
fDoCleanup = (InterlockedExchange(&lpParams->fDoCleanup, TRUE)
|
|
||
|
|
hThread == NULL);
|
|
|
|
} while (FALSE);
|
|
|
|
//
|
|
// Is this thread supposed to clean up?
|
|
//
|
|
if (fDoCleanup)
|
|
{
|
|
MPR_LOG0(RESTORE, "Main thread will perform cleanup\n");
|
|
|
|
ASSERT(lpParams != NULL);
|
|
|
|
//
|
|
// Free up resources in preparation to return.
|
|
//
|
|
if (!CloseHandle(lpParams->hDlgCreated))
|
|
status = GetLastError();
|
|
|
|
if (!CloseHandle(lpParams->hDlgFailed))
|
|
status = GetLastError();
|
|
|
|
if (!CloseHandle(lpParams->hDonePassword))
|
|
status = GetLastError();
|
|
|
|
LocalFree(lpParams);
|
|
|
|
MprFreeConnectionArray(ConnectArray, numSubKeys);
|
|
}
|
|
|
|
// Send a notification about the new network drive(s).
|
|
if( (g_LUIDDeviceMapsEnabled == TRUE) && (lpDevice != NULL))
|
|
{
|
|
// Use LUID broadcast mechanism
|
|
MprBroadcastDriveChange(
|
|
lpDevice,
|
|
FALSE ); // not a Delete Message
|
|
}
|
|
else
|
|
{
|
|
MprNotifyShell(L" :");
|
|
}
|
|
|
|
if (status != WN_SUCCESS)
|
|
{
|
|
SetLastError(status);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
MprSortConnectionArray(
|
|
LPCONNECTION_INFO lpcConnectArray,
|
|
DWORD dwNumSubKeys
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function sorts the array of connections by placing the
|
|
connections that can be deferred before those that can't
|
|
|
|
Arguments:
|
|
|
|
lpcConnectArray -- The array of CONNECTION_INFO structures
|
|
|
|
dwNumSubKeys -- The number of structures in the array
|
|
|
|
Notes:
|
|
|
|
This sort is done to improve behavior at boot. Deferred connections are
|
|
faster and less error-prone than non-deferred connections and errors can
|
|
cause popups that allow the user to stop reconnecting drives, which leaves
|
|
the remaining drives in the annoying "Unavailable" state. Note that most
|
|
popups for drive reconnection at boot are for credentials, which is a popup
|
|
associated only with non-deferred connection. By reconnecting deferred
|
|
connections first, we avoid this problem.
|
|
|
|
--*/
|
|
{
|
|
INT nLow = 0;
|
|
INT nHigh = dwNumSubKeys - 1;
|
|
BOOL fSwap;
|
|
|
|
CONNECTION_INFO ciTemp;
|
|
|
|
do {
|
|
|
|
//
|
|
// Find the first non-deferred connection
|
|
//
|
|
while (nLow < (INT)dwNumSubKeys
|
|
&&
|
|
lpcConnectArray[nLow].Defer) {
|
|
|
|
nLow++;
|
|
}
|
|
|
|
//
|
|
// Find the last deferred connection
|
|
//
|
|
while (nHigh >= 0
|
|
&&
|
|
!lpcConnectArray[nHigh].Defer) {
|
|
|
|
nHigh--;
|
|
}
|
|
|
|
fSwap = (nLow < nHigh);
|
|
|
|
//
|
|
// Only swap if the pointers haven't crossed
|
|
// (otherwise we'd be undoing the sorting)
|
|
//
|
|
if (fSwap) {
|
|
|
|
ciTemp = lpcConnectArray[nLow];
|
|
lpcConnectArray[nLow++] = lpcConnectArray[nHigh];
|
|
lpcConnectArray[nHigh--] = ciTemp;
|
|
}
|
|
}
|
|
while (fSwap);
|
|
|
|
#if DBG
|
|
|
|
MPR_LOG0(RESTORE, "Order of sorted connection array is as follows:\n");
|
|
|
|
for (UINT i = 0; i < dwNumSubKeys; i++) {
|
|
|
|
MPR_LOG3(RESTORE,
|
|
"\tLocal: %ws, \tRemote: %ws, \tDefer: %d\n",
|
|
lpcConnectArray[i].NetResource.lpLocalName,
|
|
lpcConnectArray[i].NetResource.lpRemoteName,
|
|
lpcConnectArray[i].Defer);
|
|
}
|
|
|
|
#endif // DBG
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
MprRefcountConnectionArray(
|
|
LPCONNECTION_INFO lpcConnectArray,
|
|
DWORD dwNumSubkeys
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function sorts refcounts the provider DLLs in the array
|
|
of connections in preparation for a call outside of mpr.dll
|
|
(which requires releasing the provider lock to avoid the
|
|
possibility of reentrancy and deadlock)
|
|
|
|
Arguments:
|
|
|
|
lpcConnectArray -- The array of CONNECTION_INFO structures
|
|
|
|
dwNumSubKeys -- The number of structures in the array
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
UINT i;
|
|
LPPROVIDER lpProvider;
|
|
|
|
ASSERT_INITIALIZED(NETWORK);
|
|
|
|
for (i = 0; i < dwNumSubkeys; i++)
|
|
{
|
|
//
|
|
// If this hits, we're over-refcounting
|
|
//
|
|
ASSERT(lpcConnectArray[i].hProviderDll == NULL);
|
|
|
|
lpProvider = &GlobalProviderInfo[lpcConnectArray[i].ProviderIndex];
|
|
|
|
lpcConnectArray[i].hProviderDll = LoadLibraryEx(lpProvider->DllName,
|
|
NULL,
|
|
LOAD_WITH_ALTERED_SEARCH_PATH);
|
|
|
|
if (lpcConnectArray[i].hProviderDll != NULL)
|
|
{
|
|
lpcConnectArray[i].pfGetCaps = lpProvider->GetCaps;
|
|
lpcConnectArray[i].pfAddConnection3 = lpProvider->AddConnection3;
|
|
lpcConnectArray[i].pfAddConnection = lpProvider->AddConnection;
|
|
lpcConnectArray[i].pfCancelConnection = lpProvider->CancelConnection;
|
|
lpcConnectArray[i].dwConnectCaps = lpProvider->ConnectCaps;
|
|
}
|
|
else
|
|
{
|
|
MPR_LOG2(ERROR,
|
|
"MprRefcountConnectionArray: LoadLibraryEx on %ws failed %d\n",
|
|
lpProvider->DllName,
|
|
GetLastError());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
DWORD
|
|
WNetSetConnectionW(
|
|
IN LPCWSTR lpName,
|
|
IN DWORD dwProperty,
|
|
IN LPVOID pvValue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function changes the characteristics of a network connection.
|
|
|
|
Arguments:
|
|
|
|
lpName - The name of either the redirected local device or a remote
|
|
network resource.
|
|
|
|
dwProperty - Identifies the property to be changed.
|
|
Current properties supported:
|
|
NETPROPERTY_PERSISTENT - pvValue points to a DWORD. If TRUE,
|
|
the connection is made persistent. If FALSE, the connection
|
|
is made non-persistent.
|
|
|
|
pvValue - Pointer to the property value. Type depends on dwProperty.
|
|
|
|
Return Value:
|
|
|
|
WN_SUCCESS - successful
|
|
|
|
WN_BAD_LOCALNAME or WN_NOT_CONNECTED - lpName is not a redirected device
|
|
|
|
--*/
|
|
{
|
|
DWORD status = WN_SUCCESS;
|
|
|
|
if (!(ARGUMENT_PRESENT(lpName) &&
|
|
ARGUMENT_PRESENT(pvValue)))
|
|
{
|
|
SetLastError(WN_BAD_POINTER);
|
|
return(WN_BAD_POINTER);
|
|
}
|
|
|
|
// NPGetUser and some Mpr internal functions use lpName as a non-const
|
|
// argument, so we have to make a copy
|
|
WCHAR * lpNameCopy = (WCHAR *) LocalAlloc(LMEM_FIXED, WCSSIZE(lpName));
|
|
if (lpNameCopy == NULL)
|
|
{
|
|
SetLastError(WN_OUT_OF_MEMORY);
|
|
return(WN_OUT_OF_MEMORY);
|
|
}
|
|
wcscpy(lpNameCopy, lpName);
|
|
|
|
switch (dwProperty)
|
|
{
|
|
case NETPROPERTY_PERSISTENT:
|
|
{
|
|
MprCheckProviders();
|
|
|
|
CProviderSharedLock PLock;
|
|
|
|
__try
|
|
{
|
|
//
|
|
// Value should be a boolean DWORD telling whether to
|
|
// remember or forget the connection
|
|
//
|
|
BOOL bRemember = * ((DWORD *) pvValue);
|
|
|
|
//
|
|
// Verify that lpName is a redirected local device, and
|
|
// identify the provider responsible
|
|
//
|
|
WCHAR wszRemoteName[MAX_PATH+1];
|
|
DWORD cbBuffer = sizeof(wszRemoteName);
|
|
DWORD iProvider; // provider index
|
|
|
|
status = MprGetConnection(
|
|
lpName,
|
|
wszRemoteName,
|
|
&cbBuffer,
|
|
&iProvider);
|
|
|
|
if (status == WN_CONNECTION_CLOSED)
|
|
{
|
|
//
|
|
// It's already a remembered connection. Nothing to do.
|
|
//
|
|
status = WN_SUCCESS;
|
|
__leave;
|
|
}
|
|
|
|
if (status != WN_SUCCESS)
|
|
{
|
|
ASSERT(status != WN_MORE_DATA);
|
|
__leave;
|
|
}
|
|
|
|
if (bRemember)
|
|
{
|
|
//
|
|
// Get the username for the connection from the provider
|
|
//
|
|
WCHAR wszUser[MAX_PATH+1];
|
|
cbBuffer = LENGTH(wszUser);
|
|
status = GlobalProviderInfo[iProvider].GetUser(
|
|
lpNameCopy, wszUser, &cbBuffer);
|
|
|
|
if (status != WN_SUCCESS)
|
|
{
|
|
ASSERT(status != WN_MORE_DATA);
|
|
__leave;
|
|
}
|
|
|
|
//
|
|
// Get the provider flags, if any
|
|
//
|
|
ASSERT_INITIALIZED(NETWORK);
|
|
|
|
BYTE ProviderFlags = 0;
|
|
if (GlobalProviderInfo[iProvider].GetReconnectFlags != NULL)
|
|
{
|
|
// This is an internal entry point so we don't bother with
|
|
// try-except
|
|
DWORD status2 = GlobalProviderInfo[iProvider].GetReconnectFlags(
|
|
lpNameCopy,
|
|
&ProviderFlags
|
|
);
|
|
if (status2 != WN_SUCCESS)
|
|
{
|
|
ProviderFlags = 0;
|
|
}
|
|
MPR_LOG3(RESTORE, "%ws wants flags %#x saved for %ws\n",
|
|
GlobalProviderInfo[iProvider].Resource.lpProvider,
|
|
ProviderFlags,
|
|
lpNameCopy);
|
|
}
|
|
|
|
//
|
|
// Make the connection persistent
|
|
//
|
|
status = I_MprSaveConn(
|
|
HKEY_CURRENT_USER,
|
|
GlobalProviderInfo[iProvider].Resource.lpProvider,
|
|
GlobalProviderInfo[iProvider].Type,
|
|
wszUser,
|
|
lpNameCopy,
|
|
wszRemoteName,
|
|
(wcslen(lpName) == 2 && lpName[1] == L':')
|
|
? RESOURCETYPE_DISK
|
|
: RESOURCETYPE_PRINT,
|
|
ProviderFlags,
|
|
DEFER_UNKNOWN
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Make the connection non-persistent
|
|
//
|
|
MprForgetRedirConnection(lpNameCopy);
|
|
status = WN_SUCCESS;
|
|
}
|
|
}
|
|
__except (EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
status = WN_BAD_POINTER;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
status = WN_BAD_VALUE;
|
|
}
|
|
|
|
LocalFree(lpNameCopy);
|
|
|
|
if (status != WN_SUCCESS)
|
|
{
|
|
SetLastError(status);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
typedef LRESULT WINAPI FN_PostMessage( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam) ;
|
|
#ifdef UNICODE
|
|
#define USER32_DLL_NAME L"user32.dll"
|
|
#define POST_MSG_API_NAME "PostMessageW"
|
|
#else
|
|
#define USER32_DLL_NAME "user32.dll"
|
|
#define POST_MSG_API_NAME "PostMessageA"
|
|
#endif
|
|
|
|
static HMODULE _static_hUser32 = NULL;
|
|
static FN_PostMessage * _static_pfPostMsg = NULL;
|
|
|
|
|
|
VOID
|
|
DoRestoreConnection(
|
|
PARAMETERS *Params
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function is run as a separate thread from the main thread (which
|
|
services a windows event loop). It attempts to restore all connections
|
|
that were saved in the registry for this user.
|
|
|
|
For each connection that we try to restore, a message is posted to
|
|
the main thread that causes it to put up a dialog box which describes
|
|
the connection and allows the user the option of cancelling.
|
|
|
|
If a longer timeout than the default is desired, this function will
|
|
look in the following location in the registry for a timeout value:
|
|
|
|
\HKEY_LOCAL_MACHINE\system\CurrentControlSet\Control\NetworkProvider
|
|
RestoreTimeout = REG_DWORD ??
|
|
--*/
|
|
{
|
|
DWORD status = WN_SUCCESS;
|
|
DWORD providerIndex=0;
|
|
DWORD Temp;
|
|
DWORD numSubKeys = Params->numSubKeys;
|
|
DWORD MaxWait = 0; // timeout interval
|
|
DWORD RegMaxWait = Params->RegMaxWait; // timeout interval stored in registry.
|
|
DWORD ElapsedTime; // interval between start and current time.
|
|
DWORD CurrentTime; // Current ClockTick time
|
|
DWORD StartTime;
|
|
DWORD i;
|
|
BOOL UserCancelled = FALSE;
|
|
BOOL ContinueFlag;
|
|
BOOL fDisconnect = FALSE;
|
|
LPCONNECTION_INFO ConnectArray = Params->ConnectArray;
|
|
HANDLE lpHandles[2];
|
|
DWORD dwSleepTime = RECONNECT_SLEEP_INCREMENT ;
|
|
DWORD j;
|
|
DWORD numStillMustContinue;
|
|
DWORD dwValue;
|
|
DWORD RestoredDrivesMask = 0;
|
|
DWORD CurrDriveMask;
|
|
LPWSTR lpLocalName;
|
|
WCHAR DriveLetterName[3]; // "<drive letter>:<NULL>"
|
|
HINSTANCE hDll = Params->hDll; // This needs to be checked
|
|
// Params is freed
|
|
|
|
//
|
|
// Don't check providers here as this isn't a top-level WNet API.
|
|
// No need to grab the shared lock here because the ConnectArray
|
|
// already contains all the entrypoints we need and the provider
|
|
// DLLs have all been refcounted.
|
|
//
|
|
|
|
lpHandles[0] = Params->hDlgCreated;
|
|
lpHandles[1] = Params->hDlgFailed;
|
|
|
|
StartTime = GetTickCount();
|
|
|
|
if (RegMaxWait != 0)
|
|
{
|
|
MaxWait = RegMaxWait;
|
|
}
|
|
|
|
if ( _static_pfPostMsg == NULL )
|
|
{
|
|
MprEnterLoadLibCritSect();
|
|
|
|
if (_static_hUser32 = LoadLibraryEx(USER32_DLL_NAME,
|
|
NULL,
|
|
LOAD_WITH_ALTERED_SEARCH_PATH))
|
|
{
|
|
_static_pfPostMsg = (FN_PostMessage *) GetProcAddress( _static_hUser32,
|
|
POST_MSG_API_NAME );
|
|
}
|
|
|
|
MprLeaveLoadLibCritSect();
|
|
|
|
if ( _static_pfPostMsg == NULL )
|
|
{
|
|
Params->status = GetLastError();
|
|
goto CleanExit;
|
|
}
|
|
}
|
|
|
|
if(Params->hDlg == INVALID_WINDOW_HANDLE)
|
|
{
|
|
dwValue = 0;
|
|
}
|
|
else
|
|
{
|
|
dwValue = WaitForMultipleObjects (2, lpHandles, FALSE, INFINITE);
|
|
}
|
|
|
|
switch(dwValue)
|
|
{
|
|
case 1: // hDlgFailed signaled. Info dialog failed to construct.
|
|
break;
|
|
|
|
case 0: // hDlgCreated signaled. Info dialog constructed successfully.
|
|
|
|
MPR_LOG0(RESTORE,"Enter Loop where we will attempt to restore connections\n");
|
|
|
|
do
|
|
{
|
|
//
|
|
// If HideErrors becomes TRUE, stop displaying error dialogs.
|
|
//
|
|
BOOL fHideErrors = (Params->dwRestoreFlags & WNRC_NOUI) ? TRUE : FALSE;
|
|
|
|
//
|
|
// Each time we go through all the connections, we need to
|
|
// reset the continue flag to FALSE.
|
|
//
|
|
ContinueFlag = FALSE;
|
|
|
|
//
|
|
// Go through each connection information in the array.
|
|
//
|
|
for (i = 0; i < numSubKeys; i++) {
|
|
|
|
// Let dialog thread print out the current connection.
|
|
if(Params->hDlg != INVALID_WINDOW_HANDLE)
|
|
{
|
|
(*_static_pfPostMsg) (Params->hDlg,
|
|
SHOW_CONNECTION,
|
|
(WPARAM) ConnectArray[i].NetResource.lpRemoteName,
|
|
0);
|
|
}
|
|
|
|
//
|
|
// Setting status to SUCCESS forces us down the success path
|
|
// if we are not to continue adding this connection.
|
|
//
|
|
status = WN_SUCCESS;
|
|
|
|
if (ConnectArray[i].ContinueFlag)
|
|
{
|
|
status = MprRestoreThisConnection (NULL,
|
|
Params,
|
|
&(ConnectArray[i]),
|
|
Params->dwRestoreFlags);
|
|
}
|
|
|
|
switch (status)
|
|
{
|
|
case WN_SUCCESS:
|
|
ConnectArray[i].Status = WN_SUCCESS;
|
|
ConnectArray[i].ContinueFlag = FALSE;
|
|
|
|
if( g_LUIDDeviceMapsEnabled == TRUE )
|
|
{
|
|
lpLocalName = ConnectArray[i].NetResource.lpLocalName;
|
|
if( lpLocalName != NULL &&
|
|
wcslen(lpLocalName) == 2 &&
|
|
lpLocalName[1] == L':' &&
|
|
iswalpha(lpLocalName[0]) )
|
|
{
|
|
DriveLetterName[0] = RtlUpcaseUnicodeChar( lpLocalName[0] );
|
|
RestoredDrivesMask |= 1<<(DriveLetterName[0] - L'A');
|
|
}
|
|
}
|
|
break;
|
|
|
|
case WN_CONTINUE:
|
|
break;
|
|
|
|
case WN_NO_NETWORK:
|
|
case WN_FUNCTION_BUSY:
|
|
|
|
//
|
|
// If this is the first pass through, we don't have
|
|
// the wait times figured out for each provider. Do that
|
|
// now.
|
|
//
|
|
|
|
if (ConnectArray[i].ProviderWait == 0)
|
|
{
|
|
MPR_LOG0(RESTORE,"Ask provider how long it will take "
|
|
"for the net to come up\n");
|
|
|
|
Temp = ConnectArray[i].pfGetCaps(WNNC_START);
|
|
|
|
MPR_LOG2(RESTORE,"GetCaps(START) for Provider %ws yields %d\n",
|
|
ConnectArray[i].NetResource.lpProvider,
|
|
Temp)
|
|
|
|
switch (Temp)
|
|
{
|
|
case PROVIDER_WILL_NOT_START:
|
|
MPR_LOG0(RESTORE,"Provider will not start\n");
|
|
ConnectArray[i].ContinueFlag = FALSE;
|
|
ConnectArray[i].Status = status;
|
|
break;
|
|
|
|
case NO_TIME_ESTIMATE:
|
|
MPR_LOG0(RESTORE,"Provider doesn't have time estimate\n");
|
|
|
|
if (RegMaxWait != 0) {
|
|
ConnectArray[i].ProviderWait = RegMaxWait;
|
|
}
|
|
else {
|
|
ConnectArray[i].ProviderWait = DEFAULT_WAIT_TIME;
|
|
}
|
|
if (MaxWait < ConnectArray[i].ProviderWait) {
|
|
MaxWait = ConnectArray[i].ProviderWait;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
MPR_LOG1(RESTORE,"Provider says it will take %d msec\n",
|
|
Temp);
|
|
if ((Temp <= MAX_ALLOWED_WAIT_TIME) && (Temp > MaxWait))
|
|
{
|
|
MaxWait = Temp;
|
|
}
|
|
ConnectArray[i].ProviderWait = MaxWait;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the status for this provider has just changed to
|
|
// WN_FUNCTION_BUSY from some other status, then calculate
|
|
// a timeout time by getting the provider's new timeout
|
|
// and adding that to the elapsed time since start. This
|
|
// gives a total elapsed time until timeout - which can
|
|
// be compared with the current MaxWait.
|
|
//
|
|
if ((status == WN_FUNCTION_BUSY) &&
|
|
(ConnectArray[i].Status == WN_NO_NETWORK))
|
|
{
|
|
Temp = ConnectArray[i].pfGetCaps(WNNC_START);
|
|
|
|
MPR_LOG2(RESTORE,"Changed from NO_NET to BUSY\n"
|
|
"\tGetCaps(START) for Provider %ws yields %d\n",
|
|
ConnectArray[i].NetResource.lpProvider,
|
|
Temp);
|
|
|
|
switch (Temp)
|
|
{
|
|
case PROVIDER_WILL_NOT_START:
|
|
//
|
|
// This is bizzare to find the status = BUSY,
|
|
// but to have the Provider not starting.
|
|
//
|
|
ConnectArray[i].ContinueFlag = FALSE;
|
|
break;
|
|
|
|
case NO_TIME_ESTIMATE:
|
|
//
|
|
// No need to alter the timeout for this one.
|
|
//
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// Make sure this new timeout information will take
|
|
// less than the maximum allowed time from providers.
|
|
//
|
|
if (Temp <= MAX_ALLOWED_WAIT_TIME)
|
|
{
|
|
CurrentTime = GetTickCount();
|
|
|
|
//
|
|
// Determine how much time has elapsed since
|
|
// we started.
|
|
//
|
|
ElapsedTime = CurrentTime - StartTime;
|
|
|
|
//
|
|
// Add the Elapsed time to the new timeout we
|
|
// just received from the provider to come up
|
|
// with a timeout value that can be compared
|
|
// with MaxWait.
|
|
//
|
|
Temp += ElapsedTime;
|
|
|
|
//
|
|
// If the new timeout is larger that MaxWait,
|
|
// then use the new timeout.
|
|
//
|
|
if (Temp > MaxWait)
|
|
{
|
|
MaxWait = Temp;
|
|
}
|
|
}
|
|
} // End Switch(Temp)
|
|
} // End If (change state from NO_NET to BUSY)
|
|
|
|
//
|
|
// Store the status (either NO_NET or BUSY) with the
|
|
// connect info.
|
|
//
|
|
|
|
if (ConnectArray[i].ContinueFlag)
|
|
{
|
|
ConnectArray[i].Status = status;
|
|
break;
|
|
}
|
|
|
|
case WN_CANCEL:
|
|
ConnectArray[i].Status = status;
|
|
default:
|
|
//
|
|
// For any other error, call the Error Dialog
|
|
//
|
|
|
|
Params->fReconnectFailed = TRUE;
|
|
|
|
if (fHideErrors) {
|
|
fDisconnect = FALSE;
|
|
} else {
|
|
//
|
|
// Count the number of connections which have not
|
|
// been resolved. If there is exactly one, do not
|
|
// give the user the option to cancel.
|
|
//
|
|
numStillMustContinue = 0;
|
|
|
|
for (j = 0; j < numSubKeys; j++) {
|
|
if (ConnectArray[j].ContinueFlag) {
|
|
numStillMustContinue++;
|
|
}
|
|
}
|
|
|
|
MPR_LOG1(RESTORE,"DoProfileErrorDialog with "
|
|
"%d connections remaining\n",
|
|
numStillMustContinue);
|
|
|
|
//
|
|
// We need to bump up the refcount for mprui.dll
|
|
// here since we're in a separate thread and
|
|
// WNetRestoreConnectionsW could return with this
|
|
// UI still up. If that happens and the process
|
|
// calls WNetClearConnections, we'll AV as soon as
|
|
// a message is sent to the UI window since
|
|
// WNetClearConnections unloads mprui.dll (this can
|
|
// happen in winlogon.exe). Note that this function
|
|
// will load mprui.dll globally if it hasn't already
|
|
// been done so after the first LoadLibrary call here,
|
|
// we're merely playing around with the DLL refcount
|
|
// rather than loading/unloading DLLs every time.
|
|
//
|
|
HINSTANCE hDll = LoadLibraryEx(L"mprui.dll",
|
|
NULL,
|
|
LOAD_WITH_ALTERED_SEARCH_PATH);
|
|
|
|
if (hDll == NULL)
|
|
{
|
|
MPR_LOG1(RESTORE,
|
|
"WNetRestoreConnectionW: loading mprui.dll failed %d\n",
|
|
GetLastError());
|
|
|
|
fDisconnect = FALSE;
|
|
}
|
|
else
|
|
{
|
|
DoProfileErrorDialog(
|
|
Params->hDlg == INVALID_WINDOW_HANDLE ?
|
|
NULL : Params->hDlg,
|
|
ConnectArray[i].NetResource.lpLocalName,
|
|
ConnectArray[i].NetResource.lpRemoteName,
|
|
ConnectArray[i].NetResource.lpProvider,
|
|
status,
|
|
(Params->hDlg != INVALID_WINDOW_HANDLE) &&
|
|
(numStillMustContinue > 1),
|
|
&UserCancelled,
|
|
&fDisconnect,
|
|
&fHideErrors);
|
|
|
|
FreeLibrary(hDll);
|
|
}
|
|
}
|
|
|
|
if (fDisconnect)
|
|
{
|
|
if (ConnectArray[i].NetResource.lpLocalName)
|
|
{
|
|
status = ConnectArray[i].pfCancelConnection(
|
|
ConnectArray[i].NetResource.lpLocalName,
|
|
TRUE);
|
|
}
|
|
else
|
|
{
|
|
status =
|
|
MprForgetPrintConnection(
|
|
ConnectArray[i].NetResource.lpRemoteName) ;
|
|
}
|
|
}
|
|
|
|
ConnectArray[i].ContinueFlag = FALSE;
|
|
break;
|
|
} // end switch(status)
|
|
|
|
ContinueFlag |= ConnectArray[i].ContinueFlag;
|
|
|
|
//
|
|
// If the User cancelled all further connection restoration
|
|
// work, then leave this loop.
|
|
//
|
|
if (UserCancelled)
|
|
{
|
|
status = WN_CANCEL;
|
|
ContinueFlag = FALSE;
|
|
break;
|
|
}
|
|
|
|
} // end For each connection.
|
|
|
|
if (ContinueFlag)
|
|
{
|
|
//
|
|
// Determine what the elapsed time from the start is.
|
|
//
|
|
CurrentTime = GetTickCount();
|
|
ElapsedTime = CurrentTime - StartTime;
|
|
|
|
//
|
|
// If a timeout occured, then don't continue. Otherwise, sleep for
|
|
// a bit and loop again through all connections.
|
|
//
|
|
if (ElapsedTime > MaxWait)
|
|
{
|
|
MPR_LOG0(RESTORE,"WNetRestoreConnectionW: Timed out while restoring\n");
|
|
ContinueFlag = FALSE;
|
|
status = WN_SUCCESS;
|
|
}
|
|
else
|
|
{
|
|
Sleep(dwSleepTime);
|
|
|
|
//
|
|
// increase sleeptime as we loop, but cap at 4 times
|
|
// the increment (currently that is 4 * 3 secs = 12 secs)
|
|
//
|
|
if (dwSleepTime < (RECONNECT_SLEEP_INCREMENT * 4))
|
|
{
|
|
dwSleepTime += RECONNECT_SLEEP_INCREMENT ;
|
|
}
|
|
}
|
|
}
|
|
|
|
} while (ContinueFlag);
|
|
break;
|
|
|
|
default:
|
|
status = GetLastError();
|
|
}
|
|
|
|
Params->status = status;
|
|
if(Params->hDlg != INVALID_WINDOW_HANDLE)
|
|
{
|
|
(*_static_pfPostMsg) (Params->hDlg, WM_QUIT, 0, 0);
|
|
}
|
|
|
|
|
|
CleanExit:
|
|
|
|
//
|
|
// Is this thread supposed to clean up?
|
|
//
|
|
if (InterlockedExchange(&Params->fDoCleanup, TRUE))
|
|
{
|
|
MPR_LOG0(RESTORE, "Worker thread will perform cleanup\n");
|
|
|
|
ASSERT(Params != NULL);
|
|
|
|
//
|
|
// Free up resources in preparation to return.
|
|
//
|
|
|
|
//
|
|
// If LUID device maps are enabled and we restored drive letter
|
|
// connections, then notify the shell about each restored drive
|
|
// letter connection.
|
|
//
|
|
if( (g_LUIDDeviceMapsEnabled == TRUE) &&
|
|
(RestoredDrivesMask != 0) )
|
|
{
|
|
CurrDriveMask = 1;
|
|
DriveLetterName[1] = L':';
|
|
DriveLetterName[2] = UNICODE_NULL;
|
|
|
|
for( DriveLetterName[0] = L'A';
|
|
DriveLetterName[0] <= L'Z';
|
|
DriveLetterName[0]++, CurrDriveMask <<= 1 )
|
|
{
|
|
if( CurrDriveMask & RestoredDrivesMask )
|
|
{
|
|
// Use the LUID broadcast mechanism
|
|
MprBroadcastDriveChange(
|
|
DriveLetterName,
|
|
FALSE ); // not a Delete Message
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!CloseHandle(Params->hDlgCreated))
|
|
status = GetLastError();
|
|
|
|
if (!CloseHandle(Params->hDlgFailed))
|
|
status = GetLastError();
|
|
|
|
if (!CloseHandle(Params->hDonePassword))
|
|
status = GetLastError();
|
|
|
|
MprFreeConnectionArray(Params->ConnectArray, numSubKeys);
|
|
|
|
LocalFree(Params);
|
|
}
|
|
|
|
if (hDll != NULL)
|
|
{
|
|
//
|
|
// We have an HINSTANCE, so we're running in a
|
|
// separate thread
|
|
//
|
|
|
|
FreeLibraryAndExitThread(hDll, 0);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
MprUserNameMatch (
|
|
IN PUNICODE_STRING DomainName,
|
|
IN PUNICODE_STRING UserName,
|
|
IN LPCWSTR RememberedName,
|
|
IN BOOL fMustMatchCompletely
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function tests whether the user name for a remembered connection
|
|
matches the default user name.
|
|
|
|
Arguments:
|
|
|
|
DomainName - default logon domain
|
|
|
|
UserName - default logon user
|
|
|
|
RememberedName - user name remembered for the connection
|
|
|
|
Return Value:
|
|
|
|
TRUE if the name matches, FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
if (IS_EMPTY_STRING(RememberedName))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
if (DomainName == NULL || UserName == NULL)
|
|
{
|
|
// This can happen if the LsaGetUserName call fails
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If the remembered name is in the form "domain\user", we must compare
|
|
// against the full user name; otherwise, it's sufficient to compare
|
|
// against the unqualified user name
|
|
//
|
|
WCHAR * pSlash = wcschr(RememberedName, L'\\');
|
|
if (pSlash)
|
|
{
|
|
// Compare user name portion
|
|
UNICODE_STRING RememberedUserName;
|
|
RtlInitUnicodeString(&RememberedUserName, pSlash+1);
|
|
if (! RtlEqualUnicodeString(&RememberedUserName, UserName, TRUE))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Compare domain name portion
|
|
*pSlash = L'\0';
|
|
UNICODE_STRING RememberedDomainName;
|
|
RtlInitUnicodeString(&RememberedDomainName, RememberedName);
|
|
BOOL fMatch = RtlEqualUnicodeString(&RememberedDomainName, DomainName, TRUE);
|
|
*pSlash = L'\\';
|
|
return fMatch;
|
|
}
|
|
else if (fMustMatchCompletely)
|
|
{
|
|
//
|
|
// A complete match is required but there's no domain in RememberedName
|
|
//
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
UNICODE_STRING RememberedUserName;
|
|
RtlInitUnicodeString(&RememberedUserName, RememberedName);
|
|
return (RtlEqualUnicodeString(&RememberedUserName, UserName, TRUE));
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
MprRestoreThisConnection(
|
|
HWND hWnd,
|
|
PARAMETERS *Params,
|
|
LPCONNECTION_INFO ConnectInfo,
|
|
DWORD dwFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function attempts to add a single connection specified in the
|
|
ConnectInfo.
|
|
|
|
Arguments:
|
|
|
|
Params -
|
|
|
|
ConnectInfo - This is a pointer to a connection info structure which
|
|
contains all the information necessary to restore a network
|
|
connection.
|
|
|
|
Return Value:
|
|
|
|
returns whatever the providers AddConnection function returns.
|
|
|
|
--*/
|
|
{
|
|
DWORD status;
|
|
LPTSTR password=NULL;
|
|
HANDLE lpHandle;
|
|
BOOL fDidCancel;
|
|
TCHAR passwordBuffer[PWLEN+1] = {0};
|
|
LPWSTR UserNameForProvider;
|
|
BOOLEAN BufferAllocated = FALSE;
|
|
|
|
|
|
MPR_LOG3(RESTORE,
|
|
"Doing MprRestoreThisConnection for %ws, username = %ws, defer = %lu...\n",
|
|
ConnectInfo->NetResource.lpRemoteName,
|
|
ConnectInfo->UserName,
|
|
ConnectInfo->Defer);
|
|
|
|
//
|
|
// Pass the provider NULL as the user name on the first pass
|
|
// if the original connection used default creds.
|
|
//
|
|
|
|
if ( ConnectInfo->DeferFlags & DEFER_DEFAULT_CRED ) {
|
|
UserNameForProvider = NULL;
|
|
} else {
|
|
UserNameForProvider = ConnectInfo->UserName;
|
|
}
|
|
|
|
//
|
|
// Loop until we either have a successful connection, or
|
|
// until the user stops attempting to give a proper
|
|
// password.
|
|
//
|
|
do {
|
|
|
|
if (Params && Params->status == WN_CANCEL)
|
|
{
|
|
//
|
|
// User cancelled out on reconnections -- return
|
|
// WN_SUCCESS to let DoRestoreConnection finish
|
|
//
|
|
return WN_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Attempt to add the connection.
|
|
// NOTE: The initial password is NULL.
|
|
//
|
|
|
|
#if DBG == 1
|
|
DWORD ConnectTime = GetTickCount();
|
|
#endif
|
|
|
|
//**************************************
|
|
// Actual call to Provider
|
|
//**************************************
|
|
|
|
if (ConnectInfo->Defer)
|
|
{
|
|
ASSERT(ConnectInfo->pfAddConnection3 != NULL);
|
|
|
|
status = ConnectInfo->pfAddConnection3(
|
|
NULL, // hwndOwner
|
|
&(ConnectInfo->NetResource), // lpNetResource
|
|
password, // lpPassword
|
|
UserNameForProvider, // lpUserName
|
|
CONNECT_DEFERRED | (ConnectInfo->ProviderFlags << 24)
|
|
); // dwFlags
|
|
}
|
|
else if (ConnectInfo->pfAddConnection != NULL)
|
|
{
|
|
status = ConnectInfo->pfAddConnection(
|
|
&(ConnectInfo->NetResource), // lpNetResource
|
|
password, // lpPassword
|
|
UserNameForProvider ); // lpUserName
|
|
}
|
|
else
|
|
{
|
|
status = WN_NOT_SUPPORTED;
|
|
}
|
|
|
|
#if DBG == 1
|
|
ConnectTime = GetTickCount() - ConnectTime;
|
|
MPR_LOG2(RESTORE, "...provider took %lu ms to return status %lu\n",
|
|
ConnectTime, status);
|
|
if (ConnectInfo->Defer && ConnectTime > 100)
|
|
{
|
|
DbgPrint("[MPR] ------ %ws took %lu ms to restore a DEFERRED\n"
|
|
" connection to %ws !\n",
|
|
ConnectInfo->NetResource.lpProvider, ConnectTime,
|
|
ConnectInfo->NetResource.lpRemoteName);
|
|
}
|
|
#endif
|
|
|
|
if (status == WN_CONNECTED_OTHER_PASSWORD_DEFAULT)
|
|
{
|
|
//
|
|
// Provider reconnected using default creds
|
|
//
|
|
|
|
status = WN_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// If that fails due to a bad password, then
|
|
// loop until we either have a successful connection, or until
|
|
// the user stops attempting to give a proper password.
|
|
//
|
|
|
|
if (CREDUI_IS_AUTHENTICATION_ERROR(status))
|
|
{
|
|
//
|
|
// The password needs to be cleared each time around the loop,
|
|
// so that on subsequent add connections, we go back to the
|
|
// logon password.
|
|
//
|
|
password = NULL;
|
|
|
|
//
|
|
// If failure was due to bad password, then attempt
|
|
// to get a new password from the user.
|
|
//
|
|
|
|
// Changes made by congpay because of another thread.
|
|
|
|
if (Params == NULL) //lpDevice != NULL, restoring ONE
|
|
{
|
|
if (!(dwFlags & WNRC_NOUI))
|
|
{
|
|
//
|
|
// We need a username buffer to prompt for a username
|
|
//
|
|
|
|
if ( ConnectInfo->UserName == NULL ) {
|
|
ConnectInfo->UserName = (LPTSTR)LocalAlloc(LMEM_FIXED, CRED_MAX_USERNAME_LENGTH * sizeof(WCHAR));
|
|
|
|
if ( ConnectInfo->UserName == NULL ) {
|
|
status = ERROR_NOT_ENOUGH_MEMORY;
|
|
SetLastError (status);
|
|
memset(passwordBuffer, 0, sizeof(passwordBuffer)) ;
|
|
return status;
|
|
} else {
|
|
ConnectInfo->UserName[0] = L'\0';
|
|
BufferAllocated = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Prompt for a username
|
|
//
|
|
|
|
status = DoPasswordDialog(hWnd,
|
|
ConnectInfo->NetResource.lpRemoteName,
|
|
ConnectInfo->UserName,
|
|
passwordBuffer,
|
|
sizeof (passwordBuffer),
|
|
&fDidCancel,
|
|
status);
|
|
|
|
//
|
|
// Convert zero length username to NULL buffer
|
|
//
|
|
|
|
if ( BufferAllocated && status == NO_ERROR ) {
|
|
//
|
|
// If there is no user name (the length is 0), then set the
|
|
// return pointer to NULL.
|
|
//
|
|
if (STRLEN(ConnectInfo->UserName) == 0) {
|
|
LocalFree(ConnectInfo->UserName);
|
|
ConnectInfo->UserName = NULL;
|
|
BufferAllocated = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (status != WN_SUCCESS)
|
|
{
|
|
SetLastError (status);
|
|
memset(passwordBuffer, 0, sizeof(passwordBuffer)) ;
|
|
return status;
|
|
}
|
|
else
|
|
{
|
|
if (fDidCancel)
|
|
{
|
|
status = WN_CANCEL;
|
|
}
|
|
else
|
|
{
|
|
password = passwordBuffer;
|
|
status = WN_ACCESS_DENIED;
|
|
}
|
|
}
|
|
}
|
|
else // restoring all
|
|
{
|
|
if (!(Params->dwRestoreFlags & WNRC_NOUI))
|
|
{
|
|
if (Params->status == WN_CANCEL)
|
|
{
|
|
//
|
|
// User cancelled out of restoring connections. Return
|
|
// WN_SUCCESS to let DoRestoreConnection finish looping
|
|
//
|
|
|
|
continue;
|
|
}
|
|
|
|
if ( _static_pfPostMsg == NULL ) {
|
|
|
|
MprEnterLoadLibCritSect();
|
|
if ( _static_hUser32 = LoadLibraryEx(
|
|
USER32_DLL_NAME,
|
|
NULL,
|
|
LOAD_WITH_ALTERED_SEARCH_PATH ) ) {
|
|
_static_pfPostMsg = (FN_PostMessage *) GetProcAddress( _static_hUser32,
|
|
POST_MSG_API_NAME );
|
|
}
|
|
MprLeaveLoadLibCritSect();
|
|
if ( _static_pfPostMsg == NULL ) {
|
|
memset(passwordBuffer, 0, sizeof(passwordBuffer)) ;
|
|
return GetLastError();
|
|
}
|
|
|
|
}
|
|
lpHandle = Params->hDonePassword;
|
|
|
|
Params->pchResource = ConnectInfo->NetResource.lpRemoteName;
|
|
Params->pchUserName = ConnectInfo->UserName;
|
|
Params->dwError = status;
|
|
|
|
if ((Params->hDlg == INVALID_WINDOW_HANDLE)
|
|
||
|
|
((*_static_pfPostMsg)
|
|
(Params->hDlg,
|
|
DO_PASSWORD_DIALOG,
|
|
(WPARAM) Params,
|
|
0) == 0))
|
|
{
|
|
//
|
|
// Either we're not using a credential dialog or the
|
|
// PostMessage call failed -- bail.
|
|
//
|
|
MPR_LOG3(ERROR,
|
|
"%ws returned %d for connection to %ws -- bailing\n",
|
|
ConnectInfo->NetResource.lpProvider,
|
|
status,
|
|
ConnectInfo->NetResource.lpRemoteName);
|
|
|
|
goto CredentialError;
|
|
}
|
|
|
|
WaitForSingleObject ( lpHandle, INFINITE );
|
|
|
|
if (Params->status == WN_SUCCESS)
|
|
{
|
|
if (Params->fDidCancel)
|
|
{
|
|
status = WN_CANCEL;
|
|
}
|
|
else
|
|
{
|
|
password = Params->passwordBuffer;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
status = Params->status;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Caller wants no UI - don't show any
|
|
status = WN_CANCEL;
|
|
|
|
// Remember that we had a failure to reconnect
|
|
Params->fReconnectFailed = TRUE;
|
|
}
|
|
}
|
|
}
|
|
else if (status == WN_SUCCESS)
|
|
{
|
|
DWORD dwIgnoredStatus;
|
|
|
|
MPR_LOG1(RESTORE,"MprRestoreThisConnection: Successful "
|
|
"restore of connection for %ws\n",
|
|
ConnectInfo->NetResource.lpRemoteName);
|
|
|
|
//
|
|
// Username for the connection might have changed via the
|
|
// password dialog -- update it.
|
|
//
|
|
|
|
dwIgnoredStatus = MprSetRegValue(ConnectInfo->RegKey,
|
|
USER_NAME,
|
|
ConnectInfo->UserName,
|
|
0);
|
|
|
|
if (dwIgnoredStatus != ERROR_SUCCESS)
|
|
{
|
|
MPR_LOG(ERROR,
|
|
"MprSetRegValue for user name failed %lu\n",
|
|
dwIgnoredStatus);
|
|
}
|
|
|
|
//
|
|
// If the DEFER_UNKNOWN flag was set, we can clear it now.
|
|
// If the default password worked, we know that we can defer the
|
|
// connection in future. If we had to prompt for a password, we
|
|
// can't defer the connection in future.
|
|
// Note: This may not do the right thing for share-level
|
|
// connections where the level of access depends on the password.
|
|
// But we'll let users fix that by manually recreating the
|
|
// connection.
|
|
//
|
|
if (ConnectInfo->DeferFlags & DEFER_UNKNOWN)
|
|
{
|
|
if (password == NULL)
|
|
{
|
|
ConnectInfo->DeferFlags &=
|
|
~(DEFER_UNKNOWN | DEFER_EXPLICIT_PASSWORD);
|
|
MPR_LOG1(RESTORE,"MprRestoreThisConnection: WILL defer "
|
|
"connection for %ws in future\n",
|
|
ConnectInfo->NetResource.lpRemoteName);
|
|
}
|
|
else
|
|
{
|
|
ConnectInfo->DeferFlags &= ~DEFER_UNKNOWN;
|
|
ConnectInfo->DeferFlags |= DEFER_EXPLICIT_PASSWORD;
|
|
MPR_LOG1(RESTORE,"MprRestoreThisConnection: will NOT defer "
|
|
"connection for %ws in future\n",
|
|
ConnectInfo->NetResource.lpRemoteName);
|
|
}
|
|
|
|
dwIgnoredStatus = MprSaveDeferFlags(ConnectInfo->RegKey,
|
|
ConnectInfo->DeferFlags);
|
|
|
|
if (dwIgnoredStatus != ERROR_SUCCESS)
|
|
{
|
|
MPR_LOG(ERROR, "MprSaveDeferFlags failed %lu\n", dwIgnoredStatus);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// An unexpected error occured. In this case,
|
|
// we want to leave the loop.
|
|
//
|
|
|
|
MPR_LOG2(ERROR,
|
|
"MprRestoreThisConnection: AddConnection for (%ws) Error %d \n",
|
|
ConnectInfo->NetResource.lpProvider,
|
|
status);
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// On subsequent iterations,
|
|
// pass the provider the user name typed by the caller.
|
|
//
|
|
|
|
UserNameForProvider = ConnectInfo->UserName;
|
|
|
|
}
|
|
while (CREDUI_IS_AUTHENTICATION_ERROR(status));
|
|
|
|
CredentialError:
|
|
|
|
memset(passwordBuffer, 0, sizeof(passwordBuffer)) ;
|
|
return status;
|
|
}
|
|
|
|
|
|
DWORD
|
|
MprCreateConnectionArray(
|
|
LPDWORD lpNumConnections,
|
|
LPCTSTR lpDevice,
|
|
LPDWORD lpRegMaxWait,
|
|
LPCONNECTION_INFO *ConnectArray
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function creates an array of CONNECTION_INFO structures and fills
|
|
each element in the array with the info that is stored in the registry
|
|
for that connection.
|
|
|
|
NOTE: This function allocates memory for the array.
|
|
|
|
Arguments:
|
|
|
|
NumConnections - This is a pointer to the place where the number of
|
|
connections is to be placed. This indicates how many elements
|
|
are stored in the array.
|
|
|
|
lpDevice - If this is NULL, information on all remembered connections
|
|
is required. Otherwise, information for only the lpDevice connection
|
|
is required.
|
|
|
|
lpRegMaxWait - This is a pointer to the location where the wait time
|
|
read from the registry is to be placed. If this value does not
|
|
exist in the registry, then the returned value is 0.
|
|
|
|
ConnectArray - This is a pointer to the location where the pointer to
|
|
the array is to be placed.
|
|
|
|
Return Value:
|
|
|
|
An error status code is returned only if something happens that will not
|
|
allow us to restore even one connection.
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD status = WN_SUCCESS;
|
|
HKEY connectHandle;
|
|
HKEY providerKeyHandle;
|
|
DWORD maxSubKeyLen;
|
|
DWORD maxValueLen;
|
|
DWORD ValueType;
|
|
DWORD Temp;
|
|
DWORD i;
|
|
BOOL AtLeastOneSuccess = FALSE;
|
|
|
|
//
|
|
// init return data
|
|
//
|
|
*lpNumConnections = 0 ;
|
|
*ConnectArray = NULL ;
|
|
|
|
//
|
|
// Get a handle for the connection section of the user's registry
|
|
// space.
|
|
//
|
|
if (!MprOpenKey(
|
|
HKEY_CURRENT_USER,
|
|
CONNECTION_KEY_NAME,
|
|
&connectHandle,
|
|
DA_READ)) {
|
|
|
|
MPR_LOG(ERROR,"WNetRestoreConnection: MprOpenKey Failed\n",0);
|
|
return(WN_CANNOT_OPEN_PROFILE);
|
|
}
|
|
|
|
//
|
|
// Find out the number of connections to restore (numSubKeys) and
|
|
// the max lengths of subkeys and values.
|
|
//
|
|
if(!MprGetKeyInfo(
|
|
connectHandle,
|
|
NULL,
|
|
lpNumConnections,
|
|
&maxSubKeyLen,
|
|
NULL,
|
|
&maxValueLen))
|
|
{
|
|
MPR_LOG(ERROR,"WNetRestoreConnection: MprGetKeyInfo Failed\n",0);
|
|
*lpNumConnections = 0 ;
|
|
RegCloseKey(connectHandle);
|
|
return(WN_CANNOT_OPEN_PROFILE);
|
|
}
|
|
|
|
if (*lpNumConnections == 0) {
|
|
RegCloseKey(connectHandle);
|
|
return(WN_SUCCESS);
|
|
}
|
|
|
|
if (lpDevice != NULL) {
|
|
*lpNumConnections = 1;
|
|
}
|
|
|
|
//
|
|
// Allocate the array.
|
|
//
|
|
*ConnectArray = (LPCONNECTION_INFO)LocalAlloc(
|
|
LPTR,
|
|
*lpNumConnections * sizeof(CONNECTION_INFO));
|
|
|
|
if (*ConnectArray == NULL) {
|
|
*lpNumConnections = 0 ;
|
|
RegCloseKey(connectHandle);
|
|
return(GetLastError());
|
|
}
|
|
|
|
//
|
|
// Level 1 initialization for the call to MprGetProviderIndex in the loop
|
|
//
|
|
if (!(GlobalInitLevel & FIRST_LEVEL)) {
|
|
status = MprLevel1Init();
|
|
if (status != WN_SUCCESS) {
|
|
RegCloseKey(connectHandle);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
for (i=0; i < *lpNumConnections; i++) {
|
|
|
|
//
|
|
// Read a Connection Key and accompanying information from the
|
|
// registry.
|
|
//
|
|
// NOTE: If successful, this function will allocate memory for
|
|
// netResource.lpRemoteName,
|
|
// netResource.lpProvider,
|
|
// netResource.lpLocalName, and optionally....
|
|
// userName
|
|
//
|
|
if (!MprReadConnectionInfo(
|
|
connectHandle,
|
|
lpDevice,
|
|
i,
|
|
&((*ConnectArray)[i].ProviderFlags),
|
|
&((*ConnectArray)[i].DeferFlags),
|
|
&((*ConnectArray)[i].UserName),
|
|
&((*ConnectArray)[i].NetResource),
|
|
&((*ConnectArray)[i].RegKey),
|
|
maxSubKeyLen)) {
|
|
|
|
//
|
|
// The read failed even though this should be a valid index.
|
|
//
|
|
MPR_LOG0(ERROR,
|
|
"MprCreateConnectionArray: ReadConnectionInfo Failed\n");
|
|
status = WN_CANNOT_OPEN_PROFILE;
|
|
}
|
|
else {
|
|
|
|
//
|
|
// Get the Provider Index
|
|
//
|
|
|
|
if (MprGetProviderIndex(
|
|
(*ConnectArray)[i].NetResource.lpProvider,
|
|
&((*ConnectArray)[i].ProviderIndex))) {
|
|
|
|
AtLeastOneSuccess = TRUE;
|
|
(*ConnectArray)[i].ContinueFlag = TRUE;
|
|
|
|
}
|
|
else {
|
|
|
|
//
|
|
// The provider index could not be found. This may mean
|
|
// that the provider information stored in the registry
|
|
// is for a provider that is no longer in the ProviderOrder
|
|
// list. (The provider has been removed). In that case,
|
|
// we will just skip this provider. We will leave the
|
|
// ContinueFlag set to 0 (FALSE).
|
|
//
|
|
MPR_LOG0(ERROR,
|
|
"MprCreateConnectionArray:MprGetProviderIndex Failed\n");
|
|
|
|
status = WN_BAD_PROVIDER;
|
|
(*ConnectArray)[i].Status = status;
|
|
}
|
|
|
|
} // endif (MprReadConnectionInfo)
|
|
|
|
} // endfor (i=0; i<numSubKeys)
|
|
|
|
|
|
if (!AtLeastOneSuccess) {
|
|
//
|
|
// If we gather any connection information, return the last error
|
|
// that occured.
|
|
//
|
|
MprFreeConnectionArray(*ConnectArray,*lpNumConnections);
|
|
*ConnectArray = NULL ;
|
|
*lpNumConnections = 0 ;
|
|
RegCloseKey(connectHandle);
|
|
goto CleanExit;
|
|
}
|
|
|
|
RegCloseKey(connectHandle);
|
|
|
|
//
|
|
// Read the MaxWait value that is stored in the registry.
|
|
// If it is not there or if the value is less than our default
|
|
// maximum value, then use the default instead.
|
|
//
|
|
|
|
if(!MprOpenKey(
|
|
HKEY_LOCAL_MACHINE, // hKey
|
|
NET_PROVIDER_KEY, // lpSubKey
|
|
&providerKeyHandle, // Newly Opened Key Handle
|
|
DA_READ)) { // Desired Access
|
|
|
|
MPR_LOG(ERROR,"MprCreateConnectionArray: MprOpenKey (%ws) Error\n",
|
|
NET_PROVIDER_KEY);
|
|
|
|
*lpRegMaxWait = 0;
|
|
status = WN_SUCCESS;
|
|
goto CleanExit;
|
|
}
|
|
MPR_LOG(TRACE,"OpenKey %ws\n, ",NET_PROVIDER_KEY);
|
|
|
|
Temp = sizeof(*lpRegMaxWait);
|
|
|
|
status = RegQueryValueEx(
|
|
providerKeyHandle,
|
|
RESTORE_WAIT_VALUE,
|
|
NULL,
|
|
&ValueType,
|
|
(LPBYTE)lpRegMaxWait,
|
|
&Temp);
|
|
|
|
RegCloseKey(providerKeyHandle);
|
|
|
|
if (status != NO_ERROR) {
|
|
*lpRegMaxWait = 0;
|
|
}
|
|
|
|
status = WN_SUCCESS;
|
|
|
|
CleanExit:
|
|
|
|
return(WN_SUCCESS);
|
|
|
|
}
|
|
|
|
VOID
|
|
MprFreeConnectionArray(
|
|
LPCONNECTION_INFO ConnectArray,
|
|
DWORD NumConnections
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function frees up all the elements in the connection array, and
|
|
finally frees the array itself.
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
DWORD status = WN_SUCCESS;
|
|
LPNETRESOURCEW netResource;
|
|
DWORD i;
|
|
|
|
for (i=0; i<NumConnections; i++)
|
|
{
|
|
netResource = &(ConnectArray[i].NetResource);
|
|
|
|
//
|
|
// Free the allocated memory resources.
|
|
//
|
|
LocalFree(netResource->lpLocalName);
|
|
LocalFree(netResource->lpRemoteName);
|
|
LocalFree(netResource->lpProvider);
|
|
LocalFree(ConnectArray[i].UserName);
|
|
|
|
if (ConnectArray[i].RegKey != NULL)
|
|
{
|
|
RegCloseKey(ConnectArray[i].RegKey);
|
|
}
|
|
|
|
if (ConnectArray[i].hProviderDll != NULL)
|
|
{
|
|
FreeLibrary(ConnectArray[i].hProviderDll);
|
|
}
|
|
}
|
|
|
|
LocalFree(ConnectArray);
|
|
return;
|
|
}
|
|
|
|
|
|
DWORD
|
|
MprNotifyErrors(
|
|
HWND hWnd,
|
|
LPCONNECTION_INFO ConnectArray,
|
|
DWORD NumConnections,
|
|
DWORD dwFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function calls the error dialog for each connection that still
|
|
has the continue flag set, and does not have a SUCCESS status.
|
|
|
|
Arguments:
|
|
|
|
hWnd - This is a window handle that will be used as owner of the
|
|
error dialog.
|
|
|
|
ConnectArray - This is the array of connection information.
|
|
At the point when this function is called, the following fields
|
|
are meaningful:
|
|
ContinueFlag - If set, it means that this connection has not yet
|
|
been established.
|
|
StatusFlag - If this is not SUCCESS, then it contains the error
|
|
status from the last call to the provider.
|
|
|
|
ContinueFlag Status
|
|
---------------|---------------
|
|
| FALSE | NotSuccess | Provider will not start
|
|
| FALSE | Success | Connection was successfully established
|
|
| TRUE | NotSuccess | Time-out occured
|
|
| TRUE | Success | This can never occur.
|
|
-------------------------------
|
|
|
|
NumConnections - This is the number of entries in the array of
|
|
connection information.
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD i;
|
|
BOOL fDisconnect = FALSE;
|
|
DWORD status = WN_SUCCESS;
|
|
|
|
//
|
|
// If HideErrors becomes TRUE, stop displaying error dialogs
|
|
//
|
|
BOOL fHideErrors = (dwFlags & WNRC_NOUI) ? TRUE : FALSE;
|
|
|
|
for (i=0; (i<NumConnections) && (!fHideErrors); i++ )
|
|
{
|
|
if ((ConnectArray[i].ContinueFlag) &&
|
|
(ConnectArray[i].Status != WN_SUCCESS) &&
|
|
(ConnectArray[i].Status != WN_CANCEL) &&
|
|
(ConnectArray[i].Status != WN_CONTINUE))
|
|
{
|
|
//
|
|
// For any other error, call the Error Dialog
|
|
//
|
|
DoProfileErrorDialog (
|
|
hWnd,
|
|
ConnectArray[i].NetResource.lpLocalName,
|
|
ConnectArray[i].NetResource.lpRemoteName,
|
|
ConnectArray[i].NetResource.lpProvider,
|
|
ConnectArray[i].Status,
|
|
FALSE, //No cancel button.
|
|
NULL,
|
|
&fDisconnect,
|
|
&fHideErrors);
|
|
|
|
if (fDisconnect)
|
|
{
|
|
status = ConnectArray[i].pfCancelConnection(
|
|
ConnectArray[i].NetResource.lpLocalName,
|
|
TRUE);
|
|
}
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
DWORD
|
|
MprAddPrintersToConnArray(
|
|
LPDWORD lpNumConnections,
|
|
LPCONNECTION_INFO *ConnectArray
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function augments the array of CONNECTION_INFO with print connections.
|
|
|
|
NOTE: This function allocates memory for the array if need.
|
|
|
|
Arguments:
|
|
|
|
NumConnections - This is a pointer to the place where the number of
|
|
connections is to be placed. This indicates how many elements
|
|
are stored in the array.
|
|
|
|
ConnectArray - This is a pointer to the location where the pointer to
|
|
the array is to be placed.
|
|
|
|
Return Value:
|
|
|
|
An error status code is returned only if something happens that will not
|
|
allow us to restore even one connection.
|
|
|
|
|
|
--*/
|
|
{
|
|
DWORD status = WN_SUCCESS;
|
|
HKEY connectHandle;
|
|
DWORD i,j;
|
|
DWORD NumValueNames ;
|
|
DWORD MaxValueNameLength;
|
|
DWORD MaxValueLen ;
|
|
LPNETRESOURCE lpNetResource ;
|
|
LPWSTR lpUserName = NULL ;
|
|
LPWSTR lpProviderName = NULL ;
|
|
LPWSTR lpRemoteName = NULL ;
|
|
LPBYTE lpBuffer = NULL ;
|
|
|
|
|
|
//
|
|
// Get a handle for the connection section of the user's registry
|
|
// space.
|
|
//
|
|
if (!MprOpenKey(
|
|
HKEY_CURRENT_USER,
|
|
PRINT_CONNECTION_KEY_NAME,
|
|
&connectHandle,
|
|
DA_READ))
|
|
{
|
|
return(WN_SUCCESS); // ignore the restored connections.
|
|
}
|
|
|
|
//
|
|
// Find out the number of connections to restore and
|
|
// the max lengths of names and values.
|
|
//
|
|
status = MprGetPrintKeyInfo(connectHandle,
|
|
&NumValueNames,
|
|
&MaxValueNameLength,
|
|
&MaxValueLen) ;
|
|
|
|
if (status != WN_SUCCESS || NumValueNames == 0)
|
|
{
|
|
//
|
|
// ignore the restored connections, or nothing to add
|
|
//
|
|
RegCloseKey(connectHandle);
|
|
return(WN_SUCCESS);
|
|
}
|
|
|
|
|
|
//
|
|
// Allocate the array and copy over the info if previous pointer not null.
|
|
//
|
|
lpBuffer = (LPBYTE) LocalAlloc(LPTR,
|
|
(*lpNumConnections + NumValueNames) *
|
|
sizeof(CONNECTION_INFO)) ;
|
|
if (lpBuffer == NULL)
|
|
{
|
|
RegCloseKey(connectHandle);
|
|
return(GetLastError());
|
|
}
|
|
if (*ConnectArray)
|
|
{
|
|
memcpy(lpBuffer,
|
|
*ConnectArray,
|
|
(*lpNumConnections * sizeof(CONNECTION_INFO))) ;
|
|
LocalFree (*ConnectArray) ;
|
|
}
|
|
|
|
|
|
//
|
|
// set j to index from previous location, update the count and pointer.
|
|
// then loop thru all new entries, adding to the connect array.
|
|
//
|
|
j = *lpNumConnections ;
|
|
*lpNumConnections += NumValueNames ;
|
|
*ConnectArray = (CONNECTION_INFO *) lpBuffer ;
|
|
|
|
//
|
|
// Level 1 initialization for the call to MprGetProviderIndex in the loop
|
|
//
|
|
if (!(GlobalInitLevel & FIRST_LEVEL)) {
|
|
status = MprLevel1Init();
|
|
if (status != WN_SUCCESS) {
|
|
RegCloseKey(connectHandle);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
for (i=0; i < NumValueNames; i++, j++)
|
|
{
|
|
|
|
DWORD TypeCode ;
|
|
DWORD cbRemoteName = (MaxValueNameLength + 1) * sizeof (WCHAR) ;
|
|
DWORD cbProviderName = MaxValueLen ;
|
|
|
|
//
|
|
// allocate the strings for the providername, remotename
|
|
//
|
|
if (!(lpProviderName = (LPWSTR) LocalAlloc(0, cbProviderName )))
|
|
{
|
|
status = GetLastError() ;
|
|
goto ErrorExit ;
|
|
}
|
|
if (!(lpRemoteName = (LPWSTR) LocalAlloc(0, cbRemoteName )))
|
|
{
|
|
status = GetLastError() ;
|
|
goto ErrorExit ;
|
|
}
|
|
|
|
//
|
|
// Init the rest. Username currently not set by system, so always NULL
|
|
//
|
|
lpUserName = NULL ;
|
|
lpNetResource = &(*ConnectArray)[j].NetResource ;
|
|
lpNetResource->lpLocalName = NULL ;
|
|
lpNetResource->lpRemoteName = lpRemoteName ;
|
|
lpNetResource->lpProvider = lpProviderName ;
|
|
lpNetResource->dwType = 0 ;
|
|
|
|
//
|
|
// null these so we dont free twice if error exit later
|
|
//
|
|
lpRemoteName = NULL ;
|
|
lpProviderName = NULL ;
|
|
|
|
status = RegEnumValue(connectHandle,
|
|
i,
|
|
lpNetResource->lpRemoteName,
|
|
&cbRemoteName,
|
|
0,
|
|
&TypeCode,
|
|
(LPBYTE) lpNetResource->lpProvider,
|
|
&cbProviderName) ;
|
|
|
|
if (status == NO_ERROR)
|
|
{
|
|
(*ConnectArray)[j].UserName = lpUserName ;
|
|
|
|
//
|
|
// Get the Provider Index
|
|
//
|
|
if (MprGetProviderIndex(
|
|
(*ConnectArray)[j].NetResource.lpProvider,
|
|
&((*ConnectArray)[j].ProviderIndex)))
|
|
{
|
|
(*ConnectArray)[j].ContinueFlag = TRUE;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The provider index could not be found. This may mean
|
|
// that the provider information stored in the registry
|
|
// is for a provider that is no longer in the ProviderOrder
|
|
// list. (The provider has been removed). In that case,
|
|
// we will just skip this provider. We will leave the
|
|
// ContinueFlag set to 0 (FALSE).
|
|
//
|
|
status = WN_BAD_PROVIDER;
|
|
(*ConnectArray)[j].Status = status;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// should not happen, but if it does the array is half built,
|
|
// and cannot be used, so ErrorExit (this will clean it up).
|
|
//
|
|
goto ErrorExit ;
|
|
}
|
|
}
|
|
|
|
RegCloseKey(connectHandle);
|
|
return(WN_SUCCESS);
|
|
|
|
ErrorExit:
|
|
|
|
RegCloseKey(connectHandle);
|
|
|
|
LocalFree(lpProviderName) ;
|
|
LocalFree(lpRemoteName) ;
|
|
|
|
MprFreeConnectionArray(*ConnectArray,*lpNumConnections);
|
|
*ConnectArray = NULL ;
|
|
*lpNumConnections = 0 ;
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
MprNotifyShell(
|
|
IN LPCWSTR pwszDevice
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function sets an event that asks a trusted system component
|
|
(the service controller) to discover the changed network drives and
|
|
asynchronously broadcast a device change message on our behalf.
|
|
|
|
CODEWORK: Replace this entire mechanism with real plug-n-play.
|
|
|
|
Arguments:
|
|
|
|
pwszDevice - Name of the local device (NULL for UNC connections)
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
History:
|
|
|
|
BruceFo 19-May-1995 Created, calls BSM directly
|
|
AnirudhS 06-Jun-1996 Set event to have another component do
|
|
the BSM on our behalf
|
|
|
|
--*/
|
|
{
|
|
// The shell is only interested in drive redirections
|
|
if (pwszDevice == NULL || wcslen(pwszDevice) != 2 || pwszDevice[1] != L':')
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Ask for a device change message to be broadcast
|
|
HANDLE hBSMEvent = OpenEvent(
|
|
EVENT_MODIFY_STATE, // desired access
|
|
FALSE, // don't inherit
|
|
SC_BSM_EVENT_NAME // name
|
|
);
|
|
if (hBSMEvent == NULL)
|
|
{
|
|
MPR_LOG(ERROR, "Couldn't open event for BSM request, %lu\n",
|
|
GetLastError());
|
|
}
|
|
else
|
|
{
|
|
if (! SetEvent(hBSMEvent))
|
|
{
|
|
MPR_LOG(ERROR, "Couldn't set event for BSM request, %lu\n",
|
|
GetLastError());
|
|
}
|
|
|
|
CloseHandle(hBSMEvent);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
MprBroadcastDriveChange(
|
|
IN LPCWSTR pwszDevice,
|
|
IN BOOL DeleteMessage
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This function asynchronously broadcasts a device change message on
|
|
our behalf.
|
|
|
|
Arguments:
|
|
|
|
pwszDevice - Name of the local device (NULL for UNC connections)
|
|
|
|
DeleteMessage - denotes where a delete/add message is needed
|
|
TRUE - send a device deleted message
|
|
FALSE - send a device added message
|
|
|
|
Return Value:
|
|
|
|
TRUE - Operations completed
|
|
FALSE - Error encountered
|
|
--*/
|
|
{
|
|
BOOL Result;
|
|
DWORD dwFlags = DDD_LUID_BROADCAST_DRIVE;
|
|
|
|
// The shell is only interested in drive redirections
|
|
if (pwszDevice == NULL || wcslen(pwszDevice) != 2 || pwszDevice[1] != L':')
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
if( DeleteMessage == TRUE )
|
|
{
|
|
dwFlags |= DDD_REMOVE_DEFINITION;
|
|
}
|
|
|
|
Result = DefineDosDeviceW( dwFlags, pwszDevice, NULL );
|
|
|
|
return( Result );
|
|
}
|